Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions newsfragments/5133.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fixed ``build_py`` to avoid copying extension source files from ``SOURCES.txt``
into ``build/lib`` as package data.
20 changes: 19 additions & 1 deletion setuptools/command/build_py.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ def analyze_manifest(self) -> None:
for package in self.packages or ():
# Locate package source directory
src_dirs[assert_relative(self.get_package_dir(package))] = package
extension_files = self._get_extension_source_files()

if (
self.existing_egg_info_dir
Expand All @@ -197,7 +198,12 @@ def analyze_manifest(self) -> None:
files = ei_cmd.filelist.files

check = _IncludePackageDataAbuse()
for path in self._filter_build_files(files, egg_info_dir):
files = (
path
for path in self._filter_build_files(files, egg_info_dir)
if self._path_norm(path) not in extension_files
)
for path in files:
d, f = os.path.split(assert_relative(path))
prev = None
oldf = f
Expand All @@ -215,6 +221,18 @@ def analyze_manifest(self) -> None:
check.warn(importable)
self.manifest_files.setdefault(src_dirs[d], []).append(path)

def _get_extension_source_files(self) -> set[str]:
"""Return extension source files that are build inputs."""
return {
self._path_norm(path)
for ext in self.distribution.ext_modules or ()
for path in getattr(ext, "sources", None) or ()
}

@staticmethod
def _path_norm(path: StrPath) -> str:
return os.path.normcase(os.path.abspath(os.fspath(path)))

def _filter_build_files(
self, files: Iterable[str], egg_info: StrPath
) -> Iterator[str]:
Expand Down
43 changes: 43 additions & 0 deletions setuptools/tests/test_build_py.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from setuptools import SetuptoolsDeprecationWarning
from setuptools.dist import Distribution
from setuptools.extension import Extension

from .textwrap import DALS

Expand Down Expand Up @@ -258,6 +259,48 @@ def test_existing_egg_info(tmpdir_cwd, monkeypatch):
assert example in outputs


def test_extension_sources_are_not_package_data(tmpdir_cwd):
"""
Extension source files belong in sdists, but build_py should not copy them
to build/lib as package data.
"""
jaraco.path.build({
"src": {
"foo": {
"__init__.py": "",
"resource.txt": "",
"extension": {"bar.cpp": ""},
},
},
})
egg_info_dir = Path("src/foo.egg-info")
egg_info_dir.mkdir()
(egg_info_dir / "SOURCES.txt").write_text(
f"{os.path.join('src', 'foo', 'resource.txt')}\n"
f"{os.path.join('src', 'foo', 'extension', 'bar.cpp')}\n",
encoding="utf-8",
)
dist = Distribution({
"name": "foo",
"version": "1",
"script_name": "%build_py-test%",
"packages": ["foo"],
"package_dir": {"": "src"},
"include_package_data": True,
"ext_modules": [Extension("foo.extension", ["src/foo/extension/bar.cpp"])],
})

build_py = dist.get_command_obj("build_py")
build_py.finalize_options()
build_py.existing_egg_info_dir = egg_info_dir
build_py.run()

outputs = get_outputs(build_py)
assert "foo/__init__.py" in outputs
assert "foo/resource.txt" in outputs
assert "foo/extension/bar.cpp" not in outputs


EXAMPLE_ARBITRARY_MAPPING = {
"pyproject.toml": DALS(
"""
Expand Down