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.
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)
| File | Size | Uploaded | |
|---|---|---|---|
| kicad_evaltor-0.2.0.tar.gz | 92.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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
|