patchcraft 0.5.0__tar.gz → 0.5.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {patchcraft-0.5.0 → patchcraft-0.5.2}/.gitignore +4 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/CHANGELOG.md +201 -3
- patchcraft-0.5.2/CONTRIBUTING.md +326 -0
- patchcraft-0.5.2/MANIFEST.in +39 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/PKG-INFO +31 -30
- {patchcraft-0.5.0 → patchcraft-0.5.2}/README.md +14 -13
- {patchcraft-0.5.0 → patchcraft-0.5.2}/README.pt-BR.md +14 -13
- {patchcraft-0.5.0 → patchcraft-0.5.2}/README.pypi.md +15 -13
- patchcraft-0.5.2/accel/Cargo.lock +232 -0
- patchcraft-0.5.2/accel/Cargo.toml +32 -0
- patchcraft-0.5.2/accel/README.md +48 -0
- patchcraft-0.5.2/accel/src/kernel.rs +252 -0
- patchcraft-0.5.2/accel/src/lib.rs +88 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/docs/ADR/0003-reversibility-classes.md +1 -0
- patchcraft-0.5.2/docs/ADR/0004-precision-and-effort-parameters.md +227 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/docs/AUXILIARY.md +11 -6
- {patchcraft-0.5.0 → patchcraft-0.5.2}/docs/GUIDE.md +34 -23
- patchcraft-0.5.2/docs/PERFORMANCE.md +116 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/docs/ROADMAP.md +2 -1
- {patchcraft-0.5.0 → patchcraft-0.5.2}/docs/SCOPE.md +9 -6
- {patchcraft-0.5.0 → patchcraft-0.5.2}/docs/THEORY.md +5 -4
- patchcraft-0.5.2/pyproject.toml +130 -0
- patchcraft-0.5.2/setup.cfg +4 -0
- patchcraft-0.5.2/setup.py +81 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/src/patchcraft/__init__.py +15 -1
- {patchcraft-0.5.0 → patchcraft-0.5.2}/src/patchcraft/_accel.py +12 -11
- patchcraft-0.5.2/src/patchcraft/_version.py +24 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/src/patchcraft/reconstruct.py +9 -2
- {patchcraft-0.5.0 → patchcraft-0.5.2}/src/patchcraft/resize.py +12 -3
- {patchcraft-0.5.0 → patchcraft-0.5.2}/src/patchcraft/stitch.py +10 -4
- patchcraft-0.5.2/src/patchcraft.egg-info/PKG-INFO +188 -0
- patchcraft-0.5.2/src/patchcraft.egg-info/SOURCES.txt +74 -0
- patchcraft-0.5.2/src/patchcraft.egg-info/dependency_links.txt +1 -0
- patchcraft-0.5.2/src/patchcraft.egg-info/requires.txt +13 -0
- patchcraft-0.5.2/src/patchcraft.egg-info/scm_file_list.json +83 -0
- patchcraft-0.5.2/src/patchcraft.egg-info/scm_version.json +8 -0
- patchcraft-0.5.2/src/patchcraft.egg-info/top_level.txt +1 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/tests/test_accel.py +10 -10
- {patchcraft-0.5.0 → patchcraft-0.5.2}/tests/test_exactness.py +1 -1
- {patchcraft-0.5.0 → patchcraft-0.5.2}/tests/test_import.py +1 -1
- {patchcraft-0.5.0 → patchcraft-0.5.2}/tests/test_reconstruct.py +21 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/tests/test_resize.py +35 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/tests/test_stitch.py +35 -0
- patchcraft-0.5.2/tools/benchmark.py +165 -0
- patchcraft-0.5.2/tools/check_dist.py +209 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/uv.lock +26 -10
- patchcraft-0.5.0/.github/workflows/release.yml +0 -203
- patchcraft-0.5.0/.github/workflows/test.yml +0 -93
- patchcraft-0.5.0/CONTRIBUTING.md +0 -161
- patchcraft-0.5.0/docs/superpowers/plans/2026-09-01-fase1-bugs-performance.md +0 -1179
- patchcraft-0.5.0/docs/superpowers/plans/2026-09-01-fase2-acel.md +0 -1643
- patchcraft-0.5.0/docs/superpowers/plans/2026-09-01-fase3-g1-predicado.md +0 -1705
- patchcraft-0.5.0/pyproject.toml +0 -112
- {patchcraft-0.5.0 → patchcraft-0.5.2}/.python-version +0 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/LICENSE +0 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/docs/ADR/0001-patch-extraction-api.md +0 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/docs/ADR/0002-patchify-transform.md +0 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/docs/FOCO-1.0.md +0 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/docs/STUDIES/2026-08-04-patch-techniques.md +0 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/docs/USAGE.md +0 -0
- /patchcraft-0.5.0/docs/superpowers/specs/2026-09-01-fase1-bugs-performance-design.md → /patchcraft-0.5.2/docs/design/2026-09-01-fase1-bugs-performance.md +0 -0
- /patchcraft-0.5.0/docs/superpowers/specs/2026-09-01-fase2-acel-design.md → /patchcraft-0.5.2/docs/design/2026-09-01-fase2-acelerador.md +0 -0
- /patchcraft-0.5.0/docs/superpowers/specs/2026-09-01-fase3-g1-predicado-design.md → /patchcraft-0.5.2/docs/design/2026-09-01-fase3-predicado.md +0 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/src/patchcraft/_foldgeom.py +0 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/src/patchcraft/cache.py +0 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/src/patchcraft/extract.py +0 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/src/patchcraft/geometry.py +0 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/src/patchcraft/metrics.py +0 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/src/patchcraft/pair.py +0 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/src/patchcraft/py.typed +0 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/tests/__init__.py +0 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/tests/_datasets.py +0 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/tests/_rng.py +0 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/tests/conftest.py +0 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/tests/test_cache.py +0 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/tests/test_datasets_helper.py +0 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/tests/test_extract.py +0 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/tests/test_geometry.py +0 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/tests/test_metrics.py +0 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/tests/test_pair.py +0 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/tests/test_public_api.py +0 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/tests/test_reference.py +0 -0
- {patchcraft-0.5.0 → patchcraft-0.5.2}/tests/test_rng.py +0 -0
|
@@ -6,6 +6,200 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.5.2] - 2026-09-03
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **`stitch(weight="hann")` corrupted the corner band of every patch of 99 or
|
|
14
|
+
more.** The denominator was floored by an absolute `clamp(min=1e-6)` whose
|
|
15
|
+
comment called it a defensive no-op. It was not: the 2-D hann corner weight
|
|
16
|
+
is `(pi/(n+1))**4`, which crosses below `1e-6` at patch 99, so the corner
|
|
17
|
+
band was divided by the floor rather than by the real weight. Measured at
|
|
18
|
+
640x640, patch 256, stride 128: maximum error 0.94 on data in [0, 1] and 960
|
|
19
|
+
wrong pixels, identical in float64 because the constant and not the precision
|
|
20
|
+
was the cause. The floor is now `finfo(dtype).tiny`, the model already used
|
|
21
|
+
in `metrics.py`, which never reaches a legitimate weight: the smallest 2-D
|
|
22
|
+
hann corner even at patch 1024 is 8.8e-11. THEORY 9.9 claimed no covered
|
|
23
|
+
pixel is zeroed by the window, which was true of the window and false of the
|
|
24
|
+
result.
|
|
25
|
+
- **`reconstruct` returned a view aliasing the caller's patches on a
|
|
26
|
+
single-patch grid.** Writing to the patches afterwards changed an image the
|
|
27
|
+
function had already returned, measured as 0.744 becoming 99.0. `extract`
|
|
28
|
+
guards the mirror image of this case and `reconstruct` did not.
|
|
29
|
+
- **`resize(..., backend="pil")` raised a raw `RuntimeError` on `int8`.** The
|
|
30
|
+
inbound `clamp(0, 255)` was applied in the input dtype, where the bound 255
|
|
31
|
+
is unrepresentable, so torch raised `value cannot be converted to type
|
|
32
|
+
int8_t without overflow` before clamping anything. Every integer dtype now
|
|
33
|
+
works on both backends, and THEORY 9.4 gained the row for integer tensors on
|
|
34
|
+
the PIL backend, which it had never specified.
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
### Removed
|
|
38
|
+
|
|
39
|
+
- The agentic workflow's leftovers. `.superpowers/` at the repository root
|
|
40
|
+
held 62 files of review diffs and task-progress notes, hidden from
|
|
41
|
+
`git status` by a nested `.gitignore` containing `*`, so it sat in the tree
|
|
42
|
+
unseen and reached the 0.5.0 sdist. The three implementation plans under
|
|
43
|
+
`docs/superpowers/plans/` were 4527 lines of checkbox task lists addressed
|
|
44
|
+
"For agentic workers", and they shipped to PyPI inside every sdist.
|
|
45
|
+
|
|
46
|
+
### Added
|
|
47
|
+
|
|
48
|
+
- `docs/ADR/0004-precision-and-effort-parameters.md`, in `Proposed` status: names the
|
|
49
|
+
precision policy the library already applies in five places, and designs an `effort=`
|
|
50
|
+
preset under the constraint that it may change speed and memory but never a returned
|
|
51
|
+
value. Nothing is implemented; the ADR records the design, the effort per step and the
|
|
52
|
+
order. It amends ADR 0003 on one measured point, that the exactness predicate is a
|
|
53
|
+
property of the geometry and the accumulator rather than of the geometry alone.
|
|
54
|
+
- `outreach/`, material for presenting the project publicly, in the shape the
|
|
55
|
+
sibling projects use: a dated news source at the root and one subfolder per
|
|
56
|
+
channel, with the rule that no channel text changes before the source does.
|
|
57
|
+
It publishes no new measurement, and it is pruned from the sdist.
|
|
58
|
+
|
|
59
|
+
### Changed
|
|
60
|
+
|
|
61
|
+
- The three design specs that were under `docs/superpowers/specs/` moved to
|
|
62
|
+
`docs/design/`. They are the record of what was measured before each phase
|
|
63
|
+
was built, including the spike table that rejected `conv_transpose2d` and
|
|
64
|
+
`index_add_` before the Rust kernel was written, and Amendment A, which
|
|
65
|
+
`tests/test_exactness.py` cites for the per-pixel error bound.
|
|
66
|
+
- The sdist is 264 KiB where it was 316 KiB.
|
|
67
|
+
- Nothing ephemeral is kept in the project folder any more. `accel/target`
|
|
68
|
+
had reached 269 MiB of Rust build output, and `dist/`, `.pytest_cache` and
|
|
69
|
+
`src/patchcraft.egg-info` were accumulating alongside it. The caches are
|
|
70
|
+
now redirected by user-level environment variables, joining the ones this
|
|
71
|
+
machine already had for uv, ruff, mypy, pip and `__pycache__`, and
|
|
72
|
+
`CONTRIBUTING.md` records the convention and why it is not committed here.
|
|
73
|
+
- `.vscode/settings.json` stays tracked and is pruned from the sdist instead.
|
|
74
|
+
- **The versioning rule was self-contradictory and is corrected.** One row said
|
|
75
|
+
an output value change is a `0.Y.0`, the next said a fix is a `0.y.Z`, and a
|
|
76
|
+
bug fix satisfies both, so the table made every fix a minor and left patch
|
|
77
|
+
releases unable to fix anything. It also contradicted the project's own
|
|
78
|
+
history: `0.2.1` changed `stitch(weight="hann")` output for every geometry
|
|
79
|
+
and shipped as a patch. The rule now turns on whether the contract moved or
|
|
80
|
+
the implementation stopped violating it, which sorts `0.2.1`, `0.3.0`,
|
|
81
|
+
`0.5.0` and `0.5.2` correctly. `CONTRIBUTING.md` carries the rule with those
|
|
82
|
+
four worked, and GUIDE section 8 item 6 no longer cites a patch release as
|
|
83
|
+
an example of what only a minor may do.
|
|
84
|
+
Its `${workspaceFolder}/.venv` path is not broken: `.venv` is a directory
|
|
85
|
+
junction to the real environment outside the tree, so the setting resolves
|
|
86
|
+
and is portable to any checkout following the same convention.
|
|
87
|
+
|
|
88
|
+
## [0.5.1] - 2026-09-02
|
|
89
|
+
|
|
90
|
+
> Numbered `0.6.0` while it was being built. Corrected before release: the
|
|
91
|
+
> middle digit is the compatibility boundary, and this release changes no
|
|
92
|
+
> signature, no name and no output value. See the versioning policy and its
|
|
93
|
+
> errata in [`CONTRIBUTING.md`](CONTRIBUTING.md).
|
|
94
|
+
|
|
95
|
+
### Changed
|
|
96
|
+
|
|
97
|
+
- **The native accelerator ships inside this wheel.** It was going to be a
|
|
98
|
+
second PyPI project, `patchcraft-accel`, reached through
|
|
99
|
+
`pip install patchcraft[accel]`. That project was never published and now
|
|
100
|
+
never will be. A release produces one sdist and six wheels for the single
|
|
101
|
+
`patchcraft` project: five tagged `cp312-abi3-<platform>` with the Rust
|
|
102
|
+
kernel compiled in as `patchcraft._accel_native`, and one `py3-none-any`
|
|
103
|
+
for every other platform. Installers prefer the most specific compatible
|
|
104
|
+
tag, so `pip install patchcraft` picks up the accelerator where one exists
|
|
105
|
+
and the pure wheel everywhere else, with no extra to remember. The pattern
|
|
106
|
+
and its universal fallback follow `coverage`, which publishes the same two
|
|
107
|
+
shapes for one version.
|
|
108
|
+
- The `accel` extra is removed. Nothing depended on it, because it never
|
|
109
|
+
reached the index.
|
|
110
|
+
- The accelerator is now on by default where a wheel carries it, rather than
|
|
111
|
+
opt-in. The two paths are bit-identical by test, and `PATCHCRAFT_ACCEL=0`
|
|
112
|
+
still forces the pure one at runtime.
|
|
113
|
+
- Build backend moves from hatchling to setuptools with setuptools-rust, which
|
|
114
|
+
is what makes one project able to emit both shapes of wheel. `setup.py`
|
|
115
|
+
holds the single decision, driven by `PATCHCRAFT_PURE_PYTHON` and
|
|
116
|
+
`PATCHCRAFT_REQUIRE_EXTENSION`; everything else stays declarative in
|
|
117
|
+
`pyproject.toml`. Installing from the sdist compiles the extension when a
|
|
118
|
+
Rust toolchain is present and degrades to a pure install when it is not.
|
|
119
|
+
- License metadata follows PEP 639: the SPDX expression `MIT` with
|
|
120
|
+
`license-files`, and the superseded `License :: OSI Approved` classifier is
|
|
121
|
+
gone. setuptools rejects carrying both.
|
|
122
|
+
|
|
123
|
+
### Added
|
|
124
|
+
|
|
125
|
+
- **The version comes from the git tag.** `setuptools-scm` derives it at build
|
|
126
|
+
time, so no file in the source states a version, the release procedure loses
|
|
127
|
+
its bump step, and a tag and a release can no longer disagree.
|
|
128
|
+
`patchcraft.__version__` still works: the build writes a generated
|
|
129
|
+
`_version.py` that `__init__.py` imports, which costs nothing at import time,
|
|
130
|
+
with `importlib.metadata` as a fallback for a tree that was never built.
|
|
131
|
+
Measured before choosing: `importlib.metadata` alone was the wrong default,
|
|
132
|
+
because it took 56 ms on first call and reported a stale version in an
|
|
133
|
+
editable install.
|
|
134
|
+
- `docs/PERFORMANCE.md`, which is where the accelerator's measurements live.
|
|
135
|
+
It carries the test machine, the versions, the date, the reproduction
|
|
136
|
+
command and what the numbers do not say, and its table is pasted from
|
|
137
|
+
`tools/benchmark.py --markdown` rather than retyped.
|
|
138
|
+
- The Rust kernel is now always built in release mode, including in an
|
|
139
|
+
editable install. It was following the build command, so a development
|
|
140
|
+
checkout compiled a debug kernel: measured here, that turned a 14x speedup
|
|
141
|
+
into 2.1x, which made local benchmarks meaningless and made the accelerated
|
|
142
|
+
CI job exercise code no user runs.
|
|
143
|
+
- `patchcraft.__version__` is re-exported explicitly, so `mypy --strict` in a
|
|
144
|
+
consuming project still accepts it. Moving it out of a literal and into an
|
|
145
|
+
import had quietly made it an implicit re-export, which strict mode rejects.
|
|
146
|
+
- `tools/benchmark.py` measures the accelerated fold against the pure one and
|
|
147
|
+
refuses to report a timing unless the two results are bit-identical. The
|
|
148
|
+
numbers in the documentation come from it, and it is the reproduction
|
|
149
|
+
command they cite.
|
|
150
|
+
- Every build checkout uses `fetch-depth: 0`. The default shallow clone hides
|
|
151
|
+
the tags, and setuptools-scm's fallback for that is a silent
|
|
152
|
+
`0.1.dev1+g<sha>`, a version that walks backwards rather than an error.
|
|
153
|
+
- `tools/check_dist.py` grew `--expect-version` and `--require-universal`, and
|
|
154
|
+
it now rejects any artifact carrying a local version segment. PyPI refuses
|
|
155
|
+
those on upload, so this turns a late and confusing server-side rejection
|
|
156
|
+
into an early local one. It also fixes a bug in the job that checks the
|
|
157
|
+
native wheels, which demanded a universal wheel that job never has.
|
|
158
|
+
- Linux aarch64 wheels, which the previous four-target matrix did not cover.
|
|
159
|
+
- `tools/check_dist.py`, run before every upload. It fails the release if a
|
|
160
|
+
platform wheel lost its extension, which `optional=True` would otherwise
|
|
161
|
+
let through as a silently slow wheel, or if the universal wheel gained one.
|
|
162
|
+
- The versioning policy now names the resolver rule it follows, which is that
|
|
163
|
+
`0.y` is the compatibility boundary in Cargo, npm and PEP 440 alike, and it
|
|
164
|
+
carries an errata for the two releases numbered before the rule was written.
|
|
165
|
+
- Version numbers are gone from the documentation surface. The three cover
|
|
166
|
+
pages carry none at all, since the PyPI badge already shows the current
|
|
167
|
+
release, and the manual keeps only the one the citation entry needs. The
|
|
168
|
+
release archaeology those pages used to recite lives here, and the rest is
|
|
169
|
+
in the git history.
|
|
170
|
+
- `MANIFEST.in`, which puts `accel/` in the sdist so the source path can build
|
|
171
|
+
the extension, and keeps `lab/`, `.superpowers/` and cargo's `target/` out.
|
|
172
|
+
|
|
173
|
+
- **Corrected: the hand-rolled and library tile-and-blend results are not
|
|
174
|
+
bit-identical.** The three cover pages and GUIDE section 1 claimed they were,
|
|
175
|
+
and the GUIDE block asserted it. Re-measured: they agree to 1.17e-05 on a
|
|
176
|
+
value in [0, 1], differing on 24500 of 49152 elements. The cause is the 0.3.0
|
|
177
|
+
separable denominator, where `stitch` sums two 1-D window folds while the
|
|
178
|
+
hand-rolled twin folds a replicated 2-D kernel. `stitch(weight="uniform")`
|
|
179
|
+
is still bit-identical to `reconstruct`. Nothing about the round-trip
|
|
180
|
+
contract changed; the documentation was simply asserting more than the code
|
|
181
|
+
delivered.
|
|
182
|
+
- Documentation corrections found by auditing every page against the code:
|
|
183
|
+
the changelog's reference links did not define the two most recent versions;
|
|
184
|
+
`docs/AUXILIARY.md` listed wheel contents that predate three releases;
|
|
185
|
+
`docs/SCOPE.md` still promised "pure-Python single-threaded", which the
|
|
186
|
+
rayon-parallel kernel contradicts; `docs/ROADMAP.md` still named hatchling;
|
|
187
|
+
GUIDE said the library "is not faster", that the no-zstandard branch is
|
|
188
|
+
untested, and that CI is six cells; the cover pages pointed at `USAGE.md` as
|
|
189
|
+
a walkthrough of 20 symbols when it covers 18 and says so. `CONTRIBUTING.md`
|
|
190
|
+
told a contributor to run `pytest` after a `uv sync` that does not install it.
|
|
191
|
+
|
|
192
|
+
### Notes
|
|
193
|
+
|
|
194
|
+
- No signature, no name and no output value changed. The public surface is the
|
|
195
|
+
same 20 names, still pinned by `tests/test_public_api.py`.
|
|
196
|
+
- Measured on torch 2.14, CPU, mean over the dev machine: `reconstruct` at
|
|
197
|
+
3x512x512 p=32 s=16 goes 11.4 ms to 2.5 ms (4.5x), at 3x2048x2048 p=64 s=32
|
|
198
|
+
477.9 ms to 31.8 ms (15.0x); `stitch[hann]` 21.6 ms to 7.0 ms (3.1x) and
|
|
199
|
+
488.7 ms to 38.8 ms (12.6x). The gain is algorithmic rather than a thread
|
|
200
|
+
count: the pure `F.fold` measured between 365 and 465 ms on that largest
|
|
201
|
+
case at 4, 8, 16 and 36 torch threads.
|
|
202
|
+
|
|
9
203
|
## [0.5.0] - 2026-09-02
|
|
10
204
|
|
|
11
205
|
### Added
|
|
@@ -472,9 +666,13 @@ First public release. Public API stable; signatures will only change in 1.x.
|
|
|
472
666
|
- [`README.md`](README.md) covers installation, the car-vs-track metaphor,
|
|
473
667
|
validation lab.
|
|
474
668
|
|
|
475
|
-
[Unreleased]: https://github.com/LeoPR/PatchCraft/compare/v0.
|
|
476
|
-
[0.
|
|
477
|
-
[0.
|
|
669
|
+
[Unreleased]: https://github.com/LeoPR/PatchCraft/compare/v0.5.2...HEAD
|
|
670
|
+
[0.5.2]: https://github.com/LeoPR/PatchCraft/releases/tag/v0.5.2
|
|
671
|
+
[0.5.1]: https://github.com/LeoPR/PatchCraft/releases/tag/v0.5.1
|
|
672
|
+
[0.5.0]: https://github.com/LeoPR/PatchCraft/releases/tag/v0.5.0
|
|
673
|
+
|
|
674
|
+
<!-- 0.4.0 and 0.3.0 are deliberately unlinked: they are source milestones that
|
|
675
|
+
were never tagged and never released, as their own entries say. -->
|
|
478
676
|
[0.2.2]: https://github.com/LeoPR/PatchCraft/releases/tag/v0.2.2
|
|
479
677
|
[0.2.1]: https://github.com/LeoPR/PatchCraft/releases/tag/v0.2.1
|
|
480
678
|
[0.2.0]: https://github.com/LeoPR/PatchCraft/releases/tag/v0.2.0
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
# Contributing to PatchCraft
|
|
2
|
+
|
|
3
|
+
If you've cloned this repo and want to run the test suite, understand how the project is laid out, or follow the validation conventions, this is the page. For installation and usage, see the [README](README.md).
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Run tests
|
|
8
|
+
|
|
9
|
+
The test runner and the linters live in the `dev` extra, so a plain `uv sync`
|
|
10
|
+
does not install them:
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
uv sync --locked --extra cache --extra dev
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
pytest
|
|
18
|
+
pytest -m "not gpu" # skip GPU-requiring tests
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
CI runs the full suite on every push and PR. See [`.github/workflows/test.yml`](.github/workflows/test.yml). Matrix is `{ubuntu-latest, windows-latest} × {python 3.12, 3.13, 3.14}` for the pure path, plus a second job on both operating systems for the accelerated one. To run the same checks locally before pushing:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
ruff check src tests
|
|
25
|
+
mypy --strict src
|
|
26
|
+
pytest -m "not gpu"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## The two paths
|
|
32
|
+
|
|
33
|
+
The overlapping fold in `reconstruct` and `stitch` has a Rust implementation
|
|
34
|
+
in [`accel/`](accel/), compiled into the wheel on the platforms CI builds for.
|
|
35
|
+
Everything else runs the same operation in torch. The two are bit-identical by
|
|
36
|
+
test, so a contribution has to keep both green rather than pick one.
|
|
37
|
+
|
|
38
|
+
Which one your checkout runs depends on whether the extension got built:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
python -c "import patchcraft; print(patchcraft.accel_available())"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`uv sync --extra cache --extra dev` builds it when a Rust toolchain is on
|
|
45
|
+
PATH, always in release mode. Two environment variables override that, and CI
|
|
46
|
+
sets both explicitly rather than relying on what happens to be installed:
|
|
47
|
+
|
|
48
|
+
| Variable | Effect |
|
|
49
|
+
|---|---|
|
|
50
|
+
| `PATCHCRAFT_PURE_PYTHON=1` | Skip the extension. How the universal wheel is built. |
|
|
51
|
+
| `PATCHCRAFT_REQUIRE_EXTENSION=1` | Make a Rust build failure fatal instead of degrading to the pure path. |
|
|
52
|
+
| `PATCHCRAFT_ACCEL=0` | Runtime only: ignore an extension that is already built. |
|
|
53
|
+
|
|
54
|
+
The Rust kernel has its own tests, which need no Python at all:
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
cargo test --manifest-path accel/Cargo.toml
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
One local wrinkle worth knowing. If your checkout sits under a path with
|
|
61
|
+
non-ASCII characters, `setuptools-rust` mis-decodes the artifact path that
|
|
62
|
+
cargo reports and the copy step fails. Point the build at an ASCII directory
|
|
63
|
+
and it works:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
CARGO_TARGET_DIR=/tmp/pc-target uv sync --extra cache --extra dev
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Nothing ephemeral lives in the project folder
|
|
72
|
+
|
|
73
|
+
Caches, build output and virtual environments are kept off the tree, because
|
|
74
|
+
they are large, regenerable and machine-specific. They are redirected by
|
|
75
|
+
user-level environment variables rather than by anything committed here: a
|
|
76
|
+
machine path in `pyproject.toml` would be wrong for every other machine and
|
|
77
|
+
for CI, which is why one was removed in 0.3.0.
|
|
78
|
+
|
|
79
|
+
| Variable | Sends |
|
|
80
|
+
|---|---|
|
|
81
|
+
| `CARGO_TARGET_DIR` | the Rust build output, which reached 269 MiB inside `accel/target` before it was redirected |
|
|
82
|
+
| `PYTEST_ADDOPTS=-o cache_dir=...` | `.pytest_cache` |
|
|
83
|
+
| `RUFF_CACHE_DIR`, `MYPY_CACHE_DIR` | the linter caches |
|
|
84
|
+
| `UV_CACHE_DIR`, `PIP_CACHE_DIR` | downloaded wheels |
|
|
85
|
+
| `PYTHONPYCACHEPREFIX` | `__pycache__` |
|
|
86
|
+
|
|
87
|
+
The virtual environment lives outside the tree too; see
|
|
88
|
+
[`docs/AUXILIARY.md`](docs/AUXILIARY.md).
|
|
89
|
+
|
|
90
|
+
Two directories still appear during a build and are gitignored: `build/` and
|
|
91
|
+
`src/patchcraft.egg-info/`, both setuptools working state. They exist only
|
|
92
|
+
while you build, and redirecting them would need a machine path in a tracked
|
|
93
|
+
file, which is the thing this section is avoiding.
|
|
94
|
+
|
|
95
|
+
Three files are written into `src/patchcraft/` by an editable install and
|
|
96
|
+
are gitignored: the compiled `_accel_native`, the generated `_version.py`, and
|
|
97
|
+
`__pycache__`. Those have to be there for the package to import.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Layout
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
PatchCraft/
|
|
105
|
+
├── pyproject.toml package metadata, build backend, cibuildwheel targets
|
|
106
|
+
├── setup.py the one build decision: with or without the Rust extension
|
|
107
|
+
├── tools/check_dist.py gate: extension present, versions agree, tag respected
|
|
108
|
+
├── tools/benchmark.py accelerated vs pure, and proves they agree first
|
|
109
|
+
├── MANIFEST.in what the sdist carries
|
|
110
|
+
├── README.md the call page, canonical, English
|
|
111
|
+
├── README.pt-BR.md the same call page in Portuguese
|
|
112
|
+
├── README.pypi.md the PyPI page (long_description), links absolute
|
|
113
|
+
├── CONTRIBUTING.md this file
|
|
114
|
+
├── CHANGELOG.md Keep-a-Changelog format
|
|
115
|
+
├── LICENSE MIT
|
|
116
|
+
├── .python-version 3.13
|
|
117
|
+
├── .gitignore ignores archive/, venvs, caches, outputs
|
|
118
|
+
├── .github/workflows/
|
|
119
|
+
│ ├── test.yml matrix CI on PRs/main, plus the accelerated job
|
|
120
|
+
│ └── release.yml publishes to PyPI on vX.Y.Z tag push (Trusted Publishing)
|
|
121
|
+
├── src/patchcraft/ library core, one-image-at-a-time primitives
|
|
122
|
+
│ ├── __init__.py re-exports the full public API and the generated version
|
|
123
|
+
│ ├── extract.py patches via strided view or F.unfold; Patchify (ADR 0002)
|
|
124
|
+
│ ├── reconstruct.py inverse via F.fold + count map, closed-form fast path
|
|
125
|
+
│ ├── stitch.py weighted reassembly for modified patches
|
|
126
|
+
│ ├── geometry.py pre-flight: num_patches, tilings, scale_factor, paired_tilings
|
|
127
|
+
│ ├── metrics.py patch_metrics, per_patch_mse, per_patch_psnr
|
|
128
|
+
│ ├── pair.py LR↔HR pairing; PatchPair, PatchMeta
|
|
129
|
+
│ ├── resize.py resize with PIL or torch backends
|
|
130
|
+
│ ├── cache.py content-addressed disk cache
|
|
131
|
+
│ ├── _accel.py bridge to the optional native accelerator, falls back silently
|
|
132
|
+
│ └── _foldgeom.py shared fold-geometry validation for reconstruct and stitch
|
|
133
|
+
├── accel/ the Rust crate compiled into this wheel (pyo3, setuptools-rust)
|
|
134
|
+
│ ├── Cargo.toml crate metadata; the version there is internal
|
|
135
|
+
│ ├── src/kernel.rs the gather-fold kernel, pure Rust, cargo-tested
|
|
136
|
+
│ ├── src/lib.rs pyo3 glue, exports patchcraft._accel_native
|
|
137
|
+
│ └── README.md how to build it, and how to check it is active
|
|
138
|
+
├── tests/ pytest suite (contract tests for src/)
|
|
139
|
+
│ ├── test_extract.py extract + Patchify
|
|
140
|
+
│ ├── test_reconstruct.py
|
|
141
|
+
│ ├── test_stitch.py
|
|
142
|
+
│ ├── test_geometry.py num_patches + tilings + scale_factor + paired_tilings
|
|
143
|
+
│ ├── test_metrics.py
|
|
144
|
+
│ ├── test_pair.py
|
|
145
|
+
│ ├── test_resize.py
|
|
146
|
+
│ ├── test_cache.py
|
|
147
|
+
│ ├── test_exactness.py falsifies the count-map predicate over the legal space
|
|
148
|
+
│ ├── test_reference.py naive loop-based reference for the fast paths
|
|
149
|
+
│ ├── test_public_api.py freezes the 20 names, their signatures and carrier fields
|
|
150
|
+
│ ├── test_accel.py native path, skipped unless the extension was built
|
|
151
|
+
│ ├── test_datasets_helper.py label_subset
|
|
152
|
+
│ ├── test_import.py
|
|
153
|
+
│ ├── _rng.py audited round-trip helpers (data generation, bit equality)
|
|
154
|
+
│ ├── test_rng.py tests for those helpers
|
|
155
|
+
│ └── _datasets.py dev-only fixtures (MNIST, etc), NOT public API
|
|
156
|
+
├── lab/ ephemeral experiments; see lab/README.md
|
|
157
|
+
│ ├── README.md bench rules (tracked; the scripts themselves are not)
|
|
158
|
+
│ └── .gitignore ignores everything else (tracked)
|
|
159
|
+
├── docs/
|
|
160
|
+
│ ├── GUIDE.md the manual, every README claim measured with its output
|
|
161
|
+
│ ├── PERFORMANCE.md what the accelerator is worth, and how to re-measure
|
|
162
|
+
│ ├── USAGE.md REPL walkthrough of every public symbol (behind, see B4)
|
|
163
|
+
│ ├── SCOPE.md responsibilities matrix + parallelization analysis
|
|
164
|
+
│ ├── AUXILIARY.md tests/_datasets, lab/, Z:\ conventions (NOT part of the wheel)
|
|
165
|
+
│ ├── THEORY.md distilled design + §9 condition contract; §0 binding scope
|
|
166
|
+
│ ├── ROADMAP.md milestone plan
|
|
167
|
+
│ ├── FOCO-1.0.md what 1.0 freezes and the blockers in the way
|
|
168
|
+
│ ├── ADR/ one file per decision that shaped the API
|
|
169
|
+
│ ├── STUDIES/ background reading behind those decisions
|
|
170
|
+
│ └── design/ one spec per work phase: the alternatives measured and the decision
|
|
171
|
+
├── outreach/ material for presenting the project publicly, not shipped
|
|
172
|
+
│ ├── README.md how it is organized and what to check before publishing
|
|
173
|
+
│ ├── 2026-09-03-lancamento.md the dated news source every channel text derives from
|
|
174
|
+
│ └── linkedin/ post (short) and artigo (long), each in PT and EN
|
|
175
|
+
└── archive/ reference-only; gitignored (pruned 2026-05-17, only HISTORY.md kept)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Validation lab
|
|
181
|
+
|
|
182
|
+
The library is "one image in, one tensor out" by design, but you only know it works once you run it end-to-end on real images. That happens in two places, neither of which is part of the shipped package:
|
|
183
|
+
|
|
184
|
+
- [`tests/`](tests/) holds the formal pytest suite that defines the contract from [`docs/THEORY.md`](docs/THEORY.md) §9.
|
|
185
|
+
- [`lab/`](lab/) holds ephemeral scripts and notebooks for fast hypothesis-checking. See [`lab/README.md`](lab/README.md) for the bench rules; outputs go to `Z:\outputs\patchcraft\` (off-tree).
|
|
186
|
+
|
|
187
|
+
Datasets used by tests/lab are downloaded lazily into `Z:\caches\datasets\<name>\` on first use; they do not ship with the package and are never bundled into the wheel.
|
|
188
|
+
|
|
189
|
+
The dev framework that makes this all work (fixtures, dataset helpers, `Z:\` conventions) is documented in [`docs/AUXILIARY.md`](docs/AUXILIARY.md).
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Versioning policy
|
|
194
|
+
|
|
195
|
+
The scheme is [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html),
|
|
196
|
+
expressed in the [PEP 440](https://peps.python.org/pep-0440/) form that PyPI
|
|
197
|
+
accepts. Two rules matter more than the arithmetic, because both were broken
|
|
198
|
+
once and both cost work to undo.
|
|
199
|
+
|
|
200
|
+
**The number names a published artifact, not a unit of work.** Finishing a
|
|
201
|
+
phase, merging a branch and landing a refactor are not releases, and none of
|
|
202
|
+
them touches `__version__`. Everything between releases accumulates under
|
|
203
|
+
`[Unreleased]` in [`CHANGELOG.md`](CHANGELOG.md), and the number is assigned
|
|
204
|
+
in the release commit itself. Versions 0.3.0 and 0.4.0 came from the opposite
|
|
205
|
+
habit: each internal work phase minted a minor, so three numbers existed
|
|
206
|
+
before any of them reached PyPI.
|
|
207
|
+
|
|
208
|
+
**A published version is frozen.** PyPI refuses a filename it has already
|
|
209
|
+
seen, so a version that shipped can only be superseded, never edited. That is
|
|
210
|
+
also why a failed pipeline never costs a number: `skip-existing` makes the
|
|
211
|
+
upload a no-op and the same tag can be re-run.
|
|
212
|
+
|
|
213
|
+
**While the project is pre-1.0, the middle digit is the compatibility
|
|
214
|
+
boundary.** SemVer itself leaves `0.y.z` open, saying only that "anything MAY
|
|
215
|
+
change at any time", so the meaning comes from the resolvers instead, and they
|
|
216
|
+
agree: Cargo expands `^0.2.3` to `>=0.2.3, <0.3.0`, npm does the same, and
|
|
217
|
+
PEP 440's `~=0.5.0` means `>=0.5.0, <0.6.0`. In every one of them a `0.y` bump
|
|
218
|
+
is the announcement that something may break, and a `0.y.z` bump is the
|
|
219
|
+
promise that nothing does.
|
|
220
|
+
|
|
221
|
+
That fixes what each digit is for here:
|
|
222
|
+
|
|
223
|
+
The question that decides it is not "did a returned value change". A bug fix
|
|
224
|
+
changes returned values by definition, and if that forced a `0.Y.0` then a
|
|
225
|
+
patch release could never fix anything. The question is **whether the contract
|
|
226
|
+
moved, or whether the implementation stopped violating it**:
|
|
227
|
+
|
|
228
|
+
| Change | Bump | Why |
|
|
229
|
+
|---|---|---|
|
|
230
|
+
| The documented contract moves: a signature, a name, or behaviour the documentation described as correct | `0.Y.0` | Code that was right against the old documentation can now be wrong |
|
|
231
|
+
| The implementation stops violating the contract, however visibly | `0.y.Z` | The documentation always promised this; only the code was wrong |
|
|
232
|
+
| New functionality that breaks nothing | `0.y.Z` | Anyone pinned to the y-series should get it |
|
|
233
|
+
| Documentation, tests, CI, packaging or tooling only | `0.y.Z` | Still a release if it is published, still no break |
|
|
234
|
+
| Nothing published | nothing | Accumulate under `[Unreleased]` |
|
|
235
|
+
|
|
236
|
+
The project's own history sorts cleanly under that rule, and does not sort
|
|
237
|
+
under any rule keyed on whether values changed:
|
|
238
|
+
|
|
239
|
+
- `0.2.1` rewrote the hann window, changing `stitch(weight="hann")` output for
|
|
240
|
+
**every** geometry. THEORY §9.9 had promised no covered pixel is zeroed by
|
|
241
|
+
the window; the old window zeroed 108 of 144 pixels in one measured case. A
|
|
242
|
+
violation, so a **z**, and it shipped as one.
|
|
243
|
+
- `0.5.2` floored the `stitch` denominator by the dtype's tiny instead of an
|
|
244
|
+
absolute `1e-6`, changing hann output at patch 99 and above. The same
|
|
245
|
+
promise, the same kind of violation, so also a **z**.
|
|
246
|
+
- `0.3.0` moved `stitch` output by ULPs through a different summation order.
|
|
247
|
+
Nothing was wrong before, so behaviour the documentation endorsed changed.
|
|
248
|
+
A **y**.
|
|
249
|
+
- `0.5.0` made `tilings` return 73 specs where it returned 100. The
|
|
250
|
+
enumeration's output set is the contract. A **y**.
|
|
251
|
+
|
|
252
|
+
After 1.0 the ordinary reading applies: breaking is major, additive is minor,
|
|
253
|
+
fixed is patch. What 1.0 freezes is written in
|
|
254
|
+
[`docs/FOCO-1.0.md`](docs/FOCO-1.0.md).
|
|
255
|
+
|
|
256
|
+
### Errata
|
|
257
|
+
|
|
258
|
+
The rule above was written after the fact, and two releases predate it.
|
|
259
|
+
|
|
260
|
+
`0.4.0` should have been `0.3.1`. It added `accel_available()` and nothing
|
|
261
|
+
else: no signature changed, and the accelerated path was bit-exact against the
|
|
262
|
+
pure one. Additive and non-breaking is a z-bump under the boundary rule, and
|
|
263
|
+
minting a y announced a break that never happened.
|
|
264
|
+
|
|
265
|
+
`0.5.1` was briefly numbered `0.6.0` before release. It changes only how the
|
|
266
|
+
package is built and shipped, so it is a z-bump.
|
|
267
|
+
|
|
268
|
+
Two others were right and are worth recording as the shape to copy. `0.3.0`
|
|
269
|
+
moved `stitch` output by ULPs through a different summation order, which is an
|
|
270
|
+
output change and belongs at a y boundary. `0.5.0` made `tilings` return 73
|
|
271
|
+
specs where it returned 100, which is squarely a break.
|
|
272
|
+
|
|
273
|
+
The accelerator in [`accel/`](accel/) has no version of its own to manage. It
|
|
274
|
+
is compiled into this wheel, so it ships when `patchcraft` ships. The version
|
|
275
|
+
in `accel/Cargo.toml` is internal and nothing reads it; what the Python side
|
|
276
|
+
actually checks is `_ABI_VERSION` in `accel/src/lib.rs`, and a mismatch there
|
|
277
|
+
means a silent fall back to the pure path rather than a crash.
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Where the version comes from
|
|
282
|
+
|
|
283
|
+
The git tag, and nowhere else. `setuptools-scm` derives it at build time, so
|
|
284
|
+
no file in the source states a version and there is no literal to forget.
|
|
285
|
+
`v0.5.1` produces `0.5.1`; the leading `v` is stripped for you.
|
|
286
|
+
|
|
287
|
+
At build time setuptools-scm writes `src/patchcraft/_version.py`, which
|
|
288
|
+
`__init__.py` imports and re-exports as `patchcraft.__version__`. That file is
|
|
289
|
+
generated and gitignored, so a checkout that was never built falls back to
|
|
290
|
+
`importlib.metadata` and then to `0+unknown`. The fallback exists so an
|
|
291
|
+
un-built tree still imports; it is not a supported way to learn the version.
|
|
292
|
+
|
|
293
|
+
Anything other than a clean tree sitting exactly on a tag produces a version
|
|
294
|
+
with a local segment, like `0.5.2.dev0+gb4edfd81.d20260902` from a dirty tree
|
|
295
|
+
or `0.1.dev1+gb4edfd81` from a checkout whose tags were never fetched. PyPI
|
|
296
|
+
refuses local segments outright, so such a build can never be published by
|
|
297
|
+
accident, and `tools/check_dist.py` rejects it before the upload rather than
|
|
298
|
+
letting the upload fail.
|
|
299
|
+
|
|
300
|
+
That last point is why every `actions/checkout` that builds uses
|
|
301
|
+
`fetch-depth: 0`. The default shallow clone hides the tags, and the failure is
|
|
302
|
+
silent: a version that walks backwards rather than an error.
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## Releasing (maintainer only)
|
|
307
|
+
|
|
308
|
+
1. Close the `[Unreleased]` section in [`CHANGELOG.md`](CHANGELOG.md) as `[X.Y.Z] YYYY-MM-DD`.
|
|
309
|
+
2. Update the `version` field of the BibTeX entry in [`docs/GUIDE.md`](docs/GUIDE.md) section 9. It is the one version string left in the documentation, because a citation needs a concrete one.
|
|
310
|
+
3. Update [`docs/ROADMAP.md`](docs/ROADMAP.md) milestone checkboxes.
|
|
311
|
+
4. Commit: `release: vX.Y.Z`.
|
|
312
|
+
5. Tag + push: `git tag -a vX.Y.Z -m "..."` then `git push origin vX.Y.Z`.
|
|
313
|
+
6. `release.yml` fires automatically: validates, builds, publishes to PyPI via Trusted Publishing, then creates the GitHub Release with every artifact attached.
|
|
314
|
+
|
|
315
|
+
There is no version to bump in step 4, and no way for a tag and a release to
|
|
316
|
+
disagree. The `validate` job still cross-checks that setuptools-scm derived
|
|
317
|
+
exactly what the tag says, which catches a dirty or shallow checkout before
|
|
318
|
+
anything is built.
|
|
319
|
+
|
|
320
|
+
One release produces one sdist and six wheels: five `cp312-abi3-<platform>`
|
|
321
|
+
wheels with the Rust extension inside, and one `py3-none-any` wheel for
|
|
322
|
+
everywhere else. `tools/check_dist.py` runs before the upload and fails the
|
|
323
|
+
release if a platform wheel lost its extension, if the universal wheel gained
|
|
324
|
+
one, if the artifacts disagree about the version, or if any of them is not the
|
|
325
|
+
version the tag names. Publishing is a single project, so there is nothing to
|
|
326
|
+
register on PyPI beyond the publisher that already exists.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# The sdist is the reproducible source snapshot: the product (`src/`, `accel/`)
|
|
2
|
+
# plus the track that proves it works (`tests/`, `docs/`). `accel/` has to be
|
|
3
|
+
# here, because an sdist build compiles it when a Rust toolchain is present.
|
|
4
|
+
#
|
|
5
|
+
# The `prune` lines below are load-bearing rather than decorative, and the
|
|
6
|
+
# reason changed with setuptools-scm. Its file finder offers every git-tracked
|
|
7
|
+
# file to the sdist, so this file stopped being an allowlist and became a
|
|
8
|
+
# blocklist over everything git knows about. Measured: with no prune for it,
|
|
9
|
+
# `.vscode/settings.json` reached the sdist.
|
|
10
|
+
#
|
|
11
|
+
# Note that `lab/` is only *mostly* ignored: `lab/README.md` and
|
|
12
|
+
# `lab/.gitignore` are tracked, so the finder offers them and the prune is what
|
|
13
|
+
# stops them. The rest of `lab/` is scratch work that no commit can reproduce,
|
|
14
|
+
# and patchcraft-0.2.0.tar.gz shipped three such files to PyPI.
|
|
15
|
+
#
|
|
16
|
+
# Adding a tracked directory to this repository ships it to PyPI by default.
|
|
17
|
+
# `tools/check_dist.py` holds the matching allowlist and will fail the release
|
|
18
|
+
# for anything unexpected, which is the reminder to come back here.
|
|
19
|
+
|
|
20
|
+
include LICENSE
|
|
21
|
+
include CHANGELOG.md CONTRIBUTING.md
|
|
22
|
+
include README.md README.pt-BR.md README.pypi.md
|
|
23
|
+
include pyproject.toml setup.py uv.lock .python-version
|
|
24
|
+
|
|
25
|
+
recursive-include src *.py
|
|
26
|
+
include src/patchcraft/py.typed
|
|
27
|
+
recursive-include tests *.py
|
|
28
|
+
recursive-include docs *.md
|
|
29
|
+
recursive-include accel *.rs *.toml *.lock *.md
|
|
30
|
+
prune accel/target
|
|
31
|
+
recursive-include tools *.py
|
|
32
|
+
|
|
33
|
+
prune lab
|
|
34
|
+
prune archive
|
|
35
|
+
prune .github
|
|
36
|
+
prune .vscode
|
|
37
|
+
# Presentation material, not source. It is tracked, so the file finder offers
|
|
38
|
+
# it and only this line keeps it out.
|
|
39
|
+
prune outreach
|