Skip to content

Commit

Permalink
Do not import duplicated modules with --importmode=importlib
Browse files Browse the repository at this point in the history
Regression brought up by pytest-dev#11475.
  • Loading branch information
nicoddemus committed Mar 4, 2024
1 parent 0951cbc commit f408b94
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 0 deletions.
1 change: 1 addition & 0 deletions changelog/11475.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed regression where ``--importmode=importlib`` would import non-test modules more than once.
4 changes: 4 additions & 0 deletions src/_pytest/pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,10 @@ def import_path(
except CouldNotResolvePathError:
pass
else:
# If the given module name is already in sys.modules, do not import it again.
with contextlib.suppress(KeyError):
return sys.modules[module_name]

mod = _import_module_using_spec(
module_name, path, pkg_root, insert_modules=False
)
Expand Down
71 changes: 71 additions & 0 deletions testing/test_pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,12 @@ class Data:
assert data.value == "foo"
assert data.__module__ == "_src.tests.test_dataclass"

# Ensure we do not import the same module again (#11475).
module2 = import_path(
fn, mode="importlib", root=tmp_path, consider_namespace_packages=ns_param
)
assert module is module2

def test_importmode_importlib_with_pickle(
self, tmp_path: Path, ns_param: bool
) -> None:
Expand Down Expand Up @@ -616,6 +622,12 @@ def round_trip():
action = round_trip()
assert action() == 42

# Ensure we do not import the same module again (#11475).
module2 = import_path(
fn, mode="importlib", root=tmp_path, consider_namespace_packages=ns_param
)
assert module is module2

def test_importmode_importlib_with_pickle_separate_modules(
self, tmp_path: Path, ns_param: bool
) -> None:
Expand Down Expand Up @@ -816,6 +828,14 @@ def __init__(self) -> None:
consider_namespace_packages=ns_param,
)
assert len(mod.instance.INSTANCES) == 1
# Ensure we do not import the same module again (#11475).
mod2 = import_path(
init,
root=tmp_path,
mode=ImportMode.importlib,
consider_namespace_packages=ns_param,
)
assert mod is mod2

def test_importlib_root_is_package(self, pytester: Pytester) -> None:
"""
Expand Down Expand Up @@ -942,6 +962,15 @@ def test_import_using_normal_mechanism_first(
assert mod.__name__ == "app.core"
assert mod.__file__ and Path(mod.__file__) == core_py

# Ensure we do not import the same module again (#11475).
mod2 = import_path(
core_py,
mode="importlib",
root=pytester.path,
consider_namespace_packages=ns_param,
)
assert mod is mod2

# tests are not reachable from sys.path, so they are imported as a standalone modules.
# Instead of '.tests.a.test_core', we import as "_tests.a.test_core" because
# importlib considers module names starting with '.' to be local imports.
Expand All @@ -952,6 +981,16 @@ def test_import_using_normal_mechanism_first(
consider_namespace_packages=ns_param,
)
assert mod.__name__ == "_tests.a.test_core"

# Ensure we do not import the same module again (#11475).
mod2 = import_path(
test_path1,
mode="importlib",
root=pytester.path,
consider_namespace_packages=ns_param,
)
assert mod is mod2

mod = import_path(
test_path2,
mode="importlib",
Expand All @@ -960,6 +999,15 @@ def test_import_using_normal_mechanism_first(
)
assert mod.__name__ == "_tests.b.test_core"

# Ensure we do not import the same module again (#11475).
mod2 = import_path(
test_path2,
mode="importlib",
root=pytester.path,
consider_namespace_packages=ns_param,
)
assert mod is mod2

def test_import_using_normal_mechanism_first_integration(
self, monkeypatch: MonkeyPatch, pytester: Pytester, ns_param: bool
) -> None:
Expand Down Expand Up @@ -1021,6 +1069,14 @@ def test_import_path_imports_correct_file(
assert mod.__file__ and Path(mod.__file__) == x_in_sub_folder
assert mod.X == "a/b/x"

mod2 = import_path(
x_in_sub_folder,
mode=ImportMode.importlib,
root=pytester.path,
consider_namespace_packages=ns_param,
)
assert mod is mod2

# Attempt to import root 'x.py'.
with pytest.raises(AssertionError, match="x at root"):
_ = import_path(
Expand Down Expand Up @@ -1124,6 +1180,12 @@ def test_resolve_pkg_root_and_module_name_ns_multiple_levels(
assert mod.__name__ == "com.company.app.core.models"
assert mod.__file__ == str(models_py)

# Ensure we do not import the same module again (#11475).
mod2 = import_path(
models_py, mode=import_mode, root=tmp_path, consider_namespace_packages=True
)
assert mod is mod2

pkg_root, module_name = resolve_pkg_root_and_module_name(
algorithms_py, consider_namespace_packages=True
)
Expand All @@ -1141,6 +1203,15 @@ def test_resolve_pkg_root_and_module_name_ns_multiple_levels(
assert mod.__name__ == "com.company.calc.algo.algorithms"
assert mod.__file__ == str(algorithms_py)

# Ensure we do not import the same module again (#11475).
mod2 = import_path(
algorithms_py,
mode=import_mode,
root=tmp_path,
consider_namespace_packages=True,
)
assert mod is mod2

@pytest.mark.parametrize("import_mode", ["prepend", "append", "importlib"])
def test_incorrect_namespace_package(
self,
Expand Down

0 comments on commit f408b94

Please sign in to comment.