Skip to content

Commit 1a2b9e9

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

File tree

5 files changed

+173
-0
lines changed

5 files changed

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

0 commit comments

Comments
 (0)