python: change `pytest pkg/__init__.py` to only collect the `__init__.py` Module
Previously it would collect the entire package, but this is not what users expect. Refs #3749 Fixes #8976 Fixes #9263 Fixes #9313
This commit is contained in:
parent
2870157234
commit
c8b1790ee7
|
@ -0,0 +1,5 @@
|
||||||
|
Running `pytest pkg/__init__.py` now collects the `pkg/__init__.py` file (module) only.
|
||||||
|
Previously, it collected the entire `pkg` package, including other test files in the directory, but excluding tests in the `__init__.py` file itself
|
||||||
|
(unless :confval:`python_files` was changed to allow `__init__.py` file).
|
||||||
|
|
||||||
|
To collect the entire package, specify just the directory: `pytest pkg`.
|
|
@ -467,12 +467,26 @@ The ``yield_fixture`` function/decorator
|
||||||
It has been so for a very long time, so can be search/replaced safely.
|
It has been so for a very long time, so can be search/replaced safely.
|
||||||
|
|
||||||
|
|
||||||
Removed Features
|
Removed Features and Breaking Changes
|
||||||
----------------
|
-------------------------------------
|
||||||
|
|
||||||
As stated in our :ref:`backwards-compatibility` policy, deprecated features are removed only in major releases after
|
As stated in our :ref:`backwards-compatibility` policy, deprecated features are removed only in major releases after
|
||||||
an appropriate period of deprecation has passed.
|
an appropriate period of deprecation has passed.
|
||||||
|
|
||||||
|
Some breaking changes which could not be deprecated are also listed.
|
||||||
|
|
||||||
|
|
||||||
|
Collecting ``__init__.py`` files no longer collects package
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. versionremoved:: 8.0
|
||||||
|
|
||||||
|
Running `pytest pkg/__init__.py` now collects the `pkg/__init__.py` file (module) only.
|
||||||
|
Previously, it collected the entire `pkg` package, including other test files in the directory, but excluding tests in the `__init__.py` file itself
|
||||||
|
(unless :confval:`python_files` was changed to allow `__init__.py` file).
|
||||||
|
|
||||||
|
To collect the entire package, specify just the directory: `pytest pkg`.
|
||||||
|
|
||||||
|
|
||||||
The ``pytest.collect`` module
|
The ``pytest.collect`` module
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
|
@ -736,7 +736,9 @@ class Package(Module):
|
||||||
this_path = self.path.parent
|
this_path = self.path.parent
|
||||||
|
|
||||||
# Always collect the __init__ first.
|
# Always collect the __init__ first.
|
||||||
if path_matches_patterns(self.path, self.config.getini("python_files")):
|
if self.session.isinitpath(self.path) or path_matches_patterns(
|
||||||
|
self.path, self.config.getini("python_files")
|
||||||
|
):
|
||||||
yield Module.from_parent(self, path=self.path)
|
yield Module.from_parent(self, path=self.path)
|
||||||
|
|
||||||
pkg_prefixes: Set[Path] = set()
|
pkg_prefixes: Set[Path] = set()
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
def test_init():
|
||||||
|
pass
|
|
@ -1,2 +1,2 @@
|
||||||
def test():
|
def test_foo():
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -1420,10 +1420,15 @@ def test_package_collection_infinite_recursion(pytester: Pytester) -> None:
|
||||||
|
|
||||||
|
|
||||||
def test_package_collection_init_given_as_argument(pytester: Pytester) -> None:
|
def test_package_collection_init_given_as_argument(pytester: Pytester) -> None:
|
||||||
"""Regression test for #3749"""
|
"""Regression test for #3749, #8976, #9263, #9313.
|
||||||
|
|
||||||
|
Specifying an __init__.py file directly should collect only the __init__.py
|
||||||
|
Module, not the entire package.
|
||||||
|
"""
|
||||||
p = pytester.copy_example("collect/package_init_given_as_arg")
|
p = pytester.copy_example("collect/package_init_given_as_arg")
|
||||||
result = pytester.runpytest(p / "pkg" / "__init__.py")
|
items, hookrecorder = pytester.inline_genitems(p / "pkg" / "__init__.py")
|
||||||
result.stdout.fnmatch_lines(["*1 passed*"])
|
assert len(items) == 1
|
||||||
|
assert items[0].name == "test_init"
|
||||||
|
|
||||||
|
|
||||||
def test_package_with_modules(pytester: Pytester) -> None:
|
def test_package_with_modules(pytester: Pytester) -> None:
|
||||||
|
|
|
@ -1392,19 +1392,27 @@ def test_collect_pkg_init_and_file_in_args(pytester: Pytester) -> None:
|
||||||
p = subdir.joinpath("test_file.py")
|
p = subdir.joinpath("test_file.py")
|
||||||
p.write_text("def test_file(): pass", encoding="utf-8")
|
p.write_text("def test_file(): pass", encoding="utf-8")
|
||||||
|
|
||||||
# NOTE: without "-o python_files=*.py" this collects test_file.py twice.
|
# Just the package directory, the __init__.py module is filtered out.
|
||||||
# This changed/broke with "Add package scoped fixtures #2283" (2b1410895)
|
result = pytester.runpytest("-v", subdir)
|
||||||
# initially (causing a RecursionError).
|
|
||||||
result = pytester.runpytest("-v", str(init), str(p))
|
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
[
|
[
|
||||||
"sub/test_file.py::test_file PASSED*",
|
"sub/test_file.py::test_file PASSED*",
|
||||||
|
"*1 passed in*",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
# But it's included if specified directly.
|
||||||
|
result = pytester.runpytest("-v", init, p)
|
||||||
|
result.stdout.fnmatch_lines(
|
||||||
|
[
|
||||||
|
"sub/__init__.py::test_init PASSED*",
|
||||||
"sub/test_file.py::test_file PASSED*",
|
"sub/test_file.py::test_file PASSED*",
|
||||||
"*2 passed in*",
|
"*2 passed in*",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
result = pytester.runpytest("-v", "-o", "python_files=*.py", str(init), str(p))
|
# Or if the pattern allows it.
|
||||||
|
result = pytester.runpytest("-v", "-o", "python_files=*.py", subdir)
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
[
|
[
|
||||||
"sub/__init__.py::test_init PASSED*",
|
"sub/__init__.py::test_init PASSED*",
|
||||||
|
@ -1419,10 +1427,13 @@ def test_collect_pkg_init_only(pytester: Pytester) -> None:
|
||||||
init = subdir.joinpath("__init__.py")
|
init = subdir.joinpath("__init__.py")
|
||||||
init.write_text("def test_init(): pass", encoding="utf-8")
|
init.write_text("def test_init(): pass", encoding="utf-8")
|
||||||
|
|
||||||
result = pytester.runpytest(str(init))
|
result = pytester.runpytest(subdir)
|
||||||
result.stdout.fnmatch_lines(["*no tests ran in*"])
|
result.stdout.fnmatch_lines(["*no tests ran in*"])
|
||||||
|
|
||||||
result = pytester.runpytest("-v", "-o", "python_files=*.py", str(init))
|
result = pytester.runpytest("-v", init)
|
||||||
|
result.stdout.fnmatch_lines(["sub/__init__.py::test_init PASSED*", "*1 passed in*"])
|
||||||
|
|
||||||
|
result = pytester.runpytest("-v", "-o", "python_files=*.py", subdir)
|
||||||
result.stdout.fnmatch_lines(["sub/__init__.py::test_init PASSED*", "*1 passed in*"])
|
result.stdout.fnmatch_lines(["sub/__init__.py::test_init PASSED*", "*1 passed in*"])
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -114,7 +114,7 @@ class TestDoctests:
|
||||||
reprec.assertoutcome(failed=1)
|
reprec.assertoutcome(failed=1)
|
||||||
|
|
||||||
def test_importmode(self, pytester: Pytester):
|
def test_importmode(self, pytester: Pytester):
|
||||||
p = pytester.makepyfile(
|
pytester.makepyfile(
|
||||||
**{
|
**{
|
||||||
"namespacepkg/innerpkg/__init__.py": "",
|
"namespacepkg/innerpkg/__init__.py": "",
|
||||||
"namespacepkg/innerpkg/a.py": """
|
"namespacepkg/innerpkg/a.py": """
|
||||||
|
@ -132,7 +132,7 @@ class TestDoctests:
|
||||||
""",
|
""",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
reprec = pytester.inline_run(p, "--doctest-modules", "--import-mode=importlib")
|
reprec = pytester.inline_run("--doctest-modules", "--import-mode=importlib")
|
||||||
reprec.assertoutcome(passed=1)
|
reprec.assertoutcome(passed=1)
|
||||||
|
|
||||||
def test_new_pattern(self, pytester: Pytester):
|
def test_new_pattern(self, pytester: Pytester):
|
||||||
|
|
|
@ -504,7 +504,7 @@ def test_nose_setup_skipped_if_non_callable(pytester: Pytester) -> None:
|
||||||
pass
|
pass
|
||||||
""",
|
""",
|
||||||
)
|
)
|
||||||
result = pytester.runpytest(p, "-p", "nose")
|
result = pytester.runpytest(p.parent, "-p", "nose")
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue