Skip to content

Conversation

@bluetech
Copy link
Member

Fix #11137.

I'll leave detailed notes on the changes below to hopefully make them easier to understand.

@bluetech bluetech added this to the 8.0 milestone Jun 24, 2023

# 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.
Copy link
Member Author

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

def pytest_make_collect_report(
self, collector: nodes.Collector
) -> Optional[CollectReport]:
# Packages are Files, but we only want to skip test-bearing Files,
Copy link
Member Author

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

from _pytest.python import Package

current: Optional[Union[nodes.Item, nodes.Collector]] = node
fixture_package_name = "{}/{}".format(fixturedef.baseid, "__init__.py")
Copy link
Member Author

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.

return False
return True

def _collectpackage(self, fspath: Path) -> Optional["Package"]:
Copy link
Member Author

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).

# 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):
Copy link
Member Author

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.

)


def importtestmodule(
Copy link
Member Author

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.

nodeid=nodeid,
)
self.name = self.path.parent.name
self.name = self.path.name
Copy link
Member Author

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.

self.name = self.path.name

def setup(self) -> None:
init_mod = importtestmodule(self.path / "__init__.py", self.config)
Copy link
Member Author

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 Packages, it's pretty obscure, but for now I retained full compat.

Copy link
Member

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

Copy link
Member Author

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.

this_path = self.path.parent

# Always collect the __init__ first.
if self.session.isinitpath(self.path) or path_matches_patterns(
Copy link
Member Author

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.

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
Copy link
Member Author

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)

@bluetech
Copy link
Member Author

  • Rebased to fix conflicts
  • Added the change in get_parametrized_fixture_keys, which I had missed before

Copy link
Member

@nicoddemus nicoddemus left a 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!

@bluetech
Copy link
Member Author

Thanks a lot for the review @nicoddemus!

I'll try to get the coverage passing (in separate PRs) then merge.

Copy link
Member

@RonnyPfannschmidt RonnyPfannschmidt left a 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 👍

@Pierre-Sassoulas
Copy link
Member

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 sort_key) ? I assume it could have revealed an existing test contamination issue in pylint's test suite. I see a visit call and occurrence of nodes.Collector but pytest does not modify the ast during test collection, right ?

@RonnyPfannschmidt
Copy link
Member

AST modification may occur during collection in case assertion rewriting needs to act

@bluetech
Copy link
Member Author

bluetech commented May 1, 2024

@Pierre-Sassoulas from a quick look it looks like maybe something changed with regards to the AudioTime precedence test: https://github.com/search?q=repo%3Apylint-dev%2Fpylint%20AudioTime&type=code

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?

Pierre-Sassoulas added a commit to pylint-dev/pylint that referenced this pull request May 1, 2024
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
Pierre-Sassoulas added a commit to pylint-dev/pylint that referenced this pull request May 1, 2024
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
@Pierre-Sassoulas
Copy link
Member

Thank you for the quick answers, very helpful !

AST modification may occur during collection in case assertion rewriting needs to act

Interesting, I wonder if we have some pylint features that do not work without a pytest test collection prior to the analysis 😄

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?

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).

DanielNoord pushed a commit to pylint-dev/pylint that referenced this pull request Jun 3, 2024
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
Pierre-Sassoulas added a commit to pylint-dev/pylint that referenced this pull request Jun 4, 2024
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]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Package should not be a Module/File

4 participants