prusaslicer-py 0.1.0__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.
- prusaslicer_py-0.1.0/.github/dependabot.yml +20 -0
- prusaslicer_py-0.1.0/.github/workflows/ci.yml +70 -0
- prusaslicer_py-0.1.0/.github/workflows/engine.yml +158 -0
- prusaslicer_py-0.1.0/.github/workflows/release.yml +87 -0
- prusaslicer_py-0.1.0/.gitignore +89 -0
- prusaslicer_py-0.1.0/.pre-commit-config.yaml +21 -0
- prusaslicer_py-0.1.0/AGENTS.md +120 -0
- prusaslicer_py-0.1.0/CHANGELOG.md +64 -0
- prusaslicer_py-0.1.0/CONTRIBUTING.md +54 -0
- prusaslicer_py-0.1.0/LICENSE +216 -0
- prusaslicer_py-0.1.0/PKG-INFO +190 -0
- prusaslicer_py-0.1.0/README.md +167 -0
- prusaslicer_py-0.1.0/SECURITY.md +33 -0
- prusaslicer_py-0.1.0/docs/DECISIONS.md +332 -0
- prusaslicer_py-0.1.0/examples/basic_slicing.py +41 -0
- prusaslicer_py-0.1.0/examples/torus_example_attempt.py +49 -0
- prusaslicer_py-0.1.0/justfile +54 -0
- prusaslicer_py-0.1.0/prusaslicer_py/__init__.py +15 -0
- prusaslicer_py-0.1.0/prusaslicer_py/slicer.py +402 -0
- prusaslicer_py-0.1.0/pyproject.toml +62 -0
- prusaslicer_py-0.1.0/scripts/01_store_helps.py +38 -0
- prusaslicer_py-0.1.0/scripts/02_json_cli.py +179 -0
- prusaslicer_py-0.1.0/scripts/03_restructure_cli.py +102 -0
- prusaslicer_py-0.1.0/tests/__init__.py +0 -0
- prusaslicer_py-0.1.0/tests/conftest.py +40 -0
- prusaslicer_py-0.1.0/tests/fixtures/synthetic_help_output.txt +30 -0
- prusaslicer_py-0.1.0/tests/test_cli_extraction.py +275 -0
- prusaslicer_py-0.1.0/tests/test_engine.py +51 -0
- prusaslicer_py-0.1.0/tests/test_slice_output.py +182 -0
- prusaslicer_py-0.1.0/tests/test_slicer.py +110 -0
- prusaslicer_py-0.1.0/uv.lock +381 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
# The pins live in pyproject.toml's [dependency-groups] and uv.lock; there is
|
|
4
|
+
# no requirements.txt left for the pip ecosystem to read.
|
|
5
|
+
- package-ecosystem: uv
|
|
6
|
+
directory: /
|
|
7
|
+
schedule:
|
|
8
|
+
interval: weekly
|
|
9
|
+
commit-message:
|
|
10
|
+
prefix: build
|
|
11
|
+
groups:
|
|
12
|
+
python:
|
|
13
|
+
patterns: ["*"]
|
|
14
|
+
|
|
15
|
+
- package-ecosystem: github-actions
|
|
16
|
+
directory: /
|
|
17
|
+
schedule:
|
|
18
|
+
interval: weekly
|
|
19
|
+
commit-message:
|
|
20
|
+
prefix: ci
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
# No branch filter: a stacked pull request is based on its parent branch, not
|
|
7
|
+
# on main, and a base change fires `edited`, which is not a default activity
|
|
8
|
+
# type -- so filtering on main means a retargeted PR gets no checks at all.
|
|
9
|
+
pull_request:
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
14
|
+
concurrency:
|
|
15
|
+
group: ci-${{ github.ref }}
|
|
16
|
+
cancel-in-progress: true
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
check:
|
|
20
|
+
name: Check
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
timeout-minutes: 10
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v7
|
|
25
|
+
- uses: extractions/setup-just@v4
|
|
26
|
+
- uses: astral-sh/setup-uv@v7
|
|
27
|
+
with:
|
|
28
|
+
enable-cache: true
|
|
29
|
+
- run: just setup
|
|
30
|
+
- run: just check
|
|
31
|
+
|
|
32
|
+
test:
|
|
33
|
+
name: Test (Python ${{ matrix.python-version }}, ${{ matrix.os }})
|
|
34
|
+
runs-on: ${{ matrix.os }}
|
|
35
|
+
timeout-minutes: 10
|
|
36
|
+
strategy:
|
|
37
|
+
fail-fast: false
|
|
38
|
+
matrix:
|
|
39
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
40
|
+
os: [ubuntu-latest]
|
|
41
|
+
include:
|
|
42
|
+
- python-version: "3.11"
|
|
43
|
+
os: windows-latest
|
|
44
|
+
steps:
|
|
45
|
+
- uses: actions/checkout@v7
|
|
46
|
+
- uses: extractions/setup-just@v4
|
|
47
|
+
- uses: astral-sh/setup-uv@v7
|
|
48
|
+
with:
|
|
49
|
+
enable-cache: true
|
|
50
|
+
python-version: ${{ matrix.python-version }}
|
|
51
|
+
# PrusaSlicer is not installed on these runners, so the engine-dependent
|
|
52
|
+
# tests skip. That is an environment fault, not a pass -- the skip count
|
|
53
|
+
# in the log says how many tests were withheld. Do not set
|
|
54
|
+
# PRUSASLICER_PY_REQUIRE_ENGINE here until a runner actually has the
|
|
55
|
+
# engine; see docs/DECISIONS.md D3.
|
|
56
|
+
- run: just setup
|
|
57
|
+
- run: just test
|
|
58
|
+
|
|
59
|
+
ok:
|
|
60
|
+
name: ok
|
|
61
|
+
if: always()
|
|
62
|
+
needs: [check, test]
|
|
63
|
+
runs-on: ubuntu-latest
|
|
64
|
+
timeout-minutes: 5
|
|
65
|
+
steps:
|
|
66
|
+
- name: Fail if any upstream job failed or was cancelled
|
|
67
|
+
if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
|
|
68
|
+
run: exit 1
|
|
69
|
+
- name: Pass
|
|
70
|
+
run: echo 'All checks passed'
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
name: Engine
|
|
2
|
+
|
|
3
|
+
# The real engine, across the ways people actually install it.
|
|
4
|
+
#
|
|
5
|
+
# ci.yml runs the mocked suite everywhere and SKIPS the engine tests, because
|
|
6
|
+
# no standard runner has PrusaSlicer. That leaves the end-to-end slice path --
|
|
7
|
+
# the reason this package exists -- covered by stubs alone, and stubs are what
|
|
8
|
+
# hid `--version` (which PrusaSlicer has never supported) and the fact that a
|
|
9
|
+
# Flatpak install was invisible to discovery.
|
|
10
|
+
#
|
|
11
|
+
# Every job here sets PRUSASLICER_PY_REQUIRE_ENGINE=1, so a failed install is a
|
|
12
|
+
# LOUD failure rather than a green run full of skips. A job that cannot get the
|
|
13
|
+
# engine must not look like a job that tested it.
|
|
14
|
+
#
|
|
15
|
+
# Not on every push: these install a large GUI application and are slower and
|
|
16
|
+
# more fragile than the gate. Run on demand, and weekly so upstream packaging
|
|
17
|
+
# changes surface without anyone remembering to look.
|
|
18
|
+
|
|
19
|
+
on:
|
|
20
|
+
workflow_dispatch:
|
|
21
|
+
schedule:
|
|
22
|
+
- cron: "0 6 * * 1" # Mondays, 06:00 UTC
|
|
23
|
+
|
|
24
|
+
permissions:
|
|
25
|
+
contents: read
|
|
26
|
+
|
|
27
|
+
concurrency:
|
|
28
|
+
group: engine-${{ github.ref }}
|
|
29
|
+
cancel-in-progress: true
|
|
30
|
+
|
|
31
|
+
env:
|
|
32
|
+
PRUSASLICER_PY_REQUIRE_ENGINE: "1"
|
|
33
|
+
|
|
34
|
+
jobs:
|
|
35
|
+
flatpak:
|
|
36
|
+
name: Linux / Flatpak (Flathub)
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
timeout-minutes: 45
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/checkout@v7
|
|
41
|
+
- uses: extractions/setup-just@v4
|
|
42
|
+
- uses: astral-sh/setup-uv@v7
|
|
43
|
+
with:
|
|
44
|
+
enable-cache: true
|
|
45
|
+
- name: Install PrusaSlicer from Flathub
|
|
46
|
+
run: |
|
|
47
|
+
sudo apt-get update
|
|
48
|
+
sudo apt-get install -y flatpak
|
|
49
|
+
sudo flatpak remote-add --if-not-exists flathub \
|
|
50
|
+
https://dl.flathub.org/repo/flathub.flatpakrepo
|
|
51
|
+
sudo flatpak install -y --noninteractive flathub com.prusa3d.PrusaSlicer
|
|
52
|
+
- name: Show what was installed
|
|
53
|
+
run: flatpak info com.prusa3d.PrusaSlicer | head -20
|
|
54
|
+
- run: just setup
|
|
55
|
+
- name: Full suite, engine required
|
|
56
|
+
# PRUSASLICER_PY_REQUIRE_ENGINE is set at workflow level, so a missing
|
|
57
|
+
# engine fails here rather than skipping. `just test` rather than
|
|
58
|
+
# `just test-engine`: the latter prefixes `VAR=x cmd`, which is
|
|
59
|
+
# POSIX-only and does not run on the Windows job.
|
|
60
|
+
run: just test
|
|
61
|
+
- name: Capture and extract the CLI surface
|
|
62
|
+
run: |
|
|
63
|
+
just capture-cli
|
|
64
|
+
just extract-cli
|
|
65
|
+
- name: Full suite again, now with the captured corpus present
|
|
66
|
+
# The corpus tests skip when scripts/01_helps is empty; this is the one
|
|
67
|
+
# place they actually run, against a real PrusaSlicer's real --help.
|
|
68
|
+
run: just test
|
|
69
|
+
|
|
70
|
+
linux-apt:
|
|
71
|
+
name: Linux / distro package on PATH
|
|
72
|
+
runs-on: ubuntu-latest
|
|
73
|
+
timeout-minutes: 45
|
|
74
|
+
steps:
|
|
75
|
+
- uses: actions/checkout@v7
|
|
76
|
+
- uses: extractions/setup-just@v4
|
|
77
|
+
- uses: astral-sh/setup-uv@v7
|
|
78
|
+
with:
|
|
79
|
+
enable-cache: true
|
|
80
|
+
- name: Install PrusaSlicer from the distro archive
|
|
81
|
+
# Was an AppImage job. PrusaSlicer publishes NO Linux assets on its
|
|
82
|
+
# GitHub releases -- checked across the last five, including 2.9.6:
|
|
83
|
+
# only setup.exe, .dmg and a Windows .zip. Linux is Flathub and distro
|
|
84
|
+
# packages, so this exercises plain PATH discovery via the distro
|
|
85
|
+
# package rather than an artifact that does not exist.
|
|
86
|
+
run: |
|
|
87
|
+
set -euo pipefail
|
|
88
|
+
sudo apt-get update
|
|
89
|
+
sudo apt-get install -y prusa-slicer
|
|
90
|
+
command -v prusa-slicer
|
|
91
|
+
prusa-slicer --help | head -1
|
|
92
|
+
- run: just setup
|
|
93
|
+
- name: Full suite, engine required
|
|
94
|
+
run: just test
|
|
95
|
+
|
|
96
|
+
macos:
|
|
97
|
+
name: macOS / Homebrew cask
|
|
98
|
+
runs-on: macos-latest
|
|
99
|
+
timeout-minutes: 45
|
|
100
|
+
steps:
|
|
101
|
+
- uses: actions/checkout@v7
|
|
102
|
+
- uses: extractions/setup-just@v4
|
|
103
|
+
- uses: astral-sh/setup-uv@v7
|
|
104
|
+
with:
|
|
105
|
+
enable-cache: true
|
|
106
|
+
- name: Install PrusaSlicer
|
|
107
|
+
run: |
|
|
108
|
+
set -euo pipefail
|
|
109
|
+
brew install --cask prusaslicer
|
|
110
|
+
# The cask installs an .app bundle; expose the CLI on PATH.
|
|
111
|
+
sudo ln -sf "/Applications/PrusaSlicer.app/Contents/MacOS/PrusaSlicer" \
|
|
112
|
+
/usr/local/bin/prusa-slicer
|
|
113
|
+
command -v prusa-slicer
|
|
114
|
+
- run: just setup
|
|
115
|
+
- name: Full suite, engine required
|
|
116
|
+
# PRUSASLICER_PY_REQUIRE_ENGINE is set at workflow level, so a missing
|
|
117
|
+
# engine fails here rather than skipping. `just test` rather than
|
|
118
|
+
# `just test-engine`: the latter prefixes `VAR=x cmd`, which is
|
|
119
|
+
# POSIX-only and does not run on the Windows job.
|
|
120
|
+
run: just test
|
|
121
|
+
|
|
122
|
+
windows:
|
|
123
|
+
name: Windows / official portable zip
|
|
124
|
+
runs-on: windows-latest
|
|
125
|
+
timeout-minutes: 45
|
|
126
|
+
steps:
|
|
127
|
+
- uses: actions/checkout@v7
|
|
128
|
+
- uses: extractions/setup-just@v4
|
|
129
|
+
- uses: astral-sh/setup-uv@v7
|
|
130
|
+
with:
|
|
131
|
+
enable-cache: true
|
|
132
|
+
- name: Install PrusaSlicer from the official portable zip
|
|
133
|
+
# Was `choco install prusaslicer`. The package exists, but its install
|
|
134
|
+
# location is not something to guess at, and a recursive sweep of
|
|
135
|
+
# Program Files reported "not found" for an install that had probably
|
|
136
|
+
# succeeded. The upstream .zip is the same build the installer ships,
|
|
137
|
+
# needs no admin, pins an exact version, and lands the layout discovery
|
|
138
|
+
# expects: <dir>/prusa-slicer-console.exe beside <dir>/resources/shapes.
|
|
139
|
+
shell: pwsh
|
|
140
|
+
run: |
|
|
141
|
+
$ErrorActionPreference = 'Stop'
|
|
142
|
+
$rel = Invoke-RestMethod https://api.github.com/repos/prusa3d/PrusaSlicer/releases/latest
|
|
143
|
+
$asset = $rel.assets | Where-Object { $_.name -like '*.zip' } | Select-Object -First 1
|
|
144
|
+
if (-not $asset) { throw "no .zip asset in $($rel.tag_name)" }
|
|
145
|
+
Write-Host "downloading $($asset.name)"
|
|
146
|
+
Invoke-WebRequest $asset.browser_download_url -OutFile prusaslicer.zip
|
|
147
|
+
Expand-Archive prusaslicer.zip -DestinationPath "$env:RUNNER_TEMP\ps"
|
|
148
|
+
$exe = Get-ChildItem "$env:RUNNER_TEMP\ps" -Recurse -Filter 'prusa-slicer-console.exe' |
|
|
149
|
+
Select-Object -First 1
|
|
150
|
+
if (-not $exe) { throw 'prusa-slicer-console.exe not present in the zip' }
|
|
151
|
+
Write-Host "found: $($exe.FullName)"
|
|
152
|
+
Add-Content -Path $env:GITHUB_PATH -Value $exe.DirectoryName
|
|
153
|
+
- name: Confirm it is on PATH
|
|
154
|
+
shell: pwsh
|
|
155
|
+
run: (Get-Command prusa-slicer-console.exe).Source
|
|
156
|
+
- run: just setup
|
|
157
|
+
- name: Full suite, engine required
|
|
158
|
+
run: just test
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Publishes to PyPI via Trusted Publishing (OIDC). No API token is stored.
|
|
4
|
+
#
|
|
5
|
+
# The publisher on PyPI is pinned to heibench/prusaslicer-py, this workflow
|
|
6
|
+
# filename, and the `pypi` environment -- all three must match or the publish
|
|
7
|
+
# is refused.
|
|
8
|
+
|
|
9
|
+
on:
|
|
10
|
+
release:
|
|
11
|
+
types: [published]
|
|
12
|
+
workflow_dispatch:
|
|
13
|
+
inputs:
|
|
14
|
+
expected_version:
|
|
15
|
+
description: "Exact version the dist must carry (e.g. 0.1.0) — guards against publishing the wrong ref"
|
|
16
|
+
required: true
|
|
17
|
+
type: string
|
|
18
|
+
|
|
19
|
+
permissions:
|
|
20
|
+
contents: read
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
build:
|
|
24
|
+
name: Build and verify
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
timeout-minutes: 15
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v7
|
|
29
|
+
- uses: extractions/setup-just@v4
|
|
30
|
+
- uses: astral-sh/setup-uv@v7
|
|
31
|
+
with:
|
|
32
|
+
enable-cache: true
|
|
33
|
+
|
|
34
|
+
- name: Gate on the same checks a pull request runs
|
|
35
|
+
run: |
|
|
36
|
+
just setup
|
|
37
|
+
just check
|
|
38
|
+
just test
|
|
39
|
+
|
|
40
|
+
- name: Build sdist and wheel
|
|
41
|
+
run: uv build
|
|
42
|
+
|
|
43
|
+
- name: Guard built version against the intended release
|
|
44
|
+
# A tag and a version are two statements of one fact; publishing when
|
|
45
|
+
# they disagree ships something nobody asked for under a name someone
|
|
46
|
+
# will trust.
|
|
47
|
+
run: |
|
|
48
|
+
set -eu
|
|
49
|
+
version=$(ls dist/*.tar.gz | sed -E 's|.*/prusaslicer_py-(.+)\.tar\.gz|\1|')
|
|
50
|
+
echo "built: $version"
|
|
51
|
+
case "${{ github.event_name }}" in
|
|
52
|
+
release) want="${GITHUB_REF_NAME#v}" ;;
|
|
53
|
+
workflow_dispatch) want="${{ inputs.expected_version }}" ;;
|
|
54
|
+
esac
|
|
55
|
+
if [ "$version" != "$want" ]; then
|
|
56
|
+
echo "::error::built version '$version' does not match intended release '$want'"
|
|
57
|
+
exit 1
|
|
58
|
+
fi
|
|
59
|
+
case "$version" in
|
|
60
|
+
*.dev*|*rc*|*a[0-9]*|*b[0-9]*)
|
|
61
|
+
echo "::error::refusing to publish pre-release version '$version'"
|
|
62
|
+
exit 1 ;;
|
|
63
|
+
esac
|
|
64
|
+
|
|
65
|
+
- name: Check the metadata PyPI will see
|
|
66
|
+
run: uv run --with twine twine check dist/*
|
|
67
|
+
|
|
68
|
+
- uses: actions/upload-artifact@v7
|
|
69
|
+
with:
|
|
70
|
+
name: dist
|
|
71
|
+
path: dist/
|
|
72
|
+
|
|
73
|
+
publish:
|
|
74
|
+
name: Publish to PyPI
|
|
75
|
+
needs: build
|
|
76
|
+
runs-on: ubuntu-latest
|
|
77
|
+
timeout-minutes: 15
|
|
78
|
+
environment: pypi
|
|
79
|
+
permissions:
|
|
80
|
+
id-token: write # PyPI Trusted Publishing (OIDC)
|
|
81
|
+
steps:
|
|
82
|
+
- uses: actions/download-artifact@v8
|
|
83
|
+
with:
|
|
84
|
+
name: dist
|
|
85
|
+
path: dist/
|
|
86
|
+
- name: Publish
|
|
87
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Python bytecode files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
|
|
5
|
+
# Distribution / packaging
|
|
6
|
+
.Python
|
|
7
|
+
env/
|
|
8
|
+
venv/
|
|
9
|
+
ENV/
|
|
10
|
+
env.bak/
|
|
11
|
+
venv.bak/
|
|
12
|
+
|
|
13
|
+
# Installer logs
|
|
14
|
+
pip-log.txt
|
|
15
|
+
pip-delete-this-directory.txt
|
|
16
|
+
|
|
17
|
+
# mypy
|
|
18
|
+
.mypy_cache/
|
|
19
|
+
|
|
20
|
+
# Pytest
|
|
21
|
+
.cache/
|
|
22
|
+
|
|
23
|
+
# Coverage reports
|
|
24
|
+
.coverage
|
|
25
|
+
.coverage.*
|
|
26
|
+
|
|
27
|
+
# dotenv
|
|
28
|
+
.env
|
|
29
|
+
.env.*
|
|
30
|
+
|
|
31
|
+
# VS Code settings
|
|
32
|
+
.vscode/
|
|
33
|
+
|
|
34
|
+
# PyCharm
|
|
35
|
+
.idea/
|
|
36
|
+
|
|
37
|
+
# Operating system files
|
|
38
|
+
.DS_Store
|
|
39
|
+
Thumbs.db
|
|
40
|
+
|
|
41
|
+
# Jupyter Notebook
|
|
42
|
+
.ipynb_checkpoints/
|
|
43
|
+
|
|
44
|
+
# PrusaSlicer Python egg-info
|
|
45
|
+
prusaslicer_py.egg-info/
|
|
46
|
+
|
|
47
|
+
# Python wheels
|
|
48
|
+
*.whl
|
|
49
|
+
|
|
50
|
+
# Cython build
|
|
51
|
+
build/
|
|
52
|
+
dist/
|
|
53
|
+
|
|
54
|
+
# Sphinx documentation
|
|
55
|
+
docs/_build/
|
|
56
|
+
|
|
57
|
+
# PyInstaller
|
|
58
|
+
*.spec
|
|
59
|
+
|
|
60
|
+
# JetBrains IDE files
|
|
61
|
+
.idea/
|
|
62
|
+
|
|
63
|
+
# Sublime Text project files
|
|
64
|
+
*.sublime-project
|
|
65
|
+
*.sublime-workspace
|
|
66
|
+
|
|
67
|
+
# MacOS Finder files
|
|
68
|
+
._*
|
|
69
|
+
|
|
70
|
+
# Windows Installer files
|
|
71
|
+
*.cab
|
|
72
|
+
*.msi
|
|
73
|
+
*.msm
|
|
74
|
+
*.msp
|
|
75
|
+
|
|
76
|
+
# Ignore the output directory
|
|
77
|
+
output/
|
|
78
|
+
# uv / virtualenv
|
|
79
|
+
.venv/
|
|
80
|
+
|
|
81
|
+
# Tool caches
|
|
82
|
+
.ruff_cache/
|
|
83
|
+
.pytest_cache/
|
|
84
|
+
|
|
85
|
+
# Captured PrusaSlicer help and everything derived from it (D8).
|
|
86
|
+
# Regenerate with `just capture-cli` + `just extract-cli`.
|
|
87
|
+
scripts/01_helps/
|
|
88
|
+
scripts/02_structured_data/
|
|
89
|
+
scripts/03_restructured_data/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/gitleaks/gitleaks
|
|
3
|
+
rev: v8.27.2
|
|
4
|
+
hooks:
|
|
5
|
+
- id: gitleaks
|
|
6
|
+
|
|
7
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
8
|
+
rev: v5.0.0
|
|
9
|
+
hooks:
|
|
10
|
+
- id: trailing-whitespace
|
|
11
|
+
- id: end-of-file-fixer
|
|
12
|
+
- id: check-yaml
|
|
13
|
+
- id: check-merge-conflict
|
|
14
|
+
- id: check-added-large-files
|
|
15
|
+
|
|
16
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
17
|
+
rev: v0.11.12
|
|
18
|
+
hooks:
|
|
19
|
+
- id: ruff
|
|
20
|
+
args: [--fix]
|
|
21
|
+
- id: ruff-format
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Instructions for humans and AI coding agents working in this repository.
|
|
4
|
+
|
|
5
|
+
The org-wide contract at <https://github.com/heibench/.github/blob/main/AGENTS.md>
|
|
6
|
+
is the floor. This file carries what is specific to this repository; where the
|
|
7
|
+
two conflict, this file wins.
|
|
8
|
+
|
|
9
|
+
## Project
|
|
10
|
+
|
|
11
|
+
`prusaslicer-py` is a **driver**, not a checker. It puts the PrusaSlicer
|
|
12
|
+
command-line interface under program control: locating the executable, invoking
|
|
13
|
+
it, and returning what it established about the engine's output. It does not
|
|
14
|
+
adjudicate a design, and it does not post-process G-code.
|
|
15
|
+
|
|
16
|
+
`scripts/` holds a small extraction pipeline that turns PrusaSlicer's own
|
|
17
|
+
`--help` output into a machine-readable description of its CLI surface
|
|
18
|
+
(`scripts/03_restructured_data/*.json`). That artifact is a large part of what
|
|
19
|
+
this repository is for. Each entry is
|
|
20
|
+
`{option, aliases, value, description}`; see `docs/DECISIONS.md` D6, and treat
|
|
21
|
+
the schema as stable.
|
|
22
|
+
|
|
23
|
+
## Stack
|
|
24
|
+
|
|
25
|
+
- **Python >= 3.11**, packaged with `hatchling`, dependencies managed by `uv`
|
|
26
|
+
against a committed `uv.lock`.
|
|
27
|
+
- **No runtime dependencies.** The engine boundary is `subprocess`; keep it
|
|
28
|
+
that way. Adding a runtime dependency needs a decision entry.
|
|
29
|
+
- **`prusaslicer_py/slicer.py` is the only module in the package that may
|
|
30
|
+
import `subprocess` or name an executable.** `scripts/01_store_helps.py` is
|
|
31
|
+
the one recorded exception; see `docs/DECISIONS.md` D1.
|
|
32
|
+
- **Tooling** -- `ruff` (format + lint), `mypy` (types), `pytest` (tests),
|
|
33
|
+
`just` (task runner), `pre-commit`.
|
|
34
|
+
|
|
35
|
+
## Layout
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
prusaslicer_py/ the driver; the only module that touches the engine
|
|
39
|
+
tests/ pytest suite
|
|
40
|
+
examples/ runnable usage examples
|
|
41
|
+
scripts/ CLI-surface extraction pipeline (01 -> 02 -> 03)
|
|
42
|
+
docs/DECISIONS.md numbered decisions and their reasoning
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Commands
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
just setup # uv sync
|
|
49
|
+
just fmt # format + autofix
|
|
50
|
+
just check # fmt-check + lint + typecheck (CI-equivalent)
|
|
51
|
+
just test # run tests; engine tests skip if PrusaSlicer is absent
|
|
52
|
+
just test-engine # run tests; engine tests FAIL if PrusaSlicer is absent
|
|
53
|
+
just extract-cli # regenerate scripts/02_ and 03_ data from captured help
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Run `just check && just test` before every commit. Never `--no-verify`.
|
|
57
|
+
|
|
58
|
+
## The engine is optional at test time, and that is load-bearing
|
|
59
|
+
|
|
60
|
+
PrusaSlicer is not installed on most machines that will run this suite,
|
|
61
|
+
including CI. A missing engine is an **environment fault, not a verdict**: it
|
|
62
|
+
says nothing about whether this code is correct.
|
|
63
|
+
|
|
64
|
+
- Tests that need the engine request the `engine` fixture in
|
|
65
|
+
`tests/conftest.py`. It skips when PrusaSlicer is not on `PATH`.
|
|
66
|
+
- Setting `PRUSASLICER_PY_REQUIRE_ENGINE` turns that skip into a hard failure.
|
|
67
|
+
Use it on any machine where the engine is supposed to be present.
|
|
68
|
+
- **A skipped test is not a passing test.** Guard per-test, never at module
|
|
69
|
+
import: a module-level gate reports one skipped line and silently takes every
|
|
70
|
+
test in the file with it.
|
|
71
|
+
|
|
72
|
+
## Never let silence read as success
|
|
73
|
+
|
|
74
|
+
The engine can exit `0` without producing the file it was asked for. A driver
|
|
75
|
+
call that did not do the thing must not return as though it did:
|
|
76
|
+
|
|
77
|
+
- Verify the artifact before reporting it. `slice_model` checks that the G-code
|
|
78
|
+
file exists and is non-empty, and returns a result describing it.
|
|
79
|
+
- Capture the engine's `stdout`/`stderr` and hand them to the caller. A
|
|
80
|
+
diagnostic that only reaches the parent process's terminal is unavailable to
|
|
81
|
+
the program that needs it.
|
|
82
|
+
- **Decode the engine's output with `errors="replace"`, always.** A strict
|
|
83
|
+
decode raises before the result can be checked, so a byte we cannot read
|
|
84
|
+
becomes a verdict on a slice that succeeded. See `docs/DECISIONS.md` D5.
|
|
85
|
+
- Failure carries the same fields as success. Both `SliceEngineError` and
|
|
86
|
+
`SliceOutputError` expose `output_path`, `returncode`, `stdout` and `stderr`;
|
|
87
|
+
do not put a fact in a message string that the success path returns.
|
|
88
|
+
- Do not add an "assume it worked" escape hatch.
|
|
89
|
+
|
|
90
|
+
## Status -- what has actually been established
|
|
91
|
+
|
|
92
|
+
Treat these lines as code: if a change makes one false, the change is not
|
|
93
|
+
finished.
|
|
94
|
+
|
|
95
|
+
- **The end-to-end path runs against a real engine on four install modes**,
|
|
96
|
+
in the `Engine` workflow: Flathub Flatpak, a Linux distro package on PATH,
|
|
97
|
+
a macOS Homebrew cask `.app` bundle, and the official Windows portable zip.
|
|
98
|
+
Each runs the whole suite with `PRUSASLICER_PY_REQUIRE_ENGINE=1`, including
|
|
99
|
+
`test_slice_produces_gcode_with_the_real_engine`.
|
|
100
|
+
- **`CI` runs the mocked suite** on Linux (3.11/3.12/3.13) and Windows with no
|
|
101
|
+
engine, so the engine tests skip and the count says how many were withheld.
|
|
102
|
+
- Earlier revisions of this file claimed `check_version` was in regular use on
|
|
103
|
+
Windows. That was false -- it called `--version`, which PrusaSlicer supports
|
|
104
|
+
on no platform. Kept here as a reminder that a Status line is a claim.
|
|
105
|
+
|
|
106
|
+
## Constraints
|
|
107
|
+
|
|
108
|
+
- Do not hand-edit `scripts/02_structured_data/` or
|
|
109
|
+
`scripts/03_restructured_data/`. They are generated; fix the generator and run
|
|
110
|
+
`just extract-cli`. A test regenerates them and fails if the committed files
|
|
111
|
+
have drifted from the committed parser.
|
|
112
|
+
- Always name an encoding when reading or writing the help output and the JSON
|
|
113
|
+
-- in every stage, `01_store_helps.py` included. The help contains non-ASCII
|
|
114
|
+
characters, and a locale-dependent `open()` is how the data came to say
|
|
115
|
+
`°C`. Decode the engine itself with `errors="replace"`.
|
|
116
|
+
- Do not reimplement PrusaSlicer's arithmetic. The premise is that the engine
|
|
117
|
+
knows what the slice is and we do not.
|
|
118
|
+
- Do not add AI attribution to commits or PR descriptions -- no co-author
|
|
119
|
+
trailers, session links, or "generated with" footers.
|
|
120
|
+
- Do not name, link, or describe any private repository in public output.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to prusaslicer-py are documented here. Follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com) and
|
|
5
|
+
[Semantic Versioning](https://semver.org).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.1.0] — 2026-09-06
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
First release. Notable, given this began as a thin `subprocess` wrapper:
|
|
14
|
+
|
|
15
|
+
- Flatpak discovery. `shutil.which` cannot see a Flatpak — the app is not on
|
|
16
|
+
PATH and there is no binary to find — so a Flathub install, which is how
|
|
17
|
+
PrusaSlicer is normally installed on Linux, previously read as "not
|
|
18
|
+
installed". `PrusaSlicer.engine_kind` reports `path` or `flatpak` so a caller
|
|
19
|
+
can branch (D9).
|
|
20
|
+
- `SliceResult` returned from `slice_model`, carrying `output_path`,
|
|
21
|
+
`size_bytes`, `returncode`, `stdout` and `stderr`. The artifact is verified to
|
|
22
|
+
exist and be non-empty before a result is returned, so "the engine produced
|
|
23
|
+
the file" and "the call did not raise" stop being the same claim (D5).
|
|
24
|
+
- `SliceError`, with `SliceEngineError` and `SliceOutputError` siblings. Both
|
|
25
|
+
carry the same fields, so the failure path is as inspectable as the success
|
|
26
|
+
path.
|
|
27
|
+
- `just capture-cli` regenerates PrusaSlicer's help locally, through the driver
|
|
28
|
+
rather than a hardcoded Windows executable name.
|
|
29
|
+
- Real-engine CI across install modes — Flathub Flatpak, AppImage on PATH,
|
|
30
|
+
Homebrew cask, Chocolatey — each with a missing engine as a hard failure
|
|
31
|
+
rather than a skip.
|
|
32
|
+
- Repository floor: `pyproject` + `uv` with a committed lock, `justfile`, CI on
|
|
33
|
+
Linux and Windows, pre-commit, `AGENTS.md`, `docs/DECISIONS.md`.
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- `check_version()` called `--version`, which PrusaSlicer has never supported —
|
|
38
|
+
2.9.6 answers `Unknown option --version` and exits 1. The version is read
|
|
39
|
+
from the first line of `--help`, which is the only place the engine prints
|
|
40
|
+
it. Every stub in the suite answered `--version`, so the tests agreed with
|
|
41
|
+
the code and both were wrong.
|
|
42
|
+
- `get_example_shapes()` ignored an explicitly supplied `slicer_path` and
|
|
43
|
+
re-ran discovery, raising `FileNotFoundError` even when handed a working
|
|
44
|
+
engine.
|
|
45
|
+
- A Flatpak's own files are mounted at `/app` inside the sandbox, so bundled
|
|
46
|
+
example shapes were real to the caller and absent to the engine. Paths under
|
|
47
|
+
the app tree are translated; paths outside it are granted with
|
|
48
|
+
`--filesystem=<dir>`, only for the directories involved.
|
|
49
|
+
- An undecodable byte in the engine's output raised `UnicodeDecodeError` before
|
|
50
|
+
the result could be verified, reporting failure for a slice that succeeded.
|
|
51
|
+
- The CLI-surface extraction dropped 68 distinct options — every one whose
|
|
52
|
+
description began on the following line — mis-split 7 aliased options, and
|
|
53
|
+
carried cp1252 mojibake from a Windows capture. It also discarded any section
|
|
54
|
+
whose name matched none of three hardcoded buckets, which silently lost
|
|
55
|
+
PrusaSlicer 2.9.6's entire `input:` section.
|
|
56
|
+
|
|
57
|
+
### Changed
|
|
58
|
+
|
|
59
|
+
- Licensed under Apache-2.0, superseding MIT (D7).
|
|
60
|
+
- PrusaSlicer's captured `--help` output is no longer committed; regenerate it
|
|
61
|
+
with `just capture-cli` (D8).
|
|
62
|
+
|
|
63
|
+
[Unreleased]: https://github.com/heibench/prusaslicer-py/compare/v0.1.0...HEAD
|
|
64
|
+
[0.1.0]: https://github.com/heibench/prusaslicer-py/releases/tag/v0.1.0
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
## Getting set up
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
just setup # sync the environment
|
|
7
|
+
just check # format check + lint + typecheck
|
|
8
|
+
just test # the suite
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
`just check && just test` is what CI runs. Run it before every commit, and
|
|
12
|
+
never bypass hooks.
|
|
13
|
+
|
|
14
|
+
## Tests and the engine
|
|
15
|
+
|
|
16
|
+
Most of the suite runs without PrusaSlicer installed. The tests that need the
|
|
17
|
+
real engine request the `engine` fixture, which **skips** when it is absent —
|
|
18
|
+
a missing engine is an environment fault, not a verdict on the code.
|
|
19
|
+
|
|
20
|
+
Set `PRUSASLICER_PY_REQUIRE_ENGINE=1` to turn that skip into a hard failure.
|
|
21
|
+
CI does this on the runners that install the engine, so "the engine is missing"
|
|
22
|
+
cannot masquerade as a green run.
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
just test-engine # the whole suite, engine required (POSIX shells)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
If you have PrusaSlicer installed, please run it and report what happens. The
|
|
29
|
+
engine is installed several different ways — PATH, Flatpak, AppImage, macOS
|
|
30
|
+
bundle, Windows installer — and discovery has been wrong about at least one of
|
|
31
|
+
them before.
|
|
32
|
+
|
|
33
|
+
## The CLI surface data
|
|
34
|
+
|
|
35
|
+
`scripts/01_helps/` and everything derived from it are **not committed**: they
|
|
36
|
+
are PrusaSlicer's own output and this repository is Apache-2.0 (D8). Regenerate
|
|
37
|
+
with:
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
just capture-cli # needs PrusaSlicer installed
|
|
41
|
+
just extract-cli
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The extraction tests skip without that corpus, and say so.
|
|
45
|
+
|
|
46
|
+
## Conventions
|
|
47
|
+
|
|
48
|
+
- Conventional Commits: `type(scope): description`, imperative, lowercase, no
|
|
49
|
+
trailing period, subject ≤ 72 characters.
|
|
50
|
+
- One logical change per commit; branch and open a pull request.
|
|
51
|
+
- Decisions with consequences go in [`docs/DECISIONS.md`](docs/DECISIONS.md),
|
|
52
|
+
numbered. Do not relitigate a numbered decision — supersede it.
|
|
53
|
+
- A check that cannot fail is not a check. If you add one, break the thing it
|
|
54
|
+
checks and watch it go red before you trust it.
|