Switch packaging to uv; enable Python 3.14 via build123d dev branch#9
Open
bitranox wants to merge 8 commits into
Open
Switch packaging to uv; enable Python 3.14 via build123d dev branch#9bitranox wants to merge 8 commits into
bitranox wants to merge 8 commits into
Conversation
…dev branch Replace poetry/pipx setup with PEP 621 metadata + hatchling backend. Add SPDX license expression, dependency-groups for tests, and project scripts. Update README with uv install (Linux/Windows), uv tool install, uvx usage, and dev workflow. For Python 3.14, depend on build123d's upstream dev branch via a marker-conditional [tool.uv.sources] entry; 3.10-3.13 keep the PyPI release. Drop poetry.lock and ignore uv.lock. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Python 3.14 raises SyntaxWarning on every run for invalid escape sequences in string literals; the literal asterisk does not need escaping. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Pin.__init__: plate_base_height was used as a number, but became a function (short: bool) -> float when --short support was added in e7296b9. Pass False to preserve the original 2.9mm constant. * gfpin.main: export_step/export_stl were called with `bin` (Python's built-in) instead of the local `pin` object. Together these prevented `gfpin -o file.step|.stl` from ever producing output; only the --vscode path worked. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The released build123d 0.10.0 transitively pulls vtk, which has no Python 3.13 or 3.14 wheel, so a plain install fails on those interpreters. Route 3.13 and 3.14 to build123d's upstream `dev` branch (which uses cadquery-ocp-novtk and skips vtk) via a marker-conditional [tool.uv.sources] entry. 3.10-3.12 keep the released wheel from PyPI. The dev branch has small API drifts from 0.10.0 that the existing gfthings code tripped on; fix them so the test suite passes on 3.12 (released), 3.13 (dev), and 3.14 (dev): * GFProfile: Plane(origin, z_dir) on the dev branch picks a different default x_dir when z_dir is exactly axis-aligned vs nearly so. For non-square paths (depth != width) path%0 is exactly (-1, 0, 0) and the resulting y_dir flips, sending the swept profile along world Y instead of Z and collapsing GFProfile to volume 0. Pin x_dir explicitly. * Bin: when the cavity carve depth equals the box height, the dev branch leaves two coplanar faces at the cavity-floor Z (the actual cavity floor at +/-19.55 and the BinBase top at +/-20.75 with a near-degenerate inner edge ring). `sort_by(Axis.Z)[-2]` picks the wrong one at certain heights, which 1) places inner_front_centre at the bin's outer wall so the scoop overlaps wall material, and 2) makes the floor fillet fail outright. Select the smallest-area XY face at the cavity-floor Z instead. * Bin: a -2e-9 floating-point offset on inner_front_centre.X (from symmetric construction) triggers an OCCT boolean-ADD bug on the dev branch that drops the entire bin solid when the scoop is placed at that point. Snap sub-um X values to zero. * Pin: plate_base_height became a function (short: bool) -> float when --short bases were added; Pin still treated it as a number, raising TypeError on every gfpin invocation. Pass False to preserve the original 2.9mm geometry. * tests/Utils.py: loosen float_eq epsilon from 0.001 to 0.05 mm^3 to accommodate sub-mm^3 OCCT variance between build123d versions for the same input geometry. * README: expand the Python 3.13/3.14 note to explain the vtk wheel gap and that uv is the supported install path on those versions for now. Verified: 21/21 tests pass on Python 3.12 + released 0.10.0, on Python 3.13 + dev branch, and on Python 3.14 + dev branch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Skips the magnet pocket without removing screw functionality. For gfbase, the counterbore (screw + magnet recess) becomes a plain through-hole at screw_rad so the base can still be screwed down. For gfbin, both refined side slots and unrefined bottom holes are skipped (bins have no screw holes, so the result is a plain base profile). Plumbed through ScrewSupport/BaseSquare/BaseGrid in Base.py and BinBase/ Bin/FunkyBin/HalfWallBin in Bin.py via a new `magnet : bool = True` parameter on each. Default is True, preserving existing behavior; the test suite continues to pass on Python 3.12 + released build123d 0.10.0 and Python 3.13 / 3.14 + dev branch. CLI: gfbase ... --no-magnet gfbin ... --no-magnet Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cover the new `magnet=False` paths: * test_base.py: 2x2 base, 1x1 base, 2x2 base with 4 holes (counterbore branch in ScrewSupport replaced by a plain through-hole at screw_rad). * test_bins.py: 1x1x4 bin (refined slots skipped) plus an unrefined variant — both produce the same volume because both magnet branches are short-circuited when magnet=False. Reference volumes verified to match within 1e-6 between Python 3.12 + released build123d 0.10.0 and Python 3.13 / 3.14 + dev branch. Total: 26/26 tests pass on all three Python+build123d combos. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Each tool gets a paired Bash and PowerShell launcher (gfbin, gfbase, gfedge, gfpin). The scripts declare the CLI parameters as variables at the top, auto-derive an output filename from those parameters (so a tweak like NoMagnet=true produces a uniquely-named sibling without clobbering anything), and call `uvx --from <git URL> <tool>` for you. The .sh and .ps1 versions take exactly the same variable names and produce identical CAD output, so a recipe is portable between Linux / macOS and Windows. Both pin GfthingsSource to bitranox/gfthings@py314compat so 3.13 / 3.14 users get the build123d dev branch automatically. The .ps1 versions were added in an earlier commit; this commit adds the matching .sh versions and a "Recipe scripts" section to the README explaining the workflow (copy + rename, edit variables, run). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* README: add the "Recipe scripts" section explaining the copy-and-edit workflow, that the .sh and .ps1 versions take the same variables and produce identical CAD output, and listing the four covered tools. (This section was prepared with the previous commit but didn't make it into the index — folding it in here.) * All 8 launcher scripts: add a NOTE above the `GfthingsSource` line pointing out that the URL targets the bitranox fork's `py314compat` branch only because the Python 3.13 / 3.14 compatibility work is not yet upstream (PaulBone#9). Once that PR is merged, the URL should be replaced with `git+https://github.com/PaulBone/gfthings.git` or, after a PyPI release that supports 3.13/3.14, just `gfthings`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This branch covers four loosely-related areas of work:
build123d0.10.0 currently can't even install.gfpinCLI entirely.--no-magnetflag ongfbaseandgfbin, plus paired Bash + PowerShell recipe launcher scripts for every tool.Verified: 26/26 tests pass on Python 3.12 (released
build123d0.10.0), Python 3.13 (build123dgitdevbranch) and Python 3.14 (build123dgitdevbranch).1. Packaging: poetry → uv
[tool.poetry.*]blocks with PEP 621[project]metadata and switch the build backend to hatchling.License-Expression: CC-BY-NC-SA-4.0instead of the free-textLicense:field.[dependency-groups]table (uv run --group test pytest).[project.scripts]forgfpin/gfbin/gfbase/gfedge.poetry.lockremoved;uv.locknot committed (added to.gitignore).uvx --from gfthings gfbin -h— one-shot, no global state, nopipx installneeded.uv tool install gfthingsfor a persistent CLI install (the equivalent ofpipx install).uv sync/uv rundeveloper workflow (incl.uv run --group test pytest).Requires-Dist: build123d>=0.10.0; uv-only metadata is not baked in, so the project remains PyPI-publishable.2. Python 3.13 / 3.14 support
The released
build123d0.10.0 transitively pullsvtk, which has no Python 3.13 or 3.14 wheels, so a plain install fails on those interpreters regardless of any of our other changes. To unblock 3.13 / 3.14 the project routesbuild123dthrough its upstreamdevbranch (which depends oncadquery-ocp-novtkand skipsvtkentirely) using a marker-conditional[tool.uv.sources]entry:Python 3.10–3.12 keep using the released wheel from PyPI.
The dev branch has small API drifts from 0.10.0 that the existing gfthings code tripped on. Each one is fixed with comprehensive inline comments explaining the WHY:
GFProfile.py— sketch plane orientation.Plane(origin, z_dir)on the dev branch picks a different defaultx_dirwhenz_diris exactly axis-aligned vs. only nearly so. For non-square paths (e.g.width=1, depth=2)path%0returns exactly(-1, 0, 0)and the resultingy_dirflips, sending the swept profile along world Y instead of Z and collapsingGFProfileto volume 0. Fix: pinx_direxplicitly so the polyline's local +Y always lands on world +Z.Bin.py— cavity-floor face selection. When the cavity carve depth equals the box height (1×1 bins), the cavity floor lands exactly on the BinBase top face. The dev-branch boolean leaves two coplanar faces at that Z (the actual cavity floor at ±19.55 and the BinBase top at ±20.75 with a near-degenerate inner edge ring).sort_by(Axis.Z)[-2]picks one or the other depending on internal sort stability, which varies with bin height (h=4/h=6 happen to pick the cavity floor; h=5/h=8 pick BinBase top). When BinBase top is picked, two things break:inner_front_centrelands at Y=−20.75 (bin outer wall) instead of Y=−19.55 (cavity inner wall) — the scoop is then positioned 1.2 mm too far back, with part of it embedded in the wall material — and the floor fillet fails outright because of the degenerate inner edge ring. Fix: select the smallest-area XY face at the cavity-floor Z; that's reliably the cavity floor in every case.Bin.py— X-coordinate snap. The midpoint of the front-most X-axis edge can land atX = −2e-9due to symmetric construction. That exact magnitude/sign, when fed intoLocations(...)for the subsequent Scoop placement, triggers an OCCT boolean-ADD bug on the dev branch: the resulting solid is just the scoop alone (the entire bin gets dropped). Other tiny X values (0, +1e-9, −1e-12) all work fine. Fix: snap any sub-µm X value to zero.3. Latent bug fixes (independent of the dev-branch work)
gfthings/gfbin.py:25—SyntaxWarning: invalid escape sequence '\*'. Python 3.14 raises this every run. The literal asterisk in the--loophelp string didn't need escaping. One-character fix.gfthings/gfpin.py—binvspintypo. Lines 37/39 calledexport_step(bin, ...)andexport_stl(bin, ...)withbin(Python's built-in) instead of the localpin. The--vscodepath worked, butgfpin -o file.stephad never produced output.gfthings/Pin.py:16—TypeError: unsupported operand type(s) for +: 'float' and 'function'.plate_base_heightwas a constant (2.9) at the timePinwas written; commite7296b9turned it into a function(short: bool) -> floatfor the--shortbase feature, butPinwas not migrated. PassFalseto preserve the original 2.9 mm geometry.4. New feature:
--no-magnetAdds a
--no-magnetflag to bothgfbaseandgfbinwith the obvious meaning: keep screw functionality, skip the magnet pocket.gfbase --no-magnetreplaces theCounterBoreHole(screw_rad, magnet_rad, magnet_depth)(orCounterSinkHole) insideScrewSupportwith a plainHole(screw_rad), so the base can still be screwed down but no magnet pocket is drilled.gfbin --no-magnetskips both the refined side slots and the unrefined bottom holes insideBinBase. Bins have no screw holes, so the result is a plain base profile.ScrewSupport/BaseSquare/BaseGrid(Base.py) andBinBase/Bin/FunkyBin/HalfWallBin(Bin.py) via a newmagnet : bool = Trueparameter on each. Default isTrue, preserving existing behavior.test_2x2_base_no_magnet,test_1x1_base_no_magnet,test_4_holes_no_magnet,test_simple_bin_no_magnet,test_simple_bin_no_magnet_unrefined.5. Recipe launcher scripts (
.sh+.ps1)Each tool gets a paired Bash and PowerShell launcher (
gfbin.sh/gfbin.ps1,gfbase.sh/gfbase.ps1,gfedge.sh/gfedge.ps1,gfpin.sh/gfpin.ps1):NoMagnet=trueadds_nomagnetto the name and produces a uniquely-named sibling without clobbering anything.uvx --from <git URL> <tool> ...command and runs it.The Bash and PowerShell versions take exactly the same variable names and produce identical CAD output, so a recipe is portable between Linux/macOS and Windows.
Workflow (from the new README section):
Each script also has a
NOTE:block above theGfthingsSourceURL pointing out that the URL is currently pinned to the fork'spy314compatbranch because the 3.13 / 3.14 work is not yet upstream, and listing the two replacements to use after this PR merges (git+https://github.com/PaulBone/gfthings.gitwhile waiting for a release, or justgfthingsonce a 3.13/3.14-compatible release is on PyPI).6. Test infrastructure
tests/Utils.py'sfloat_eqepsilon is loosened from 0.001 to 0.05 mm³ to accommodate sub-cubic-millimeter OCCT variance between build123d versions for the same input geometry. The comment on the helper now explains where the variance comes from.Verified test matrix
devbranch (via[tool.uv.sources])devbranch (via[tool.uv.sources])3.10 / 3.11 use the same resolution path as 3.12 (released
build123d0.10.0); not exercised in CI here but should behave identically.Note on plain
pip installfor 3.13 / 3.14pipignores[tool.uv.sources], sopip install gfthingson Python 3.13 or 3.14 will still fail to resolvebuild123d(it tries the released 0.10.0 wheel and hits the missingvtkwheels).uv(uv tool install,uvx --from <git URL>, oruv syncfrom a checkout) is the supported install path on those interpreters until upstreambuild123dships a release that targets 3.13+.