Skip to main content

kicad-evaltor

A Python library for validating KiCad designs programmatically. Describe the properties a design is supposed to have — reference designators, values, connectivity, footprint assignment, ERC results, and whether the sheet is readable — and run them as a test suite.

Works against KiCad 10 and later. It does not need the KiCad IPC API server, which KiCad 10 removed.

CI PyPI Python License: MIT

Installation

pip install kicad-evaltor

What you need installed

Most checks need nothing but the file itself. The schematic reader parses .kicad_sch directly, so symbols, fields, wires, labels and text geometry all work with no KiCad present.

A kicad-cli binary is needed only for the checks that shell out:

Check Needs
sch.components.connected kicad-cli sch export netlist
sch.erc kicad-cli sch erc

Without it those checks report an error and the rest of the suite still runs. On Windows, KiCad is not put on PATH by default; pass kicad_cli_path="C:/Program Files/KiCad/10.0/bin/kicad-cli.exe" or let DesignContext find it.

Quick start

from kicad_evaltor import (
    ComponentValueCheck,
    DesignContext,
    ERCRunCheck,
    TestRunner,
    TextTextOverlapCheck,
)

with DesignContext(project_path="myproject.kicad_pro", headless=True) as ctx:
    report = TestRunner(
        [
            ComponentValueCheck(reference="R1", expected="10k"),
            ERCRunCheck(severity="error"),
            TextTextOverlapCheck(),
        ]
    ).run(ctx)

    print(report.summary())
    assert report.all_passed

A complete, runnable example lives in examples/simple_circuit_test.py, which runs the schematic checks against examples/demo_circuit/simple_circuit_test.kicad_sch. It exits non-zero if that sheet stops matching its documented expectations, so it doubles as a smoke test:

python examples/simple_circuit_test.py

examples/visual_test.py does the same for the layout checks, against examples/demo_circuit/visual_test.kicad_sch. It runs every sch.layout.* and sch.noconnect.* check the registry knows and lists each finding with the properties and coordinates involved. It is a report, not a test: it exits 0 even when the checks find defects. It needs no KiCad at all, and takes any sheet as an argument:

python examples/visual_test.py                                 # human report
python examples/visual_test.py --json                          # the same failures as JSON
python examples/visual_test.py path/to/your.kicad_sch          # any other sheet

Checks

Schematic

Check ID Class Description
sch.component.exists ComponentExistsCheck A component exists, by reference or lib_id pattern
sch.component.value ComponentValueCheck A component's value matches, case-insensitively
sch.component.property ComponentPropertyCheck An arbitrary field on a component matches
sch.components.connected ComponentsConnectedCheck Two components share at least N nets
sch.footprint.assigned FootprintAssignedCheck Footprints are assigned, optionally allowing None
sch.symbol.in_library SymbolInLibraryCheck A symbol exists in the project libraries
sch.erc ERCRunCheck Runs ERC via kicad-cli and filters by severity

Layout

These read the sheet's own geometry rather than the netlist. Text boxes are computed from a stroke font calibrated against a real KiCad renderer, so the verdicts match what you see in the viewer.

Check ID Class Description
sch.layout.text_text_overlap TextTextOverlapCheck Two text items overlap
sch.layout.text_symbol_overlap TextSymbolOverlapCheck Text sits on a symbol
sch.layout.symbol_symbol_overlap SymbolSymbolOverlapCheck Two symbol bodies share space
sch.layout.text_off_sheet TextOffSheetCheck Text falls outside the sheet boundary
sch.layout.text_wire_overlap TextWireOverlapCheck Text sits on a wire. Opt-in: KiCad masks wires behind text, so this is usually benign
sch.layout.symbol_wire_overlap SymbolWireOverlapCheck A wire runs across a symbol's artwork, or into a pin from the body side
sch.noconnect.floating NoConnectFloatingCheck A no-connect flag marks no pin, or a drawn pin is reached by nothing at all

Hidden items are never reported. A field marked (hide yes) is skipped, a pin marked (hide yes) is not counted as a pin left open, and a power symbol's name is treated as symbol artwork rather than as text.

