Skip to content

Commit

Permalink
feat: read build-backend ignore lists
Browse files Browse the repository at this point in the history
Signed-off-by: Henry Schreiner <[email protected]>
  • Loading branch information
henryiii committed Oct 14, 2024
1 parent 08067d2 commit 322cf83
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 0 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ git-only = []
default-ignore = true
recurse-submodules = true
mode = "git"
build-backend = "auto"
```

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

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

### See also

- [check-manifest](https://github.com/mgedmin/check-manifest): A (currently)
Expand Down
5 changes: 5 additions & 0 deletions src/check_sdist/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from . import __version__
from ._compat import tomllib
from .backends import backend_ignored_patterns
from .git import git_files
from .inject import inject_junk_files
from .resources import resources
Expand Down Expand Up @@ -54,6 +55,7 @@ def compare(

installer = select_installer(installer)

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

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

git_only = backend_ignored_patterns(backend, pyproject, git_only)

if verbose:
print("SDist contents:")
print(*(f" {x}" for x in sorted(sdist)), sep="\n")
Expand Down
64 changes: 64 additions & 0 deletions src/check_sdist/backends.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from __future__ import annotations

from pathlib import Path
from typing import Any

import pathspec


def backend_ignored_patterns(
backend: str, pyproject: dict[str, Any], files: frozenset[str]
) -> frozenset[str]:
"""
Return the ignored patterns for the given backend. If the generator is
"none", then no patterns are ignored. If the generator is "auto", then the
value is read from the pyproject.toml file. Any recognized backend can also
be specified directly.
"""

if backend == "none":
return files

backend_resolved = (
pyproject.get("build-system", {}).get(
"build-backend", "setuptools.build_meta.__legacy__"
)
if backend == "auto"
else backend
)

if backend_resolved == "flit_core.buildapi":
flit_core_exclude = (
pyproject.get("tool", {})
.get("flit", {})
.get("sdist", {})
.get("exclude", [])
)
for pattern in flit_core_exclude:
results = {str(p) for p in Path().glob(pattern)}
files = files - results
return files
if backend_resolved == "hatchling.build":
hatch_exclude = (
pyproject.get("tool", {})
.get("hatch", {})
.get("build", {})
.get("targets", {})
.get("sdist", {})
.get("exclude", [])
)
sdist_spec = pathspec.GitIgnoreSpec.from_lines(hatch_exclude)
return frozenset(p for p in files if not sdist_spec.match_file(p))
if backend_resolved == "scikit_build_core.build":
hatch_exclude = (
pyproject.get("tool", {})
.get("scikit-build", {})
.get("sdist", {})
.get("exclude", [])
)
sdist_spec = pathspec.GitIgnoreSpec.from_lines(hatch_exclude)
return frozenset(p for p in files if not sdist_spec.match_file(p))
if backend != "auto":
msg = f"Unknown backend: {backend} - please add support in check_dist.backends"
raise ValueError(msg)

Check warning on line 63 in src/check_sdist/backends.py

View check run for this annotation

Codecov / codecov/patch

src/check_sdist/backends.py#L62-L63

Added lines #L62 - L63 were not covered by tests
return files
11 changes: 11 additions & 0 deletions src/check_sdist/resources/check-sdist.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@
"description": "What to use as baseline",
"default": "git",
"enum": ["git", "all"]
},
"build-backend": {
"description": "What to expect as build-backend, in order to look for exclude lists",
"default": "auto",
"enum": [
"auto",
"none",
"flit_core.buildapi",
"hatchling.build",
"scikit_build_core.build"
]
}
}
}
93 changes: 93 additions & 0 deletions tests/test_package.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
from __future__ import annotations

import inspect
import os
import subprocess
from pathlib import Path

import pytest

from check_sdist.__main__ import compare
from check_sdist.inject import inject_junk_files

Expand All @@ -27,3 +31,92 @@ def test_self_dir_injected():
assert compare(DIR.parent, isolated=True) == 0
end = get_all_files(DIR.parent)
assert start == end


@pytest.fixture
def git_dir(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
monkeypatch.chdir(tmp_path)
subprocess.run(["git", "init"], check=True)
subprocess.run(["git", "config", "user.email", "[email protected]"], check=True)
subprocess.run(["git", "config", "user.name", "CI"], check=True)
return tmp_path


@pytest.mark.usefixtures("git_dir")
@pytest.mark.parametrize("backend", ["auto", "none"])
def test_hatchling(backend: str):
Path("pyproject.toml").write_text(
inspect.cleandoc(f"""
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "hatchling-test"
version = "0.1.0"
[tool.hatch]
build.targets.sdist.exclude = ["ignore*", "some-file", "**/notme.txt"]
[tool.check-sdist]
build-backend = "{backend}"
""")
)
Path(".gitignore").write_text(
inspect.cleandoc("""
some-ignored-file.txt
""")
)
Path("ignore-me.txt").touch()
Path("not-ignored.txt").touch()
Path("some-file").touch()
Path("some-dir").mkdir()
Path("some-dir/notme.txt").touch()
subprocess.run(["git", "add", "."], check=True)
subprocess.run(["git", "commit", "-m", "Initial commit"], check=True)
Path("some-ignored-file.txt").touch()
assert compare(Path(), isolated=True, verbose=True) == (
0 if backend == "auto" else 2
)


@pytest.mark.usefixtures("git_dir")
@pytest.mark.parametrize("backend", ["auto", "none"])
def test_flit_core(backend: str):
Path("pyproject.toml").write_text(
inspect.cleandoc(f"""
[build-system]
requires = ["flit-core"]
build-backend = "flit_core.buildapi"
[project]
name = "flit-core-test"
version = "0.1.0"
description = "A test package"
[tool.flit.sdist]
include = ["not-ignored.txt", ".gitignore"]
exclude = ["ignore*", "some-file", "**/notme.txt"]
[tool.check-sdist]
build-backend = "{backend}"
""")
)
Path(".gitignore").write_text(
inspect.cleandoc("""
some-ignored-file.txt
""")
)
Path("ignore-me.txt").touch()
Path("not-ignored.txt").touch()
Path("some-file").touch()
Path("some-dir").mkdir()
Path("some-dir/notme.txt").touch()
Path("flit_core_test").mkdir()
Path("flit_core_test/__init__.py").touch()
subprocess.run(["git", "add", "."], check=True)
subprocess.run(["git", "commit", "-m", "Initial commit"], check=True)
Path("some-ignored-file.txt").touch()
assert compare(Path(), isolated=True, verbose=True) == (
0 if backend == "auto" else 2
)

0 comments on commit 322cf83

Please sign in to comment.