-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
python: change Package
to no longer be a Module
/File
#11138
Conversation
@@ -228,12 +228,7 @@ def pytest_make_collect_report(self, collector: nodes.Collector): | |||
|
|||
# Use stable sort to priorize last failed. | |||
def sort_key(node: Union[nodes.Item, nodes.Collector]) -> bool: | |||
# Package.path is the __init__.py file, we need the directory. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Simplification - previous special case for Package
no longer needed
@@ -280,9 +275,7 @@ def __init__(self, lfplugin: "LFPlugin") -> None: | |||
def pytest_make_collect_report( | |||
self, collector: nodes.Collector | |||
) -> Optional[CollectReport]: | |||
# Packages are Files, but we only want to skip test-bearing Files, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Simplification - previous special case for Package
no longer needed
@@ -120,9 +120,8 @@ def get_scope_package( | |||
from _pytest.python import Package | |||
|
|||
current: Optional[Union[nodes.Item, nodes.Collector]] = node | |||
fixture_package_name = "{}/{}".format(fixturedef.baseid, "__init__.py") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No longer need special code to adjust to the fact the package's path is the __init__.py
.
This change will be relevant for fixing #10993 and other similar bugs.
@@ -571,6 +572,17 @@ def _recurse(self, direntry: "os.DirEntry[str]") -> bool: | |||
return False | |||
return True | |||
|
|||
def _collectpackage(self, fspath: Path) -> Optional["Package"]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Package
is now no longer collected as a Module
, so need to do it manually.
This is temporary, will be entirely revamped using the planned pytest_collect_directory
hook (see #7777).
|
||
for argpath, names in self._initial_parts: | ||
self.trace("processing argument", (argpath, names)) | ||
self.trace.root.indent += 1 | ||
|
||
# Start with a Session root, and delve to argpath item (dir or file) | ||
# and stack all Packages found on the way. | ||
# No point in finding packages when collecting doctests. | ||
if not self.config.getoption("doctestmodules", False): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't been able to understand this optimization, and it hinders the implementation, so I removed it. It will become irrelevant in upcoming changes anyway.
@@ -521,11 +518,62 @@ def _genfunctions(self, name: str, funcobj) -> Iterator["Function"]: | |||
) | |||
|
|||
|
|||
def importtestmodule( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactor: moving this code from a method to a standalone function because we need to reuse it. No logic changes.
@@ -681,22 +682,24 @@ def __init__( | |||
session=session, | |||
nodeid=nodeid, | |||
) | |||
self.name = self.path.parent.name | |||
self.name = self.path.name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The path
is now the directory, not the __init__.py
. The name is the name of the directory, so no need for the parent
anymore.
|
||
def setup(self) -> None: | ||
init_mod = importtestmodule(self.path / "__init__.py", self.config) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was previously handled by Module
importing its module file (the __init__.py
file), now we need to do it ad-hoc.
As discussed in the issue, I think we should consider deprecating setup_module
/teardown_module
in __init__.py
files for Package
s, it's pretty obscure, but for now I retained full compat.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makes me wonder if we want to consider deprecating xunit style setup methods alltogether as they are practically better served by fixtures
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it will be too disruptive at this point, but maybe with nose gone we can start considering this as well. Overall though I think it's not worth deprecating, I know a lot of people use them.
This particular case though (setup_module
xunit fixtures in the __init__.py
file being package scoped) is more dubious than the others, I'd be surprised to learn that anyone knows about it at all. So we might want to consider deprecating it specifically.
# Always collect the __init__ first. | ||
if self.session.isinitpath(self.path) or path_matches_patterns( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since __init__.py
is no longer treated special by pytest_pycollect_makemodule
, can just use _collectfile
for it.
@@ -733,21 +736,16 @@ def _collectfile( | |||
return ihook.pytest_collect_file(file_path=fspath, parent=self) # type: ignore[no-any-return] | |||
|
|||
def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: | |||
this_path = self.path.parent |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self.path
is now this path (the directory)
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great, thanks @bluetech!
Thanks a lot for the review @nicoddemus! I'll try to get the coverage passing (in separate PRs) then merge. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i love how much this cleans things up, awesome 👍
This commit prevents upgrading pytest above 8.0.0 in pylint (pylint-dev/pylint#9576) . Am I correct in assuming that the order the tests are discovered and launched changed (with the change to |
AST modification may occur during collection in case assertion rewriting needs to act |
@Pierre-Sassoulas from a quick look it looks like maybe something changed with regards to the Are you able to run this test https://github.com/pylint-dev/pylint/blob/82ef6475e665ca8ae1542822a485c98a067e1834/tests/test_regr.py#L59 "manually" (outside of pytest) and see if it passes? |
Suggestion from pytest-dev/pytest#11138 (comment) With 'pytest': FAILED tests/test_precedence.py::test_package - AssertionError: E: 21: Module 'package.AudioTime' has no 'DECIMAL' member<function Equals.<locals>.<lambda> at 0x76c566741750> With 'python tests/test_precedence.py': Checked ['package.__init__'] successfully Checked ['precedence_test'] successfully Checked ['import_package_subpackage_module'] successfully Checked ['pylint.checkers.__init__'] successfully Checked ['/home/pierre/pylint/tests/regrtest_data/classdoc_usage.py'] successfully Checked ['/home/pierre/pylint/tests/regrtest_data/module_global.py'] successfully Checked ['/home/pierre/pylint/tests/regrtest_data/decimal_inference.py'] successfully Checked ['/home/pierre/pylint/tests/regrtest_data/absimp/string.py'] successfully Checked ['/home/pierre/pylint/tests/regrtest_data/bad_package'] successfully
Suggestion from pytest-dev/pytest#11138 (comment) With 'pytest' (launching the whole pylint test suite): FAILED tests/test_precedence.py::test_package - AssertionError: E: 21: Module 'package.AudioTime' has no 'DECIMAL' member<function Equals.<locals>.<lambda> at 0x76c566741750> With 'pytest tests/test_precedence.py': tests/test_precedence.py . [100%] ============================================================================================ 1 passed in 1.04s ============================================================================================= With 'python tests/test_precedence.py': Checked ['package.__init__'] successfully Checked ['precedence_test'] successfully Checked ['import_package_subpackage_module'] successfully Checked ['pylint.checkers.__init__'] successfully Checked ['/home/pierre/pylint/tests/regrtest_data/classdoc_usage.py'] successfully Checked ['/home/pierre/pylint/tests/regrtest_data/module_global.py'] successfully Checked ['/home/pierre/pylint/tests/regrtest_data/decimal_inference.py'] successfully Checked ['/home/pierre/pylint/tests/regrtest_data/absimp/string.py'] successfully Checked ['/home/pierre/pylint/tests/regrtest_data/bad_package'] successfully
Thank you for the quick answers, very helpful !
Interesting, I wonder if we have some pylint features that do not work without a pytest test collection prior to the analysis 😄
Tried that in pylint-dev/pylint@49a7a61, it seems this is indeed not passing due to pytest collection (but only when it collects the whole pylint test suite). |
Suggestion from pytest-dev/pytest#11138 (comment) With 'pytest' (launching the whole pylint test suite): FAILED tests/test_precedence.py::test_package - AssertionError: E: 21: Module 'package.AudioTime' has no 'DECIMAL' member<function Equals.<locals>.<lambda> at 0x76c566741750> With 'pytest tests/test_precedence.py': tests/test_precedence.py . [100%] ============================================================================================ 1 passed in 1.04s ============================================================================================= With 'python tests/test_precedence.py': Checked ['package.__init__'] successfully Checked ['precedence_test'] successfully Checked ['import_package_subpackage_module'] successfully Checked ['pylint.checkers.__init__'] successfully Checked ['/home/pierre/pylint/tests/regrtest_data/classdoc_usage.py'] successfully Checked ['/home/pierre/pylint/tests/regrtest_data/module_global.py'] successfully Checked ['/home/pierre/pylint/tests/regrtest_data/decimal_inference.py'] successfully Checked ['/home/pierre/pylint/tests/regrtest_data/absimp/string.py'] successfully Checked ['/home/pierre/pylint/tests/regrtest_data/bad_package'] successfully
Updates the requirements on [pytest](https://github.com/pytest-dev/pytest) to permit the latest version. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](pytest-dev/pytest@7.4.0...8.2.0) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:production ... Prevent some test pollution created by mocking ``sys.exit``, and some name clashes. Commit originally responsible for issue from pytest is pytest-dev/pytest@a21fb8. We still don't know what exactly caused the issue that made the test pollution apparent. Used a bisection and suggestion from pytest-dev/pytest#11138 (comment) to fix it. With 'pytest' (launching the whole pylint test suite): FAILED tests/test_precedence.py::test_package - AssertionError: E: 21: Module 'package.AudioTime' has no 'DECIMAL' member<function Equals.<locals>.<lambda> at 0x76c566741750> With 'pytest tests/test_precedence.py': tests/test_precedence.py . [100%] ============================================================================================ 1 passed in 1.04s ============================================================================================= With 'python tests/test_precedence.py': Checked ['package.__init__'] successfully Checked ['precedence_test'] successfully Checked ['import_package_subpackage_module'] successfully Checked ['pylint.checkers.__init__'] successfully Checked ['/home/pierre/pylint/tests/regrtest_data/classdoc_usage.py'] successfully Checked ['/home/pierre/pylint/tests/regrtest_data/module_global.py'] successfully Checked ['/home/pierre/pylint/tests/regrtest_data/decimal_inference.py'] successfully Checked ['/home/pierre/pylint/tests/regrtest_data/absimp/string.py'] successfully Checked ['/home/pierre/pylint/tests/regrtest_data/bad_package'] successfully --------- Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Pierre Sassoulas <[email protected]> Co-authored-by: Daniël van Noord <[email protected]>
Fix #11137.
I'll leave detailed notes on the changes below to hopefully make them easier to understand.