A component's own text is measured against its own body rather than the full body-and-pins box, and only crossing the outline counts. KiCad places a reference directly above a body, which is where the topmost pin stub is too, so measuring pins would report the default placement of every part. A small symbol is mostly empty space, so a value that fits wholly inside one is left alone. Text straddling the outline, half in and half out, is reported — and so is text over another symbol's pins.

Every finding carries the properties behind each label, so a report says what is on the sheet rather than only which field collided. Value is one property among many: user-defined ones such as an LCSC part number are reported the same way.

The two text-on-anything checks answer a legibility question and leave a 0.2mm clearance between the pair: text that comes within it is unreadable even though nothing is drawn on top of anything. Set clearance=0 to report only what genuinely overlaps. The remaining checks answer "is this drawn on that", so they take no clearance, and take a margin if the measurement noise of sampled outlines and bare pin lines needs hiding.

{
  "first": "SW2.Value", "first_kind": "text", "first_properties": {"Value": "SW_Push"},
  "second": "PWR012", "second_kind": "symbol", "second_properties": {"Value": "GND"},
  "area": [256.54, 44.78, 259.08, 46.17], "width": 2.54, "height": 1.39
}

Hidden properties are left out, on both sides: a field marked (hide yes) is not drawn, so it is not part of what the sheet shows.

report.to_json() serialises the whole run, report.to_dict() and result.to_dict() give the same data as Python objects, and report.failures_json() returns just the failures, which is usually what a CI job wants to read.

Custom checks

A check is a class with a stable dotted id, registered so it can be looked up by name.

from dataclasses import dataclass

from kicad_evaltor import Check, CheckResult, TestStatus, register


@dataclass
class MyParams:
    min_count: int
    prefix: str


@register
class MyComponentCountCheck(Check):
    id = "custom.component.count"
    name = "Component Count"
    description = "Ensures a minimum number of components exist"
    Params = MyParams

    def run(self, ctx):
        symbols = ctx.schematic.get_symbols()
        matching = [s for s in symbols if s.reference.startswith(self.params.prefix)]
        if len(matching) >= self.params.min_count:
            return CheckResult.pass_(self.id, f"Found {len(matching)} components")
        return CheckResult.fail(self.id, f"Only {len(matching)} components found")


result = MyComponentCountCheck(prefix="R", min_count=10)

TestStatus is one of PASS, FAIL, ERROR, or SKIP. Use SKIP for a check that cannot run in the current environment, such as a missing kicad-cli, so it is reported honestly rather than passing silently.

Development

uv sync --extra dev
uv run ruff check .
uv run mypy src
uv run pytest -m "not integration"

See CONTRIBUTING.md for the conventions, and CHANGELOG.md for what changed.

Documentation

  • Architecture — how parsing, geometry and the check registry fit together.
  • Contributing — setup, gates, and conventions.

License

MIT — see LICENSE.

Metadata

Release files for kicad-evaltor 0.2.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 kicad-evaltor 0.2.0
File Size Uploaded
kicad_evaltor-0.2.0.tar.gz 92.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for kicad-evaltor 0.2.0
File Interpreter ABI Platform
kicad_evaltor-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 157.3 kB

Release files / kicad_evaltor-0.2.0.tar.gz

Download URL kicad_evaltor-0.2.0.tar.gz
Size 92.9 kB
Tags Source
SHA-256 checksum
How to use checksums
69fb52e8e3941ccf0017931cf7a965304dc16bc1c9a9336d94f24055fe536dd7
BLAKE2b-256 checksum
How to use checksums
a59247e54d7799e1ea3aa992da29e7e8944642bc5a16531edc27e43006f70928
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.12

Release files / kicad_evaltor-0.2.0-py3-none-any.whl

Download URL kicad_evaltor-0.2.0-py3-none-any.whl
Size 64.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5d354dbaa4c6ad4ab2cc595190071e363913ac760285a61ceb531f028f491466
BLAKE2b-256 checksum
How to use checksums
b70063313f8995cf2d1625324f014abe8ae98a40eb2d0e1677a874b04dc608a3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.12

Release history Release notifications | RSS feed

0.3.0

2 release files

This release

0.2.0 This release

2 release files

0.1.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