main: add `module_name` to `CollectionArgument`

This is available when the argument is a `--pyargs` argument (resolved
from a python module path). Will be used in an upcoming commit.
This commit is contained in:
Ran Benita 2024-03-01 11:59:26 +02:00
parent 5e0d11746c
commit 1612d4e393
2 changed files with 25 additions and 5 deletions

View File

@ -957,17 +957,18 @@ class Session(nodes.Collector):
node.ihook.pytest_collectreport(report=rep) node.ihook.pytest_collectreport(report=rep)
def search_pypath(module_name: str) -> str: def search_pypath(module_name: str) -> Optional[str]:
"""Search sys.path for the given a dotted module name, and return its file system path.""" """Search sys.path for the given a dotted module name, and return its file
system path if found."""
try: try:
spec = importlib.util.find_spec(module_name) spec = importlib.util.find_spec(module_name)
# AttributeError: looks like package module, but actually filename # AttributeError: looks like package module, but actually filename
# ImportError: module does not exist # ImportError: module does not exist
# ValueError: not a module name # ValueError: not a module name
except (AttributeError, ImportError, ValueError): except (AttributeError, ImportError, ValueError):
return module_name return None
if spec is None or spec.origin is None or spec.origin == "namespace": if spec is None or spec.origin is None or spec.origin == "namespace":
return module_name return None
elif spec.submodule_search_locations: elif spec.submodule_search_locations:
return os.path.dirname(spec.origin) return os.path.dirname(spec.origin)
else: else:
@ -980,6 +981,7 @@ class CollectionArgument:
path: Path path: Path
parts: Sequence[str] parts: Sequence[str]
module_name: Optional[str]
def resolve_collection_argument( def resolve_collection_argument(
@ -997,6 +999,7 @@ def resolve_collection_argument(
CollectionArgument( CollectionArgument(
path=Path("/full/path/to/pkg/tests/test_foo.py"), path=Path("/full/path/to/pkg/tests/test_foo.py"),
parts=["TestClass", "test_foo"], parts=["TestClass", "test_foo"],
module_name=None,
) )
When as_pypath is True, expects that the command-line argument actually contains When as_pypath is True, expects that the command-line argument actually contains
@ -1010,6 +1013,7 @@ def resolve_collection_argument(
CollectionArgument( CollectionArgument(
path=Path("/home/u/myvenv/lib/site-packages/pkg/tests/test_foo.py"), path=Path("/home/u/myvenv/lib/site-packages/pkg/tests/test_foo.py"),
parts=["TestClass", "test_foo"], parts=["TestClass", "test_foo"],
module_name="pkg.tests.test_foo",
) )
If the path doesn't exist, raise UsageError. If the path doesn't exist, raise UsageError.
@ -1019,8 +1023,12 @@ def resolve_collection_argument(
strpath, *parts = base.split("::") strpath, *parts = base.split("::")
if parts: if parts:
parts[-1] = f"{parts[-1]}{squacket}{rest}" parts[-1] = f"{parts[-1]}{squacket}{rest}"
module_name = None
if as_pypath: if as_pypath:
strpath = search_pypath(strpath) pyarg_strpath = search_pypath(strpath)
if pyarg_strpath is not None:
module_name = strpath
strpath = pyarg_strpath
fspath = invocation_path / strpath fspath = invocation_path / strpath
fspath = absolutepath(fspath) fspath = absolutepath(fspath)
if not safe_exists(fspath): if not safe_exists(fspath):
@ -1040,4 +1048,5 @@ def resolve_collection_argument(
return CollectionArgument( return CollectionArgument(
path=fspath, path=fspath,
parts=parts, parts=parts,
module_name=module_name,
) )

View File

@ -139,24 +139,28 @@ class TestResolveCollectionArgument:
) == CollectionArgument( ) == CollectionArgument(
path=invocation_path / "src/pkg/test.py", path=invocation_path / "src/pkg/test.py",
parts=[], parts=[],
module_name=None,
) )
assert resolve_collection_argument( assert resolve_collection_argument(
invocation_path, "src/pkg/test.py::" invocation_path, "src/pkg/test.py::"
) == CollectionArgument( ) == CollectionArgument(
path=invocation_path / "src/pkg/test.py", path=invocation_path / "src/pkg/test.py",
parts=[""], parts=[""],
module_name=None,
) )
assert resolve_collection_argument( assert resolve_collection_argument(
invocation_path, "src/pkg/test.py::foo::bar" invocation_path, "src/pkg/test.py::foo::bar"
) == CollectionArgument( ) == CollectionArgument(
path=invocation_path / "src/pkg/test.py", path=invocation_path / "src/pkg/test.py",
parts=["foo", "bar"], parts=["foo", "bar"],
module_name=None,
) )
assert resolve_collection_argument( assert resolve_collection_argument(
invocation_path, "src/pkg/test.py::foo::bar::" invocation_path, "src/pkg/test.py::foo::bar::"
) == CollectionArgument( ) == CollectionArgument(
path=invocation_path / "src/pkg/test.py", path=invocation_path / "src/pkg/test.py",
parts=["foo", "bar", ""], parts=["foo", "bar", ""],
module_name=None,
) )
def test_dir(self, invocation_path: Path) -> None: def test_dir(self, invocation_path: Path) -> None:
@ -166,6 +170,7 @@ class TestResolveCollectionArgument:
) == CollectionArgument( ) == CollectionArgument(
path=invocation_path / "src/pkg", path=invocation_path / "src/pkg",
parts=[], parts=[],
module_name=None,
) )
with pytest.raises( with pytest.raises(
@ -185,18 +190,21 @@ class TestResolveCollectionArgument:
) == CollectionArgument( ) == CollectionArgument(
path=invocation_path / "src/pkg/test.py", path=invocation_path / "src/pkg/test.py",
parts=[], parts=[],
module_name="pkg.test",
) )
assert resolve_collection_argument( assert resolve_collection_argument(
invocation_path, "pkg.test::foo::bar", as_pypath=True invocation_path, "pkg.test::foo::bar", as_pypath=True
) == CollectionArgument( ) == CollectionArgument(
path=invocation_path / "src/pkg/test.py", path=invocation_path / "src/pkg/test.py",
parts=["foo", "bar"], parts=["foo", "bar"],
module_name="pkg.test",
) )
assert resolve_collection_argument( assert resolve_collection_argument(
invocation_path, "pkg", as_pypath=True invocation_path, "pkg", as_pypath=True
) == CollectionArgument( ) == CollectionArgument(
path=invocation_path / "src/pkg", path=invocation_path / "src/pkg",
parts=[], parts=[],
module_name="pkg",
) )
with pytest.raises( with pytest.raises(
@ -212,6 +220,7 @@ class TestResolveCollectionArgument:
) == CollectionArgument( ) == CollectionArgument(
path=invocation_path / "src/pkg/test.py", path=invocation_path / "src/pkg/test.py",
parts=["test[a::b]"], parts=["test[a::b]"],
module_name=None,
) )
def test_does_not_exist(self, invocation_path: Path) -> None: def test_does_not_exist(self, invocation_path: Path) -> None:
@ -237,6 +246,7 @@ class TestResolveCollectionArgument:
) == CollectionArgument( ) == CollectionArgument(
path=Path(os.path.abspath("src")), path=Path(os.path.abspath("src")),
parts=[], parts=[],
module_name=None,
) )
# ensure full paths given in the command-line without the drive letter resolve # ensure full paths given in the command-line without the drive letter resolve
@ -247,6 +257,7 @@ class TestResolveCollectionArgument:
) == CollectionArgument( ) == CollectionArgument(
path=Path(os.path.abspath("src")), path=Path(os.path.abspath("src")),
parts=[], parts=[],
module_name=None,
) )