Respect excluded subpackages in build_py - #5248
Conversation
|
Tick the box to add this pull request to the merge queue (same as
|
There was a problem hiding this comment.
Hi @itscloud0, thank you very much for the contribution.
I think I understand the motivation behind the PR. However, I think we should add another test to cover what I consider to be an interesting edge case.
Would it be possible to add a test along the following lines?
pyproject.toml
MANIFEST.in
src/
└── mypkg/
├── __init__.py
├── data.txt
└── subpkg/
├── sample1.json
└── sample2.json
with:
[tool.setuptools]
include-package-data = true
[tool.setuptools.packages.find]
where = ["src"]
exclude = ["mypkg.subpkg", "mypkg.subpkg.*"]and a MANIFEST.in that includes the contents of mypkg/subpkg (for the sake of the sdist).
My understanding of the reasoning behind this PR is that an explicitly excluded nested package should not be reintroduced into the final wheel via include-package-data = true.
If that interpretation is correct, I wouldn't expect the files under mypkg/subpkg/ to appear in the wheel.
I realise there are different views on whether directories without Python files should be considered packages. Personally, I don't make any differentiation (which is consistent with the Python interpreter behaviour). But, for this example in particular, the distinction should be irrelevant since the directory is explicitly excluded (so the user expectation would be for the directory to not be present in the final wheel).
| for part in candidate.relative_to(directory).with_suffix("").parts | ||
| ) | ||
| for candidate in Path(directory).glob("**/*.py") | ||
| ) |
There was a problem hiding this comment.
I am a bit concerned about the amount of nested iteration introduced by this change.
_find_package_data_owner is called for each file discovered by the _filter_build_files function.
In turn, _find_package_data_owner contains a while loop.
Inside this loop, _contains_python_sources is called.
Inside _contains_python_sources, we have another two levels of iteration.
It is fine that while directory and directory != previous and directory not in src_dirs: is essentially the same as in the original code, but the addition of these extra loops appears to increase the overall complexity, specially because Path.glob("**/*.py") may repeatly invoke recursive filesystem scans.
Maybe this will impact performance on larger source trees...
Summary of changes
Fix
packages.find.excludeso excluded nested Python subpackages are not copied into wheels throughinclude_package_data=True.Closes #3260.
Problem
On current
main, a project configured with:include_package_data = Truepackages = find:exclude = *.tests*still ships
mypkg/tests/*in the built wheel when those files are pulled in throughMANIFEST.in.The repo already had an xfailed regression for this case in
setuptools/tests/test_build_py.py, and a direct repro on currentmainstill produced a wheel containingmypkg/tests/test_mypkg.pyandmypkg/tests/test_file.txt.Solution
When
build_pywalks manifest entries, stop promoting files from nested directories that already contain Python sources into the parent package's data files. That keeps explicitly excluded subpackages out of the wheel instead of reintroducing them throughinclude_package_data.The existing regression now asserts the intended behavior directly instead of warning and xfail-ing.
Verification
uv run --extra test --extra cover pytest setuptools/tests/test_build_py.py -quvx ruff check setuptools/command/build_py.py setuptools/tests/test_build_py.pyuvx ruff format --check setuptools/command/build_py.py setuptools/tests/test_build_py.pymypkg/testswheel build onmain, then reran it after the patch and confirmed the wheel no longer containsmypkg/tests/*.