Skip to content

Commit 322cf83

Browse files
committed
feat: read build-backend ignore lists
Signed-off-by: Henry Schreiner <[email protected]>
1 parent 08067d2 commit 322cf83

File tree

5 files changed

+180
-0
lines changed

5 files changed

+180
-0
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ git-only = []
9999
default-ignore = true
100100
recurse-submodules = true
101101
mode = "git"
102+
build-backend = "auto"
102103
```
103104

104105
You can add `.gitignore` style lines here, and you can turn off the default
@@ -112,6 +113,12 @@ You can also select `mode = "all"`, which will instead check every file on your
112113
system. Be prepared to ignore lots of things manually, like `*.pyc` files, if
113114
you use this.
114115

116+
You can tell check-sdist to look for exclude lists for a specific build backend
117+
with `build-backend`, or `"none"` to only use it's own exclude list. Build
118+
backends supported are `"flit_core.buildapi"`, `"hatchling.build"`, and
119+
`"scikit_build_core.build"`. The default, `"auto"`, will try to detect the build
120+
backend if `build-system.build-backend` is set to a known value.
121+
115122
### See also
116123

117124
- [check-manifest](https://github.com/mgedmin/check-manifest): A (currently)

src/check_sdist/__main__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
from . import __version__
1212
from ._compat import tomllib
13+
from .backends import backend_ignored_patterns
1314
from .git import git_files
1415
from .inject import inject_junk_files
1516
from .resources import resources
@@ -54,6 +55,7 @@ def compare(
5455

5556
installer = select_installer(installer)
5657

58+
pyproject = {}
5759
config = {}
5860
pyproject_toml = source_dir.joinpath("pyproject.toml")
5961
with contextlib.suppress(FileNotFoundError), pyproject_toml.open("rb") as f:
@@ -65,6 +67,7 @@ def compare(
6567
default_ignore = config.get("default-ignore", True)
6668
recurse_submodules = config.get("recurse-submodules", True)
6769
mode = config.get("mode", "git")
70+
backend = config.get("build-backend", "auto")
6871

6972
sdist = sdist_files(source_dir, isolated=isolated, installer=installer) - {
7073
"PKG-INFO"
@@ -90,6 +93,8 @@ def compare(
9093
sdist_only = frozenset(p for p in sdist - git if not sdist_spec.match_file(p))
9194
git_only = frozenset(p for p in git - sdist if not git_spec.match_file(p))
9295

96+
git_only = backend_ignored_patterns(backend, pyproject, git_only)
97+
9398
if verbose:
9499
print("SDist contents:")
95100
print(*(f" {x}" for x in sorted(sdist)), sep="\n")

src/check_sdist/backends.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
from __future__ import annotations
2+
3+
from pathlib import Path
4+
from typing import Any
5+
6+
import pathspec
7+
8+
9+
def backend_ignored_patterns(
10+
backend: str, pyproject: dict[str, Any], files: frozenset[str]
11+
) -> frozenset[str]:
12+
"""
13+
Return the ignored patterns for the given backend. If the generator is
14+
"none", then no patterns are ignored. If the generator is "auto", then the
15+
value is read from the pyproject.toml file. Any recognized backend can also
16+
be specified directly.
17+
"""
18+
19+
if backend == "none":
20+
return files
21+
22+
backend_resolved = (
23+
pyproject.get("build-system", {}).get(
24+
"build-backend", "setuptools.build_meta.__legacy__"
25+
)
26+
if backend == "auto"
27+
else backend
28+
)
29+
30+
if backend_resolved == "flit_core.buildapi":
31+
flit_core_exclude = (
32+
pyproject.get("tool", {})
33+
.get("flit", {})
34+
.get("sdist", {})
35+
.get("exclude", [])
36+
)
37+
for pattern in flit_core_exclude:
38+
results = {str(p) for p in Path().glob(pattern)}
39+
files = files - results
40+
return files
41+
if backend_resolved == "hatchling.build":
42+
hatch_exclude = (
43+
pyproject.get("tool", {})
44+
.get("hatch", {})
45+
.get("build", {})
46+
.get("targets", {})
47+
.get("sdist", {})
48+
.get("exclude", [])
49+
)
50+
sdist_spec = pathspec.GitIgnoreSpec.from_lines(hatch_exclude)
51+
return frozenset(p for p in files if not sdist_spec.match_file(p))
52+
if backend_resolved == "scikit_build_core.build":
53+
hatch_exclude = (
54+
pyproject.get("tool", {})
55+
.get("scikit-build", {})
56+
.get("sdist", {})
57+
.get("exclude", [])
58+
)
59+
sdist_spec = pathspec.GitIgnoreSpec.from_lines(hatch_exclude)
60+
return frozenset(p for p in files if not sdist_spec.match_file(p))
61+
if backend != "auto":
62+
msg = f"Unknown backend: {backend} - please add support in check_dist.backends"
63+
raise ValueError(msg)
64+
return files

src/check_sdist/resources/check-sdist.schema.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,17 @@
3333
"description": "What to use as baseline",
3434
"default": "git",
3535
"enum": ["git", "all"]
36+
},
37+
"build-backend": {
38+
"description": "What to expect as build-backend, in order to look for exclude lists",
39+
"default": "auto",
40+
"enum": [
41+
"auto",
42+
"none",
43+
"flit_core.buildapi",
44+
"hatchling.build",
45+
"scikit_build_core.build"
46+
]
3647
}
3748
}
3849
}

tests/test_package.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
from __future__ import annotations
22

3+
import inspect
34
import os
5+
import subprocess
46
from pathlib import Path
57

8+
import pytest
9+
610
from check_sdist.__main__ import compare
711
from check_sdist.inject import inject_junk_files
812

@@ -27,3 +31,92 @@ def test_self_dir_injected():
2731
assert compare(DIR.parent, isolated=True) == 0
2832
end = get_all_files(DIR.parent)
2933
assert start == end
34+
35+
36+
@pytest.fixture
37+
def git_dir(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
38+
monkeypatch.chdir(tmp_path)
39+
subprocess.run(["git", "init"], check=True)
40+
subprocess.run(["git", "config", "user.email", "[email protected]"], check=True)
41+
subprocess.run(["git", "config", "user.name", "CI"], check=True)
42+
return tmp_path
43+
44+
45+
@pytest.mark.usefixtures("git_dir")
46+
@pytest.mark.parametrize("backend", ["auto", "none"])
47+
def test_hatchling(backend: str):
48+
Path("pyproject.toml").write_text(
49+
inspect.cleandoc(f"""
50+
[build-system]
51+
requires = ["hatchling"]
52+
build-backend = "hatchling.build"
53+
54+
[project]
55+
name = "hatchling-test"
56+
version = "0.1.0"
57+
58+
[tool.hatch]
59+
build.targets.sdist.exclude = ["ignore*", "some-file", "**/notme.txt"]
60+
61+
[tool.check-sdist]
62+
build-backend = "{backend}"
63+
""")
64+
)
65+
Path(".gitignore").write_text(
66+
inspect.cleandoc("""
67+
some-ignored-file.txt
68+
""")
69+
)
70+
Path("ignore-me.txt").touch()
71+
Path("not-ignored.txt").touch()
72+
Path("some-file").touch()
73+
Path("some-dir").mkdir()
74+
Path("some-dir/notme.txt").touch()
75+
subprocess.run(["git", "add", "."], check=True)
76+
subprocess.run(["git", "commit", "-m", "Initial commit"], check=True)
77+
Path("some-ignored-file.txt").touch()
78+
assert compare(Path(), isolated=True, verbose=True) == (
79+
0 if backend == "auto" else 2
80+
)
81+
82+
83+
@pytest.mark.usefixtures("git_dir")
84+
@pytest.mark.parametrize("backend", ["auto", "none"])
85+
def test_flit_core(backend: str):
86+
Path("pyproject.toml").write_text(
87+
inspect.cleandoc(f"""
88+
[build-system]
89+
requires = ["flit-core"]
90+
build-backend = "flit_core.buildapi"
91+
92+
[project]
93+
name = "flit-core-test"
94+
version = "0.1.0"
95+
description = "A test package"
96+
97+
[tool.flit.sdist]
98+
include = ["not-ignored.txt", ".gitignore"]
99+
exclude = ["ignore*", "some-file", "**/notme.txt"]
100+
101+
[tool.check-sdist]
102+
build-backend = "{backend}"
103+
""")
104+
)
105+
Path(".gitignore").write_text(
106+
inspect.cleandoc("""
107+
some-ignored-file.txt
108+
""")
109+
)
110+
Path("ignore-me.txt").touch()
111+
Path("not-ignored.txt").touch()
112+
Path("some-file").touch()
113+
Path("some-dir").mkdir()
114+
Path("some-dir/notme.txt").touch()
115+
Path("flit_core_test").mkdir()
116+
Path("flit_core_test/__init__.py").touch()
117+
subprocess.run(["git", "add", "."], check=True)
118+
subprocess.run(["git", "commit", "-m", "Initial commit"], check=True)
119+
Path("some-ignored-file.txt").touch()
120+
assert compare(Path(), isolated=True, verbose=True) == (
121+
0 if backend == "auto" else 2
122+
)

0 commit comments

Comments
 (0)