@@ -492,6 +492,21 @@ def emit_load_instr(self, module):
492492 def emit_unload_instr (self , module ):
493493 '''Emit the instruction that unloads module.'''
494494
495+ def process (self , source ):
496+ '''Process the Python source emitted by the Python bindings of the
497+ different backends.
498+
499+ Backends should call this before executing any Python commands.
500+
501+ :arg source: The Python source code to be executed.
502+ :returns: The modified Python source code to be executed. By default
503+ ``source`` is returned unchanged.
504+
505+ .. versionadded:: 3.4
506+
507+ '''
508+ return source
509+
495510 def __repr__ (self ):
496511 return type (self ).__name__ + '()'
497512
@@ -562,7 +577,7 @@ def _execute(self, cmd, *args):
562577 completed .stderr ,
563578 completed .returncode )
564579
565- exec (completed .stdout )
580+ exec (self . process ( completed .stdout ) )
566581 return completed .stderr
567582
568583 def available_modules (self , substr ):
@@ -692,7 +707,7 @@ def _execute(self, cmd, *args):
692707 with open (exec_match .group (1 ), 'r' ) as content_file :
693708 cmd = content_file .read ()
694709
695- exec (cmd )
710+ exec (self . process ( cmd ) )
696711 return completed .stderr
697712
698713
@@ -729,6 +744,7 @@ def __init__(self):
729744 (version , self .MIN_VERSION ))
730745
731746 self ._version = version
747+ self ._extra_module_paths = []
732748
733749 def name (self ):
734750 return 'tmod4'
@@ -740,7 +756,7 @@ def _execute(self, cmd, *args):
740756 modulecmd = self .modulecmd (cmd , * args )
741757 completed = osext .run_command (modulecmd , check = False )
742758 namespace = {}
743- exec (completed .stdout , {}, namespace )
759+ exec (self . process ( completed .stdout ) , {}, namespace )
744760
745761 # _mlstatus is set by the TMod4 only if the command was unsuccessful,
746762 # but Lmod sets it always
@@ -755,6 +771,15 @@ def _execute(self, cmd, *args):
755771 def load_module (self , module ):
756772 if module .collection :
757773 self .execute ('restore' , str (module ))
774+
775+ # Here the module search path removal/addition is repeated since
776+ # 'restore' discards previous module path manipulations
777+ for op , mp in self ._extra_module_paths :
778+ if op == '+' :
779+ super ().searchpath_add (mp )
780+ else :
781+ super ().searchpath_remove (mp )
782+
758783 return []
759784 else :
760785 return super ().load_module (module )
@@ -777,7 +802,15 @@ def conflicted_modules(self, module):
777802
778803 def emit_load_instr (self , module ):
779804 if module .collection :
780- return f'module restore { module } '
805+ cmds = [f'module restore { module } ' ]
806+
807+ # Here we append module searchpath removal/addition commands
808+ # since 'restore' discards previous module path manipulations
809+ for op , mp in self ._extra_module_paths :
810+ operation = 'use' if op == '+' else 'unuse'
811+ cmds += [f'module { operation } { mp } ' ]
812+
813+ return '\n ' .join (cmds )
781814
782815 return super ().emit_load_instr (module )
783816
@@ -787,6 +820,18 @@ def emit_unload_instr(self, module):
787820
788821 return super ().emit_unload_instr (module )
789822
823+ def searchpath_add (self , * dirs ):
824+ if dirs :
825+ self ._extra_module_paths += [('+' , mp ) for mp in dirs ]
826+
827+ super ().searchpath_add (* dirs )
828+
829+ def searchpath_remove (self , * dirs ):
830+ if dirs :
831+ self ._extra_module_paths += [('-' , mp ) for mp in dirs ]
832+
833+ super ().searchpath_remove (* dirs )
834+
790835
791836class LModImpl (TMod4Impl ):
792837 '''Module system for Lmod (Tcl/Lua).'''
@@ -821,9 +866,22 @@ def __init__(self):
821866 raise ConfigError ('Python is not supported by '
822867 'this Lmod installation' )
823868
869+ self ._extra_module_paths = []
870+
824871 def name (self ):
825872 return 'lmod'
826873
874+ def process (self , source ):
875+ major , minor , * _ = self .version ().split ('.' )
876+ major , minor = int (major ), int (minor )
877+ if (major , minor ) < (8 , 2 ):
878+ # Older Lmod versions do not emit an `import os` and emit an
879+ # invalid `false` statement in case of errors; we fix these here
880+ return 'import os\n \n ' + source .replace ('false' ,
881+ '_mlstatus = False' )
882+
883+ return source
884+
827885 def modulecmd (self , * args ):
828886 return ' ' .join ([self ._lmod_cmd , 'python' , * args ])
829887
@@ -865,20 +923,6 @@ def conflicted_modules(self, module):
865923
866924 return ret
867925
868- def load_module (self , module ):
869- if module .collection :
870- self .execute ('restore' , str (module ))
871- return []
872- else :
873- return super ().load_module (module )
874-
875- def unload_module (self , module ):
876- if module .collection :
877- # Module collection are not unloaded
878- return
879-
880- super ().unload_module (module )
881-
882926 def unload_all (self ):
883927 # Currently, we don't take any provision for sticky modules in Lmod, so
884928 # we forcefully unload everything.
0 commit comments