Skip to content

Commit fc051b9

Browse files
authored
Merge pull request #6597 from grondo/issue#6594
do not export `PYTHONPATH` by default in Flux commands
2 parents e9f7fcd + f770eff commit fc051b9

File tree

10 files changed

+124
-18
lines changed

10 files changed

+124
-18
lines changed

doc/man1/flux.rst

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,6 @@ is provided below:
8080
* - :envvar:`LUA_CPATH`
8181
- Lua binary module search path
8282

83-
* - :envvar:`PYTHONPATH`
84-
- Python module search path:
85-
8683

8784
RESOURCES
8885
=========

doc/man7/flux-environment.rst

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -552,11 +552,16 @@ of compiled-in install paths and the environment.
552552
.. envvar:: PYTHONPATH
553553
FLUX_PYTHONPATH_PREPEND
554554

555-
:envvar:`PYTHONPATH` is set so that sub-commands can find required Python
556-
libraries:
555+
With the :command:`flux python` and :man1:`flux-env` subcommands,
556+
:envvar:`PYTHONPATH` is set such that the correct version of
557+
Python modules can be found. In these cases the path is set
558+
to the following:
557559

558560
$FLUX_PYTHONPATH_PREPEND : install-path : $PYTHONPATH
559561

562+
The :man1:`flux` command does not otherwise modify :envvar:`PYTHONPATH`
563+
unless :envvar:`FLUX_PYTHONPATH_PREPEND` is set.
564+
560565
Values may include multiple directories separated by colons.
561566

562567
.. note::

doc/python/basics.rst

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,10 @@ Flux Python Basics
55
Importing the ``flux`` Python package
66
-------------------------------------
77

8-
.. note:: The ``flux`` package which is used to interact with Flux *cannot* be
9-
installed into a virtual environment with pip or conda.
8+
.. note:: The ``flux`` package may now be installed via pip using the
9+
``flux-python`` package.
1010

1111
Flux's Python bindings are available with any installation of Flux.
12-
When running in a Flux instance, Flux will export the
13-
`PYTHONPATH <https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH>`_
14-
environment variable so that Python processes can import the flux package
15-
by the usual import mechanism (``import flux``).
1612

1713
If you want to import the package from outside of a Flux instance,
1814
running ``/path/to/flux env | grep PYTHONPATH`` in your shell will show you

src/cmd/builtin.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <flux/optparse.h>
1313
#include "src/common/libutil/xzmalloc.h"
1414
#include "src/common/libutil/log.h"
15+
#include "src/common/libutil/environment.h"
1516

1617
#ifndef HAVE_CMD_BUILTIN_H
1718
#define HAVE_CMD_BUILTIN_H 1
@@ -24,6 +25,7 @@ struct builtin_cmd {
2425
};
2526

2627
void usage (optparse_t *p);
28+
void builtin_env_add_pythonpath (struct environment *env);
2729
flux_t *builtin_get_flux_handle (optparse_t *p);
2830

2931
#endif /* !HAVE_CMD_BUILTIN_H */

src/cmd/builtin/env.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,20 @@
1616
#include "builtin.h"
1717
#include "src/common/libutil/environment.h"
1818

