Skip to content

Commit 98ac453

Browse files
committed
Support PEP420 (implicit namespace packages) as --pyargs target.
Previously, when running `--pyargs pkg`, if you didn't have `pkg/__init__.py`, pytest would fail with `ERROR: module or package not found: pkg (missing __init__.py?)`. Now it's discovering the package and tests inside it correctly. If used in conjunction with `consider_namespace_packages` in config, test modules get correct `__package__` and `__name__` attributes as well. Fixes: pytest-dev#478 Relevant: - pytest-dev#2371 - pytest-dev#10569 In addition, remove `"namespace"` origin handling -- this value isn't used since python 3.8. See python/cpython#5481 and https://docs.python.org/3/library/importlib.html#importlib.machinery.ModuleSpec.submodule_search_locations .
1 parent cfbe319 commit 98ac453

File tree

4 files changed

+19
-4
lines changed

4 files changed

+19
-4
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ Deysha Rivera
134134
Dheeraj C K
135135
Dhiren Serai
136136
Diego Russo
137+
Dima Gerasimov
137138
Dmitry Dygalo
138139
Dmitry Pribysh
139140
Dominic Mortlock

changelog/478.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Support PEP420 (implicit namespace packages) as `--pyargs` target.

src/_pytest/main.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -991,11 +991,19 @@ def search_pypath(module_name: str) -> str | None:
991991
# ValueError: not a module name
992992
except (AttributeError, ImportError, ValueError):
993993
return None
994-
if spec is None or spec.origin is None or spec.origin == "namespace":
994+
995+
if spec is None:
995996
return None
996-
elif spec.submodule_search_locations:
997-
return os.path.dirname(spec.origin)
997+
elif (
998+
spec.submodule_search_locations is not None
999+
and len(spec.submodule_search_locations) > 0
1000+
):
1001+
# If submodule_search_locations is set, it's a package (regular or namespace).
1002+
# Typically there is a single entry, but documentation claims it can be empty too
1003+
# (e.g. if the package has no physical location).
1004+
return spec.submodule_search_locations[0]
9981005
else:
1006+
# Must be a simple module.
9991007
return spec.origin
10001008

10011009

testing/test_main.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,13 @@ def test_dir(self, invocation_path: Path) -> None:
169169
):
170170
resolve_collection_argument(invocation_path, "src/pkg::foo::bar")
171171

172-
def test_pypath(self, invocation_path: Path) -> None:
172+
@pytest.mark.parametrize("namespace_package", [False, True])
173+
def test_pypath(self, namespace_package: bool, invocation_path: Path) -> None:
173174
"""Dotted name and parts."""
175+
if namespace_package:
176+
# Namespace package doesn't have to contain __init__py
177+
(invocation_path / "src/pkg/__init__.py").unlink()
178+
174179
assert resolve_collection_argument(
175180
invocation_path, "pkg.test", as_pypath=True
176181
) == CollectionArgument(

0 commit comments

Comments
 (0)