swdesigntables 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.
Files changed (29) hide show
  1. swdesigntables-0.1.0/.github/workflows/release.yml +61 -0
  2. swdesigntables-0.1.0/.github/workflows/test.yml +35 -0
  3. swdesigntables-0.1.0/.gitignore +18 -0
  4. swdesigntables-0.1.0/CHANGELOG.md +52 -0
  5. swdesigntables-0.1.0/LICENSE +21 -0
  6. swdesigntables-0.1.0/PKG-INFO +334 -0
  7. swdesigntables-0.1.0/README.md +303 -0
  8. swdesigntables-0.1.0/pyproject.toml +66 -0
  9. swdesigntables-0.1.0/src/swdesigntables/__init__.py +172 -0
  10. swdesigntables-0.1.0/src/swdesigntables/columns.py +272 -0
  11. swdesigntables-0.1.0/src/swdesigntables/errors.py +67 -0
  12. swdesigntables-0.1.0/src/swdesigntables/parameters.py +401 -0
  13. swdesigntables-0.1.0/src/swdesigntables/py.typed +0 -0
  14. swdesigntables-0.1.0/src/swdesigntables/table.py +368 -0
  15. swdesigntables-0.1.0/src/swdesigntables/template.py +122 -0
  16. swdesigntables-0.1.0/src/swdesigntables/validation.py +515 -0
  17. swdesigntables-0.1.0/src/swdesigntables/values.py +160 -0
  18. swdesigntables-0.1.0/src/swdesigntables/vocabulary.py +51 -0
  19. swdesigntables-0.1.0/src/swdesigntables/writer.py +130 -0
  20. swdesigntables-0.1.0/tests/conftest.py +65 -0
  21. swdesigntables-0.1.0/tests/test_columns.py +139 -0
  22. swdesigntables-0.1.0/tests/test_determinism.py +35 -0
  23. swdesigntables-0.1.0/tests/test_examples.py +177 -0
  24. swdesigntables-0.1.0/tests/test_sheets.py +52 -0
  25. swdesigntables-0.1.0/tests/test_table_model.py +90 -0
  26. swdesigntables-0.1.0/tests/test_template.py +97 -0
  27. swdesigntables-0.1.0/tests/test_validation.py +259 -0
  28. swdesigntables-0.1.0/tests/test_values.py +70 -0
  29. swdesigntables-0.1.0/tests/test_writer.py +182 -0
