diff --git a/AUTHORS b/AUTHORS index 9be48958485..6e59e98be10 100644 --- a/AUTHORS +++ b/AUTHORS @@ -134,6 +134,7 @@ Deysha Rivera Dheeraj C K Dhiren Serai Diego Russo +Dima Gerasimov Dmitry Dygalo Dmitry Pribysh Dominic Mortlock diff --git a/changelog/478.feature.rst b/changelog/478.feature.rst new file mode 100644 index 00000000000..28935bea866 --- /dev/null +++ b/changelog/478.feature.rst @@ -0,0 +1 @@ +Support PEP420 (implicit namespace packages) as `--pyargs` target. diff --git a/src/_pytest/main.py b/src/_pytest/main.py index dac084b553a..98027584f3d 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -991,11 +991,19 @@ def search_pypath(module_name: str) -> str | None: # ValueError: not a module name except (AttributeError, ImportError, ValueError): return None - if spec is None or spec.origin is None or spec.origin == "namespace": + + if spec is None: return None - elif spec.submodule_search_locations: - return os.path.dirname(spec.origin) + elif ( + spec.submodule_search_locations is not None + and len(spec.submodule_search_locations) > 0 + ): + # If submodule_search_locations is set, it's a package (regular or namespace). + # Typically there is a single entry, but documentation claims it can be empty too + # (e.g. if the package has no physical location). + return spec.submodule_search_locations[0] else: + # Must be a simple module. return spec.origin diff --git a/testing/test_main.py b/testing/test_main.py index 94eac02ce63..3d8d6adc35e 100644 --- a/testing/test_main.py +++ b/testing/test_main.py @@ -169,8 +169,13 @@ def test_dir(self, invocation_path: Path) -> None: ): resolve_collection_argument(invocation_path, "src/pkg::foo::bar") - def test_pypath(self, invocation_path: Path) -> None: + @pytest.mark.parametrize("namespace_package", [False, True]) + def test_pypath(self, namespace_package: bool, invocation_path: Path) -> None: """Dotted name and parts.""" + if namespace_package: + # Namespace package doesn't have to contain __init__py + (invocation_path / "src/pkg/__init__.py").unlink() + assert resolve_collection_argument( invocation_path, "pkg.test", as_pypath=True ) == CollectionArgument(