Skip to main content

symdef

Standard-agnostic toolkit for graphical-symbol data. Symbols are TOML files; the toolkit loads them, resolves composition, lints the wiring contract, renders SVG, and builds a resolved JSON bundle plus a gallery. Data packages hold only data and reuse the toolkit and its gates, for any standard: IEC 60617, ISO 14617, ISA 5.1 or your own.

Two doors: symdef is what a consumer draws with (load, orient, render, measure), and symdef.authoring is what a library's author builds with (build, check, init, lint, write_build, stale_build, RULES, finding_key, LibraryConfig). No name is on both.

Status: alpha. The toolkit the spec below describes is built: reading, resolving, linting, SVG and the build, with every lint rule and gate proven by a failing fixture. Published on PyPI from v0.4.0; v0.1.0 to v0.3.0 were git tags named graphical-symbols.

Every example symbol in this repository, the docs and the gallery included, is drawn independently in the style of a published standard (IEC 60617 for the S000xx numbers). None is copied from the IEC database or from any standard's own drawings. Each is marked status = "unverified": nobody has checked it against the standard.

Documentation: https://olejbondahl.github.io/symdef/ (tutorial, guide, symbol interface, API reference and a gallery).

Install

uv add symdef

or pip install symdef.

Python 3.14 or later. The one runtime dependency is fonttools, which measures a font's TrueType files.

The functions in symdef.files, symdef.project and symdef.cli read or write files, or print. Everything else is pure: the same input gives the same output, with no file I/O, clock, randomness or global state. The top-level symdef namespace re-exports both kinds. More depth: docs/GUIDE.md.

The design

src/symdef/docs/SYMBOL_INTERFACE.html is the binding, final spec. Open it in a browser; it ships as package data, readable at runtime through importlib.resources.files("symdef").joinpath("docs/SYMBOL_INTERFACE.html"). The key ideas:

  • A symbol is a definition: no tag, terminal numbers, position or orientation. Those belong to the placement in a drawing library.
  • Port ids are roles (in, out, anode, ...), never terminal markings (13, A1).
  • Electrical function is data: nodes and typed paths, so a consumer can netlist without per-symbol knowledge.
  • Text lives in slots: the symbol reserves boxes for tags and terminal markings.
  • Module units (M = 2.5 mm), a 0.125 M drawing grid and a 1 M wiring grid. A strict wiring contract is linted in all 8 orientations.
  • Composition mirrors the standard: composites reference parts by anchor, no copied coordinates.

What is here

The data model and orient, the boxes and repeat, the TOML reader with its JSON Schema (src/symdef/schema/symbol.schema.json), the composition resolver, 35 lint rules, plain and annotated SVG, and the build (resolved JSON per symbol, the bundle, SVGs, a gallery README). Decisions the spec leaves open, and concerns about the spec, are in docs/DECISIONS.md.

Use

A data repo has library.toml and symbols/<number>.toml; root below is its directory.

from symdef import load_library
from symdef.authoring import lint
from symdef.lint import RULES

library = load_library(root)  # raises LibraryError, listing every problem in the files
contact = library.get("S00227")  # by number; iterating a library is sorted by number
assert len(RULES) == 35  # the spec's 33 rules plus lead-off-port (D40) and stretch-cut
findings = lint(contact)  # all 35 rules, geometric ones in all 8 orientations
assert findings == ()  # the spec's make contact lints clean
for finding in findings:  # each has .rule, .severity, .message, .location, .orientation
    print(finding.rule, finding.location, finding.message)

Symbols are immutable; orient, translate and repeat return new ones. A repeated symbol is an ordinary symbol with ports such as 1.in and 3.out.

from symdef import Orientation, keepout_box, orient, repeat, to_svg

breaker = orient(repeat(contact, 3), Orientation.R0)
print(keepout_box(breaker))
svg = to_svg(breaker, annotate=True, texts={"tag": "-Q1"})

write_build writes build/ and src/<package>/bundle.json under the repo; stale_build lists the files that differ from what it would write, so a test can fail when a committed build is out of date. load_bundle reads a bundle back without the sources. <package> is the standard's normalised name (lowercase letters and digits, D8): the example's standard is IEC 60617, so its package is iec60617.

from symdef import load_bundle
from symdef.authoring import stale_build, write_build

write_build(library, root)
assert stale_build(library, root) == ()
assert len(load_bundle(root / "src" / "iec60617" / "bundle.json")) == len(library)

Development

uv sync
just ci        # format check, ruff, ty, vulture, purity gate, pytest with coverage

Coverage must stay at 100% (--cov-fail-under=100), so a partial run such as uv run pytest tests/test_units.py fails on coverage alone: add --no-cov for one. tests/test_readme.py runs the examples above and those in docs/GUIDE.md, so they stay true. Agents: start at AGENTS.md.

License

MIT, see LICENSE.

Metadata

Release files for symdef 0.15.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for symdef 0.15.0
File Size Uploaded
symdef-0.15.0.tar.gz 1.1 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for symdef 0.15.0
File Interpreter ABI Platform
symdef-0.15.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.2 MB

Release files / symdef-0.15.0.tar.gz

Download URL symdef-0.15.0.tar.gz
Size 1.1 MB
Tags Source
SHA-256 checksum
How to use checksums
a9c6eea3d00eb7720f781cdea7f97506d44be6057b04e3d8fb2c80917cdbc7f4
BLAKE2b-256 checksum
How to use checksums
7a0e0a76445872cd583afd0e207372f727ae1330252d8d071b9d385ecbef98ac
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

Release files / symdef-0.15.0-py3-none-any.whl

Download URL symdef-0.15.0-py3-none-any.whl
Size 128.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ca1f6da61f447dc1dba84567c9bceb1e4002558ac76157918352aff5e6ffea42
BLAKE2b-256 checksum
How to use checksums
ee8e6f2d8d1087185b128e581079a34ff4dea51e453c3dcd4ba62d538bf6c9dd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

Release history Release notifications | RSS feed

0.16.0

2 release files

This release

0.15.0 This release

2 release files

0.14.0

2 release files

0.13.0

2 release files

0.12.0

2 release files

0.11.0

2 release files

0.10.1

2 release files

0.10.0

2 release files

0.9.0

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page