Skip to content
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

Conditionally ignore collection of setuptools dirnames #12918

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

sus-pe
Copy link
Contributor

@sus-pe sus-pe commented Oct 25, 2024

Fix #12625

@psf-chronographer psf-chronographer bot added the bot:chronographer:provided (automation) changelog entry is part of PR label Oct 25, 2024
testing/test_collection.py Show resolved Hide resolved
testing/test_collection.py Show resolved Hide resolved
src/_pytest/main.py Outdated Show resolved Hide resolved
@@ -423,6 +430,10 @@ def pytest_ignore_collect(collection_path: Path, config: Config) -> bool | None:
if any(fnmatch_ex(pat, collection_path) for pat in norecursepatterns):
return True

if any(fnmatch_ex(pat, collection_path) for pat in ("build", "dist")):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be wrapped by a "allow_in_setuptools" like they do for a venv?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't understand this reference at first. But now I realize that this is what I've been thinking of when I wrote another comment @ #12918 (comment). So I'd say yes. This would improve the maintainability.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i have rewritten this in light of what I understood from our conversation, please let me know if this is the direction we want to go in

indicators = ("setup.py", "setup.cfg", "pyproject.toml")
return any((path / f).exists() for f in indicators)
indicators = ("setup.py", "setup.cfg")
if any((path / f).exists() for f in indicators):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if any((path / f).exists() for f in indicators):
if any((path / f).is_file() for f in indicators):

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@@ -0,0 +1,2 @@
Conditionally ignore collection of setuptools artifacts dirnames only if the
directories reside inside a setuptools project, i.e. `setup.cfg`, is present, etc.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, enumerate all the files taken into consideration in this logic. Change notes are targeting the end-users who would not be able to guess. It might also be a good idea to mention the same somewhere in the regular docs, additionally.

except tomllib.TOMLDecodeError as exc:
raise UsageError(f"{toml_file}: {exc}") from exc

result = config.get("build-system", {}).get("requires", {})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend using a more verbose variable name, one that is not generic but corresponds to the data being assigned to it. This is because we should optimize for readability, since any code is read much more often than it's written.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted, for now this code has been removed

@@ -423,6 +450,10 @@ def pytest_ignore_collect(collection_path: Path, config: Config) -> bool | None:
if any(fnmatch_ex(pat, collection_path) for pat in norecursepatterns):
return True

if any(fnmatch_ex(pat, collection_path) for pat in ("build", "dist")):
if _is_setuptools_project(collection_path.parent):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This case does not need a nested block. Instead, both conditions could be merged using and. The line would be quite long, though, so it might be a good idea to assign that to a variable that would also be an opportunity to assign semantics to this check through naming the var with something that corresponds to the high-level purpose of the check.

For example, using is_distributable_library would make that check if is_distributable_library:, which is both short and communicates what the list of low-level checks mean.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tried an allow_in_build approach, let me know what you think

@sus-pe sus-pe force-pushed the bugfix-12625-ignorebuild branch 2 times, most recently from 4e4ef2e to 71fc2ad Compare October 26, 2024 11:22
@@ -63,9 +63,7 @@ def pytest_addoption(parser: Parser) -> None:
"*.egg",
".*",
"_darcs",
"build",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that the description string above should mention that these are still conditionally excluded by default.

@@ -367,6 +365,22 @@ def pytest_runtestloop(session: Session) -> bool:
return True


def _in_build(path: Path) -> bool:
"""Attempt to detect if ``path`` is the root of a buildsystem's artifacts
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels like this isn't just an attempt but a complete piece of logic. So I'd just go with “Detect” since it seems more accurate.

Suggested change
"""Attempt to detect if ``path`` is the root of a buildsystem's artifacts
"""Detect if ``path`` is the root of a buildsystem's artifacts

return False

if any(fnmatch_ex(pat, path) for pat in ("build", "dist")):
indicators = ("setup.py", "setup.cfg", "pyproject.toml")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is important of understand that setup.cfg can be an indicator only in case pyproject.toml with setuptools pointers exists, or setup.py is present.

So the check would need to be more involved.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to verify my understanding of the logic required. I've re-written it and the tests to reflect how I currently understood the discussion here and in #12625, basically, we first need to see setup.cfg, and then only if setup.py or pyproject.toml with appropriate setuptools pointers exist then we'll ignore collecting build and dist


if any(fnmatch_ex(pat, path) for pat in ("build", "dist")):
indicators = ("setup.py", "setup.cfg", "pyproject.toml")
if any((path.parent / f).is_file() for f in indicators):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels like this is cheaper than fnmatch. Could you try adding a “guard expression” and bailing early if none of these exist?

build_system = parsed_toml.get("build-system", {}).get("requires")
if "setuptools" in build_system:
return True
except Exception:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to avoid using a except Exception here unless absolute necessary -- from what I see on the code inside the try/except block, seems that we are taking in account missing keys already.

Did you account that tomlib.loads might fail due to a malformed TOML file? If so let's catch the precise exception here instead.

@@ -418,6 +457,10 @@ def pytest_ignore_collect(collection_path: Path, config: Config) -> bool | None:
if not allow_in_venv and _in_venv(collection_path):
return True

allow_in_build = False # config.getoption("collect_in_build")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left over?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bot:chronographer:provided (automation) changelog entry is part of PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

only ignore build/dist directory if next to a project config file
3 participants