cadgen 0.4.1__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.
- cadgen-0.4.1/LICENSE +21 -0
- cadgen-0.4.1/PKG-INFO +122 -0
- cadgen-0.4.1/README.md +95 -0
- cadgen-0.4.1/pyproject.toml +48 -0
- cadgen-0.4.1/setup.cfg +4 -0
- cadgen-0.4.1/src/cadgen/__init__.py +44 -0
- cadgen-0.4.1/src/cadgen/_internal/__init__.py +6 -0
- cadgen-0.4.1/src/cadgen/_internal/assembly_spec.py +54 -0
- cadgen-0.4.1/src/cadgen/_internal/cli_errors.py +107 -0
- cadgen-0.4.1/src/cadgen/_internal/cli_locking.py +99 -0
- cadgen-0.4.1/src/cadgen/_internal/component_package.py +782 -0
- cadgen-0.4.1/src/cadgen/_internal/drawing_package.py +476 -0
- cadgen-0.4.1/src/cadgen/_internal/file_metadata.py +61 -0
- cadgen-0.4.1/src/cadgen/_internal/generation.py +2412 -0
- cadgen-0.4.1/src/cadgen/_internal/glb.py +1163 -0
- cadgen-0.4.1/src/cadgen/_internal/glb_mesh_payload.py +699 -0
- cadgen-0.4.1/src/cadgen/_internal/glb_topology.py +513 -0
- cadgen-0.4.1/src/cadgen/_internal/implicit_package.py +325 -0
- cadgen-0.4.1/src/cadgen/_internal/node_resolve_hooks.mjs +49 -0
- cadgen-0.4.1/src/cadgen/_internal/node_resolve_register.mjs +10 -0
- cadgen-0.4.1/src/cadgen/_internal/node_runtime.py +417 -0
- cadgen-0.4.1/src/cadgen/_internal/package_freshness.py +89 -0
- cadgen-0.4.1/src/cadgen/_internal/source_hash.py +466 -0
- cadgen-0.4.1/src/cadgen/_internal/step_hash.py +12 -0
- cadgen-0.4.1/src/cadgen/_internal/step_metadata.py +321 -0
- cadgen-0.4.1/src/cadgen/_internal/step_scene.py +2837 -0
- cadgen-0.4.1/src/cadgen/_internal/stl.py +31 -0
- cadgen-0.4.1/src/cadgen/_internal/threemf.py +563 -0
- cadgen-0.4.1/src/cadgen/_internal/validators.py +119 -0
- cadgen-0.4.1/src/cadgen/analysis.py +683 -0
- cadgen-0.4.1/src/cadgen/api.py +6 -0
- cadgen-0.4.1/src/cadgen/assembly.py +436 -0
- cadgen-0.4.1/src/cadgen/cad_ref_syntax.py +143 -0
- cadgen-0.4.1/src/cadgen/catalog.py +528 -0
- cadgen-0.4.1/src/cadgen/cli_logging.py +58 -0
- cadgen-0.4.1/src/cadgen/color.py +65 -0
- cadgen-0.4.1/src/cadgen/coordination/__init__.py +448 -0
- cadgen-0.4.1/src/cadgen/coordination/kinds.py +89 -0
- cadgen-0.4.1/src/cadgen/coordination/lock.py +252 -0
- cadgen-0.4.1/src/cadgen/coordination/paths.py +76 -0
- cadgen-0.4.1/src/cadgen/coordination/phases.py +360 -0
- cadgen-0.4.1/src/cadgen/coordination/record.py +194 -0
- cadgen-0.4.1/src/cadgen/drawing_checks.py +263 -0
- cadgen-0.4.1/src/cadgen/drawing_render.py +464 -0
- cadgen-0.4.1/src/cadgen/dxf_artifact.py +210 -0
- cadgen-0.4.1/src/cadgen/flatten.py +345 -0
- cadgen-0.4.1/src/cadgen/generation.py +18 -0
- cadgen-0.4.1/src/cadgen/implicit_artifact.py +225 -0
- cadgen-0.4.1/src/cadgen/implicit_export.py +232 -0
- cadgen-0.4.1/src/cadgen/instances.py +85 -0
- cadgen-0.4.1/src/cadgen/interference.py +247 -0
- cadgen-0.4.1/src/cadgen/lookup.py +243 -0
- cadgen-0.4.1/src/cadgen/metadata.py +426 -0
- cadgen-0.4.1/src/cadgen/py.typed +1 -0
- cadgen-0.4.1/src/cadgen/render.py +35 -0
- cadgen-0.4.1/src/cadgen/reporting.py +133 -0
- cadgen-0.4.1/src/cadgen/selector_types.py +18 -0
- cadgen-0.4.1/src/cadgen/snapshot_cli.py +1545 -0
- cadgen-0.4.1/src/cadgen/snapshot_core.py +1027 -0
- cadgen-0.4.1/src/cadgen/sources.py +34 -0
- cadgen-0.4.1/src/cadgen/step_artifact.py +490 -0
- cadgen-0.4.1/src/cadgen/step_artifacts.py +513 -0
- cadgen-0.4.1/src/cadgen/step_export.py +420 -0
- cadgen-0.4.1/src/cadgen/step_export_target.py +401 -0
- cadgen-0.4.1/src/cadgen/step_scene.py +23 -0
- cadgen-0.4.1/src/cadgen/step_targets.py +519 -0
- cadgen-0.4.1/src/cadgen/validity.py +227 -0
- cadgen-0.4.1/src/cadgen.egg-info/PKG-INFO +122 -0
- cadgen-0.4.1/src/cadgen.egg-info/SOURCES.txt +71 -0
- cadgen-0.4.1/src/cadgen.egg-info/dependency_links.txt +1 -0
- cadgen-0.4.1/src/cadgen.egg-info/entry_points.txt +2 -0
- cadgen-0.4.1/src/cadgen.egg-info/requires.txt +4 -0
- cadgen-0.4.1/src/cadgen.egg-info/top_level.txt +1 -0
cadgen-0.4.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Thompson Labs LLC
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
cadgen-0.4.1/PKG-INFO
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cadgen
|
|
3
|
+
Version: 0.4.1
|
|
4
|
+
Summary: STEP-first CAD artifact generation runtime: build123d STEP/GLB/topology generation, validation, and inspection for CAD agent skills.
|
|
5
|
+
Author: earthtojake
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/earthtojake/text-to-cad
|
|
8
|
+
Project-URL: Repository, https://github.com/earthtojake/text-to-cad
|
|
9
|
+
Project-URL: Issues, https://github.com/earthtojake/text-to-cad/issues
|
|
10
|
+
Keywords: cad,step,glb,build123d,parametric,agent-skills
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Manufacturing
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Multimedia :: Graphics :: 3D Modeling
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: build123d
|
|
23
|
+
Requires-Dist: cadquery-ocp
|
|
24
|
+
Requires-Dist: ezdxf
|
|
25
|
+
Requires-Dist: shapely
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# cadgen
|
|
29
|
+
|
|
30
|
+
STEP-first CAD artifact generation runtime for CAD agent skills, built on
|
|
31
|
+
[build123d](https://github.com/gumyr/build123d) and OCCT.
|
|
32
|
+
|
|
33
|
+
The package boundary is intentionally narrow: it owns artifact generation,
|
|
34
|
+
validation, selector/topology extraction, mesh settings, source hashing, and the
|
|
35
|
+
`cadgen-step-artifact` CLI. It also includes small generated-script helpers such
|
|
36
|
+
as `cadgen.assembly.AssemblyHelper`, which wraps native build123d labels, joints,
|
|
37
|
+
and compounds without owning skill-specific UX. Prompts, viewer UI, and snapshot
|
|
38
|
+
job orchestration stay in their owning skills.
|
|
39
|
+
|
|
40
|
+
`cadgen` is developed in
|
|
41
|
+
[earthtojake/text-to-cad](https://github.com/earthtojake/text-to-cad) and was
|
|
42
|
+
previously named `cadpy` inside that repository.
|
|
43
|
+
|
|
44
|
+
## Public API
|
|
45
|
+
|
|
46
|
+
The supported import surface is the root `cadgen` exports plus the top-level
|
|
47
|
+
`cadgen.*` modules:
|
|
48
|
+
|
|
49
|
+
- Generator-script helpers: root exports (`AssemblyHelper`, `MateRelation`,
|
|
50
|
+
`MateTarget`, `label_text`, `label_shape`, `target`,
|
|
51
|
+
`ensure_step_glb_artifact`, `validate_step_glb_artifact`), `cadgen.assembly`,
|
|
52
|
+
and `cadgen.step_scene` (`import_step`, `load_step_scene`, `located_shape`,
|
|
53
|
+
`occurrence_selector_id`, `scene_occurrence_shape`).
|
|
54
|
+
- Generator-script helpers (2D): `cadgen.sources` (`load_source_module`) and
|
|
55
|
+
`cadgen.flatten` (planar-face projection/unfold, contour emission, kerf
|
|
56
|
+
offsetting) for `.dxf.py` drawing generators.
|
|
57
|
+
- Skill CLI surface: `cadgen.generation` (`generate_step_targets`,
|
|
58
|
+
`generate_dxf_targets`, `targets_include_output_pairs`), `cadgen.catalog`,
|
|
59
|
+
`cadgen.metadata`, `cadgen.analysis`, `cadgen.lookup`, `cadgen.cad_ref_syntax`,
|
|
60
|
+
`cadgen.selector_types`, `cadgen.reporting`, `cadgen.cli_logging`,
|
|
61
|
+
`cadgen.render`, `cadgen.step_artifacts`, `cadgen.step_targets`,
|
|
62
|
+
`cadgen.step_export`, `cadgen.drawing_checks` (DXF drawing validation), and
|
|
63
|
+
`cadgen.drawing_render` (DXF render payload + SVG snapshots).
|
|
64
|
+
- Process entry points: `cadgen-step-artifact`, `python -m cadgen.step_artifact`,
|
|
65
|
+
`python -m cadgen.step_export_target`, and `python -m cadgen.dxf_artifact`.
|
|
66
|
+
|
|
67
|
+
Everything under `cadgen._internal` is private implementation (the STEP scene,
|
|
68
|
+
generation, GLB/topology, and export engines live there) with no import
|
|
69
|
+
stability between releases; `cadgen.generation` and `cadgen.step_scene` are
|
|
70
|
+
thin facades over those engines that re-export only the supported names.
|
|
71
|
+
|
|
72
|
+
## Install
|
|
73
|
+
|
|
74
|
+
Released versions are published to PyPI by the repository's `Release` workflow;
|
|
75
|
+
the package version always matches the CAD plugin release version:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
python -m pip install cadgen
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Production skill bundles pin the exact release version in their
|
|
82
|
+
`requirements.txt` (for example `cadgen==0.4.0`) and keep a vendored copy of
|
|
83
|
+
this package as an offline fallback:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
python -m pip install ./scripts/packages/cadgen
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Local Development
|
|
90
|
+
|
|
91
|
+
Install it editable into the repo CAD runtime when working on the source
|
|
92
|
+
package directly:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
./.venv/bin/python -m pip install -e packages/cadgen
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
After that, changes under `packages/cadgen/src/cadgen` are immediately visible to
|
|
99
|
+
local source checkouts that import the package directly.
|
|
100
|
+
|
|
101
|
+
On `develop`, the CAD skill and root Viewer point at this package through the
|
|
102
|
+
development symlinks `skills/cad/scripts/packages/cadgen` and
|
|
103
|
+
`viewer/packages/cadgen`. Keep those links intact with
|
|
104
|
+
`scripts/dev/setup-symlinks.sh --check`.
|
|
105
|
+
|
|
106
|
+
## Production Bundling
|
|
107
|
+
|
|
108
|
+
Build a wheel and install it into each skill's bundled Python environment during
|
|
109
|
+
packaging:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
./.venv/bin/python -m build packages/cadgen
|
|
113
|
+
python -m pip install packages/cadgen/dist/cadgen-*.whl
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The CAD and cad-viewer skills should depend on the package artifact they bundle,
|
|
117
|
+
not on `skills/cad` or the repository root. Production packaging vendors
|
|
118
|
+
installable packages under `skills/cad/scripts/packages/cadgen` and
|
|
119
|
+
`skills/cad-viewer/scripts/viewer/packages/cadgen`; production packaging can also
|
|
120
|
+
set `VIEWER_CAD_PYTHON` to a skill-local Python runtime with this package
|
|
121
|
+
installed. Production skill bundles install `cadgen==<release version>` from
|
|
122
|
+
PyPI first and fall back to the vendored copy when offline.
|
cadgen-0.4.1/README.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# cadgen
|
|
2
|
+
|
|
3
|
+
STEP-first CAD artifact generation runtime for CAD agent skills, built on
|
|
4
|
+
[build123d](https://github.com/gumyr/build123d) and OCCT.
|
|
5
|
+
|
|
6
|
+
The package boundary is intentionally narrow: it owns artifact generation,
|
|
7
|
+
validation, selector/topology extraction, mesh settings, source hashing, and the
|
|
8
|
+
`cadgen-step-artifact` CLI. It also includes small generated-script helpers such
|
|
9
|
+
as `cadgen.assembly.AssemblyHelper`, which wraps native build123d labels, joints,
|
|
10
|
+
and compounds without owning skill-specific UX. Prompts, viewer UI, and snapshot
|
|
11
|
+
job orchestration stay in their owning skills.
|
|
12
|
+
|
|
13
|
+
`cadgen` is developed in
|
|
14
|
+
[earthtojake/text-to-cad](https://github.com/earthtojake/text-to-cad) and was
|
|
15
|
+
previously named `cadpy` inside that repository.
|
|
16
|
+
|
|
17
|
+
## Public API
|
|
18
|
+
|
|
19
|
+
The supported import surface is the root `cadgen` exports plus the top-level
|
|
20
|
+
`cadgen.*` modules:
|
|
21
|
+
|
|
22
|
+
- Generator-script helpers: root exports (`AssemblyHelper`, `MateRelation`,
|
|
23
|
+
`MateTarget`, `label_text`, `label_shape`, `target`,
|
|
24
|
+
`ensure_step_glb_artifact`, `validate_step_glb_artifact`), `cadgen.assembly`,
|
|
25
|
+
and `cadgen.step_scene` (`import_step`, `load_step_scene`, `located_shape`,
|
|
26
|
+
`occurrence_selector_id`, `scene_occurrence_shape`).
|
|
27
|
+
- Generator-script helpers (2D): `cadgen.sources` (`load_source_module`) and
|
|
28
|
+
`cadgen.flatten` (planar-face projection/unfold, contour emission, kerf
|
|
29
|
+
offsetting) for `.dxf.py` drawing generators.
|
|
30
|
+
- Skill CLI surface: `cadgen.generation` (`generate_step_targets`,
|
|
31
|
+
`generate_dxf_targets`, `targets_include_output_pairs`), `cadgen.catalog`,
|
|
32
|
+
`cadgen.metadata`, `cadgen.analysis`, `cadgen.lookup`, `cadgen.cad_ref_syntax`,
|
|
33
|
+
`cadgen.selector_types`, `cadgen.reporting`, `cadgen.cli_logging`,
|
|
34
|
+
`cadgen.render`, `cadgen.step_artifacts`, `cadgen.step_targets`,
|
|
35
|
+
`cadgen.step_export`, `cadgen.drawing_checks` (DXF drawing validation), and
|
|
36
|
+
`cadgen.drawing_render` (DXF render payload + SVG snapshots).
|
|
37
|
+
- Process entry points: `cadgen-step-artifact`, `python -m cadgen.step_artifact`,
|
|
38
|
+
`python -m cadgen.step_export_target`, and `python -m cadgen.dxf_artifact`.
|
|
39
|
+
|
|
40
|
+
Everything under `cadgen._internal` is private implementation (the STEP scene,
|
|
41
|
+
generation, GLB/topology, and export engines live there) with no import
|
|
42
|
+
stability between releases; `cadgen.generation` and `cadgen.step_scene` are
|
|
43
|
+
thin facades over those engines that re-export only the supported names.
|
|
44
|
+
|
|
45
|
+
## Install
|
|
46
|
+
|
|
47
|
+
Released versions are published to PyPI by the repository's `Release` workflow;
|
|
48
|
+
the package version always matches the CAD plugin release version:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
python -m pip install cadgen
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Production skill bundles pin the exact release version in their
|
|
55
|
+
`requirements.txt` (for example `cadgen==0.4.0`) and keep a vendored copy of
|
|
56
|
+
this package as an offline fallback:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
python -m pip install ./scripts/packages/cadgen
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Local Development
|
|
63
|
+
|
|
64
|
+
Install it editable into the repo CAD runtime when working on the source
|
|
65
|
+
package directly:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
./.venv/bin/python -m pip install -e packages/cadgen
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
After that, changes under `packages/cadgen/src/cadgen` are immediately visible to
|
|
72
|
+
local source checkouts that import the package directly.
|
|
73
|
+
|
|
74
|
+
On `develop`, the CAD skill and root Viewer point at this package through the
|
|
75
|
+
development symlinks `skills/cad/scripts/packages/cadgen` and
|
|
76
|
+
`viewer/packages/cadgen`. Keep those links intact with
|
|
77
|
+
`scripts/dev/setup-symlinks.sh --check`.
|
|
78
|
+
|
|
79
|
+
## Production Bundling
|
|
80
|
+
|
|
81
|
+
Build a wheel and install it into each skill's bundled Python environment during
|
|
82
|
+
packaging:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
./.venv/bin/python -m build packages/cadgen
|
|
86
|
+
python -m pip install packages/cadgen/dist/cadgen-*.whl
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The CAD and cad-viewer skills should depend on the package artifact they bundle,
|
|
90
|
+
not on `skills/cad` or the repository root. Production packaging vendors
|
|
91
|
+
installable packages under `skills/cad/scripts/packages/cadgen` and
|
|
92
|
+
`skills/cad-viewer/scripts/viewer/packages/cadgen`; production packaging can also
|
|
93
|
+
set `VIEWER_CAD_PYTHON` to a skill-local Python runtime with this package
|
|
94
|
+
installed. Production skill bundles install `cadgen==<release version>` from
|
|
95
|
+
PyPI first and fall back to the vendored copy when offline.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "cadgen"
|
|
7
|
+
version = "0.4.1"
|
|
8
|
+
description = "STEP-first CAD artifact generation runtime: build123d STEP/GLB/topology generation, validation, and inspection for CAD agent skills."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
requires-python = ">=3.11"
|
|
13
|
+
authors = [{ name = "earthtojake" }]
|
|
14
|
+
keywords = ["cad", "step", "glb", "build123d", "parametric", "agent-skills"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Intended Audience :: Manufacturing",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Topic :: Multimedia :: Graphics :: 3D Modeling",
|
|
23
|
+
"Topic :: Scientific/Engineering",
|
|
24
|
+
]
|
|
25
|
+
dependencies = [
|
|
26
|
+
"build123d",
|
|
27
|
+
"cadquery-ocp",
|
|
28
|
+
"ezdxf",
|
|
29
|
+
"shapely",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.urls]
|
|
33
|
+
Homepage = "https://github.com/earthtojake/text-to-cad"
|
|
34
|
+
Repository = "https://github.com/earthtojake/text-to-cad"
|
|
35
|
+
Issues = "https://github.com/earthtojake/text-to-cad/issues"
|
|
36
|
+
|
|
37
|
+
[project.scripts]
|
|
38
|
+
cadgen-step-artifact = "cadgen.step_artifact:main"
|
|
39
|
+
|
|
40
|
+
[tool.setuptools.packages.find]
|
|
41
|
+
where = ["src"]
|
|
42
|
+
include = ["cadgen*"]
|
|
43
|
+
|
|
44
|
+
[tool.setuptools.package-data]
|
|
45
|
+
cadgen = ["py.typed"]
|
|
46
|
+
# The Node builder bridge (cadgen/_internal/node_runtime.py) spawns node with
|
|
47
|
+
# `--import node_resolve_register.mjs`; both .mjs files must ship with the package.
|
|
48
|
+
"cadgen._internal" = ["*.mjs"]
|
cadgen-0.4.1/setup.cfg
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Shared CAD artifact generation runtime."""
|
|
2
|
+
|
|
3
|
+
__all__ = [
|
|
4
|
+
"AssemblyHelper",
|
|
5
|
+
"srgb",
|
|
6
|
+
"MateRelation",
|
|
7
|
+
"MateTarget",
|
|
8
|
+
"compound_from_instances",
|
|
9
|
+
"ensure_step_glb_artifact",
|
|
10
|
+
"label_text",
|
|
11
|
+
"label_shape",
|
|
12
|
+
"target",
|
|
13
|
+
"validate_step_glb_artifact",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def __getattr__(name: str):
|
|
18
|
+
if name in {"ensure_step_glb_artifact", "validate_step_glb_artifact"}:
|
|
19
|
+
from cadgen.api import ensure_step_glb_artifact, validate_step_glb_artifact
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
"ensure_step_glb_artifact": ensure_step_glb_artifact,
|
|
23
|
+
"validate_step_glb_artifact": validate_step_glb_artifact,
|
|
24
|
+
}[name]
|
|
25
|
+
if name in {"AssemblyHelper", "MateRelation", "MateTarget", "label_shape", "label_text", "target"}:
|
|
26
|
+
from cadgen.assembly import AssemblyHelper, MateRelation, MateTarget, label_shape, label_text, target
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
"AssemblyHelper": AssemblyHelper,
|
|
30
|
+
"MateRelation": MateRelation,
|
|
31
|
+
"MateTarget": MateTarget,
|
|
32
|
+
"label_text": label_text,
|
|
33
|
+
"label_shape": label_shape,
|
|
34
|
+
"target": target,
|
|
35
|
+
}[name]
|
|
36
|
+
if name in {"srgb", "srgb_to_linear", "linear_to_srgb"}:
|
|
37
|
+
from cadgen import color
|
|
38
|
+
|
|
39
|
+
return getattr(color, name)
|
|
40
|
+
if name == "compound_from_instances":
|
|
41
|
+
from cadgen.instances import compound_from_instances
|
|
42
|
+
|
|
43
|
+
return compound_from_instances
|
|
44
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Catalog path utilities and identity-transform constants.
|
|
2
|
+
|
|
3
|
+
Historically this module also parsed Python ``gen_step()`` assembly recipes
|
|
4
|
+
(``instances`` / ``children`` envelopes) into a typed ``AssemblySpec`` tree.
|
|
5
|
+
That contract is gone — ``gen_step()`` is shape-only — and the parser was
|
|
6
|
+
deleted with it. Only the catalog-path helpers and the identity-transform
|
|
7
|
+
constants survive, kept here so the established import paths in
|
|
8
|
+
``step_artifacts``, ``step_targets``, and ``generation`` continue to resolve.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
from cadgen.catalog import find_source_by_cad_ref
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
STEP_SUFFIXES = (".step", ".stp")
|
|
19
|
+
|
|
20
|
+
IDENTITY_TRANSFORM: tuple[float, ...] = (
|
|
21
|
+
1.0, 0.0, 0.0, 0.0,
|
|
22
|
+
0.0, 1.0, 0.0, 0.0,
|
|
23
|
+
0.0, 0.0, 1.0, 0.0,
|
|
24
|
+
0.0, 0.0, 0.0, 1.0,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def find_step_path(cad_ref: str) -> Path | None:
|
|
29
|
+
source = find_source_by_cad_ref(cad_ref)
|
|
30
|
+
if source is not None and source.kind in {"part", "assembly"}:
|
|
31
|
+
return source.step_path.resolve() if source.step_path is not None else None
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def resolve_cad_source_path(cad_ref: str) -> tuple[str, Path] | None:
|
|
36
|
+
source = find_source_by_cad_ref(cad_ref)
|
|
37
|
+
if source is not None:
|
|
38
|
+
if source.kind == "assembly":
|
|
39
|
+
return "assembly", source.source_path
|
|
40
|
+
if source.kind == "part":
|
|
41
|
+
step_path = source.step_path
|
|
42
|
+
return ("part", step_path) if step_path is not None else None
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def multiply_transforms(left: tuple[float, ...], right: tuple[float, ...]) -> tuple[float, ...]:
|
|
47
|
+
product: list[float] = []
|
|
48
|
+
for row in range(4):
|
|
49
|
+
for column in range(4):
|
|
50
|
+
total = 0.0
|
|
51
|
+
for offset in range(4):
|
|
52
|
+
total += left[(row * 4) + offset] * right[(offset * 4) + column]
|
|
53
|
+
product.append(total)
|
|
54
|
+
return tuple(product)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""Compact failure reporting for the CAD CLIs.
|
|
2
|
+
|
|
3
|
+
A generator that raises used to reach the interpreter uncaught, so an ordinary authoring
|
|
4
|
+
mistake printed a 62-line, 4.2 KB traceback whose one useful line -- the exception -- was
|
|
5
|
+
last, under ~50 frames of ``runpy``, the launcher, and cadgen internals. A `gen_step()`
|
|
6
|
+
missing its return printed 43 lines to say ``must return one value``.
|
|
7
|
+
|
|
8
|
+
What a caller actually needs is the failure and WHERE IN THEIR MODEL it happened. Those
|
|
9
|
+
frames are already identifiable: :func:`is_first_party_source_file` is the same predicate
|
|
10
|
+
the source-closure capture uses to tell model code from the stdlib, site-packages, and the
|
|
11
|
+
running runtime. So the report keeps the model's own frames and drops everything else.
|
|
12
|
+
|
|
13
|
+
``--verbose`` still prints the full traceback -- when the fault IS in cadgen, the internal
|
|
14
|
+
frames are the point.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import sys
|
|
20
|
+
import traceback
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import TextIO
|
|
23
|
+
|
|
24
|
+
# Deep recursion in a generator (a parametric model calling itself) can produce hundreds of
|
|
25
|
+
# identical model frames. The innermost few are where the failure is.
|
|
26
|
+
_MAX_FRAMES = 6
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _display(filename: str) -> str:
|
|
30
|
+
"""Cwd-relative where that is meaningful, otherwise the tail.
|
|
31
|
+
|
|
32
|
+
A runtime frame's absolute path runs through the symlinked skill bundle and is longer
|
|
33
|
+
than the line it is annotating; the last few components identify the module just as
|
|
34
|
+
well without burying the message above it.
|
|
35
|
+
"""
|
|
36
|
+
try:
|
|
37
|
+
resolved = Path(filename).resolve()
|
|
38
|
+
except (OSError, ValueError):
|
|
39
|
+
return filename
|
|
40
|
+
try:
|
|
41
|
+
return resolved.relative_to(Path.cwd().resolve()).as_posix()
|
|
42
|
+
except ValueError:
|
|
43
|
+
parts = resolved.parts
|
|
44
|
+
return ("…/" + "/".join(parts[-3:])) if len(parts) > 3 else resolved.as_posix()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _model_frames(exc: BaseException) -> list[traceback.FrameSummary]:
|
|
48
|
+
"""The frames in the caller's own CAD sources, outermost first.
|
|
49
|
+
|
|
50
|
+
Everything else -- the launcher, cadgen, build123d/OCP, the stdlib -- is runtime: it is
|
|
51
|
+
the same in every failure and says nothing about which model broke.
|
|
52
|
+
"""
|
|
53
|
+
from cadgen._internal.source_hash import is_first_party_source_file
|
|
54
|
+
|
|
55
|
+
frames: list[traceback.FrameSummary] = []
|
|
56
|
+
for frame in traceback.extract_tb(exc.__traceback__):
|
|
57
|
+
try:
|
|
58
|
+
path = Path(frame.filename).resolve()
|
|
59
|
+
except (OSError, ValueError):
|
|
60
|
+
continue
|
|
61
|
+
if is_first_party_source_file(path):
|
|
62
|
+
frames.append(frame)
|
|
63
|
+
return frames
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def report_cli_error(
|
|
67
|
+
exc: BaseException,
|
|
68
|
+
*,
|
|
69
|
+
tool: str,
|
|
70
|
+
verbose: bool = False,
|
|
71
|
+
stream: TextIO | None = None,
|
|
72
|
+
) -> int:
|
|
73
|
+
"""Print ``exc`` as a CLI failure and return the exit code (always 1).
|
|
74
|
+
|
|
75
|
+
Under ``verbose`` this is the raw traceback, unchanged -- a cadgen bug needs its own
|
|
76
|
+
frames. Otherwise it is the exception plus the model frames that led to it.
|
|
77
|
+
"""
|
|
78
|
+
out = stream if stream is not None else sys.stderr
|
|
79
|
+
if verbose:
|
|
80
|
+
traceback.print_exception(type(exc), exc, exc.__traceback__, file=out)
|
|
81
|
+
return 1
|
|
82
|
+
|
|
83
|
+
def line(text: str) -> None:
|
|
84
|
+
print(f"[{tool}] {text}", file=out)
|
|
85
|
+
|
|
86
|
+
detail = str(exc).strip()
|
|
87
|
+
line(f"FAILED: {type(exc).__name__}: {detail}" if detail else f"FAILED: {type(exc).__name__}")
|
|
88
|
+
|
|
89
|
+
frames = _model_frames(exc)
|
|
90
|
+
if frames:
|
|
91
|
+
if len(frames) > _MAX_FRAMES:
|
|
92
|
+
line(f" ... {len(frames) - _MAX_FRAMES} earlier frame(s) omitted")
|
|
93
|
+
for frame in frames[-_MAX_FRAMES:]:
|
|
94
|
+
line(f" {_display(frame.filename)}:{frame.lineno} in {frame.name}")
|
|
95
|
+
if frame.line:
|
|
96
|
+
line(f" {frame.line.strip()}")
|
|
97
|
+
else:
|
|
98
|
+
# Nothing of the caller's ran -- a validation or resolution error raised before the
|
|
99
|
+
# generator was reached. Name the frame that raised so it is still diagnosable; the
|
|
100
|
+
# message itself usually already names the offending file.
|
|
101
|
+
raised = traceback.extract_tb(exc.__traceback__)
|
|
102
|
+
if raised:
|
|
103
|
+
innermost = raised[-1]
|
|
104
|
+
line(f" raised in {_display(innermost.filename)}:{innermost.lineno}")
|
|
105
|
+
|
|
106
|
+
line("re-run with --verbose for the full traceback")
|
|
107
|
+
return 1
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""The CLI half of artifact coordination: how a command-line producer waits, reports the
|
|
2
|
+
wait, and answers when it decides not to wait at all.
|
|
3
|
+
|
|
4
|
+
One implementation for every artifact CLI (``step_artifact``, ``dxf_artifact``,
|
|
5
|
+
``implicit_artifact``, and the ``gen``/``export`` paths in ``_internal.generation``),
|
|
6
|
+
because the three things below have to agree across all of them:
|
|
7
|
+
|
|
8
|
+
* the flag name and its default, since the CAD Viewer passes ``--lock-timeout`` to whichever
|
|
9
|
+
module a given entry maps to and a module that did not accept it would fail argument
|
|
10
|
+
parsing rather than build;
|
|
11
|
+
* the wait notice, which is the only thing distinguishing a contended acquire from a hang --
|
|
12
|
+
cadgen's acquire is blocking by default, so without it a queued build emits nothing on
|
|
13
|
+
either stream for as long as the peer holds the lock;
|
|
14
|
+
* the shape of the "a peer has it" payload, which the viewer branches on.
|
|
15
|
+
|
|
16
|
+
Not in :mod:`cadgen.coordination`: that package is the cross-process protocol shared with
|
|
17
|
+
the viewer's long-lived server, and it has no business knowing about argparse or loggers.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import argparse
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import Callable
|
|
25
|
+
|
|
26
|
+
from cadgen.cli_logging import CliLogger, format_elapsed
|
|
27
|
+
|
|
28
|
+
# Waiting is the CLI default on purpose: an agent that asked for a build wants the build,
|
|
29
|
+
# and giving up would leave it with no artifact and nothing to do about it. The viewer is
|
|
30
|
+
# the caller that must not block, and it passes an explicit timeout.
|
|
31
|
+
WAIT_FOREVER = 0.0
|
|
32
|
+
|
|
33
|
+
_LOCK_TIMEOUT_HELP = (
|
|
34
|
+
"Give up after SECONDS if another run holds this model's generation lock, reporting "
|
|
35
|
+
'{"ok":true,"contended":true} instead of building. 0 (the default) waits for the peer; '
|
|
36
|
+
"either way the wait itself is reported on stderr."
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def add_lock_timeout_argument(parser: argparse.ArgumentParser) -> None:
|
|
41
|
+
parser.add_argument(
|
|
42
|
+
"--lock-timeout",
|
|
43
|
+
type=float,
|
|
44
|
+
default=WAIT_FOREVER,
|
|
45
|
+
metavar="SECONDS",
|
|
46
|
+
help=_LOCK_TIMEOUT_HELP,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def deadline_ms(lock_timeout_s: float | None) -> float | None:
|
|
51
|
+
"""``deadline_ms`` for :func:`cadgen.coordination.artifact_build`. None means block."""
|
|
52
|
+
timeout = float(lock_timeout_s or 0.0)
|
|
53
|
+
return timeout * 1000.0 if timeout > 0 else None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def lock_wait_notice(logger: CliLogger | None, ref: str) -> Callable[[float], None] | None:
|
|
57
|
+
"""A callback that says WHY the process is sitting still, while it still is.
|
|
58
|
+
|
|
59
|
+
Repeats on the interval :mod:`cadgen.coordination.lock` applies, so a long wait keeps
|
|
60
|
+
proving it is alive instead of looking wedged."""
|
|
61
|
+
if logger is None:
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
def notice(elapsed_s: float) -> None:
|
|
65
|
+
logger.info(
|
|
66
|
+
f"waiting for another run to finish building {ref} "
|
|
67
|
+
f"({format_elapsed(elapsed_s)} so far)"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
return notice
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def contended_payload(
|
|
74
|
+
*,
|
|
75
|
+
source_ref: str,
|
|
76
|
+
cad_ref: str = "",
|
|
77
|
+
package_dir: Path | str | None = None,
|
|
78
|
+
**extra: object,
|
|
79
|
+
) -> dict[str, object]:
|
|
80
|
+
"""The answer when a peer holds the lock and this run declined to wait for it.
|
|
81
|
+
|
|
82
|
+
``ok`` is true because nothing went wrong -- the model is being built, just not here --
|
|
83
|
+
and ``contended`` is what a caller branches on. Deliberately claims no artifact: the
|
|
84
|
+
peer decides what the package ends up containing.
|
|
85
|
+
"""
|
|
86
|
+
payload: dict[str, object] = {
|
|
87
|
+
"ok": True,
|
|
88
|
+
"contended": True,
|
|
89
|
+
"skipped": True,
|
|
90
|
+
"sourceRef": source_ref,
|
|
91
|
+
}
|
|
92
|
+
if cad_ref:
|
|
93
|
+
payload["cadPath"] = cad_ref
|
|
94
|
+
if package_dir is not None:
|
|
95
|
+
from cadgen.render import relative_to_cwd
|
|
96
|
+
|
|
97
|
+
payload["packagePath"] = relative_to_cwd(Path(package_dir))
|
|
98
|
+
payload.update({key: value for key, value in extra.items() if value is not None})
|
|
99
|
+
return payload
|