From 5e39cd5e71cb624dd5c3bf823a3c9168be3f0ff3 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 21 Aug 2020 10:59:55 +0300 Subject: [PATCH] main: improve message on `pytest path/to/a/directory::mytest` The path part of a `::part1::part2` style collection argument must be a file, not a directory. Previously this crashed with an uncool assert "invalid arg". --- src/_pytest/main.py | 8 ++++++++ testing/test_main.py | 35 +++++++++++++++++------------------ 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 2abfffba0..7ff362c34 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -808,6 +808,7 @@ def resolve_collection_argument( found module. If the path doesn't exist, raise UsageError. + If the path is a directory and selection parts are present, raise UsageError. """ strpath, *parts = str(arg).split("::") if as_pypath: @@ -821,4 +822,11 @@ def resolve_collection_argument( else "file or directory not found: {arg}" ) raise UsageError(msg.format(arg=arg)) + if parts and fspath.is_dir(): + msg = ( + "package argument cannot contain :: selection parts: {arg}" + if as_pypath + else "directory argument cannot contain :: selection parts: {arg}" + ) + raise UsageError(msg.format(arg=arg)) return py.path.local(str(fspath)), parts diff --git a/testing/test_main.py b/testing/test_main.py index 4546c83ba..71eae16b0 100644 --- a/testing/test_main.py +++ b/testing/test_main.py @@ -136,23 +136,21 @@ class TestResolveCollectionArgument: ["foo", "bar", ""], ) - def test_dir(self, root): + def test_dir(self, root: py.path.local) -> None: """Directory and parts.""" assert resolve_collection_argument(root, "src/pkg") == (root / "src/pkg", []) - assert resolve_collection_argument(root, "src/pkg::") == ( - root / "src/pkg", - [""], - ) - assert resolve_collection_argument(root, "src/pkg::foo::bar") == ( - root / "src/pkg", - ["foo", "bar"], - ) - assert resolve_collection_argument(root, "src/pkg::foo::bar::") == ( - root / "src/pkg", - ["foo", "bar", ""], - ) - def test_pypath(self, root): + with pytest.raises( + UsageError, match=r"directory argument cannot contain :: selection parts" + ): + resolve_collection_argument(root, "src/pkg::") + + with pytest.raises( + UsageError, match=r"directory argument cannot contain :: selection parts" + ): + resolve_collection_argument(root, "src/pkg::foo::bar") + + def test_pypath(self, root: py.path.local) -> None: """Dotted name and parts.""" assert resolve_collection_argument(root, "pkg.test", as_pypath=True) == ( root / "src/pkg/test.py", @@ -165,10 +163,11 @@ class TestResolveCollectionArgument: root / "src/pkg", [], ) - assert resolve_collection_argument(root, "pkg::foo::bar", as_pypath=True) == ( - root / "src/pkg", - ["foo", "bar"], - ) + + with pytest.raises( + UsageError, match=r"package argument cannot contain :: selection parts" + ): + resolve_collection_argument(root, "pkg::foo::bar", as_pypath=True) def test_does_not_exist(self, root): """Given a file/module that does not exist raises UsageError."""