@@ -71,18 +71,8 @@ def _do_import_module_from_file(filename, module_name=None):
7171 return module
7272
7373
74- def import_module_from_file (filename , force = False , parent = None ):
75- '''Import module from file.
76-
77- If the file location refers to a directory, the contained ``__init__.py``
78- will be loaded. If the filename resolves to a location that is within the
79- current working directory, a module name will be derived from the supplied
80- file name and Python's :func:`importlib.import_module` will be invoked to
81- actually load the module. If the file location refers to a path outside
82- the current working directory, then the module will be loaded directly
83- from the file, but it will be assigned a mangled name in
84- :obj:`sys.modules`, to avoid clashes with other modules loaded using the
85- standard import mechanism.
74+ def _import_module_from_file (filename , force , parent ):
75+ '''Low-level non-recusrive import from file
8676
8777 :arg filename: The path to the filename of a Python module.
8878 :arg force: Force reload of module in case it is already loaded.
@@ -91,9 +81,6 @@ def import_module_from_file(filename, force=False, parent=None):
9181 ``parent`` so that Python would be able to resolve relative imports in
9282 the module file.
9383 :returns: The loaded Python module.
94-
95- .. versionchanged:: 4.6
96- The ``parent`` argument is added.
9784 '''
9885
9986 # Expand and sanitize filename
@@ -130,6 +117,73 @@ def import_module_from_file(filename, force=False, parent=None):
130117 return importlib .import_module (module_name )
131118
132119
120+ def import_module_from_file (filename , * , force = False , load_parents = False ):
121+ '''Import module from file.
122+
123+ If the file location refers to a directory, the contained ``__init__.py``
124+ will be loaded. If the filename resolves to a location that is within the
125+ current working directory, a module name will be derived from the supplied
126+ file name and Python's :func:`importlib.import_module` will be invoked to
127+ actually load the module. If the file location refers to a path outside
128+ the current working directory, then the module will be loaded directly
129+ from the file, but it will be assigned a mangled name in
130+ :obj:`sys.modules`, to avoid clashes with other modules loaded using the
131+ standard import mechanism.
132+
133+ If ``load_parents`` is set, any modules along the path will also be
134+ loaded. A path will considered as a parent module and loaded if it
135+ contains an ``__init__.py`` file.
136+
137+ :arg filename: The path to the filename of a Python module.
138+ :arg force: Force reload of module in case it is already loaded. This does
139+ not apply to the parent modules that have been loaded with
140+ ``load_parents=True``.
141+ :arg load_parents: Load parent modules along the path.
142+ :returns: The loaded Python module.
143+
144+ .. versionchanged:: 4.6
145+ The ``parent`` argument is added.
146+
147+ .. versionchanged:: 4.9
148+ The ``parent`` argumet is replaced by ``load_parents``. Also, all
149+ arguments except ``filename`` are now keyword-only arguments.
150+
151+ If the old interface is desired, you should use directly the
152+ lower-level :func:`_import_module_from_file` function.
153+ '''
154+
155+ import reframe .utility .osext as osext
156+
157+
158+ if not load_parents :
159+ return _import_module_from_file (filename , force , None )
160+
161+ dirname = os .path .dirname (filename )
162+
163+ # Load all parent modules of test file
164+ parents = []
165+ while os .path .exists (os .path .join (dirname , '__init__.py' )):
166+ parents .append (os .path .join (dirname ))
167+ dirname = os .path .split (dirname )[0 ]
168+
169+ parent_module = None
170+ for pdir in reversed (parents ):
171+ with osext .change_dir (pdir ):
172+ with temp_sys_path (pdir ):
173+ package_path = os .path .join (pdir , '__init__.py' )
174+ parent_module = _import_module_from_file (
175+ package_path , force , parent = parent_module
176+ ).__name__
177+
178+ # Now load the actual test file
179+ if not parents :
180+ pdir = dirname
181+
182+ with osext .change_dir (pdir ):
183+ with temp_sys_path (pdir ):
184+ return _import_module_from_file (filename , force , parent_module )
185+
186+
133187def import_module (module_name , force = False ):
134188 '''Import a module.
135189
@@ -162,7 +216,7 @@ def import_module(module_name, force=False):
162216 else :
163217 path += '.py'
164218
165- return import_module_from_file (path , force )
219+ return _import_module_from_file (path , force , None )
166220
167221
168222def import_from_module (module_name , symbol ):
0 commit comments