diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9d9963cb..7d7982cf 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -23,7 +23,12 @@ jobs: - name: Install package + test dependencies run: | python -m pip install --upgrade pip - pip install . + sudo apt-get install -y libpango-1.0-0 libpangoft2-1.0-0 libharfbuzz-subset0 + pip install ".[pdf]" pip install pytest - name: Run pytest - run: pytest -v + # WeasyPrint 70 needs Python 3.10+: there, the sheet PDF tests must + # run, not skip (a library missing on the runner fails them). + env: + WIREVIZ_REQUIRE_PDF: ${{ matrix.python-version != '3.9' && '1' || '' }} + run: pytest -v -rs diff --git a/CLAUDE.md b/CLAUDE.md index 8d620e30..d47f60fe 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -98,6 +98,8 @@ The pipeline is **YAML → Harness object graph → GraphViz `.gv` → rendered - **`svgembed.py`** — inlines referenced raster images into the SVG so the SVG/HTML output is self-contained. - **`wv_bom.py`** — BOM aggregation/dedup logic (`mini_bom_mode`, part-number handling, additional components). Reused for both the standalone `.bom.tsv` and the BOM table embedded in HTML. - **`wv_colors.py`** — IEC 60757 color codes, color-scheme generators (DIN 47100, 25-pair, TIA/EIA 568), `ColorMode` (SHORT / FULL / HEX, upper/lower). +- **`wv_sheet.py`** — `sheet` output (`-f D`): the HTML page rendered to PDF with WeasyPrint (optional extra `wireviz[pdf]`, needs Pango). Its `url_fetcher` refuses every URL; everything in the HTML is inline. Page size comes from the template's named `@page` rules (`din-6771.html`: `.page_A3 { page: A3 }` etc.). +- **`wv_include.py`** — top-level `include:` (upstream #220): dict-level merge of `connectors`/`cables`/`additional_bom_items` from other files; main file wins, a duplicate between includes is an error; relative image paths are made absolute per included file. `parse()` refuses `include` when `untrusted=True` (it reads files). - **`wv_images.py`** — turns `data:image/...;base64` URIs and `.webp` files into PNG files in the harness's private temp dir (`Harness.temp_dir()`, removed when the Harness is garbage-collected). Graphviz needs a file in a format its build can read. - **`wv_helper.py`** — shared utilities: `awg_equiv`/`mm2_equiv` gauge conversion, `expand` (range syntax), `tuplelist2tsv`, `smart_file_resolve` (image-path resolution against the input dir + `--prepend` dirs). diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 2103c04c..615258c0 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -## [Unreleased] +## [1.1.0] (2026-10-02) Fixes for open issues in the original [wireviz/WireViz](https://github.com/wireviz/WireViz) repository. Triage: [docs/plans/2026-10-02-upstream-issue-triage.md](plans/2026-10-02-upstream-issue-triage.md). @@ -23,8 +23,19 @@ Fixes for open issues in the original [wireviz/WireViz](https://github.com/wirev - `image: file.png` works as a short form of `image: {src: file.png}` ([#292](https://github.com/wireviz/WireViz/issues/292)). - Loops accept pin labels ([#432](https://github.com/wireviz/WireViz/issues/432)); loops on non-sequential pin numbers have a regression test ([#465](https://github.com/wireviz/WireViz/issues/465)). +### Behavior changes + +- A cable named alone in a connection set (`- B1`) now uses wires 1 to n instead of wire 1 n times ([#508](https://github.com/wireviz/WireViz/issues/508)). Autogenerated cables (`- W.`) are unchanged. +- Errors in the input name the connection set (`connection set 2 (X1 → W1 → X2): ...`) and the CLI prints them as one line with exit code 1; `--debug` shows the traceback ([#505](https://github.com/wireviz/WireViz/issues/505), [#207](https://github.com/wireviz/WireViz/issues/207)). Library callers can catch `wireviz.wv_errors.WireVizError` (a `ValueError`). + ### New features +- Print-ready sheet PDF: `-f D` / `output_formats="sheet"` writes `.sheet.pdf`, the HTML page (frame, diagram, BOM, title block) on one page at the template's sheet size. Needs `pip install "wireviz[pdf]"` (WeasyPrint 70 or later, Python 3.10 or later); WeasyPrint may load only the inline `data:` images, never a file or URL, and in untrusted mode it runs in a child process with the render timeout. The `din-6771` template no longer lets the diagram overlap the BOM and title block, prints at the right page size, and defaults to A4; the simple template fits the diagram to the page when printed. New `` placeholder ([#32](https://github.com/wireviz/WireViz/issues/32), [#304](https://github.com/wireviz/WireViz/issues/304)). +- Connector `shorts: [[1, 2, 3], {YE: [N, AUX]}]` shows internal shorts and jumpers as a bar in the pin table; shorted pins count as populated ([#350](https://github.com/wireviz/WireViz/issues/350)). +- Cable `twisted: [[RD, BK], {wires: [3, 4], rate: 20/m}]` shows twisted pairs, triads and groups as framed groups in the cable box ([#3](https://github.com/wireviz/WireViz/issues/3), [#353](https://github.com/wireviz/WireViz/issues/353)). +- `include:` merges shared connector/cable libraries from other files; `-I/--include-path` adds search directories ([#220](https://github.com/wireviz/WireViz/issues/220)). Not allowed in untrusted mode. +- A cable with no `wirecount` or `colors` takes its wire count from the wire numbers used in the connections ([#508](https://github.com/wireviz/WireViz/issues/508)). + - CSV BOM output: `-f c` / `output_formats="csv"` writes `.bom.csv` ([#98](https://github.com/wireviz/WireViz/issues/98)). - Loop colors: `loops: [{RD: [VCC, SENSE]}]` ([#457](https://github.com/wireviz/WireViz/issues/457)). - `options.show_title: true` draws `metadata.title` above the diagram in PNG, SVG and PDF ([#460](https://github.com/wireviz/WireViz/issues/460)). diff --git a/docs/README.md b/docs/README.md index 472d2a65..54777730 100644 --- a/docs/README.md +++ b/docs/README.md @@ -142,6 +142,16 @@ Wildcards in the file path are also supported to process multiple files at once, $ wireviz ~/path/to/files/*.yml ``` +### Print-ready PDF sheet + +`wireviz -f D harness.yml` writes `harness.sheet.pdf`: the HTML page (diagram, BOM and, with the `din-6771` template, frame and title block) as a one-page PDF at the template's sheet size (`metadata.template.sheetsize`: A4, A3 or A2). It needs Python 3.10 or later and WeasyPrint: + +``` +$ pip install "wireviz[pdf]" +``` + +WeasyPrint also needs the Pango library (`apt install libpango-1.0-0 libpangoft2-1.0-0` on Debian/Ubuntu, `brew install pango` on macOS; if Python then cannot find it on macOS, set `DYLD_FALLBACK_LIBRARY_PATH=/opt/homebrew/lib`). `-f P` still writes the diagram alone as PDF, without this dependency. + To see how to specify the output formats, as well as additional options, run: ``` diff --git a/docs/plans/2026-10-02-batch-c-design.md b/docs/plans/2026-10-02-batch-c-design.md new file mode 100644 index 00000000..d4a82836 --- /dev/null +++ b/docs/plans/2026-10-02-batch-c-design.md @@ -0,0 +1,137 @@ +# Batch C — larger upstream features (design) + +Date: 2026-10-02. Branch: `fix/upstream-batch-c`. Issues from +[the upstream triage](2026-10-02-upstream-issue-triage.md). Decisions marked +**Decided** were confirmed by Cole on 2026-10-02. + +Upstream `dev` implements none of these. Open upstream PRs (#382, #455, +#506) are on `dev` or unreviewed, so only their ideas are used. + +Implementation order: C1 (#505, #508), C2 (#220), C3 (#3/#353), C4 +(#350), C5 (#32/#304). Each is one commit with tests in +`tests/test_upstream_issues.py`. + +## C1a. Error messages (#505, #207) + +Stage 1 only. + +- New `wireviz.wv_errors.WireVizError(ValueError)`. +- `parse()` wraps the processing of each connection set. An error is + re-raised with the prefix `connection set N (X1 → W1 → X2): `, and keeps + its class when it is a `ValueError` or `TypeError`; any other exception + becomes `WireVizError`. `raise ... from exc` keeps the cause. +- The `style: simple` error names the connector. +- CLI: `WireVizError`, `ValueError`, `TypeError`, `yaml.YAMLError` and + `FileNotFoundError` from `parse()` print one line through + `click.ClickException` (exit 1). New `--debug` flag re-raises for a + traceback. +- Not now: YAML line numbers (stage 2, needs mark tracking in the loader). + +## C1b. Infer the wire count (#508) + +**Decided:** a bare named cable means wires 1..n. + +- A pre-pass in `parse()` over all connection sets, before components + are created. For each cable designator with no `wirecount` and no + `colors`, the implied wire count is the largest of: the integer wire + numbers used for it, and the connection count of any set where it + appears as a bare name. +- A bare named cable (`- B1`, no template separator) in a set with n + parallel connections means wires 1..n (before: wire 1, n times). An + autogenerated cable (`- W.`) keeps its meaning: one new instance per + connection. +- Color or label references cannot imply a count: the existing error + stays, with a better message. +- CHANGELOG: list the bare-name change under "Behavior changes". + +## C2. Include files (#220) + +**Decided:** top-level `include:` list, merged at dict level. + +```yaml +include: + - lib/connectors.yml + - lib/cables.yml +connectors: ... +``` + +- Paths resolve against the including file's directory (or `source_path` + for str/dict input), then against `include_paths` (new `parse()` + argument, CLI `-I/--include-path`, repeatable). +- Recursive. A cycle or a depth over 16 is an error naming the files. +- Merge `connectors`, `cables` and `additional_bom_items` (list append) + from includes into the main data. The main file wins on a duplicate + key. The same key in two included files is an error naming both + files. `metadata`, `options`, `tweak` and `connections` come from the + main file only; in an included file they are an error (say so, do not + drop them silently). +- `image.src` of an included component is made absolute against the + included file's directory before merging. +- YAML anchors and `<<:` do not cross files: each file is parsed on its + own. Documented. +- `untrusted=True` refuses `include` (it reads files). +- PNG embedding stores the merged YAML, so a PNG is self-contained. +- `--prepend` stays as it is, and is now documented in syntax.md. + +## C3. Twisted pairs (#3, #353) + +Syntax as kvid proposed in #353: + +```yaml +cables: + W1: + colors: [RD, BK, WH, BU] + twisted: [[RD, BK], [WH, BU]] # or wire numbers / wire labels + # optional rate: twisted: [{wires: [1, 2], rate: 20/m}] +``` + +- Each group is 2 or more wires, referenced by number, color or wire + label (same resolution as connections). A wire in two groups, a group + under 2 wires, or an unknown wire is an error. +- Rendering in the wire table: the rows of a group are made contiguous + (the group starts at the row of its first wire) and wrapped in a + nested table with a thin **solid** border and a caption row + "Twisted pair" / "Twisted triad" / "Twisted group", plus `: `. + Solid, because a dashed border means a shield (IEC) and bundles + already use a dashed box. Ports keep the original wire number + (`w{i}`), so edges do not change. +- No drawn twist (split splines are a Graphviz limitation; out of scope). +- The grouping helper is generic, so shielded cores (#330) can reuse it. + +## C4. Jumpers / internal shorts (#350) + +**Decided:** `shorts:`, shorted pins count as populated. + +```yaml +connectors: + TB1: + pincount: 6 + shorts: [[1, 2, 3], {RD: [5, 6]}] # pin numbers or labels +``` + +- Each short is 2 or more pins, by number or label. Optional color as a + one-key mapping, like loops. +- Drawn in the pin table: one narrow column per short, a filled cell at + each member pin and a solid bar between the first and last member. + Plain HTML table cells; no gvpr/neato. +- Shorted pins are activated (visible with `hide_disconnected_pins`) and + count as `populated`. +- No automatic BOM entry; users add `additional_components`. +- `loops` keep their meaning (external wire loops). + +## C5. Print-ready sheet PDF (#32, #304) + +**Decided:** WeasyPrint as an optional extra, new format code. + +1. Template fixes (`din-6771.html`): named CSS pages per sheet size, so + a browser and WeasyPrint print one page at the right size; the + diagram area scales the SVG to fit above the BOM and title block; + `%date%` placeholder. +2. New output format `sheet` (CLI `-f D`, file `.sheet.pdf`): the + HTML output rendered to PDF with WeasyPrint. `pip install + "wireviz[pdf]"` adds it; without it a clear error says how to + install it. WeasyPrint gets a `url_fetcher` that refuses every URL + (all images are already inlined), so it reads no files or network. + `-f P` keeps its meaning (Graphviz diagram-only PDF). +3. Later (not this batch): BOM headings in `options.terminology`, page + orientation, BOM auto-scaling. diff --git a/docs/syntax.md b/docs/syntax.md index 44993508..72d29bf3 100644 --- a/docs/syntax.md +++ b/docs/syntax.md @@ -3,6 +3,8 @@ ## Main sections ```yaml +include: # optional list of files with shared definitions (see below) + connectors: # dictionary of all used connectors : # unique connector designator/name ... # connector attributes (see below) @@ -89,6 +91,12 @@ tweak: # optional tweaking of .gv output # pins may be given by number or by pin label, e.g. [VCC, SENSE]; # give a loop a wire color with a one-key mapping: {RD: [VCC, SENSE]} + # internal shorts / jumpers, e.g. on terminal blocks: each a list of 2+ pins + # (numbers or labels); optional color as a one-key mapping: {RD: [5, 6]}. + # Shorted pins count as populated and are always shown. At most 64 shorts + # per connector; not shown for style: simple. + shorts: + # stripping lengths at this connector (shown in the diagram) strip: sleeve: # numbers are taken as mm, e.g. 10 or '0.4 in' @@ -164,6 +172,9 @@ tweak: # optional tweaking of .gv output show_name: # defaults to true show_wirecount: # defaults to true show_wirenumbers: # defaults to true for cables; false for bundles + twisted: # twisted groups, each a list of 2+ wires by number, color + # or wire label, e.g. [[RD, BK], [3, 4]]; a group may be + # {wires: [...], rate: 20/m}; shown framed in the cable box show_box: # defaults to true; false hides the cable box and draws # each wire straight from connector to connector # (every wire then needs a connector at both ends) @@ -266,6 +277,10 @@ connections: - `-` auto-expands to a range. - `` to refer to a wire's label or color, if unambiguous. +- `` alone (no wire list) uses wires 1 to n, where n is the number of parallel connections in the set. An autogenerated cable (`