@@ -0,0 +1,61 @@
1
+ name: release
2
+
3
+ # Publishes to PyPI when a v* tag is pushed, using Trusted Publishing.
4
+ # No API token is stored anywhere: PyPI verifies the workflow's OIDC identity.
5
+ #
6
+ # One-time setup on PyPI (Publishing -> Add a new pending publisher):
7
+ # owner/repository: ivanperezdesigner/swdesigntables
8
+ # workflow: release.yml
9
+ # environment: pypi
10
+ # and create the `pypi` environment under Settings -> Environments here.
11
+
12
+ on:
13
+ push:
14
+ tags: ["v*"]
15
+ workflow_dispatch:
16
+
17
+ jobs:
18
+ build:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+
23
+ - uses: actions/setup-python@v5
24
+ with:
25
+ python-version: "3.12"
26
+
27
+ - name: Install build tooling
28
+ run: python -m pip install --upgrade pip build twine
29
+
30
+ - name: Check the tag matches __version__
31
+ # Publishing v0.2.0 from a 0.1.0 source is the classic own goal.
32
+ run: |
33
+ TAG="${GITHUB_REF_NAME#v}"
34
+ VERSION=$(python -c "import re,pathlib; print(re.search(r'__version__ = \"([^\"]+)\"', pathlib.Path('src/swdesigntables/__init__.py').read_text()).group(1))")
35
+ echo "tag=$TAG source=$VERSION"
36
+ test "$TAG" = "$VERSION"
37
+
38
+ - name: Build
39
+ run: python -m build
40
+
41
+ - name: Check metadata
42
+ run: python -m twine check dist/*
43
+
44
+ - uses: actions/upload-artifact@v4
45
+ with:
46
+ name: dist
47
+ path: dist/
48
+
49
+ publish:
50
+ needs: build
51
+ runs-on: ubuntu-latest
52
+ environment: pypi
53
+ permissions:
54
+ id-token: write
55
+ steps:
56
+ - uses: actions/download-artifact@v4
57
+ with:
58
+ name: dist
59
+ path: dist/
60
+
61
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,35 @@
1
+ name: test
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ${{ matrix.os }}
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ os: [ubuntu-latest, windows-latest]
16
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
17
+
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+
21
+ - uses: actions/setup-python@v5
22
+ with:
23
+ python-version: ${{ matrix.python-version }}
24
+
25
+ - name: Install
26
+ run: python -m pip install --upgrade pip && python -m pip install -e ".[dev]"
27
+
28
+ - name: Lint
29
+ run: python -m ruff check .
30
+
31
+ - name: Type check
32
+ run: python -m mypy
33
+
34
+ - name: Test
35
+ run: python -m pytest -q
@@ -0,0 +1,18 @@
1
+ # Build artefacts
2
+ dist/
3
+ build/
4
+ *.egg-info/
5
+
6
+ # Environments
7
+ venv/
8
+ .venv/
9
+
10
+ # Caches
11
+ __pycache__/
12
+ *.py[cod]
13
+ .pytest_cache/
14
+ .mypy_cache/
15
+ .ruff_cache/
16
+
17
+ # Generated tables from manual runs
18
+ *.xlsx
@@ -0,0 +1,52 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0] - 2026-09-11
11
+
12
+ First release.
13
+
14
+ ### Added
15
+
16
+ - `DesignTable`: title cell, headers from B2, the `Family` defined name, and
17
+ configuration rows addressed by column identity rather than column number.
18
+ - A parameter catalogue with typed factories for `dimension`, `global_variable`,
19
+ `state`, `prop`, `description`, `parent`, `display_state`, `component_config`,
20
+ `component_state`, `component_visibility`, `component_fixed`,
21
+ `component_display_state`, `comment`, `color`, `part_number`, `user_notes`,
22
+ `never_expand_in_bom`, `suppress_new_features`, `suppress_new_components`,
23
+ `sw_property` and `tolerance`.
24
+ - A verification `Status` on every parameter, and an `unverified-parameter`
25
+ warning for anything not confirmed against a real model.
26
+ - `register_parameter()` and `raw()` so no header is out of reach.
27
+ - `parse_header()`, which turns an existing header string into its typed column,
28
+ for migrating scripts that already hold a list of header strings.
29
+ - `TableTemplate` and `blank_table()` for reusable bases and empty skeletons.
30
+ - Value types `State`, `ComponentState`, `YesNo` and `Expression`, with
31
+ `StateFormat.NUMERIC` for tables that spell suppression `1`/`0`.
32
+ - Validation with stable issue codes, three levels of strictness, and per-code
33
+ suppression.
34
+ - Extra sheets, optionally hidden, plus extra workbook defined names. `_SWX`,
35
+ `Family` and `_SWX_0` are reserved.
36
+ - Deterministic output: fixed document timestamps, so a regenerated table only
37
+ differs in git when it actually changed.
38
+
39
+ ### Known limitations
40
+
41
+ - Roughly half the catalogue is marked `DOCUMENTED` rather than `VERIFIED`: the
42
+ syntax comes from documentation but has not been watched working in
43
+ SOLIDWORKS. See the README for the Auto-create recipe that confirms one.
44
+ - Sheet metal parameters (`$SM-…`) are not shipped, because no reliable source
45
+ confirmed their syntax. Use `raw()` or `register_parameter()`.
46
+ - The spelling of `$USER_NOTES` and `$NEVER_EXPAND_IN_BOM` is uncertain; sources
47
+ disagree on hyphens versus underscores. Both are single constants in
48
+ `Vocabulary`.
49
+ - Write only. No reading of existing tables, no COM integration, no CLI.
50
+
51
+ [Unreleased]: https://github.com/ivanperezdesigner/swdesigntables/compare/v0.1.0...HEAD
52
+ [0.1.0]: https://github.com/ivanperezdesigner/swdesigntables/releases/tag/v0.1.0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ivan Perez
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.
@@ -0,0 +1,334 @@
1
+ Metadata-Version: 2.5
2
+ Name: swdesigntables
3
+ Version: 0.1.0
4
+ Summary: Build SOLIDWORKS design tables from Python, with typed headers and validation.
5
+ Project-URL: Homepage, https://github.com/ivanperezdesigner/swdesigntables
6
+ Project-URL: Issues, https://github.com/ivanperezdesigner/swdesigntables/issues
7
+ Project-URL: Changelog, https://github.com/ivanperezdesigner/swdesigntables/blob/main/CHANGELOG.md
8
+ Author: Ivan Perez
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: cad,configurations,design table,openpyxl,solidworks,xlsx
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Manufacturing
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Scientific/Engineering
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: openpyxl<4,>=3.1
23
+ Provides-Extra: dev
24
+ Requires-Dist: build>=1.2; extra == 'dev'
25
+ Requires-Dist: mypy>=1.11; extra == 'dev'
26
+ Requires-Dist: pytest>=8; extra == 'dev'
27
+ Requires-Dist: ruff>=0.6; extra == 'dev'
28
+ Requires-Dist: twine>=5; extra == 'dev'
29
+ Requires-Dist: types-openpyxl; extra == 'dev'
30
+ Description-Content-Type: text/markdown
31
+
32
+ # swdesigntables
33
+
34
+ Build SOLIDWORKS design tables from Python, with typed headers instead of
35
+ hand-typed strings.
36
+
37
+ A design table is an Excel sheet embedded in a SOLIDWORKS model: each row makes
38
+ a configuration, each column drives a parameter. The format has a handful of
39
+ load-bearing details that are easy to get wrong and that fail *silently* — the
40
+ table inserts, nothing changes, and you go looking at the model. This package
41
+ writes those details for you and warns about the rest.
42
+
43
+ ```bash
44
+ pip install swdesigntables
45
+ ```
46
+
47
+ Only dependency: `openpyxl`. Pure Python, no SOLIDWORKS install needed to
48
+ generate a file.
49
+
50
+ ## Quickstart
51
+
52
+ ```python
53
+ import swdesigntables as sw
54
+
55
+ length = sw.dimension("Length", "Boss-Extrude1")
56
+ holes = sw.state("HolePattern")
57
+ material = sw.prop("Material")
58
+
59
+ table = sw.DesignTable("BRK-MASTER", [length, holes, material])
60
+ table.add_configuration(
61
+ "BRK-025",
62
+ {length: 25.0, holes: sw.SUPPRESSED, material: "6061-T6"},
63
+ description="Short bracket",
64
+ )
65
+ table.add_configuration(
66
+ "BRK-040",
67
+ {length: 40.0, holes: sw.UNSUPPRESSED, material: "6061-T6"},
68
+ description="Long bracket",
69
+ )
70
+
71
+ table.save("brk_master_dt.xlsx")
72
+ ```
73
+
74
+ Insert the result with **Insert → Tables → Design Table → From file**.
75
+
76
+ ## What it gets right so you do not have to
77
+
78
+ | Detail | Why it matters |
79
+ |---|---|
80
+ | `Design Table for: <model>` in A1 | The title cell SOLIDWORKS expects |
81
+ | Headers start in **B2**, A2 stays empty | Writing from A2 shifts every value one column left |
82
+ | Workbook-level defined name `Family` → `Sheet1!$A$2` | Without it SOLIDWORKS cannot find the table at all |
83
+ | Equation values written as literal text | `cell = "=W/2"` becomes an Excel *formula* with no cached result, and SOLIDWORKS reads an empty parameter |
84
+ | Fixed document timestamps | Two identical runs produce identical bytes, so git shows a change only when the table really changed |
85
+ | Numeric-looking strings stay strings | A part number of `0012` does not come back as `12` |
86
+
87
+ ## Values are addressed by identity, never by column number
88
+
89
+ ```python
90
+ length = table.add_column(sw.dimension("Length", "Boss-Extrude1"))
91
+ table.add_configuration("A", {length: 25.0})
92
+ ```
93
+
94
+ You cannot pass a positional row. That is deliberate: mixing a
95
+ header-to-index map with literal column numbers is how a table silently starts
96
+ writing the wrong values after someone inserts a column in the middle.
97
+
98
+ Supplying a value for a column that was never declared is always an error, so
99
+ a typo loses nothing quietly.
100
+
101
+ ## The parameter catalogue
102
+
103
+ Every parameter carries a **verification status**, readable at runtime via
104
+ `column.status` and listed by `sw.list_parameters()`.
105
+
106
+ - **`VERIFIED`** — confirmed against real design table files.
107
+ - **`DOCUMENTED`** — described by SOLIDWORKS documentation, not confirmed here
108
+ against a live model. Using one emits an `unverified-parameter` warning.
109
+ - **`UNVERIFIED`** — registered at runtime by you, or otherwise unconfirmed.
110
+
111
+ This distinction is not decoration. Half of the catalogue below has not been
112
+ watched working in SOLIDWORKS by the author, and saying so is more useful than
113
+ implying a certainty that is not there.
114
+
115
+ ### Verified
116
+
117
+ | Factory | Header | Values |
118
+ |---|---|---|
119
+ | `dimension("Length", "Boss-Extrude1")` | `Length@Boss-Extrude1` | number |
120
+ | `global_variable("width")` | `$VALUE@width@Equations` | number or `Expression` |
121
+ | `state("Draft2")` | `$STATE@Draft2` | `State.SUPPRESSED` / `State.UNSUPPRESSED` |
122
+ | `prop("Material")` | `$PRP@Material` | text or number |
123
+ | `component_config("Arm", 1)` | `$CONFIGURATION@Arm<1>` | configuration name |
124
+ | `description()` | `$DESCRIPTION` | text |
125
+ | `parent()` | `$PARENT` | configuration name |
126
+ | `display_state()` | `$DISPLAYSTATE` | name |
127
+
128
+ ### Documented, not verified here
129
+
130
+ | Factory | Header | Values |
131
+ |---|---|---|
132
+ | `comment()` | `$COMMENT` | text; SOLIDWORKS ignores it |
133
+ | `color()` | `$COLOR` | 32-bit RGB integer |
134
+ | `part_number()` | `$PARTNUMBER` | text |
135
+ | `user_notes()` | `$USER_NOTES` | text |
136
+ | `never_expand_in_bom()` | `$NEVER_EXPAND_IN_BOM` | `YesNo` |
137
+ | `tolerance("D1", "Sketch1")` | `$TOLERANCE@D1@Sketch1` | tolerance spec |
138
+ | `component_state("Screw", 2)` | `$STATE@Screw<2>` | `ComponentState` (`S`/`R`) |
139
+ | `component_visibility("Screw", 2)` | `$SHOW@Screw<2>` | `YesNo` |
140
+ | `component_fixed("Screw", 2)` | `$FIXED@Screw<2>` | `YesNo` |
141
+ | `component_display_state("Screw", 2)` | `$DISPLAYSTATE@Screw<2>` | name |
142
+ | `suppress_new_features()` | `$SUPPRESS NEW FEATURES` | `YesNo` |
143
+ | `suppress_new_components()` | `$SUPPRESS NEW COMPONENTS` | `YesNo` |
144
+ | `sw_property("Mass")` | `$SW-Mass` | read-only |
145
+
146
+ Two spellings are genuinely uncertain because sources disagree on punctuation:
147
+ `$USER_NOTES` vs `$USERNOTES`, and `$NEVER_EXPAND_IN_BOM` vs
148
+ `$NEVER-EXPAND-IN-BOM`. Header syntax is case insensitive in SOLIDWORKS, but
149
+ that does not extend to hyphens and underscores. Both live as single constants
150
+ in `Vocabulary`, so correcting one is a one-line change.
151
+
152
+ **Sheet metal (`$SM-…`) is deliberately absent.** No reliable source confirmed
153
+ its syntax, and shipping an invented factory is worse than shipping none. Use
154
+ `raw()` or `register_parameter()`.
155
+
156
+ ### Confirming a parameter yourself
157
+
158
+ The syntax is not worth guessing at. On a **copy** of the model:
159
+
160
+ 1. **Insert → Tables → Design Table → Auto-create**
161
+ 2. SOLIDWORKS opens an embedded sheet with the header row it proposes
162
+ 3. Copy that row, close without saving, delete the copy
163
+
164
+ A minute of checking beats an afternoon of columns the model ignores.
165
+
166
+ ### Nothing is out of reach
167
+
168
+ ```python
169
+ sw.raw("$WHATEVER@Thing<3>") # any header at all, unchecked
170
+
171
+ sw.register_parameter( # a new typed factory, at runtime
172
+ "sheet_metal_thickness",
173
+ template="$SM-THICKNESS",
174
+ summary="Sheet metal thickness.",
175
+ value_kind=sw.ValueKind.NUMBER,
176
+ )
177
+ sw.column("sheet_metal_thickness")
178
+ ```
179
+
180
+ ## Reusable bases
181
+
182
+ `TableTemplate` holds everything that does not change row to row — model, output
183
+ directory, file name, columns, naming rule — so every script in a project starts
184
+ from the same base.
185
+
186
+ ```python
187
+ from pathlib import Path
188
+ import swdesigntables as sw
189
+
190
+ BRACKET = sw.TableTemplate(
191
+ model_name="BRK-MASTER",
192
+ output_dir=Path("tables"),
193
+ file_name="brk_master_dt.xlsx",
194
+ columns=(length, width, sw.description()),
195
+ config_name=lambda width, length: f"BRK-{width:03d}-{length:03d}",
196
+ round_floats=3,
197
+ )
198
+
199
+ table = BRACKET.new_table()
200
+ table.add_configuration(BRACKET.name_for(width=25, length=40), {...})
201
+ table.save() # goes to tables/brk_master_dt.xlsx
202
+ ```
203
+
204
+ It is frozen; `BRACKET.replace(model_name="BRK-HEAVY")` gives you a variant
205
+ without disturbing the original.
206
+
207
+ To start from an empty but valid file:
208
+
209
+ ```python
210
+ sw.blank_table("BRK-MASTER", [length, width], path="skeleton.xlsx")
211
+ ```
212
+
213
+ That is also the cheapest way to find out whether SOLIDWORKS accepts a set of
214
+ headers: generate the skeleton, insert it with **From file**, and see what it
215
+ says before writing a generator around it.
216
+
217
+ ## Suppression: `S`/`U` or `1`/`0`
218
+
219
+ Documentation describes `S` and `U`. Working tables in the wild use `1` and `0`.
220
+ Both are supported and neither is silently rewritten:
221
+
222
+ ```python
223
+ sw.DesignTable(..., state_format=sw.StateFormat.NUMERIC) # writes 1 / 0
224
+ sw.State.from_legacy_int(1) # -> State.SUPPRESSED
225
+ ```
226
+
227
+ Component suppression is a different vocabulary — `ComponentState` is `S`/`R`,
228
+ where `R` is Resolved — and mixing them up is caught.
229
+
230
+ ## Migrating a script that already has header strings
231
+
232
+ `parse_header` turns an existing header string into the typed column that
233
+ renders it, so a list you already have keeps working:
234
+
235
+ ```python
236
+ table = sw.DesignTable(
237
+ "Extrusion Profile",
238
+ columns=[sw.parse_header(h) for h in HEADERS],
239
+ state_format=sw.StateFormat.NUMERIC,
240
+ )
241
+ ```
242
+
243
+ Anything unrecognized becomes a `raw()` column rather than an error.
244
+
245
+ ## Validation
246
+
247
+ ```python
248
+ report = table.validate()
249
+ for issue in report.issues:
250
+ print(issue)
251
+ ```
252
+
253
+ Issue codes are stable across releases; message wording is not, so match on the
254
+ code. Errors stop the write, warnings do not.
255
+
256
+ A selection of what is caught: duplicate columns and configurations,
257
+ configuration names containing `/ \ : * ? " < > |`, values for undeclared
258
+ columns, `$PARENT` cycles and children placed before their parent, the reserved
259
+ `_SWX` sheet name, a state letter in a numeric column, and
260
+ `equations-dimension-conflict` — driving both a dimension and a global variable
261
+ of the same name, which usually means someone expected the dimension column to
262
+ win an argument it cannot win.
263
+
264
+ Three levels of strictness:
265
+
266
+ ```python
267
+ sw.DesignTable(...) # errors raise, warnings warn
268
+ sw.DesignTable(..., strict=False) # everything degrades to a warning
269
+ sw.DesignTable(..., ignore=("float-precision",)) # silence one code
270
+ ```
271
+
272
+ In CI, promote warnings so they cannot rot:
273
+
274
+ ```bash
275
+ python -W error::swdesigntables.errors.DesignTableWarning build_tables.py
276
+ ```
277
+
278
+ ### What validation cannot do
279
+
280
+ Without a live model there is nothing to check names against. A misspelled
281
+ feature name still produces a column SOLIDWORKS silently ignores. This package
282
+ only catches problems of *shape* — stray whitespace, an embedded `@`, a value of
283
+ the wrong kind. Claiming more is how people learn to stop reading warnings.
284
+
285
+ ## Extra sheets, and why `_SWX` is refused
286
+
287
+ You can add your own sheets — source data, notes, a parameter legend — and hide
288
+ them:
289
+
290
+ ```python
291
+ table.add_sheet("Notes", [["Source"], ["main_db.xlsx"]], hidden=True)
292
+ table.add_defined_name("Lengths", "Sheet1!$B$3:$B$50")
293
+ ```
294
+
295
+ `Sheet1` always stays first and visible. Hidden means `hidden`, never
296
+ `veryHidden`, so you can unhide it in Excel when something misbehaves.
297
+
298
+ **`_SWX` is rejected.** When SOLIDWORKS embeds a design table it creates a
299
+ hidden `_SWX` sheet and a `_SWX_0` defined name of its own. Authoring them here
300
+ produces a table SOLIDWORKS cannot reconcile. Likewise `Family` is created
301
+ automatically and adding it by hand is an error.
302
+
303
+ ## Design tables are not the source of truth
304
+
305
+ When a table is generated by a script it stays an internal detail of the
306
+ generator. The source of truth is what lives outside: a database, a spreadsheet,
307
+ a configuration file. If the embedded table becomes the source, no other process
308
+ can read the data without opening SOLIDWORKS.
309
+
310
+ ```
311
+ external source -> rules -> design table -> configurations
312
+ ```
313
+
314
+ ## Preparing the master model
315
+
316
+ The table can only be as good as the model underneath it:
317
+
318
+ 1. **Name your dimensions.** Without a name they stay `D1@Sketch1`, and in three
319
+ weeks nobody knows which is which.
320
+ 2. **Name the features you intend to suppress.** `$STATE@Cut-Extrude7` breaks
321
+ the moment someone reorders the tree.
322
+ 3. **One brain only.** Equations or the table, not both governing the same thing.
323
+ A dimension driven by an equation cannot be driven by the table — drive the
324
+ global variable instead.
325
+ 4. **Set document units to at least 2 decimals.** At 0 decimals a radius of 2.4
326
+ shows as `2`, in the model and on the drawing.
327
+ 5. **Name the model after the family, not a variant.** Rename *before* creating
328
+ drawings or assemblies, or you break the references.
329
+ 6. **Model the full case and suppress downwards.** Removing is easier than
330
+ creating.
331
+
332
+ ## License
333
+
334
+ MIT.