git:20260912.1ee3107 to git:20260922.2deea24
8 added, 3 removed. Audit A to A.
# partcad_ide_client
The Python half of the socket protocol that connects `partcad` to the PartCAD IDE extension's **PartCAD
Viewer**. Source: `./src/partcad_ide_client`. Tests: `./tests/partcad_ide_client`. Run all commands below from
the repo root unless noted.
**A package, not a distribution.** Nothing is published under this name: it is one of the packages inside the
single `partcad` wheel, so `pip install partcad` is what puts `import partcad_ide_client` on a machine. See
"The PartCAD IDE viewer client" in [../partcad/AGENTS.md](../partcad/AGENTS.md) for the rationale, including
why giving this directory a `pyproject.toml` of its own would be a mistake.
The other half of the protocol lives in `ide/vscode/src/viewer/protocol.ts`. **A change to the wire
format is a change to both files**, and the frame layout is specified once, in
`src/partcad_ide_client/protocol.py` — that docstring is the normative description.
## Constraints
Two properties of this package are deliberate and easy to break:
- **No dependencies.** It lands in whatever interpreter `partcad` is installed into. Depending on anything (a
CAD library above all — which is exactly what depending on `ocp_vscode` did) risks dragging a second,
differently-provisioned stack into that interpreter, and would put that dependency into the `partcad` wheel
along with it. Standard library only.
- **It does not import `partcad`.** Geometry has already been tessellated into glTF by a PartCAD sandbox
before it reaches here, so there is nothing to import. `partcad` imports *this*, lazily, from
`partcad.viewer`.
The glTF payload codec (`encode_gltf`/`decode_gltf`) has two other implementations that have to agree with it:
`ocp_serialize.encode_gltf` in the sandbox, and `decodeGltf` in the extension. Neither can import this package,
which is why each carries its own copy; `tests/partcad/unit/test_viewer.py` and the extension's
`viewerProtocol.test.ts` are what catch a drift.
## Setup
All commands run **inside the dev container** — see "Where commands run" in the root
[AGENTS.md](../../AGENTS.md). Dependencies are already installed; re-run `poetry install` only after changing
`pyproject.toml`. Prefix commands with `poetry run`.
## Test and validate changes
```bash
poetry run pytest tests/partcad_ide_client -x -p no:error-for-skips -p no:warnings --dist no # matches CI
poetry run behave # integration tests
```
The client tests stand up a fake IDE on an ephemeral port and point the client at it with `PARTCAD_IDE_PORT`,
so they never collide with a PartCAD IDE the developer actually has open.
- To exercise the whole path — a real part, tessellated in a sandbox, over a real socket — point a `partcad` at a
- listening socket and show something; `tests/partcad/unit/test_viewer.py` covers the core side with the sandbox
- stubbed out, and the sandbox side is covered by the render tests.
+ To exercise the whole path — a real part, tessellated in a sandbox, over a real socket — run
+ `dev-tools/fake_viewer.py`, which listens where the IDE listens, acknowledges every frame and prints each show
+ with its control tree and the geometry and frames attributed to the items that draw them. Give it `--port` and
+ the same port in `PARTCAD_IDE_PORT` to stay out of the way of a PartCAD IDE that is actually open, and remember
+ that the process doing the show is usually the **daemon** rather than the `pc` you ran, one per workspace.
+ `tests/partcad/unit/test_viewer.py` covers the core side with the sandbox stubbed out,
+ `tests/partcad/unit/test_shape_tree.py` covers what the tree holds, and the sandbox side is covered by the
+ render tests.
## Lint / format
```bash
poetry run black --check src/partcad_ide_client tests/partcad_ide_client
poetry run flake8 src/partcad_ide_client tests/partcad_ide_client
poetry run isort --check --filter-files src/partcad_ide_client tests/partcad_ide_client
```
`black`, `flake8` and `isort` all gate now — each is a `pre-commit` hook and a `Lint (...)` job in `test.yml` —
and the tree satisfies all three, so a finding from any of them is yours. flake8 only reads its configuration
because `Flake8-pyproject` is installed; without it it checks at 79 columns. See the root
[AGENTS.md](../../AGENTS.md).
## Commit
`pre-commit` hooks (`dev-tools/pre-commit-config.yaml`) run `pytest`, formatting, and lint checks on commit and
are required to pass in CI before a PR can merge.