diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index b6dff81..9d83b0e 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -69,6 +69,11 @@ jobs: sed -i "s/^version = \".*\"/version = \"${VERSION}\"/" pyproject.toml grep "^version = \"${VERSION}\"" pyproject.toml + - name: Sync uv.lock to the new version + run: | + uv lock + grep -A1 '^name = "bambox"' uv.lock | grep "^version = \"${{ inputs.version }}\"" + - name: Build changelog with towncrier run: | VERSION="${{ inputs.version }}" @@ -87,7 +92,7 @@ jobs: BRANCH="release/v${VERSION}" git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - git add pyproject.toml CHANGELOG.md changes/ + git add pyproject.toml uv.lock CHANGELOG.md changes/ git commit -m "Release v${VERSION}" git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git" git push --force origin "$BRANCH" diff --git a/README.md b/README.md index eb18b70..375f6ed 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,19 @@ Or with [uv](https://docs.astral.sh/uv/): ```bash uv pip install bambox +uv tool install bambox # just the CLI +``` + +The core install covers packing, repacking and validation. Two optional +extras pull in heavier dependencies only if you need them: + +| Extra | Pulls in | Needed for | +|-------|----------|------------| +| `templates` | Jinja2 | `bambox.templates.render_template()` — rendering bundled G-code templates | +| `thumbnail` | Pillow | `bambox.thumbnail.gcode_thumbnail()` — rendering toolpath previews | + +```bash +pip install "bambox[templates,thumbnail]" ``` ### Supported printers and filaments diff --git a/changes/274.bugfix b/changes/274.bugfix new file mode 100644 index 0000000..7466642 --- /dev/null +++ b/changes/274.bugfix @@ -0,0 +1 @@ +Declare the `templates` (Jinja2) and `thumbnail` (Pillow) extras that `render_template()` and `gcode_thumbnail()` already told users to install, and give `gcode_thumbnail()` the same actionable error as `render_template()` when Pillow is missing. diff --git a/changes/274.misc b/changes/274.misc new file mode 100644 index 0000000..e3f69a2 --- /dev/null +++ b/changes/274.misc @@ -0,0 +1 @@ +Prepare Release now re-locks `uv.lock` with the new version, so the lockfile no longer drifts behind `pyproject.toml` after each release. diff --git a/pyproject.toml b/pyproject.toml index b3155f5..5e0fa1c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,6 +29,12 @@ classifiers = [ ] [project.optional-dependencies] +templates = [ + "jinja2>=3.1.0", +] +thumbnail = [ + "pillow>=10.0.0", +] dev = [ "pytest>=8.0.0", "pytest-cov>=6.0.0", diff --git a/src/bambox/thumbnail.py b/src/bambox/thumbnail.py index 5d8876c..3475f7a 100644 --- a/src/bambox/thumbnail.py +++ b/src/bambox/thumbnail.py @@ -16,7 +16,13 @@ def gcode_thumbnail( Parses G0/G1 extrusion moves from the print body (skipping startup G-code) and draws them on a dark background. Returns PNG bytes. """ - from PIL import Image, ImageDraw + try: + from PIL import Image, ImageDraw + except ImportError: + raise ImportError( + "Pillow is required for thumbnail rendering. " + "Install with: pip install bambox[thumbnail]" + ) if isinstance(gcode, bytes): gcode = gcode.decode(errors="replace") diff --git a/tests/test_release_readiness.py b/tests/test_release_readiness.py index 261a0d6..f599702 100644 --- a/tests/test_release_readiness.py +++ b/tests/test_release_readiness.py @@ -171,3 +171,19 @@ def test_notices_force_included_in_wheel(self) -> None: "THIRD-PARTY-NOTICES should be placed under bambox/ in the wheel " "so it's discoverable next to the installed package." ) + + +# --------------------------------------------------------------------------- +# 5. Optional extras named in ImportError hints must exist +# --------------------------------------------------------------------------- + + +class TestOptionalExtras: + """`pip install bambox[]` hints must point at a real extra.""" + + @pytest.mark.parametrize("extra", ["templates", "thumbnail"]) + def test_extra_is_declared(self, extra: str) -> None: + from importlib.metadata import metadata + + declared = metadata("bambox").get_all("Provides-Extra") or [] + assert extra in declared diff --git a/uv.lock b/uv.lock index 8872935..5d0945a 100644 --- a/uv.lock +++ b/uv.lock @@ -13,7 +13,7 @@ wheels = [ [[package]] name = "bambox" -version = "0.6.1" +version = "0.6.2" source = { editable = "." } dependencies = [ { name = "rich" }, @@ -33,13 +33,21 @@ dev = [ docs = [ { name = "pdoc" }, ] +templates = [ + { name = "jinja2" }, +] +thumbnail = [ + { name = "pillow" }, +] [package.metadata] requires-dist = [ { name = "jinja2", marker = "extra == 'dev'", specifier = ">=3.1.0" }, + { name = "jinja2", marker = "extra == 'templates'", specifier = ">=3.1.0" }, { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.10.0" }, { name = "pdoc", marker = "extra == 'docs'", specifier = ">=14.0.0" }, { name = "pillow", marker = "extra == 'dev'", specifier = ">=10.0.0" }, + { name = "pillow", marker = "extra == 'thumbnail'", specifier = ">=10.0.0" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" }, { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=6.0.0" }, { name = "rich", specifier = ">=13.0" }, @@ -47,7 +55,7 @@ requires-dist = [ { name = "towncrier", marker = "extra == 'dev'", specifier = ">=24.7.0" }, { name = "typer", specifier = ">=0.12" }, ] -provides-extras = ["dev", "docs"] +provides-extras = ["templates", "thumbnail", "dev", "docs"] [[package]] name = "click"