diff --git a/src/_pytest/pathlib.py b/src/_pytest/pathlib.py index 7e61a561d..e2fa4db12 100644 --- a/src/_pytest/pathlib.py +++ b/src/_pytest/pathlib.py @@ -648,6 +648,11 @@ def module_name_from_path(path: Path, root: Path) -> str: if len(path_parts) >= 2 and path_parts[-1] == "__init__": path_parts = path_parts[:-1] + # Module names cannot contain ".", normalize them to "_". This prevents + # a directory having a "." in the name (".env.310" for example) causing extra intermediate modules. + # Also, important to replace "." at the start of paths, as those are considered relative imports. + path_parts = [x.replace(".", "_") for x in path_parts] + return ".".join(path_parts) diff --git a/testing/test_pathlib.py b/testing/test_pathlib.py index d3ae00248..087090071 100644 --- a/testing/test_pathlib.py +++ b/testing/test_pathlib.py @@ -584,6 +584,18 @@ class TestImportLibMode: result = module_name_from_path(tmp_path / "__init__.py", tmp_path) assert result == "__init__" + # Modules which start with "." are considered relative and will not be imported + # unless part of a package, so we replace it with a "_" when generating the fake module name. + result = module_name_from_path(tmp_path / ".env/tests/test_foo.py", tmp_path) + assert result == "_env.tests.test_foo" + + # We want to avoid generating extra intermediate modules if some directory just happens + # to contain a "." in the name. + result = module_name_from_path( + tmp_path / ".env.310/tests/test_foo.py", tmp_path + ) + assert result == "_env_310.tests.test_foo" + def test_insert_missing_modules( self, monkeypatch: MonkeyPatch, tmp_path: Path ) -> None: