Skip to content

Commit

Permalink
Merge pull request #2390 from rsarm/bugfix/import-with-hash
Browse files Browse the repository at this point in the history
[bugfix] Avoid conflicts with already loaded Python modules when loading ReFrame tests
  • Loading branch information
Vasileios Karakasis authored Jan 22, 2022
2 parents f02136b + 1c1c773 commit bc280c5
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 19 deletions.
8 changes: 7 additions & 1 deletion reframe/utility/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import reframe

from collections import UserDict
from hashlib import sha256
from . import typecheck as typ


Expand Down Expand Up @@ -87,7 +88,12 @@ def import_module_from_file(filename, force=False):
module_name = _get_module_name(rel_filename)
if rel_filename.startswith('..'):
# We cannot use the standard Python import mechanism here, because the
# module to import is outside the top-level package
# module to import is outside the top-level package. We also mangle
# the name that we assign to the module, in order to avoid clashes
# with other modules loaded with a standard `import` or with multiple
# test files with the same name that reside in different directories.
module_hash = sha256(filename.encode('utf-8')).hexdigest()[:8]
module_name = f'{module_name}@{module_hash}'
return _do_import_module_from_file(filename, module_name)

# Extract module name if `filename` is under `site-packages/` or the
Expand Down
29 changes: 29 additions & 0 deletions unittests/test_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import os
import pytest
import shutil

import reframe as rfm
from reframe.core.exceptions import ReframeSyntaxError
Expand All @@ -24,6 +25,21 @@ def loader_with_path():
)


@pytest.fixture
def loader_with_path_tmpdir(tmp_path):
test_dir_a = tmp_path / 'a'
test_dir_b = tmp_path / 'b'
os.mkdir(test_dir_a)
os.mkdir(test_dir_b)
test_a = 'unittests/resources/checks/emptycheck.py'
test_b = 'unittests/resources/checks/hellocheck.py'
shutil.copyfile(test_a, test_dir_a / 'test.py')
shutil.copyfile(test_b, test_dir_b / 'test.py')
return RegressionCheckLoader(
[test_dir_a.as_posix(), test_dir_b.as_posix()]
)


def test_load_file_relative(loader):
checks = loader.load_from_file('unittests/resources/checks/emptycheck.py')
assert 1 == len(checks)
Expand Down Expand Up @@ -73,6 +89,19 @@ def test_load_fixtures(loader):
assert 5 == len(tests)


def test_existing_module_name(loader, tmp_path):
test_file = tmp_path / 'os.py'
shutil.copyfile('unittests/resources/checks/emptycheck.py', test_file)
checks = loader.load_from_file(test_file)
assert 1 == len(checks)
assert checks[0].name == 'EmptyTest'


def test_same_filename_different_path(loader_with_path_tmpdir):
checks = loader_with_path_tmpdir.load_all()
assert 3 == len(checks)


def test_special_test():
with pytest.raises(ReframeSyntaxError):
@rfm.simple_test
Expand Down
27 changes: 9 additions & 18 deletions unittests/test_utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,13 +479,15 @@ def test_import_from_file_load_abspath():
assert module is sys.modules.get('reframe')


def test_import_from_file_load_unknown_path():
try:
util.import_module_from_file('/foo')
pytest.fail()
except ImportError as e:
assert 'foo' == e.name
assert '/foo' == e.path
def test_import_from_file_existing_module_name(tmp_path):
test_file = tmp_path / 'os.py'
with open(test_file, 'w') as fp:
print('var = 1', file=fp)

module = util.import_module_from_file(test_file)
assert module.var == 1
assert not hasattr(module, 'path')
assert hasattr(os, 'path')


def test_import_from_file_load_directory_relative():
Expand All @@ -510,17 +512,6 @@ def test_import_from_file_load_relative():
assert module is sys.modules.get('reframe.utility.osext')


def test_import_from_file_load_outside_pkg():
module = util.import_module_from_file(os.path.__file__)

# os imports the OS-specific path libraries under the name `path`. Our
# importer will import the actual file, thus the module name should be
# the real one.
assert (module is sys.modules.get('posixpath') or
module is sys.modules.get('ntpath') or
module is sys.modules.get('macpath'))


def test_import_from_file_load_twice():
filename = os.path.abspath('reframe')
module1 = util.import_module_from_file(filename)
Expand Down

0 comments on commit bc280c5

Please sign in to comment.