19+
static void set_pythonpath (void)
20+
{
21+
struct environment *env;
22+
if (!(env = environment_create ()))
23+
log_err_exit ("error creating environment");
24+
builtin_env_add_pythonpath (env);
25+
environment_apply (env);
26+
environment_destroy (env);
27+
}
28+
1929
static void print_environment (struct environment *env)
2030
{
2131
const char *val;
32+
2233
for (val = environment_first (env); val; val = environment_next (env))
2334
printf("export %s=\"%s\"\n", environment_cursor (env), val);
2435
fflush(stdout);
@@ -28,12 +39,15 @@ static int cmd_env (optparse_t *p, int ac, char *av[])
2839
{
2940
int n = optparse_option_index (p);
3041
if (av && av[n]) {
42+
set_pythonpath ();
3143
execvp (av[n], av+n); /* no return if successful */
3244
log_err_exit ("execvp (%s)", av[n]);
3345
} else {
3446
struct environment *env = optparse_get_data (p, "env");
47+
3548
if (env == NULL)
3649
log_msg_exit ("flux-env: failed to get flux environment!");
50+
builtin_env_add_pythonpath (env);
3751
print_environment (env);
3852
}
3953
return (0);

src/cmd/builtin/python.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,19 @@
1414
#include <unistd.h>
1515

1616
#include "builtin.h"
17-
#include "config.h"
1817
#include "src/common/libutil/environment.h"
1918

19+
static void prepare_environment (void)
20+
{
21+
struct environment *env;
22+
23+
if (!(env = environment_create ()))
24+
log_err_exit ("error creating environment");
25+
builtin_env_add_pythonpath (env);
26+
environment_apply (env);
27+
environment_destroy (env);
28+
}
29+
2030
static int cmd_python (optparse_t *p, int ac, char *av[])
2131
{
2232
/*
@@ -26,6 +36,9 @@ static int cmd_python (optparse_t *p, int ac, char *av[])
2636
* that symlink'd binaries in virtualenvs are respected.
2737
*/
2838
av[0] = PYTHON_INTERPRETER;
39+
40+
prepare_environment ();
41+
2942
execv (PYTHON_INTERPRETER, av); /* no return if successful */
3043
log_err_exit ("execvp (%s)", PYTHON_INTERPRETER);
3144
return (0);

src/cmd/flux.c

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -184,12 +184,10 @@ int main (int argc, char *argv[])
184184
flux_conf_builtin_get ("lua_path_add", flags));
185185
environment_push (env, "LUA_PATH", getenv ("FLUX_LUA_PATH_PREPEND"));
186186

187-
environment_from_env (env, "PYTHONPATH", "", ':');
188-
environment_push (env,
189-
"PYTHONPATH",
190-
flux_conf_builtin_get ("python_path", flags));
191-
environment_push (env, "PYTHONPATH", getenv ("FLUX_PYTHONPATH_PREPEND"));
192-
187+
if ((s = getenv ("FLUX_PYTHONPATH_PREPEND"))) {
188+
environment_from_env (env, "PYTHONPATH", "", ':');
189+
environment_push (env, "PYTHONPATH", s);
190+
}
193191
if ((s = getenv ("MANPATH")) && strlen (s) > 0) {
194192
environment_from_env (env, "MANPATH", ":", ':');
195193
environment_push (env,
@@ -333,6 +331,37 @@ void setup_path (struct environment *env, const char *argv0)
333331
log_msg_exit ("Unable to determine flux executable dir");
334332
}
335333

334+
void builtin_env_add_pythonpath (struct environment *env)
335+
{
336+
/* prepend to PYTHONPATH, which is no longer done by default:
337+
*/
338+
environment_from_env (env, "PYTHONPATH", "", ':');
339+
environment_push (env,
340+
"PYTHONPATH",
341+
flux_conf_builtin_get ("python_path", FLUX_CONF_AUTO));
342+
environment_push (env, "PYTHONPATH", getenv ("FLUX_PYTHONPATH_PREPEND"));
343+
}
344+
345+
static void setup_python_wrapper_environment (void)
346+
{
347+
struct environment *env;
348+
const char *val;
349+
350+
/* Set FLUX_PYTHONPATH_ORIG to the current PYTHONPATH, then
351+
* prepend the builtin python_path to PYTHONPATH so the the
352+
* python wrapper py-runner.py can find the correct Flux
353+
* bindings. The wrapper will then reset PYTHONPATH to
354+
* FLUX_PYTHONPATH_ORIG to avoid polluting the user environment.
355+
*/
356+
if (!(env = environment_create ()))
357+
log_err_exit ("error creating environment");
358+
if ((val = getenv ("PYTHONPATH")))
359+
environment_set (env, "FLUX_PYTHONPATH_ORIG", val, ':');
360+
builtin_env_add_pythonpath (env);
361+
environment_apply (env);
362+
environment_destroy (env);
363+
}
364+
336365
/* Check for a flux-<command>.py in dir and execute it under the configured
337366
* PYTHON_INTERPRETER if found.
338367
*/
@@ -361,6 +390,9 @@ void exec_subcommand_py (bool vopt,
361390
PYTHON_INTERPRETER,
362391
wrapper,
363392
path);
393+
394+
setup_python_wrapper_environment ();
395+
364396
execvp (PYTHON_INTERPRETER, av);
365397
}
366398
free (path);

src/cmd/py-runner.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,27 @@ def prepend_standard_python_paths(patharray):
8484
sys.path[0:0] = prepend_paths
8585

8686

87+
def restore_pythonpath():
88+
"""
89+
Restore PYTHONPATH from the original caller's environment using
90+
FLUX_PYTHONPATH_ORIG as set by flux(1)
91+
"""
92+
try:
93+
val = os.environ["FLUX_PYTHONPATH_ORIG"]
94+
os.environ["PYTHONPATH"] = val
95+
except KeyError:
96+
# If FLUX_PYTHONPATH_ORIG not set, then unset PYTHONPATH:
97+
del os.environ["PYTHONPATH"]
98+
99+
87100
if __name__ == "__main__":
88101
# Pop first argument which is this script, modify sys.path as noted
89102
# above, then invoke target script in this interpreter using
90103
# runpy.run_path():
91104
#
92105
sys.argv.pop(0)
93106
prepend_standard_python_paths(sys.path)
107+
restore_pythonpath()
94108
runpy.run_path(sys.argv[0], run_name="__main__")
95109

96110

t/sharness.d/01-setup.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,15 @@ if ! test -x ${fluxbin}; then
7474
return 1
7575
fi
7676

77+
# flux(1) prepends the correct path(s) to PYTHONPATH when invoking
78+
# Python subcommands or when `flux env` and `flux python` are invoked.
79+
# However, this is not always the case for Python based test scripts
80+
# run under sharness, so these test scripts can end up loading system
81+
# installed Flux modules and fail in unpredictable ways. Therefore,
82+
# export the correct PYTHONPATH here:
83+
#
84+
PYTHONPATH="$($FLUX_BUILD_DIR/src/cmd/flux env printenv PYTHONPATH)"
85+
7786
# Python's site module won't be able to determine the correct path
7887
# for site.USER_SITE because sharness reassigns HOME to a per-test
7988
# trash directory. Set up a REAL_HOME here from the passwd database

t/t1102-cmddriver.t

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,30 @@ test_expect_success 'flux env passes cmddriver option to argument' "
5757
(FLUX_URI=foo://xyx \
5858
flux env sh -c 'echo \$FLUX_URI' | grep '^foo://xyx$')
5959
"
60+
test_expect_success 'flux env prepends to PYTHONPATH' '
61+
expected=$(flux config builtin python_path | sed "s/:.*//") &&
62+
result=$(PYTHONPATH= flux env sh -c "echo \$PYTHONPATH") &&
63+
test_debug "echo expecting PYTHONPATH=$expected got $result" &&
64+
echo "$result" | grep $expected
65+
'
66+
test_expect_success 'flux env outputs PYTHONPATH' '
67+
expected=$(flux config builtin python_path | sed "s/:.*//") &&
68+
result=$(PYTHONPATH= flux env | grep PYTHONPATH) &&
69+
test_debug "echo expecting PYTHONPATH=$expected got $result" &&
70+
echo "$result" | grep $expected
71+
'
72+
test_expect_success 'flux python prepends to PYTHONPATH' '
73+
expected=$(flux config builtin python_path | sed "s/:.*//") &&
74+
test_debug "echo expecting PYTHONPATH=$expected" &&
75+
PYTHONPATH= \
76+
flux python -c "import os; print(os.environ[\"PYTHONPATH\"])" \
77+
| grep $expected
78+
'
79+
test_expect_success 'flux does not prepend to PYTHONPATH' '
80+
printenv=$(which printenv) &&
81+
( unset PYTHONPATH &&
82+
test_must_fail flux $printenv PYTHONPATH)
83+
'
6084
# push /foo twice onto PYTHONPATH -- ensure it is leftmost position:
6185
#test_expect_success 'cmddriver pushes dup path elements onto front of PATH' "
6286
# flux -P /foo env flux -P /bar env flux -P /foo env \

0 commit comments

Comments
 (0)