From 4b313e914e9bf9789b022bcb0ed05bd2d86ac408 Mon Sep 17 00:00:00 2001 From: Xavier Delaruelle Date: Sat, 22 Aug 2026 19:26:58 +0200 Subject: [PATCH 1/5] install: add --with-pythondir configure opt Add the --with-pythondir option to the configure installation script to define a particular installation directory for the env_modules Python module. It enables for instance to install env_modules.py with the other Python modules available on the system. If option is not defined, env_modules.py is installed by default in initdir. Whatever the installation directory of the Python module, the python.py symbolic link created in initdir points to its location. When option is set, the PYTHONPATH environment variable defined during initialization (if --enable-set-pythonpath is used) refers to this directory rather initdir. Fixes #643 Assisted-by: Claude:claude-fable-5 Signed-off-by: Xavier Delaruelle --- Makefile | 5 +++++ Makefile.inc.in | 3 +++ configure | 9 ++++++++- init/Makefile | 24 ++++++++++++++++++++++-- site.exp.in | 2 ++ tcl/subcmd.tcl.in | 6 +++--- 6 files changed, 43 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index a00a926ea..7b500e387 100644 --- a/Makefile +++ b/Makefile @@ -247,6 +247,10 @@ ifeq ($(appendpythonpath),y) else setappendpythonpath := prepend endif +# env_modules python module is installed in initdir if no specific location +ifeq ($(pythondir),) + pythondir := $(initdir) +endif ifeq ($(setmanpath),y) setsetmanpath := else @@ -519,6 +523,7 @@ sed -e 's|@prefix@|$(prefix)|g' \ -e 's|@usemanpath@|$(setusemanpath)|g' \ -e 's|@setpythonpath@|$(setsetpythonpath)|g' \ -e 's|@appendpythonpath@|$(setappendpythonpath)|g' \ + -e 's|@pythondir@|$(pythondir)|g' \ -e 's|@notusemanpath@|$(setnotusemanpath)|g' \ -e 's|@shellcompsource@|$(shellcompsource)|g' \ -e 's|@tcllintercmd@|$(tcllintercmd)|g' \ diff --git a/Makefile.inc.in b/Makefile.inc.in index 29ee6019e..4b4e5a6c7 100644 --- a/Makefile.inc.in +++ b/Makefile.inc.in @@ -178,6 +178,9 @@ bashcompletiondir := @bashcompletiondir@ fishcompletiondir := @fishcompletiondir@ zshcompletiondir := @zshcompletiondir@ +# python module location +pythondir := @pythondir@ + # linter setup nagelfaraddons := @nagelfaraddons@ tcllinter := @tcllinter@ diff --git a/configure b/configure index a3f320bc3..3912e2521 100755 --- a/configure +++ b/configure @@ -42,7 +42,7 @@ zshcompletiondir tcllinter tcllinteropts nagelfardatadir nagelfaraddons \ stickypurge uniquenameloaded abortonerror sourcecache logger loggeropts \ loggedevents conflictunload spideroutput spiderterseoutput spiderindepth \ emacsdatadir emacsaddons requirevia compressedchangelog paginate initenvvars \ -setpythonpath appendpythonpath domainname domainnameopts" +setpythonpath appendpythonpath pythondir domainname domainnameopts" libarglist=() # flags to know if argument has been specified on command-line @@ -156,6 +156,7 @@ variantshortcut= bashcompletiondir= fishcompletiondir= zshcompletiondir= +pythondir= tcllinter='nagelfar.tcl' tcllinteropts= nagelfaraddons=y @@ -474,6 +475,9 @@ Optional Packages: a quarantine variable instead of emptying it. [] --with-python=BIN name or full path of Python interpreter command to use as shebang for helper scripts [$PYTHON] + --with-pythondir=DIR installation directory of \`env_modules' Python + module. \`env_modules.py' is installed in the + initialization script directory if not set [] --with-pager=BIN name or full path of default pager program to use to paginate informational message output (can be super- seeded at run-time by environment variable) [$pager] @@ -986,6 +990,9 @@ for arg in "$@"; do libarglist+=("$arg") ;; --with-python=*|--without-python) pythonbin=$(get_package_value "$arg") ;; + --with-pythondir=*|--without-pythondir) + # shellcheck disable=SC2034 + pythondir=$(get_package_value "$arg" "") ;; --with-module-path=*) echo_warning "Option \`--with-module-path' ignored, use \`--modulepath' instead" ;; -h|--help) diff --git a/init/Makefile b/init/Makefile index 880d19829..c50407e32 100644 --- a/init/Makefile +++ b/init/Makefile @@ -92,6 +92,14 @@ else setzshfpathre := /@setzshfpath@/d endif +# define python module location +ifeq ($(pythondir),) + pythondir := $(initdir) + makepythondir := n +else + makepythondir := y +endif + # comment entries if feature not enabled ifeq ($(versioning),y) versdir := $(baseprefix)/versions @@ -265,11 +273,19 @@ ifeq ($(makebashcompdir),y) endif ifeq ($(makefishcompdir),y) $(INSTALL_DIR) '$(DESTDIR)$(fishcompletiondir)' +endif +ifeq ($(makepythondir),y) + $(INSTALL_DIR) '$(DESTDIR)$(pythondir)' endif $(INSTALL_DIR) '$(DESTDIR)$(zshcompletiondir)' $(INSTALL_DIR) '$(DESTDIR)$(modulefilesdir)' - $(INSTALL_DATA) $(ALL_SHELLS) '$(DESTDIR)$(initdir)/' + $(INSTALL_DATA) $(filter-out env_modules.py,$(ALL_SHELLS)) '$(DESTDIR)$(initdir)/' + $(INSTALL_DATA) env_modules.py '$(DESTDIR)$(pythondir)/' +ifeq ($(makepythondir),y) + ln -f -s '$(pythondir)/env_modules.py' '$(DESTDIR)$(initdir)/python.py' +else ln -f -s env_modules.py '$(DESTDIR)$(initdir)/python.py' +endif ifeq ($(windowssupport),y) $(INSTALL_DATA) cmd.cmd '$(DESTDIR)$(initdir)/' endif @@ -300,7 +316,8 @@ ifeq ($(versioning),y) endif uninstall: - rm -f $(foreach initscript,$(ALL_SHELLS) python.py,'$(DESTDIR)$(initdir)/$(initscript)') + rm -f $(foreach initscript,$(filter-out env_modules.py,$(ALL_SHELLS)) python.py,'$(DESTDIR)$(initdir)/$(initscript)') + rm -f '$(DESTDIR)$(pythondir)/env_modules.py' ifeq ($(windowssupport),y) rm -f '$(DESTDIR)$(initdir)/cmd.cmd' endif @@ -327,6 +344,9 @@ ifeq ($(makebashcompdir),y) endif ifeq ($(makefishcompdir),y) $(RMDIR_IGN_NON_EMPTY) '$(DESTDIR)$(fishcompletiondir)' || true +endif +ifeq ($(makepythondir),y) + $(RMDIR_IGN_NON_EMPTY) '$(DESTDIR)$(pythondir)' || true endif $(RMDIR_IGN_NON_EMPTY) '$(DESTDIR)$(zshcompletiondir)' || true $(RMDIR_IGN_NON_EMPTY) '$(DESTDIR)$(initdir)' || true diff --git a/site.exp.in b/site.exp.in index a38bebbc8..8ea0fb989 100644 --- a/site.exp.in +++ b/site.exp.in @@ -135,6 +135,8 @@ set install_bashcompletiondir "@bashcompletiondir@" set install_fishcompletiondir "@fishcompletiondir@" set install_zshcompletiondir "@zshcompletiondir@" +set install_pythondir "@pythondir@" + set install_tcllinter "@tcllinter@" set install_tcllinteropts "@tcllinteropts@" set install_tcllintercmd $install_tcllinter diff --git a/tcl/subcmd.tcl.in b/tcl/subcmd.tcl.in index 0a0836f32..36c733d61 100644 --- a/tcl/subcmd.tcl.in +++ b/tcl/subcmd.tcl.in @@ -2171,9 +2171,9 @@ proc cmdModuleAutoinit {} { @setmanpath@} } - # add Modules init directory to PYTHONPATH if enabled - @setpythonpath@if {{@initdir@} ni [split [get-env PYTHONPATH] :]} { - @setpythonpath@ @appendpythonpath@-path PYTHONPATH {@initdir@} + # add env_modules python module directory to PYTHONPATH if enabled + @setpythonpath@if {{@pythondir@} ni [split [get-env PYTHONPATH] :]} { + @setpythonpath@ @appendpythonpath@-path PYTHONPATH {@pythondir@} @setpythonpath@} # source shell completion script if available, not installed in default From e768be9401925ef50bcb98eab0f2cf1535afd75f Mon Sep 17 00:00:00 2001 From: Xavier Delaruelle Date: Sat, 22 Aug 2026 19:27:13 +0200 Subject: [PATCH 2/5] ts: adapt tests for --with-pythondir Expected PYTHONPATH value now depends on the directory where the env_modules Python module is installed. Also check this Python module and the python.py symbolic link are found at their expected location once installed. Assisted-by: Claude:claude-fable-5 Signed-off-by: Xavier Delaruelle --- testsuite/install.00-init/030-options.exp | 12 +++++++++ testsuite/modules.00-init/005-init_ts.exp | 5 ++++ testsuite/modules.50-cmds/400-source-sh.exp | 26 +++++++++---------- testsuite/modules.70-maint/120-autoinit.exp | 6 ++--- testsuite/modules.70-maint/310-sh-to-mod.exp | 12 ++++----- .../modules.70-maint/311-eval-sh-to-mod.exp | 12 ++++----- 6 files changed, 45 insertions(+), 28 deletions(-) diff --git a/testsuite/install.00-init/030-options.exp b/testsuite/install.00-init/030-options.exp index 68c0c2266..17483960b 100644 --- a/testsuite/install.00-init/030-options.exp +++ b/testsuite/install.00-init/030-options.exp @@ -268,6 +268,18 @@ if {[string length $nafelgar_script]} { } +# test env_modules Python module installation location +if {$verbose} { + send_user "\tChecking env_modules Python module installation\n" +} +if {$install_pythondir ne {}} { + set pythondir $install_pythondir +} else { + set pythondir $install_initdir +} +testall_cmd_re "sh" "test -f '$pythondir/env_modules.py'" "" "" 0 +testall_cmd_re "sh" "test -f '$install_initdir/python.py'" "" "" 0 + # test Python module is found if PYTHONPATH is defined if {$install_setpythonpath eq {y} && [info exists shell_path(python)]} { # create a temporary script that checks import of module command diff --git a/testsuite/modules.00-init/005-init_ts.exp b/testsuite/modules.00-init/005-init_ts.exp index 43b17d716..34dbdacc3 100644 --- a/testsuite/modules.00-init/005-init_ts.exp +++ b/testsuite/modules.00-init/005-init_ts.exp @@ -130,6 +130,11 @@ if {$install_setzshfpath eq {y}} { set install_zshcompletiondir $install_initdir/zsh-functions } +# env_modules python module is installed in initdir if no specific location +if {$install_pythondir eq {}} { + set install_pythondir $install_initdir +} + # locate siteconfig file set siteconfig_file "$install_etcdir/siteconfig.tcl" set siteconfig_filere [regsub -all "\(\[.+?\]\)" $siteconfig_file {\\\1}] diff --git a/testsuite/modules.50-cmds/400-source-sh.exp b/testsuite/modules.50-cmds/400-source-sh.exp index 57e53b78c..918ccd079 100644 --- a/testsuite/modules.50-cmds/400-source-sh.exp +++ b/testsuite/modules.50-cmds/400-source-sh.exp @@ -1627,10 +1627,10 @@ setenv_var MODULES_CMD [file normalize $env(TESTSUITEDIR)/../modulecmd.tcl] # prepare things if PYTHONPATH is configured by autoinit if {[is_conf_enabled setpythonpath]} { unsetenv_var PYTHONPATH - if {[string first { } $install_initdir] != -1} { - set initdirenc "{$install_initdir}" + if {[string first { } $install_pythondir] != -1} { + set pythondirenc "{$install_pythondir}" } else { - set initdirenc $install_initdir + set pythondirenc $install_pythondir } } @@ -1718,19 +1718,19 @@ if {$is_modules_defined} { } if {$install_versioning eq {y}} { if {[is_conf_enabled setpythonpath]} { - lappend ans [list set __MODULES_LMSOURCESH source-sh/1\&bash\ testsuite/example/sh-to-mod.sh\|${lmsourceshpath}setenv\ MODULE_VERSION\ $install_version\|setenv\ MODULE_VERSION_STACK\ $install_version\|setenv\ MODULES_COLLECTION_TARGET\ bar\|setenv\ PYTHONPATH\ $initdirenc\|setenv\ testsuite\ yes$extraans] + lappend ans [list set __MODULES_LMSOURCESH source-sh/1\&bash\ testsuite/example/sh-to-mod.sh\|${lmsourceshpath}setenv\ MODULE_VERSION\ $install_version\|setenv\ MODULE_VERSION_STACK\ $install_version\|setenv\ MODULES_COLLECTION_TARGET\ bar\|setenv\ PYTHONPATH\ $pythondirenc\|setenv\ testsuite\ yes$extraans] } else { lappend ans [list set __MODULES_LMSOURCESH source-sh/1\&bash\ testsuite/example/sh-to-mod.sh\|${lmsourceshpath}setenv\ MODULE_VERSION\ $install_version\|setenv\ MODULE_VERSION_STACK\ $install_version\|setenv\ MODULES_COLLECTION_TARGET\ bar\|setenv\ testsuite\ yes$extraans] } } else { if {[is_conf_enabled setpythonpath]} { - lappend ans [list set __MODULES_LMSOURCESH source-sh/1\&bash\ testsuite/example/sh-to-mod.sh\|${lmsourceshpath}setenv\ MODULES_COLLECTION_TARGET\ bar\|setenv\ PYTHONPATH\ $initdirenc\|setenv\ testsuite\ yes$extraans] + lappend ans [list set __MODULES_LMSOURCESH source-sh/1\&bash\ testsuite/example/sh-to-mod.sh\|${lmsourceshpath}setenv\ MODULES_COLLECTION_TARGET\ bar\|setenv\ PYTHONPATH\ $pythondirenc\|setenv\ testsuite\ yes$extraans] } else { lappend ans [list set __MODULES_LMSOURCESH source-sh/1\&bash\ testsuite/example/sh-to-mod.sh\|${lmsourceshpath}setenv\ MODULES_COLLECTION_TARGET\ bar\|setenv\ testsuite\ yes$extraans] } } if {[is_conf_enabled setpythonpath]} { - lappend ans [list set PYTHONPATH $install_initdir] + lappend ans [list set PYTHONPATH $install_pythondir] } if {$install_versioning eq {y}} { lappend ans [list set MODULE_VERSION $install_version] @@ -1785,7 +1785,7 @@ setenv\t\tMODULE_VERSION_STACK $install_version\n" } append tserr "setenv\t\tMODULES_COLLECTION_TARGET bar" if {[is_conf_enabled setpythonpath]} { - append tserr "\nsetenv\t\tPYTHONPATH $initdirenc" + append tserr "\nsetenv\t\tPYTHONPATH $pythondirenc" } append tserr "\nsetenv\t\ttestsuite yes$extratserr module-whatis\tsource-sh/1 @@ -1828,19 +1828,19 @@ lappend ans [list set LOADEDMODULES source-sh/1] lappend ans [list set MODULES_COLLECTION_TARGET bar] if {$install_versioning eq {y}} { if {[is_conf_enabled setpythonpath]} { - lappend ans [list set __MODULES_LMSOURCESH source-sh/1\&bash\ testsuite/example/sh-to-mod.sh\|${lmsourceshpath}setenv\ MODULE_VERSION\ $install_version\|setenv\ MODULE_VERSION_STACK\ $install_version\|setenv\ MODULES_COLLECTION_TARGET\ bar\|setenv\ PYTHONPATH\ $initdirenc$extraans] + lappend ans [list set __MODULES_LMSOURCESH source-sh/1\&bash\ testsuite/example/sh-to-mod.sh\|${lmsourceshpath}setenv\ MODULE_VERSION\ $install_version\|setenv\ MODULE_VERSION_STACK\ $install_version\|setenv\ MODULES_COLLECTION_TARGET\ bar\|setenv\ PYTHONPATH\ $pythondirenc$extraans] } else { lappend ans [list set __MODULES_LMSOURCESH source-sh/1\&bash\ testsuite/example/sh-to-mod.sh\|${lmsourceshpath}setenv\ MODULE_VERSION\ $install_version\|setenv\ MODULE_VERSION_STACK\ $install_version\|setenv\ MODULES_COLLECTION_TARGET\ bar$extraans] } } else { if {[is_conf_enabled setpythonpath]} { - lappend ans [list set __MODULES_LMSOURCESH source-sh/1\&bash\ testsuite/example/sh-to-mod.sh\|${lmsourceshpath}setenv\ MODULES_COLLECTION_TARGET\ bar\|setenv\ PYTHONPATH\ $initdirenc$extraans] + lappend ans [list set __MODULES_LMSOURCESH source-sh/1\&bash\ testsuite/example/sh-to-mod.sh\|${lmsourceshpath}setenv\ MODULES_COLLECTION_TARGET\ bar\|setenv\ PYTHONPATH\ $pythondirenc$extraans] } else { lappend ans [list set __MODULES_LMSOURCESH source-sh/1\&bash\ testsuite/example/sh-to-mod.sh\|${lmsourceshpath}setenv\ MODULES_COLLECTION_TARGET\ bar$extraans] } } if {[is_conf_enabled setpythonpath]} { - lappend ans [list set PYTHONPATH $install_initdir] + lappend ans [list set PYTHONPATH $install_pythondir] } if {$install_versioning eq {y}} { lappend ans [list set MODULE_VERSION $install_version] @@ -1877,19 +1877,19 @@ lappend ans [list set LOADEDMODULES setenv/1.0:source-sh/1] lappend ans [list set MODULES_COLLECTION_TARGET bar] if {$install_versioning eq {y}} { if {[is_conf_enabled setpythonpath]} { - lappend ans [list set __MODULES_LMSOURCESH source-sh/1\&bash\ testsuite/example/sh-to-mod.sh\|${lmsourceshpath}setenv\ MODULE_VERSION\ $install_version\|setenv\ MODULE_VERSION_STACK\ $install_version\|setenv\ MODULES_COLLECTION_TARGET\ bar\|setenv\ PYTHONPATH\ $initdirenc$extraans] + lappend ans [list set __MODULES_LMSOURCESH source-sh/1\&bash\ testsuite/example/sh-to-mod.sh\|${lmsourceshpath}setenv\ MODULE_VERSION\ $install_version\|setenv\ MODULE_VERSION_STACK\ $install_version\|setenv\ MODULES_COLLECTION_TARGET\ bar\|setenv\ PYTHONPATH\ $pythondirenc$extraans] } else { lappend ans [list set __MODULES_LMSOURCESH source-sh/1\&bash\ testsuite/example/sh-to-mod.sh\|${lmsourceshpath}setenv\ MODULE_VERSION\ $install_version\|setenv\ MODULE_VERSION_STACK\ $install_version\|setenv\ MODULES_COLLECTION_TARGET\ bar$extraans] } } else { if {[is_conf_enabled setpythonpath]} { - lappend ans [list set __MODULES_LMSOURCESH source-sh/1\&bash\ testsuite/example/sh-to-mod.sh\|${lmsourceshpath}setenv\ MODULES_COLLECTION_TARGET\ bar\|setenv\ PYTHONPATH\ $initdirenc$extraans] + lappend ans [list set __MODULES_LMSOURCESH source-sh/1\&bash\ testsuite/example/sh-to-mod.sh\|${lmsourceshpath}setenv\ MODULES_COLLECTION_TARGET\ bar\|setenv\ PYTHONPATH\ $pythondirenc$extraans] } else { lappend ans [list set __MODULES_LMSOURCESH source-sh/1\&bash\ testsuite/example/sh-to-mod.sh\|${lmsourceshpath}setenv\ MODULES_COLLECTION_TARGET\ bar$extraans] } } if {[is_conf_enabled setpythonpath]} { - lappend ans [list set PYTHONPATH $install_initdir] + lappend ans [list set PYTHONPATH $install_pythondir] } if {$install_versioning eq {y}} { lappend ans [list set MODULE_VERSION $install_version] diff --git a/testsuite/modules.70-maint/120-autoinit.exp b/testsuite/modules.70-maint/120-autoinit.exp index 566aea27a..c9bd9045b 100644 --- a/testsuite/modules.70-maint/120-autoinit.exp +++ b/testsuite/modules.70-maint/120-autoinit.exp @@ -905,9 +905,9 @@ setenv_loaded_module [list foo/1.0] [list $modpath.3/foo/1.0] if {[is_conf_enabled setpythonpath]} { setenv_var PYTHONPATH /tmp if {[is_conf_enabled appendpythonpath]} { - set updatedpythonpath /tmp:$install_initdir + set updatedpythonpath /tmp:$install_pythondir } else { - set updatedpythonpath $install_initdir:/tmp + set updatedpythonpath $install_pythondir:/tmp } } @@ -1013,7 +1013,7 @@ testouterr_cmd_re bash autoinit ERR $tserr # ensure PYTHONPATH is already initialized for Modules if {[is_conf_enabled setpythonpath]} { - setenv_var PYTHONPATH $install_initdir + setenv_var PYTHONPATH $install_pythondir } # coherent environment state with loaded module that defines volatile components diff --git a/testsuite/modules.70-maint/310-sh-to-mod.exp b/testsuite/modules.70-maint/310-sh-to-mod.exp index 652dae30f..e2d0942b0 100644 --- a/testsuite/modules.70-maint/310-sh-to-mod.exp +++ b/testsuite/modules.70-maint/310-sh-to-mod.exp @@ -73,10 +73,10 @@ foreach var [array names -glob FOO*] { # prepare things if PYTHONPATH is configured by autoinit if {[is_conf_enabled setpythonpath]} { unsetenv_var PYTHONPATH - if {[string first { } $install_initdir] != -1} { - set initdirenc "{$install_initdir}" + if {[string first { } $install_pythondir] != -1} { + set pythondirenc "{$install_pythondir}" } else { - set initdirenc $install_initdir + set pythondirenc $install_pythondir } } @@ -862,7 +862,7 @@ setenv\t\tMODULE_VERSION_STACK $install_version\n" append tserr $tsvarsetwpath append tserr "setenv\t\tMODULES_COLLECTION_TARGET bar" if {[is_conf_enabled setpythonpath]} { - append tserr "\nsetenv\t\tPYTHONPATH $initdirenc" + append tserr "\nsetenv\t\tPYTHONPATH $pythondirenc" } append tserr "\nsetenv\t\ttestsuite yes" set tserrbash "$tserr$extratserr" @@ -890,7 +890,7 @@ setenv\t\tMODULE_VERSION_STACK $install_version\n" append tserr $tsvarsetwpath append tserr "setenv\t\tMODULES_COLLECTION_TARGET bar" if {[is_conf_enabled setpythonpath]} { - append tserr "\nsetenv\t\tPYTHONPATH $initdirenc" + append tserr "\nsetenv\t\tPYTHONPATH $pythondirenc" } append tserr $extratserr testouterr_shtomod bash {} OK $tserr @@ -906,7 +906,7 @@ setenv\t\tMODULE_VERSION_STACK $install_version\n" append tserr $tsvarsetwpath append tserr "setenv\t\tMODULES_COLLECTION_TARGET bar" if {[is_conf_enabled setpythonpath]} { - append tserr "\nsetenv\t\tPYTHONPATH $initdirenc" + append tserr "\nsetenv\t\tPYTHONPATH $pythondirenc" } append tserr $extratserr testouterr_shtomod bash {} OK $tserr diff --git a/testsuite/modules.70-maint/311-eval-sh-to-mod.exp b/testsuite/modules.70-maint/311-eval-sh-to-mod.exp index 43a10a412..931306349 100644 --- a/testsuite/modules.70-maint/311-eval-sh-to-mod.exp +++ b/testsuite/modules.70-maint/311-eval-sh-to-mod.exp @@ -72,10 +72,10 @@ foreach var [array names -glob FOO*] { # prepare things if PYTHONPATH is configured by autoinit if {[is_conf_enabled setpythonpath]} { unsetenv_var PYTHONPATH - if {[string first { } $install_initdir] != -1} { - set initdirenc "{$install_initdir}" + if {[string first { } $install_pythondir] != -1} { + set pythondirenc "{$install_pythondir}" } else { - set initdirenc $install_initdir + set pythondirenc $install_pythondir } } @@ -533,7 +533,7 @@ setenv\t\tMODULE_VERSION_STACK $install_version\n" append tserr $tsvarsetwpath append tserr "setenv\t\tMODULES_COLLECTION_TARGET bar" if {[is_conf_enabled setpythonpath]} { - append tserr "\nsetenv\t\tPYTHONPATH $initdirenc" + append tserr "\nsetenv\t\tPYTHONPATH $pythondirenc" } append tserr "\nsetenv\t\ttestsuite yes" set tserrbash "$tserr$extratserr" @@ -555,7 +555,7 @@ setenv\t\tMODULE_VERSION_STACK $install_version\n" append tserr $tsvarsetwpath append tserr "setenv\t\tMODULES_COLLECTION_TARGET bar" if {[is_conf_enabled setpythonpath]} { - append tserr "\nsetenv\t\tPYTHONPATH $initdirenc" + append tserr "\nsetenv\t\tPYTHONPATH $pythondirenc" } append tserr $extratserr testouterr_shtomod bash-eval {} OK $tserr @@ -571,7 +571,7 @@ setenv\t\tMODULE_VERSION_STACK $install_version\n" append tserr $tsvarsetwpath append tserr "setenv\t\tMODULES_COLLECTION_TARGET bar" if {[is_conf_enabled setpythonpath]} { - append tserr "\nsetenv\t\tPYTHONPATH $initdirenc" + append tserr "\nsetenv\t\tPYTHONPATH $pythondirenc" } append tserr $extratserr testouterr_shtomod bash-eval {} OK $tserr From 0363fb30dd01ec28675ea9b9d88c111b33fac2ce Mon Sep 17 00:00:00 2001 From: Xavier Delaruelle Date: Sat, 22 Aug 2026 19:27:13 +0200 Subject: [PATCH 3/5] doc: desc --with-pythondir in NEWS/INSTALL Also fix the installation option named in module.1 to describe the condition disabling PYTHONPATH setup during initialization (--enable-set-pythonpath, not --enable-append-pythonpath). Assisted-by: Claude:claude-fable-5 Signed-off-by: Xavier Delaruelle --- .hunspell.en.dic | 1 + INSTALL.rst | 29 ++++++++++++++++++++++++----- NEWS.rst | 7 +++++++ doc/source/changes.rst | 6 ++++-- doc/source/module.rst | 9 +++++---- 5 files changed, 41 insertions(+), 11 deletions(-) diff --git a/.hunspell.en.dic b/.hunspell.en.dic index 255762efd..fd5ced893 100644 --- a/.hunspell.en.dic +++ b/.hunspell.en.dic @@ -1594,3 +1594,4 @@ fpelt isdir bizstyle importlib +pythondir diff --git a/INSTALL.rst b/INSTALL.rst index 22a8f3acd..208eec0e4 100644 --- a/INSTALL.rst +++ b/INSTALL.rst @@ -506,8 +506,9 @@ instance :instopt:`--disable-set-manpath<--enable-set-manpath>`): .. instopt:: --enable-append-pythonpath - Append rather prepend init directory to the PYTHONPATH environment variable - when the :instopt:`--enable-set-pythonpath` option is enabled. (default=no) + Append rather prepend the ``env_modules`` Python module directory to the + PYTHONPATH environment variable when the :instopt:`--enable-set-pythonpath` + option is enabled. (default=no) .. only:: html or latex @@ -841,9 +842,9 @@ instance :instopt:`--disable-set-manpath<--enable-set-manpath>`): .. instopt:: --enable-set-pythonpath - Prepend init directory defined by the :instopt:`--initdir` option to the - PYTHONPATH environment variable in the shell initialization scripts. - (default=yes) + Prepend directory where the ``env_modules`` Python module is installed (see + :instopt:`--with-pythondir` option) to the PYTHONPATH environment + variable in the shell initialization scripts. (default=yes) .. only:: html or latex @@ -1454,6 +1455,24 @@ instance :instopt:`--without-modulepath<--with-modulepath>`): .. versionadded:: 4.5 +.. instopt:: --with-pythondir=DIR + + Installation directory of the ``env_modules`` Python module. When this + option is not set, :file:`env_modules.py` is installed in the initialization + script directory. Whatever the installation directory of this Python module, + the :file:`python.py` symbolic link created in the initialization script + directory points to its location. (default=) + + If this option is set to the ``site-packages`` directory of the Python + installation available on the system, the ``env_modules`` module can be + imported without requiring the PYTHONPATH environment variable to be set. + In this case the :instopt:`--disable-set-pythonpath<--enable-set-pythonpath>` + option may be used. + + .. only:: html or latex + + .. versionadded:: 5.7 + .. instopt:: --with-quarantine-vars= Environment variables to put in quarantine when running the module command to diff --git a/NEWS.rst b/NEWS.rst index be45f2637..c19c09357 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -223,6 +223,13 @@ Modules 5.7.0 (not yet released) :mconfig:`modulepath_ignore` configuration option and :option:`--no-modulepath-ignore` command line switch to control whether pattern definitions are applied. (fix issue #562) +* Install: add the :instopt:`--with-pythondir` installation option to + define the directory where to install the ``env_modules`` Python module. + It enables for instance to install :file:`env_modules.py` with the other + Python modules available on the system. When this option is set, + :instopt:`--enable-set-pythonpath` adds this directory to + :envvar:`PYTHONPATH` instead of the Modules ``init`` directory. (fix issue + #643) .. _5.6 release notes: diff --git a/doc/source/changes.rst b/doc/source/changes.rst index af0756122..f4c3e8a14 100644 --- a/doc/source/changes.rst +++ b/doc/source/changes.rst @@ -1029,8 +1029,10 @@ The following module sub-commands appeared on Modules 5. Starting Modules 5.5, definition of :command:`mogui` shell alias or function is added on this sub-command. - Starting Modules 5.7, initialization directory is added to ``PYTHONPATH`` - environment variable unless installation option + Starting Modules 5.7, directory where the ``env_modules`` Python module is + installed (initialization directory by default, which can be changed with + the :instopt:`--with-pythondir` installation option) is added to + ``PYTHONPATH`` environment variable unless installation option :instopt:`--disable-set-pythonpath<--enable-set-pythonpath>` is used. :subcmd:`lint` diff --git a/doc/source/module.rst b/doc/source/module.rst index ef9bc9ba9..e6a563926 100644 --- a/doc/source/module.rst +++ b/doc/source/module.rst @@ -89,10 +89,11 @@ time if :command:`mogui-cmd` command is found in :envvar:`PATH`. changes performed in the GUI is applied onto the shell session that executed :command:`mogui`. -The installation location of the ``env_modules`` Python module may also be -added to the :envvar:`PYTHONPATH` environment variable during initialization, -unless the :instopt:`--enable-append-pythonpath` installation option has been -disabled. +The installation location of the ``env_modules`` Python module (which is +defined with the :instopt:`--with-pythondir` installation option) may also +be added to the :envvar:`PYTHONPATH` environment variable during +initialization, unless the :instopt:`--enable-set-pythonpath` installation +option has been disabled. .. only:: html or latex From 61c0cfa7f25ea4927f6a14aec79f99663a227e85 Mon Sep 17 00:00:00 2001 From: Xavier Delaruelle Date: Sat, 22 Aug 2026 19:27:13 +0200 Subject: [PATCH 4/5] gh: test pythondir install option Assisted-by: Claude:claude-fable-5 Signed-off-by: Xavier Delaruelle --- .github/workflows/linux_tests.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linux_tests.yaml b/.github/workflows/linux_tests.yaml index ba4c0c859..18b89a99b 100644 --- a/.github/workflows/linux_tests.yaml +++ b/.github/workflows/linux_tests.yaml @@ -553,6 +553,7 @@ jobs: --with-bashcompletiondir=/usr/share/bash-completion/completions --with-fishcompletiondir=/usr/share/fish/vendor_completions.d --with-zshcompletiondir=/usr/share/zsh/site-functions + --with-pythondir=/usr/lib/python3.6/site-packages steps: - name: Install system packages run: | @@ -565,7 +566,7 @@ jobs: # need git in PATH for actions/checkout to get git repo instead of github export dnf install -y git yum install -y epel-release - yum install -y tcsh zsh ksh tcl perl python36 gzip ruby cmake R-core make curl perl python3-sphinx git dejagnu man-db rpm-build bzip2 autoconf gcc tcl-devel emacs + yum install -y tcsh zsh ksh tcl perl python36 python36-devel gzip ruby cmake R-core make curl perl python3-sphinx git dejagnu man-db rpm-build bzip2 autoconf gcc tcl-devel emacs yum install -y which xorg-x11-server-utils xorg-x11-server-Xvfb yum install -y fish - name: Fix git safe directory @@ -615,6 +616,7 @@ jobs: --with-bashcompletiondir=/usr/share/bash-completion/completions --with-fishcompletiondir=/usr/share/fish/vendor_completions.d --with-zshcompletiondir=/usr/share/zsh/site-functions + --with-pythondir=/usr/lib/python3.9/site-packages steps: - name: Install system packages run: | @@ -622,7 +624,7 @@ jobs: dnf install -y git dnf install -y dnf-plugins-core epel-release dnf config-manager --set-enabled crb - dnf install -y tcsh zsh ksh fish tcl perl python3 gzip ruby cmake R-core make perl python3-sphinx git dejagnu man-db rpm-build bzip2 autoconf gcc tcl-devel procps-ng emacs + dnf install -y tcsh zsh ksh fish tcl perl python3 python3-devel gzip ruby cmake R-core make perl python3-sphinx git dejagnu man-db rpm-build bzip2 autoconf gcc tcl-devel procps-ng emacs - name: Fix git safe directory run: | # avoid "detected dubious ownership in repository" error From e1f0cc6d8723dad4d5fc061fb2485525f9d3ae63 Mon Sep 17 00:00:00 2001 From: Xavier Delaruelle Date: Sun, 23 Aug 2026 09:05:32 +0200 Subject: [PATCH 5/5] rpm: install env_modules python module in sitelib Use the --with-pythondir installation option to install the env_modules Python module with the other Python modules available on the system (in python3_sitelib directory). As this location is searched by default by the Python installation, PYTHONPATH setup during module command initialization is disabled. python3-devel build requirement provides the python3_sitelib macro and the Python interpreter used to byte-compile the env_modules module. This setup is applied where /usr/bin/python3 is used as Python interpreter (Fedora 22+ and EL8+). Older platforms keep installing env_modules.py in initdir. Assisted-by: Claude:claude-fable-5 Signed-off-by: Xavier Delaruelle --- share/rpm/environment-modules.spec.in | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/share/rpm/environment-modules.spec.in b/share/rpm/environment-modules.spec.in index 376f80e9c..5ea0b2d75 100644 --- a/share/rpm/environment-modules.spec.in +++ b/share/rpm/environment-modules.spec.in @@ -19,6 +19,11 @@ BuildRequires: dejagnu BuildRequires: make BuildRequires: sed BuildRequires: less +# python3_sitelib macro and byte-compilation of env_modules.py installed +# in this directory +%if 0%{?fedora} >= 22 || 0%{?rhel} >= 8 +BuildRequires: python3-devel +%endif %if 0%{?rhel} && 0%{?rhel} <= 8 BuildRequires: util-linux %else @@ -120,6 +125,10 @@ have access to the module alias. --disable-doc-install \ --enable-modulespath \ --with-python=%{pythonbin} \ +%if 0%{?fedora} >= 22 || 0%{?rhel} >= 8 + --with-pythondir=%{python3_sitelib} \ + --disable-set-pythonpath \ +%endif --with-modulepath=%{_datadir}/Modules/modulefiles:%{_sysconfdir}/modulefiles:%{_datadir}/modulefiles \ --with-quarantine-vars='LD_LIBRARY_PATH LD_PRELOAD' \ --with-init-envvars='MANPATH=' @@ -237,6 +246,10 @@ fi %{_datadir}/Modules/libexec/modulecmd.tcl %dir %{_datadir}/Modules/init %{_datadir}/Modules/init/* +%if 0%{?fedora} >= 22 || 0%{?rhel} >= 8 +%{python3_sitelib}/env_modules.py +%{python3_sitelib}/__pycache__/env_modules.cpython-*.pyc +%endif # do not need to require shell package as we "own" completion dir %dir %{bash_completions_dir} %{bash_completions_dir}/module