API Reference

This section contains the complete API reference for Circuit Synth.

Core Module

Core circuit primitives and utilities

class circuit_synth.core.Circuit(name=None, description=None, auto_comments=True)[source]

Bases: object

class Component(symbol: str, ref: str | None = None, value: str | None = None, footprint: str | None = None, datasheet: str | None = None, description: str | None = None, **kwargs)

Bases: SimplifiedPinAccess

Represents an electronic component that references a KiCad symbol.

If multiple pins share the same name (e.g. β€œGND”), __getitem__ returns a PinGroup, so something like:

comp[β€œGND”] += net

will connect all GND pins to that net.

Additional fields can be passed as keyword arguments and will be stored in _extra_fields for later access (e.g., mfg_part_num, tolerance, etc.).

ALLOWED_REASSIGN = {'value'}
__call__(*args, **kwargs)

Cloning a prefix-based ref is allowed, but final references are not.

__init__(symbol: str, ref: str | None = None, value: str | None = None, footprint: str | None = None, datasheet: str | None = None, description: str | None = None, **kwargs)

Initialize Component with support for arbitrary additional fields.

Parameters:
  • symbol – KiCad symbol reference (e.g., β€œDevice:R”)

  • ref – Component reference (e.g., β€œR1”)

  • value – Component value (e.g., β€œ10k”)

  • footprint – KiCad footprint reference

  • datasheet – URL to component datasheet

  • description – Component description

  • **kwargs – Additional fields (e.g., mfg_part_num, tolerance, etc.)

datasheet: str | None = None
description: str | None = None
footprint: str | None = None
classmethod from_dict(data: Dict[str, Any]) Component
print_pin_names() str

Returns a formatted string of all pins, showing index, name, and number. Useful for debugging symbol pin naming mismatches.

ref: str | None = None
to_dict() Dict[str, Any]
value: str | None = None
symbol: str
__init__(name=None, description=None, auto_comments=True)[source]
add_annotation(annotation)[source]

Add a text annotation (TextProperty, TextBox, etc.) to this circuit.

add_component(comp: Component)[source]

Register a Component with this Circuit.

add_image(image_path: str, position: tuple, scale: float = 1.0)[source]

Add an embedded image to the schematic.

Images are embedded as base64-encoded data in the KiCad schematic file. The image file is read when the schematic is generated, so it only needs to exist at generation time.

Parameters:
  • image_path – Path to image file (PNG, JPG, etc.)

  • position – Tuple of (x, y) position in mm

  • scale – Scale factor (1.0 = original size, 2.0 = 2x larger, etc.)

Returns:

The created Image annotation object

Return type:

Image

Example

>>> circuit.add_image("logo.png", position=(100, 50), scale=2.0)
add_net(net: Net)[source]
add_subcircuit(subcirc: Circuit)[source]

Add a subcircuit and establish parent-child relationship

add_subcircuit_old(subcirc: Circuit)[source]
property components

Get components dictionary for compatibility.

finalize_references()[source]

Auto-assign references for any components that only had a prefix.

generate_bom(output_file: str | None = None, project_name: str | None = None, fields: str | None = None, labels: str | None = None, group_by: str | None = None, exclude_dnp: bool = False) Dict[str, Any][source]

Generate a Bill of Materials (BOM) from this circuit as a CSV file.

This method creates a KiCad project if one doesn’t already exist, then exports the schematic to a CSV BOM file using kicad-cli.

Parameters:
  • output_file – Path where CSV BOM should be written. If not provided, defaults to {project_name}/{project_name}.csv

  • project_name – Name of the KiCad project directory. If not provided, defaults to the circuit name. Required if output_file is provided but needs a project.

  • fields – Comma-separated fields to export from schematic. If not specified, KiCad will export default fields (Refs, Value, Footprint, etc.)

  • labels – Comma-separated column headers for the BOM. Must match the number of fields if fields are specified.

  • group_by – Field to group references by when exporting. Common values: β€œValue” (group by component value), β€œFootprint”, etc.

  • exclude_dnp – If True, exclude β€œDo not populate” components from BOM

Returns:

Result dictionary containing:
  • success (bool): True if BOM was successfully generated

  • file (Path): Path to the generated CSV file

  • component_count (int): Number of components in the BOM

  • project_path (Path): Path to the KiCad project directory

  • error (str, optional): Error message if generation failed

Return type:

dict

Example

>>> circuit = led_blinker()
>>> result = circuit.generate_bom(project_name="led_blinker")
>>> print(f"BOM exported to: {result['file']}")
>>> print(f"Component count: {result['component_count']}")
Raises:
generate_flattened_json_netlist(filename: str) None[source]

Produce a flattened JSON representation for this circuit + subcircuits.

generate_full_netlist() str[source]
Print a hierarchical netlist showing:
  1. Each circuit + subcircuit (name, components, and optional description)

  2. A single combined net listing from all circuits

generate_gerbers(output_dir: str | None = None, project_name: str | None = None, include_drill: bool = True, drill_format: str = 'excellon') Dict[str, Any][source]

Generate Gerber files for PCB manufacturing from this circuit.

This method creates a complete KiCad project (including PCB) if one doesn’t already exist, then exports Gerber files for manufacturing using kicad-cli. Gerbers can be submitted directly to manufacturers like JLCPCB, PCBWay, etc.

Parameters:
  • output_dir – Directory where Gerber files should be written. If not provided, defaults to {project_name}/gerbers

  • project_name – Name of the KiCad project directory. If not provided, defaults to the circuit name.

  • include_drill – Also export drill files along with Gerbers (default: True)

  • drill_format – Format for drill files: β€œexcellon” (default) or β€œgerber”

Returns:

Result dictionary containing:
  • success (bool): True if Gerbers were successfully generated

  • gerber_files (list): List of Path objects to generated .gbr files

  • drill_files (tuple): Tuple of (plated_holes_file, non_plated_holes_file) or None

  • project_path (Path): Path to the KiCad project directory

  • output_dir (Path): Directory where Gerbers were exported

  • error (str, optional): Error message if generation failed

Return type:

dict

Example

>>> circuit = esp32_board()
>>> result = circuit.generate_gerbers(project_name="esp32_board")
>>> print(f"Gerbers exported to: {result['output_dir']}")
>>> print(f"Files: {len(result['gerber_files'])} Gerber files")
Requirements:
  • KiCad 8.0 or later

  • kicad-cli must be available in PATH

  • Circuit must be complete with all components and connections

Notes

  • First run generates full KiCad project including PCB (slower)

  • Subsequent runs reuse existing project (faster)

  • Default layers: F.Cu, B.Cu, F.Mask, B.Mask, F.SilkS, B.SilkS, F.Paste, B.Paste, Edge.Cuts

  • Gerbers use standard Protel file extension format (.gbr, .gbl, etc.)

  • Compatible with JLCPCB, PCBWay, OSH Park, and most PCB manufacturers

generate_json_netlist(filename: str) None[source]

Generate a JSON representation of this circuit and its hierarchy, then write it out to β€˜filename’.

generate_kicad_netlist(filename: str) None[source]

Generate a KiCad netlist (.net) file for this circuit and its hierarchy.

generate_kicad_project(project_name: str, generate_pcb: bool = True, force_regenerate: bool = False, placement_algorithm: str = 'hierarchical', draw_bounding_boxes: bool = False, generate_ratsnest: bool = True, update_source_refs: bool | None = None, preserve_user_components: bool = False) Dict[str, Any][source]

Generate a complete KiCad project (schematic + PCB) from this circuit.

This method provides a simplified API that handles all the boilerplate setup required for KiCad project generation, creating the project directory and generating KiCad files with an automatic JSON netlist in the project directory.

Parameters:
  • project_name – Name of the KiCad project and directory to create

  • generate_pcb – Whether to generate PCB in addition to schematic (default: True)

  • force_regenerate – Force regeneration of existing files, losing manual edits (default: False)

  • placement_algorithm – Component placement algorithm to use (default: β€œhierarchical”)

  • draw_bounding_boxes – Whether to draw visual bounding boxes around components (default: False)

  • generate_ratsnest – Whether to generate ratsnest connections in PCB (default: True)

  • update_source_refs – Whether to update source file with finalized refs. None (default): Auto-update unless force_regenerate=True True: Always update source file False: Never update source file

  • preserve_user_components – Keep components in KiCad that don’t exist in Python (default: False) False: Python is source of truth - delete components not in Python True: Preserve all components in KiCad, even if not in Python

Returns:

Result dictionary containing:
  • success (bool): Whether generation succeeded

  • json_path (Path): Path to the canonical JSON netlist

  • project_path (Path): Path to the KiCad project directory

  • error (str, optional): Error message if generation failed

Return type:

dict

Example

>>> circuit = esp32s3_simple()
>>> result = circuit.generate_kicad_project("esp32s3_simple")
>>> print(f"JSON netlist: {result['json_path']}")
>>> print(f"KiCad project: {result['project_path']}")
generate_pdf_schematic(output_file: str | None = None, project_name: str | None = None, black_and_white: bool = False, theme: str | None = None, exclude_drawing_sheet: bool = False, pages: str | None = None) Dict[str, Any][source]

Generate a PDF schematic from this circuit.

This method creates a KiCad project if one doesn’t already exist, then exports the schematic to a PDF file using kicad-cli.

Parameters:
  • output_file – Path where PDF should be written. If not provided, defaults to {project_name}/{project_name}.pdf

  • project_name – Name of the KiCad project directory. If not provided, defaults to the circuit name.

  • black_and_white – Export in black and white instead of color (default: False)

  • theme – Color theme to use for export (optional). Theme name depends on KiCad installation.

  • exclude_drawing_sheet – Exclude the drawing sheet/border from PDF (default: False)

  • pages – Page range to export (e.g., β€œ1,3-5” for pages 1, 3, 4, 5). If not specified, all pages are exported.

Returns:

Result dictionary containing:
  • success (bool): True if PDF was successfully generated

  • file (Path): Path to the generated PDF file

  • project_path (Path): Path to the KiCad project directory

  • error (str, optional): Error message if generation failed

Return type:

dict

Example

>>> circuit = led_blinker()
>>> result = circuit.generate_pdf_schematic(project_name="led_blinker")
>>> print(f"PDF exported to: {result['file']}")
Raises:
generate_text_netlist() str[source]

Generate a textual netlist for display or debugging.

property nets

Return a dictionary of all nets in this circuit.

Returns:

Dictionary of nets keyed by net name

Return type:

Dict[str, Net]

register_reference(ref: str) None[source]

Register a new reference in this circuit’s scope

simulate()[source]

Create a simulator instance for this circuit.

This method provides access to circuit simulation capabilities using PySpice as the backend. The returned simulator object can be used to run various analyses such as DC operating point, transient, and AC.

Returns:

Simulator object for running analyses

Return type:

CircuitSimulator

Example

>>> circuit = voltage_divider()
>>> sim = circuit.simulate()
>>> result = sim.operating_point()
Raises:
simulator()[source]

Alias for simulate() method for backward compatibility.

Returns:

Simulator object for running analyses

Return type:

CircuitSimulator

property subcircuits

Get subcircuits list.

to_dict() Dict[str, Any][source]

Return a hierarchical dictionary representation of this circuit, including subcircuits, components (as a dictionary keyed by reference), and net connections.

to_flattened_list(parent_name: str = None, flattened: List[Dict[str, Any]] = None) List[Dict[str, Any]][source]

Build or update a shared flattened list of circuit data dicts.

validate_reference(ref: str) bool[source]

Check if reference is available in this circuit’s scope

class circuit_synth.core.Component(symbol: str, ref: str | None = None, value: str | None = None, footprint: str | None = None, datasheet: str | None = None, description: str | None = None, **kwargs)[source]

Bases: SimplifiedPinAccess

Represents an electronic component that references a KiCad symbol.

If multiple pins share the same name (e.g. β€œGND”), __getitem__ returns a PinGroup, so something like:

comp[β€œGND”] += net

will connect all GND pins to that net.

Additional fields can be passed as keyword arguments and will be stored in _extra_fields for later access (e.g., mfg_part_num, tolerance, etc.).

ALLOWED_REASSIGN = {'value'}
__call__(*args, **kwargs)[source]

Cloning a prefix-based ref is allowed, but final references are not.

__init__(symbol: str, ref: str | None = None, value: str | None = None, footprint: str | None = None, datasheet: str | None = None, description: str | None = None, **kwargs)[source]

Initialize Component with support for arbitrary additional fields.

Parameters:
  • symbol – KiCad symbol reference (e.g., β€œDevice:R”)

  • ref – Component reference (e.g., β€œR1”)

  • value – Component value (e.g., β€œ10k”)

  • footprint – KiCad footprint reference

  • datasheet – URL to component datasheet

  • description – Component description

  • **kwargs – Additional fields (e.g., mfg_part_num, tolerance, etc.)

datasheet: str | None = None
description: str | None = None
footprint: str | None = None
classmethod from_dict(data: Dict[str, Any]) Component[source]
print_pin_names() str[source]

Returns a formatted string of all pins, showing index, name, and number. Useful for debugging symbol pin naming mismatches.

ref: str | None = None
to_dict() Dict[str, Any][source]
value: str | None = None
symbol: str
class circuit_synth.core.Net(name: str | None = None, is_power: bool | None = None, power_symbol: str | None = None, trace_current: float | None = None, impedance: float | None = None, **properties: Any)[source]

Bases: object

A Net represents an electrical node (set of pins).

Supports: - Automatic power net detection (GND, VCC, etc.) - Explicit power net declaration - Physical constraints (trace current, impedance) - Custom properties - Differential pairs (via KiCad naming conventions)

Examples

# Auto-detected power net (recommended) >>> gnd = Net(name=”GND”) # Automatically becomes power net

# Explicit power net >>> vcc = Net(name=”VCC”, is_power=True, power_symbol=”power:VCC”)

# Prevent auto-detection >>> not_power = Net(name=”GND_SENSE”, is_power=False)

# Differential pair (KiCad detects automatically by name) >>> usb_dp = Net(name=”USB_DP”, impedance=90) # KiCad pairs USB_DP/USB_DN >>> usb_dn = Net(name=”USB_DN”, impedance=90)

# High current trace >>> power_5v = Net(name=”+5V”, trace_current=2000) # 2A, auto-detected as power

# Custom properties >>> rf = Net(name=”RF_OUT”, impedance=50, substrate_height=1.6)

Note

Differential pairs use KiCad naming conventions (_P/_N, +/-, etc.). KiCad automatically detects and routes them as differential pairs.

__iadd__(other)[source]

net += pin => pin connects to this net net += net => unify (if different) by bringing other net’s pins over

__init__(name: str | None = None, is_power: bool | None = None, power_symbol: str | None = None, trace_current: float | None = None, impedance: float | None = None, **properties: Any)[source]

Create an electrical net.

Parameters:
  • name – Net name (e.g., β€œGND”, β€œUSB_DP”). Auto-generated if None. For differential pairs, use KiCad naming conventions: - NAME_P / NAME_N (e.g., USB_DP / USB_DN) - NAME+ / NAME- (e.g., ETH_TX+ / ETH_TX-)

  • is_power – Power net flag. None (default) = auto-detect from name, True = explicitly mark as power net, False = explicitly NOT a power net.

  • power_symbol – KiCad power symbol (e.g., β€œpower:GND”). Auto-filled if is_power is auto-detected.

  • trace_current – Maximum current in milliamps (mA).

  • impedance – Target impedance in ohms for controlled impedance routing. For differential pairs, this is the differential impedance.

  • **properties – Custom properties for specialized applications.

Note

Differential pairs are detected automatically by KiCad based on net naming. Use matching prefixes with _P/_N, +/-, or similar suffixes.

static from_dict(data: Dict[str, Any]) Net[source]

Deserialize Net from dictionary.

Note: This creates a Net outside of circuit context, so it won’t auto-register. Caller must manually add to circuit._nets.

property pins
to_dict() Dict[str, Any][source]

Serialize Net to dictionary for JSON encoding.

class circuit_synth.core.Pin(name: str, num: str, func: str | PinType, unit: int = 1, **kwargs)[source]

Bases: object

Minimal Pin class for net connectivity.

  • No dedicated geometry fields in the constructor (x, y, length, orientation).

  • We do accept **kwargs so geometry can be passed (and ignored by core), but stored for schematic export.

__iadd__(other)[source]

Support pin += net, or pin += pin.

Parameters:

other – Either a Net or another Pin to connect to

Returns:

Self, for chaining

Return type:

Pin

Raises:
  • TypeError – If other is not a Net or Pin

  • ValueError – If pin types are incompatible

__init__(name: str, num: str, func: str | PinType, unit: int = 1, **kwargs)[source]
__json__() dict[source]

Alternative JSON serialization method.

connect_to_net(net: Net)[source]

If already on another net, remove from old net. Then join the new net.

Parameters:

net – The Net to connect this pin to

Raises:

ValueError – If connecting would create an invalid pin type combination

property connected: bool

Return True if this pin is connected to a net.

property length: float
property orientation: float
to_dict() dict[source]

Convert pin to dictionary for JSON serialization.

property x: float

Geometry helper, returns 0 if not set.

property y: float
circuit_synth.core.circuit(_func=None, *, name=None, comments=True)[source]
Decorator that can be used in three ways:
  1. @circuit def my_circuit(…):

    …

  2. @circuit(name=”someCircuitName”) def my_circuit(…):

    …

  3. @circuit(name=”someCircuitName”, comments=False) def my_circuit(…):

    …

Creates a new Circuit object, optionally using name as the circuit name. If comments=True (default), the function’s docstring will be added as a text annotation on the generated schematic. If there’s an existing current circuit (the β€œparent”), the new circuit is attached to the parent as a subcircuit. Then references are finalized before returning the child circuit.

exception circuit_synth.core.ComponentError[source]

Bases: CircuitSynthError

Raised when there is an error with a component or its pins.

exception circuit_synth.core.ValidationError[source]

Bases: CircuitSynthError

Raised when a property validation fails.

exception circuit_synth.core.CircuitSynthError[source]

Bases: Exception

Base exception for all Circuit Synth errors.

class circuit_synth.core.DependencyContainer[source]

Bases: IDependencyContainer

Concrete implementation of dependency injection container.

Provides registration and resolution of dependencies with support for different lifetime scopes and automatic dependency injection.

__init__()[source]
clear() None[source]

Clear all registrations and singletons

get_registrations() Dict[Type, DependencyRegistration][source]

Get all registrations (for debugging)

is_registered(interface: Type[T]) bool[source]

Check if an interface is registered

register(registration: DependencyRegistration) None[source]

Register a dependency

register_factory(interface: Type[T], factory: Callable[[], T]) None[source]

Register a factory function

register_instance(interface: Type[T], instance: T) None[source]

Register a specific instance

register_singleton(interface: Type[T], implementation: Type[T]) None[source]

Register a singleton dependency

register_transient(interface: Type[T], implementation: Type[T]) None[source]

Register a transient dependency

resolve(interface: Type[T]) T[source]

Resolve a dependency

class circuit_synth.core.ServiceLocator[source]

Bases: object

Service locator pattern implementation for global access to the container.

Provides a global point of access to the dependency container while maintaining testability through container replacement.

classmethod get_container() IDependencyContainer[source]

Get the global container

classmethod is_registered(interface: Type[T]) bool[source]

Check if an interface is registered in the global container

classmethod resolve(interface: Type[T]) T[source]

Resolve a dependency from the global container

classmethod set_container(container: IDependencyContainer) None[source]

Set the global container

class circuit_synth.core.IDependencyContainer[source]

Bases: ABC

Abstract interface for dependency container

abstractmethod is_registered(interface: Type[T]) bool[source]

Check if an interface is registered

abstractmethod register(registration: DependencyRegistration) None[source]

Register a dependency

abstractmethod register_factory(interface: Type[T], factory: Callable[[], T]) None[source]

Register a factory function

abstractmethod register_instance(interface: Type[T], instance: T) None[source]

Register a specific instance

abstractmethod register_singleton(interface: Type[T], implementation: Type[T]) None[source]

Register a singleton dependency

abstractmethod register_transient(interface: Type[T], implementation: Type[T]) None[source]

Register a transient dependency

abstractmethod resolve(interface: Type[T]) T[source]

Resolve a dependency

circuit_synth.core.replace_components(circuit: Circuit, match: Dict[str, str], update: Dict[str, str], dry_run: bool = False) ReplacementResult[source]

Replace property values for components matching criteria.

Finds all components with properties matching the match criteria and updates their properties with values from update.

Parameters:
  • circuit – Circuit to modify

  • match – Property criteria to match (e.g., {β€œMPN”: β€œASD123”})

  • update – Properties to update (e.g., {β€œMPN”: β€œFGS032”, β€œManufacturer”: β€œNewCo”})

  • dry_run – If True, don’t actually modify, just return what would change

Returns:

ReplacementResult with count and list of affected components

Example

>>> circuit = cs.Circuit("Test")
>>> r1 = cs.Component("Device:R", ref="R", value="10k")
>>> r1.MPN = "ASD123"
>>> circuit.add_component(r1)
>>> circuit.finalize_references()
>>>
>>> # Replace MPN
>>> result = cs.replace_components(
...     circuit,
...     match={"MPN": "ASD123"},
...     update={"MPN": "FGS032"}
... )
>>> print(result.count)  # 1
>>> print(r1.MPN)  # "FGS032"
circuit_synth.core.replace_multiple(circuit: Circuit, replacements: List[Dict[str, any]], dry_run: bool = False) ReplacementResult[source]

Apply multiple replacement operations in sequence.

Each replacement in the list should have β€˜match’ and β€˜update’ dicts.

Parameters:
  • circuit – Circuit to modify

  • replacements – List of replacement specs, each with β€˜match’ and β€˜update’

  • dry_run – If True, don’t actually modify

Returns:

Combined ReplacementResult from all operations

Example

>>> replacements = [
...     {
...         "match": {"MPN": "ASD123"},
...         "update": {"MPN": "FGS032", "Manufacturer": "NewCo"}
...     },
...     {
...         "match": {"MPN": "OLD456"},
...         "update": {"MPN": "NEW789"}
...     }
... ]
>>> result = cs.replace_multiple(circuit, replacements)
>>> print(f"Total replaced: {result.count}")
circuit_synth.core.find_replaceable_components(circuit: Circuit, match: Dict[str, str]) List[str][source]

Find components that match criteria without modifying them.

Useful for previewing what would be affected by a replacement operation.

Parameters:
  • circuit – Circuit to search

  • match – Property criteria to match

Returns:

List of component references that match criteria

Example

>>> # Find all components with specific MPN
>>> matches = cs.find_replaceable_components(
...     circuit,
...     match={"MPN": "ASD123"}
... )
>>> print(f"Would affect: {matches}")
class circuit_synth.core.ReplacementResult(count: int, affected_components: List[str], warnings: List[str], errors: List[str])[source]

Bases: object

Results from bulk component replacement operation.

count

Number of components modified

Type:

int

affected_components

List of component references that were modified

Type:

List[str]

warnings

List of warning messages

Type:

List[str]

errors

List of error messages

Type:

List[str]

__init__(count: int, affected_components: List[str], warnings: List[str], errors: List[str]) None
__str__() str[source]

Format result as string.

count: int
affected_components: List[str]
warnings: List[str]
errors: List[str]

Circuit

class circuit_synth.core.circuit.Circuit(name=None, description=None, auto_comments=True)[source]

Bases: object

class Component(symbol: str, ref: str | None = None, value: str | None = None, footprint: str | None = None, datasheet: str | None = None, description: str | None = None, **kwargs)

Bases: SimplifiedPinAccess

Represents an electronic component that references a KiCad symbol.

If multiple pins share the same name (e.g. β€œGND”), __getitem__ returns a PinGroup, so something like:

comp[β€œGND”] += net

will connect all GND pins to that net.

Additional fields can be passed as keyword arguments and will be stored in _extra_fields for later access (e.g., mfg_part_num, tolerance, etc.).

ALLOWED_REASSIGN = {'value'}
__call__(*args, **kwargs)

Cloning a prefix-based ref is allowed, but final references are not.

__init__(symbol: str, ref: str | None = None, value: str | None = None, footprint: str | None = None, datasheet: str | None = None, description: str | None = None, **kwargs)

Initialize Component with support for arbitrary additional fields.

Parameters:
  • symbol – KiCad symbol reference (e.g., β€œDevice:R”)

  • ref – Component reference (e.g., β€œR1”)

  • value – Component value (e.g., β€œ10k”)

  • footprint – KiCad footprint reference

  • datasheet – URL to component datasheet

  • description – Component description

  • **kwargs – Additional fields (e.g., mfg_part_num, tolerance, etc.)

datasheet: str | None = None
description: str | None = None
footprint: str | None = None
classmethod from_dict(data: Dict[str, Any]) Component
print_pin_names() str

Returns a formatted string of all pins, showing index, name, and number. Useful for debugging symbol pin naming mismatches.

ref: str | None = None
to_dict() Dict[str, Any]
value: str | None = None
symbol: str
__init__(name=None, description=None, auto_comments=True)[source]
validate_reference(ref: str) bool[source]

Check if reference is available in this circuit’s scope

register_reference(ref: str) None[source]

Register a new reference in this circuit’s scope

add_subcircuit(subcirc: Circuit)[source]

Add a subcircuit and establish parent-child relationship

property components

Get components dictionary for compatibility.

property subcircuits

Get subcircuits list.

add_subcircuit_old(subcirc: Circuit)[source]
add_component(comp: Component)[source]

Register a Component with this Circuit.

finalize_references()[source]

Auto-assign references for any components that only had a prefix.

generate_text_netlist() str[source]

Generate a textual netlist for display or debugging.

generate_full_netlist() str[source]
Print a hierarchical netlist showing:
  1. Each circuit + subcircuit (name, components, and optional description)

  2. A single combined net listing from all circuits

add_net(net: Net)[source]
add_annotation(annotation)[source]

Add a text annotation (TextProperty, TextBox, etc.) to this circuit.

add_image(image_path: str, position: tuple, scale: float = 1.0)[source]

Add an embedded image to the schematic.

Images are embedded as base64-encoded data in the KiCad schematic file. The image file is read when the schematic is generated, so it only needs to exist at generation time.

Parameters:
  • image_path – Path to image file (PNG, JPG, etc.)

  • position – Tuple of (x, y) position in mm

  • scale – Scale factor (1.0 = original size, 2.0 = 2x larger, etc.)

Returns:

The created Image annotation object

Return type:

Image

Example

>>> circuit.add_image("logo.png", position=(100, 50), scale=2.0)
to_dict() Dict[str, Any][source]

Return a hierarchical dictionary representation of this circuit, including subcircuits, components (as a dictionary keyed by reference), and net connections.

generate_json_netlist(filename: str) None[source]

Generate a JSON representation of this circuit and its hierarchy, then write it out to β€˜filename’.

to_flattened_list(parent_name: str = None, flattened: List[Dict[str, Any]] = None) List[Dict[str, Any]][source]

Build or update a shared flattened list of circuit data dicts.

generate_flattened_json_netlist(filename: str) None[source]

Produce a flattened JSON representation for this circuit + subcircuits.

generate_kicad_netlist(filename: str) None[source]

Generate a KiCad netlist (.net) file for this circuit and its hierarchy.

generate_kicad_project(project_name: str, generate_pcb: bool = True, force_regenerate: bool = False, placement_algorithm: str = 'hierarchical', draw_bounding_boxes: bool = False, generate_ratsnest: bool = True, update_source_refs: bool | None = None, preserve_user_components: bool = False) Dict[str, Any][source]

Generate a complete KiCad project (schematic + PCB) from this circuit.

This method provides a simplified API that handles all the boilerplate setup required for KiCad project generation, creating the project directory and generating KiCad files with an automatic JSON netlist in the project directory.

Parameters:
  • project_name – Name of the KiCad project and directory to create

  • generate_pcb – Whether to generate PCB in addition to schematic (default: True)

  • force_regenerate – Force regeneration of existing files, losing manual edits (default: False)

  • placement_algorithm – Component placement algorithm to use (default: β€œhierarchical”)

  • draw_bounding_boxes – Whether to draw visual bounding boxes around components (default: False)

  • generate_ratsnest – Whether to generate ratsnest connections in PCB (default: True)

  • update_source_refs – Whether to update source file with finalized refs. None (default): Auto-update unless force_regenerate=True True: Always update source file False: Never update source file

  • preserve_user_components – Keep components in KiCad that don’t exist in Python (default: False) False: Python is source of truth - delete components not in Python True: Preserve all components in KiCad, even if not in Python

Returns:

Result dictionary containing:
  • success (bool): Whether generation succeeded

  • json_path (Path): Path to the canonical JSON netlist

  • project_path (Path): Path to the KiCad project directory

  • error (str, optional): Error message if generation failed

Return type:

dict

Example

>>> circuit = esp32s3_simple()
>>> result = circuit.generate_kicad_project("esp32s3_simple")
>>> print(f"JSON netlist: {result['json_path']}")
>>> print(f"KiCad project: {result['project_path']}")
generate_bom(output_file: str | None = None, project_name: str | None = None, fields: str | None = None, labels: str | None = None, group_by: str | None = None, exclude_dnp: bool = False) Dict[str, Any][source]

Generate a Bill of Materials (BOM) from this circuit as a CSV file.

This method creates a KiCad project if one doesn’t already exist, then exports the schematic to a CSV BOM file using kicad-cli.

Parameters:
  • output_file – Path where CSV BOM should be written. If not provided, defaults to {project_name}/{project_name}.csv

  • project_name – Name of the KiCad project directory. If not provided, defaults to the circuit name. Required if output_file is provided but needs a project.

  • fields – Comma-separated fields to export from schematic. If not specified, KiCad will export default fields (Refs, Value, Footprint, etc.)

  • labels – Comma-separated column headers for the BOM. Must match the number of fields if fields are specified.

  • group_by – Field to group references by when exporting. Common values: β€œValue” (group by component value), β€œFootprint”, etc.

  • exclude_dnp – If True, exclude β€œDo not populate” components from BOM

Returns:

Result dictionary containing:
  • success (bool): True if BOM was successfully generated

  • file (Path): Path to the generated CSV file

  • component_count (int): Number of components in the BOM

  • project_path (Path): Path to the KiCad project directory

  • error (str, optional): Error message if generation failed

Return type:

dict

Example

>>> circuit = led_blinker()
>>> result = circuit.generate_bom(project_name="led_blinker")
>>> print(f"BOM exported to: {result['file']}")
>>> print(f"Component count: {result['component_count']}")
Raises:
generate_pdf_schematic(output_file: str | None = None, project_name: str | None = None, black_and_white: bool = False, theme: str | None = None, exclude_drawing_sheet: bool = False, pages: str | None = None) Dict[str, Any][source]

Generate a PDF schematic from this circuit.

This method creates a KiCad project if one doesn’t already exist, then exports the schematic to a PDF file using kicad-cli.

Parameters:
  • output_file – Path where PDF should be written. If not provided, defaults to {project_name}/{project_name}.pdf

  • project_name – Name of the KiCad project directory. If not provided, defaults to the circuit name.

  • black_and_white – Export in black and white instead of color (default: False)

  • theme – Color theme to use for export (optional). Theme name depends on KiCad installation.

  • exclude_drawing_sheet – Exclude the drawing sheet/border from PDF (default: False)

  • pages – Page range to export (e.g., β€œ1,3-5” for pages 1, 3, 4, 5). If not specified, all pages are exported.

Returns:

Result dictionary containing:
  • success (bool): True if PDF was successfully generated

  • file (Path): Path to the generated PDF file

  • project_path (Path): Path to the KiCad project directory

  • error (str, optional): Error message if generation failed

Return type:

dict

Example

>>> circuit = led_blinker()
>>> result = circuit.generate_pdf_schematic(project_name="led_blinker")
>>> print(f"PDF exported to: {result['file']}")
Raises:
generate_gerbers(output_dir: str | None = None, project_name: str | None = None, include_drill: bool = True, drill_format: str = 'excellon') Dict[str, Any][source]

Generate Gerber files for PCB manufacturing from this circuit.

This method creates a complete KiCad project (including PCB) if one doesn’t already exist, then exports Gerber files for manufacturing using kicad-cli. Gerbers can be submitted directly to manufacturers like JLCPCB, PCBWay, etc.

Parameters:
  • output_dir – Directory where Gerber files should be written. If not provided, defaults to {project_name}/gerbers

  • project_name – Name of the KiCad project directory. If not provided, defaults to the circuit name.

  • include_drill – Also export drill files along with Gerbers (default: True)

  • drill_format – Format for drill files: β€œexcellon” (default) or β€œgerber”

Returns:

Result dictionary containing:
  • success (bool): True if Gerbers were successfully generated

  • gerber_files (list): List of Path objects to generated .gbr files

  • drill_files (tuple): Tuple of (plated_holes_file, non_plated_holes_file) or None

  • project_path (Path): Path to the KiCad project directory

  • output_dir (Path): Directory where Gerbers were exported

  • error (str, optional): Error message if generation failed

Return type:

dict

Example

>>> circuit = esp32_board()
>>> result = circuit.generate_gerbers(project_name="esp32_board")
>>> print(f"Gerbers exported to: {result['output_dir']}")
>>> print(f"Files: {len(result['gerber_files'])} Gerber files")
Requirements:
  • KiCad 8.0 or later

  • kicad-cli must be available in PATH

  • Circuit must be complete with all components and connections

Notes

  • First run generates full KiCad project including PCB (slower)

  • Subsequent runs reuse existing project (faster)

  • Default layers: F.Cu, B.Cu, F.Mask, B.Mask, F.SilkS, B.SilkS, F.Paste, B.Paste, Edge.Cuts

  • Gerbers use standard Protel file extension format (.gbr, .gbl, etc.)

  • Compatible with JLCPCB, PCBWay, OSH Park, and most PCB manufacturers

simulate()[source]

Create a simulator instance for this circuit.

This method provides access to circuit simulation capabilities using PySpice as the backend. The returned simulator object can be used to run various analyses such as DC operating point, transient, and AC.

Returns:

Simulator object for running analyses

Return type:

CircuitSimulator

Example

>>> circuit = voltage_divider()
>>> sim = circuit.simulate()
>>> result = sim.operating_point()
Raises:
simulator()[source]

Alias for simulate() method for backward compatibility.

Returns:

Simulator object for running analyses

Return type:

CircuitSimulator

property nets

Return a dictionary of all nets in this circuit.

Returns:

Dictionary of nets keyed by net name

Return type:

Dict[str, Net]

Component

class circuit_synth.core.component.Component(symbol: str, ref: str | None = None, value: str | None = None, footprint: str | None = None, datasheet: str | None = None, description: str | None = None, **kwargs)[source]

Bases: SimplifiedPinAccess

Represents an electronic component that references a KiCad symbol.

If multiple pins share the same name (e.g. β€œGND”), __getitem__ returns a PinGroup, so something like:

comp[β€œGND”] += net

will connect all GND pins to that net.

Additional fields can be passed as keyword arguments and will be stored in _extra_fields for later access (e.g., mfg_part_num, tolerance, etc.).

ALLOWED_REASSIGN = {'value'}
__init__(symbol: str, ref: str | None = None, value: str | None = None, footprint: str | None = None, datasheet: str | None = None, description: str | None = None, **kwargs)[source]

Initialize Component with support for arbitrary additional fields.

Parameters:
  • symbol – KiCad symbol reference (e.g., β€œDevice:R”)

  • ref – Component reference (e.g., β€œR1”)

  • value – Component value (e.g., β€œ10k”)

  • footprint – KiCad footprint reference

  • datasheet – URL to component datasheet

  • description – Component description

  • **kwargs – Additional fields (e.g., mfg_part_num, tolerance, etc.)

symbol: str
ref: str | None = None
value: str | None = None
footprint: str | None = None
datasheet: str | None = None
description: str | None = None
__call__(*args, **kwargs)[source]

Cloning a prefix-based ref is allowed, but final references are not.

to_dict() Dict[str, Any][source]
classmethod from_dict(data: Dict[str, Any]) Component[source]
print_pin_names() str[source]

Returns a formatted string of all pins, showing index, name, and number. Useful for debugging symbol pin naming mismatches.

Net

class circuit_synth.core.net.Net(name: str | None = None, is_power: bool | None = None, power_symbol: str | None = None, trace_current: float | None = None, impedance: float | None = None, **properties: Any)[source]

Bases: object

A Net represents an electrical node (set of pins).

Supports: - Automatic power net detection (GND, VCC, etc.) - Explicit power net declaration - Physical constraints (trace current, impedance) - Custom properties - Differential pairs (via KiCad naming conventions)

Examples

# Auto-detected power net (recommended) >>> gnd = Net(name=”GND”) # Automatically becomes power net

# Explicit power net >>> vcc = Net(name=”VCC”, is_power=True, power_symbol=”power:VCC”)

# Prevent auto-detection >>> not_power = Net(name=”GND_SENSE”, is_power=False)

# Differential pair (KiCad detects automatically by name) >>> usb_dp = Net(name=”USB_DP”, impedance=90) # KiCad pairs USB_DP/USB_DN >>> usb_dn = Net(name=”USB_DN”, impedance=90)

# High current trace >>> power_5v = Net(name=”+5V”, trace_current=2000) # 2A, auto-detected as power

# Custom properties >>> rf = Net(name=”RF_OUT”, impedance=50, substrate_height=1.6)

Note

Differential pairs use KiCad naming conventions (_P/_N, +/-, etc.). KiCad automatically detects and routes them as differential pairs.

__init__(name: str | None = None, is_power: bool | None = None, power_symbol: str | None = None, trace_current: float | None = None, impedance: float | None = None, **properties: Any)[source]

Create an electrical net.

Parameters:
  • name – Net name (e.g., β€œGND”, β€œUSB_DP”). Auto-generated if None. For differential pairs, use KiCad naming conventions: - NAME_P / NAME_N (e.g., USB_DP / USB_DN) - NAME+ / NAME- (e.g., ETH_TX+ / ETH_TX-)

  • is_power – Power net flag. None (default) = auto-detect from name, True = explicitly mark as power net, False = explicitly NOT a power net.

  • power_symbol – KiCad power symbol (e.g., β€œpower:GND”). Auto-filled if is_power is auto-detected.

  • trace_current – Maximum current in milliamps (mA).

  • impedance – Target impedance in ohms for controlled impedance routing. For differential pairs, this is the differential impedance.

  • **properties – Custom properties for specialized applications.

Note

Differential pairs are detected automatically by KiCad based on net naming. Use matching prefixes with _P/_N, +/-, or similar suffixes.

property pins
__iadd__(other)[source]

net += pin => pin connects to this net net += net => unify (if different) by bringing other net’s pins over

to_dict() Dict[str, Any][source]

Serialize Net to dictionary for JSON encoding.

static from_dict(data: Dict[str, Any]) Net[source]

Deserialize Net from dictionary.

Note: This creates a Net outside of circuit context, so it won’t auto-register. Caller must manually add to circuit._nets.

Pin

class circuit_synth.core.pin.PinType(*values)[source]

Bases: Enum

Enumeration of valid pin types.

INPUT = 'input'
OUTPUT = 'output'
BIDIRECTIONAL = 'bidirectional'
POWER_IN = 'power_in'
POWER_OUT = 'power_out'
PASSIVE = 'passive'
TRI_STATE = 'tri_state'
NO_CONNECT = 'no_connect'
UNSPECIFIED = 'unspecified'
OPEN_COLLECTOR = 'open_collector'
OPEN_EMITTER = 'open_emitter'
__eq__(other) bool[source]

Override equality comparison to ensure type safety.

Parameters:

other – The value to compare with

Returns:

True if equal, False if not equal and same type

Return type:

bool

Raises:

TypeError – If comparing with non-PinType value

__str__() str[source]

Return the string value of the pin type.

classmethod from_string(value: str) PinType[source]

Create a PinType from a string value.

Parameters:

value – String representation of the pin type

Returns:

The corresponding PinType enum value

Return type:

PinType

Raises:

ValueError – If the string doesn’t match a valid pin type

can_connect_to(other: PinType) bool[source]

Check if this pin type can connect to another pin type.

Parameters:

other – The PinType to check compatibility with

Returns:

True if the pins can be connected, False otherwise

Return type:

bool

Note

All pin types can connect to each other except NO_CONNECT pins. Design Rule Checks (DRC) should be used to flag potentially incorrect connections.

class circuit_synth.core.pin.Pin(name: str, num: str, func: str | PinType, unit: int = 1, **kwargs)[source]

Bases: object

Minimal Pin class for net connectivity.

  • No dedicated geometry fields in the constructor (x, y, length, orientation).

  • We do accept **kwargs so geometry can be passed (and ignored by core), but stored for schematic export.

__init__(name: str, num: str, func: str | PinType, unit: int = 1, **kwargs)[source]
property x: float

Geometry helper, returns 0 if not set.

property y: float
property length: float
property orientation: float
property connected: bool

Return True if this pin is connected to a net.

connect_to_net(net: Net)[source]

If already on another net, remove from old net. Then join the new net.

Parameters:

net – The Net to connect this pin to

Raises:

ValueError – If connecting would create an invalid pin type combination

__iadd__(other)[source]

Support pin += net, or pin += pin.

Parameters:

other – Either a Net or another Pin to connect to

Returns:

Self, for chaining

Return type:

Pin

Raises:
  • TypeError – If other is not a Net or Pin

  • ValueError – If pin types are incompatible

to_dict() dict[source]

Convert pin to dictionary for JSON serialization.

__json__() dict[source]

Alternative JSON serialization method.

KiCad Integration

KiCad integration package.

class circuit_synth.kicad.BOMExporter[source]

Bases: object

Export Bill of Materials from KiCad schematic files using kicad-cli.

static export_csv(schematic_file: Path, output_file: Path, fields: str | None = None, labels: str | None = None, group_by: str | None = None, exclude_dnp: bool = False) Dict[str, Any][source]

Export BOM from a KiCad schematic to CSV format using kicad-cli.

Parameters:
  • schematic_file – Path to .kicad_sch schematic file

  • output_file – Path where CSV BOM should be written

  • fields – Comma-separated fields to export. Default: all fields from schematic

  • labels – Comma-separated column headers. Must match number of fields.

  • group_by – Field to group references by (e.g., β€œValue” to group by component value)

  • exclude_dnp – Whether to exclude β€œDo not populate” components

Returns:

Result dictionary with keys:
  • success: bool - True if BOM was successfully exported

  • file: Path - Path to generated BOM file

  • component_count: int - Number of components in BOM

  • error: str (optional) - Error message if export failed

Return type:

dict

Raises:
class circuit_synth.kicad.BOMPropertyManager[source]

Bases: object

Manage BOM properties for KiCad schematics.

High-level interface for auditing, updating, and transforming component properties across schematics. Useful for BOM cleanup and standardization.

Example

>>> manager = BOMPropertyManager()
>>>
>>> # Audit for missing properties
>>> issues = manager.audit_directory(
...     Path("~/my_designs"),
...     required_properties=["PartNumber", "Manufacturer"]
... )
>>>
>>> # Generate report
>>> manager.generate_report(issues, Path("audit.csv"))
>>>
>>> # Bulk update properties
>>> manager.update_properties(
...     Path("~/my_designs"),
...     match={"value": "10k", "lib_id": "Device:R"},
...     set_properties={"PartNumber": "RC0805FR-0710KL"}
... )
__init__()[source]

Initialize BOM property manager.

audit_directory(directory: Path, required_properties: List[str], recursive: bool = True, exclude_dnp: bool = False) List[ComponentIssue][source]

Audit directory for components missing required properties.

Parameters:
  • directory – Path to directory containing .kicad_sch files

  • required_properties – List of property names that must be present

  • recursive – Scan subdirectories (default: True)

  • exclude_dnp – Skip Do-Not-Populate components (default: False)

Returns:

List of ComponentIssue objects describing missing properties

Example

>>> manager = BOMPropertyManager()
>>> issues = manager.audit_directory(
...     Path("~/designs"),
...     required_properties=["PartNumber", "Manufacturer"],
...     exclude_dnp=True
... )
>>> print(f"Found {len(issues)} components with missing properties")
audit_schematic(schematic_path: Path, required_properties: List[str], exclude_dnp: bool = False) List[ComponentIssue][source]

Audit single schematic for missing properties.

Parameters:
  • schematic_path – Path to .kicad_sch file

  • required_properties – List of property names that must be present

  • exclude_dnp – Skip Do-Not-Populate components (default: False)

Returns:

List of ComponentIssue objects

Example

>>> manager = BOMPropertyManager()
>>> issues = manager.audit_schematic(
...     Path("circuit.kicad_sch"),
...     required_properties=["PartNumber"]
... )
generate_report(issues: List[ComponentIssue], output_path: Path) None[source]

Generate CSV report from audit results.

Parameters:
  • issues – List of ComponentIssue objects from audit

  • output_path – Path where CSV report should be saved

Example

>>> manager = BOMPropertyManager()
>>> issues = manager.audit_directory(Path("~/designs"), ["PartNumber"])
>>> manager.generate_report(issues, Path("audit_report.csv"))
static parse_match_criteria(criteria_str: str) Dict[str, str][source]

Parse criteria string into dict for matching.

Parameters:

criteria_str – Comma-separated criteria (e.g., β€œvalue=10k,lib_id=Device:R”)

Returns:

Dict of field=pattern criteria

Example

>>> criteria = BOMPropertyManager.parse_match_criteria("value=10k,footprint=*0805*")
>>> # Returns: {"value": "10k", "footprint": "*0805*"}
static parse_properties(props_str: str) Dict[str, str][source]

Parse property string into dict for setting.

Parameters:

props_str – Comma-separated properties (e.g., β€œPartNumber=XXX,Manufacturer=YYY”)

Returns:

Dict of property=value pairs

Example

>>> props = BOMPropertyManager.parse_properties("PartNumber=XXX,Manufacturer=YYY")
>>> # Returns: {"PartNumber": "XXX", "Manufacturer": "YYY"}
transform_properties(directory: Path, transformations: List[Tuple[str, str]], only_if_empty: bool = False, dry_run: bool = False, recursive: bool = True, exclude_dnp: bool = False) int[source]

Copy or rename properties across components.

Parameters:
  • directory – Path to directory containing .kicad_sch files

  • transformations – List of (source_property, dest_property) tuples

  • only_if_empty – Only copy to empty destination properties (default: False)

  • dry_run – Preview only, don’t modify files (default: False)

  • recursive – Scan subdirectories (default: True)

  • exclude_dnp – Skip Do-Not-Populate components (default: False)

Returns:

Number of components transformed

Example

>>> manager = BOMPropertyManager()
>>>
>>> # Copy MPN to PartNumber (only where PartNumber is empty)
>>> count = manager.transform_properties(
...     Path("~/designs"),
...     transformations=[("MPN", "PartNumber")],
...     only_if_empty=True
... )
>>>
>>> # Rename property (overwrite existing)
>>> count = manager.transform_properties(
...     Path("~/designs"),
...     transformations=[("OldField", "NewField")]
... )
update_properties(directory: Path, match: Dict[str, str], set_properties: Dict[str, str], dry_run: bool = False, recursive: bool = True, exclude_dnp: bool = False) int[source]

Bulk update properties on matching components.

Parameters:
  • directory – Path to directory containing .kicad_sch files

  • match – Dict of field=pattern criteria (all must match)

  • set_properties – Dict of property=value updates to apply

  • dry_run – Preview only, don’t modify files (default: False)

  • recursive – Scan subdirectories (default: True)

  • exclude_dnp – Skip Do-Not-Populate components (default: False)

Returns:

Number of components updated

Example

>>> manager = BOMPropertyManager()
>>>
>>> # Preview update
>>> count = manager.update_properties(
...     Path("~/designs"),
...     match={"value": "10k", "lib_id": "Device:R"},
...     set_properties={"PartNumber": "RC0805FR-0710KL"},
...     dry_run=True
... )
>>> print(f"Would update {count} components")
>>>
>>> # Apply update
>>> count = manager.update_properties(
...     Path("~/designs"),
...     match={"value": "10k", "lib_id": "Device:R"},
...     set_properties={
...         "PartNumber": "RC0805FR-0710KL",
...         "Manufacturer": "Yageo",
...         "Tolerance": "1%"
...     }
... )
class circuit_synth.kicad.SymbolLibCache[source]

Bases: object

Python fallback implementation of SymbolLibCache. Restored from the original working implementation.

__init__()[source]
classmethod find_symbol_library(symbol_name: str) str | None[source]

Find which library contains the given symbol name.

classmethod get_all_libraries() Dict[str, str][source]

Get a dictionary of all available libraries.

classmethod get_all_symbols() Dict[str, str][source]

Get a dictionary of all available symbols.

classmethod get_symbol_data(symbol_id: str) Dict[str, Any][source]

Get symbol data by symbol ID (LibraryName:SymbolName).

classmethod get_symbol_data_by_name(symbol_name: str) Dict[str, Any][source]

Get symbol data by name only (searches all libraries).

Unified Integration

PCB Generation

KiCad PCB API - PCB generation features have been removed.

PCB generation functionality is no longer included in the open source version. Contact Circuit Synth for licensing information if you need PCB features.

class circuit_synth.pcb.PCBBoard(*args, **kwargs)[source]

Bases: object

__init__(*args, **kwargs)[source]
class circuit_synth.pcb.PCBParser(*args, **kwargs)[source]

Bases: object

__init__(*args, **kwargs)[source]
exception circuit_synth.pcb.PCBNotAvailableError[source]

Bases: ImportError

Raised when PCB features are accessed but not available.

class circuit_synth.pcb.KiCadCLI(kicad_cli_path: str | None = None)[source]

Bases: object

Generic interface to run KiCad CLI commands.

Provides both low-level command execution and high-level convenience methods for common operations like DRC, export, etc.

__init__(kicad_cli_path: str | None = None)[source]

Initialize KiCad CLI interface.

Parameters:

kicad_cli_path – Optional explicit path to kicad-cli executable. If not provided, will attempt auto-detection.

export_drill(pcb_file: str | Path, output_dir: str | Path, format: str = 'excellon', units: str = 'mm', mirror_y: bool = False, minimal_header: bool = False) Tuple[Path | None, Path | None][source]

Export drill files from a PCB.

Parameters:
  • pcb_file – Path to the PCB file

  • output_dir – Directory to save drill files

  • format – Drill file format (excellon, gerber)

  • units – Units for coordinates (mm, in)

  • mirror_y – Mirror Y coordinates

  • minimal_header – Use minimal header

Returns:

Tuple of (plated_holes_file, non_plated_holes_file)

export_gerbers(pcb_file: str | Path, output_dir: str | Path, layers: List[str] | None = None, protel_extensions: bool = False) List[Path][source]

Export Gerber files from a PCB.

Parameters:
  • pcb_file – Path to the PCB file

  • output_dir – Directory to save Gerber files

  • layers – Optional list of layer names to export. If None, exports all copper and technical layers

  • protel_extensions – Use Protel filename extensions

Returns:

List of generated Gerber file paths

export_pos(pcb_file: str | Path, output_file: str | Path, side: str = 'both', format: str = 'csv', units: str = 'mm', use_drill_origin: bool = False, smd_only: bool = False) Path[source]

Export pick and place (position) file from a PCB.

Parameters:
  • pcb_file – Path to the PCB file

  • output_file – Output file path

  • side – Which side to export (front, back, both)

  • format – Output format (csv, ascii, gerber)

  • units – Units for coordinates (mm, in)

  • use_drill_origin – Use drill/place origin instead of page origin

  • smd_only – Only include SMD components

Returns:

Path to generated position file

export_svg(pcb_file: str | Path, output_file: str | Path, layers: List[str] | None = None, theme: str | None = None, black_and_white: bool = False, page_size: str | None = None) Path[source]

Export PCB as SVG image.

Parameters:
  • pcb_file – Path to the PCB file

  • output_file – Output SVG file path

  • layers – List of layers to include

  • theme – Color theme to use

  • black_and_white – Export in black and white

  • page_size – Page size (A4, A3, etc.)

Returns:

Path to generated SVG file

get_version() str[source]

Get KiCad CLI version information.

Returns:

Version string

run_command(args: List[str], cwd: str | Path | None = None, capture_output: bool = True, check: bool = True) CompletedProcess[source]

Run a kicad-cli command with the given arguments.

This is the low-level interface that all high-level methods use.

Parameters:
  • args – Command arguments (without β€˜kicad-cli’ prefix)

  • cwd – Working directory for the command

  • capture_output – Whether to capture stdout/stderr

  • check – Whether to raise exception on non-zero return code

Returns:

CompletedProcess instance with command results

Raises:

KiCadCLICommandError – If command fails and check=True

run_drc(pcb_file: str | Path, output_file: str | Path | None = None, units: str = 'mm', severity: str = 'error', format: str = 'json', custom_rules_file: str | Path | None = None) DRCResult[source]

Run Design Rule Check on a PCB file.

Parameters:
  • pcb_file – Path to the PCB file

  • output_file – Optional output file for the report. If not provided, will use pcb_file with .drc extension

  • units – Units for the report (mm, in, mils)

  • severity – Minimum severity to report (error, warning, info)

  • format – Output format (json, report)

  • custom_rules_file – Optional path to custom DRC rules file

Returns:

DRCResult object with violations, warnings, and unconnected items

Note

Custom DRC rules via command line are not directly supported in current KiCad versions. Rules are typically embedded in the PCB file or project. The custom_rules_file parameter is included for future compatibility.

circuit_synth.pcb.get_kicad_cli(kicad_cli_path: str | None = None) KiCadCLI[source]

Get a KiCad CLI instance with auto-detection.

Parameters:

kicad_cli_path – Optional explicit path to kicad-cli

Returns:

KiCadCLI instance

class circuit_synth.pcb.DRCResult(success: bool, violations: List[Dict[str, Any]], warnings: List[Dict[str, Any]], unconnected_items: List[Dict[str, Any]], output_file: Path | None = None)[source]

Bases: object

Result of a DRC (Design Rule Check) operation.

__init__(success: bool, violations: List[Dict[str, Any]], warnings: List[Dict[str, Any]], unconnected_items: List[Dict[str, Any]], output_file: Path | None = None) None
output_file: Path | None = None
property total_issues: int

Total number of issues found.

success: bool
violations: List[Dict[str, Any]]
warnings: List[Dict[str, Any]]
unconnected_items: List[Dict[str, Any]]
exception circuit_synth.pcb.KiCadCLIError[source]

Bases: Exception

Base exception for KiCad CLI errors.

Placement Algorithms

Schematic Generation

Component Placement

Component placement package for handling geometry, placement and wire routing.

Provides geometry utilities, automated placement algorithms, and wire routing functionality for KiCad schematics.

class circuit_synth.component_placement.PinLocation(x: float, y: float, side: str)[source]

Bases: object

Represents a pin’s location relative to component center.

__init__(x: float, y: float, side: str) None
x: float
y: float
side: str
class circuit_synth.component_placement.ComponentDimensions(width: float, height: float, pin_grid: float = 2.54)[source]

Bases: object

Represents a component’s dimensions.

__init__(width: float, height: float, pin_grid: float = 2.54) None
pin_grid: float = 2.54
width: float
height: float
class circuit_synth.component_placement.ComponentGeometryHandler(component_type: str)[source]

Bases: object

Base class for handling component geometry and pin locations.

__init__(component_type: str)[source]

Initialize with component type.

get_bounding_box(x: float, y: float, rotation: float) Tuple[float, float, float, float][source]

Get component bounding box at given position and rotation.

get_pin_location(pin_number: str, rotation: float = 0) Tuple[float, float] | None[source]

Get pin location relative to component center, accounting for rotation.

class circuit_synth.component_placement.ResistorGeometryHandler(component_type: str)[source]

Bases: ComponentGeometryHandler

Geometry handler for resistor components.

class circuit_synth.component_placement.PowerSymbolGeometryHandler(component_type: str)[source]

Bases: ComponentGeometryHandler

Geometry handler for power symbols.

circuit_synth.component_placement.create_geometry_handler(component_type: str, library: str) ComponentGeometryHandler[source]

Factory function to create appropriate geometry handler for component.

class circuit_synth.component_placement.PlacementNode(component: Component, x: float = 0, y: float = 0, rotation: float = 0, placed: bool = False, connected_components: Set[Component] = None, geometry_handler: ComponentGeometryHandler | None = None)[source]

Bases: object

Represents a component’s placement information.

__init__(component: Component, x: float = 0, y: float = 0, rotation: float = 0, placed: bool = False, connected_components: Set[Component] = None, geometry_handler: ComponentGeometryHandler | None = None) None
__post_init__()[source]

Initialize after creation.

connected_components: Set[Component] = None
geometry_handler: ComponentGeometryHandler | None = None
placed: bool = False
rotation: float = 0
x: float = 0
y: float = 0
component: Component
class circuit_synth.component_placement.ComponentPlacer(base_x: float = 77.47, base_y: float = 44.45)[source]

Bases: object

Handles intelligent placement of components in a schematic.

__init__(base_x: float = 77.47, base_y: float = 44.45)[source]

Initialize the component placer.

analyze_connectivity(circuit: Circuit) None[source]

Build a connectivity graph from the circuit nets.

place_components(circuit: Circuit) Dict[str, Tuple[float, float, float]][source]

Place all components in the circuit and return their positions.

class circuit_synth.component_placement.WireSegment(start: Tuple[float, float], end: Tuple[float, float], net_name: str, connected_pins: List[Tuple[str, str]])[source]

Bases: object

Represents a wire segment in the schematic.

__init__(start: Tuple[float, float], end: Tuple[float, float], net_name: str, connected_pins: List[Tuple[str, str]]) None
start: Tuple[float, float]
end: Tuple[float, float]
net_name: str
connected_pins: List[Tuple[str, str]]
class circuit_synth.component_placement.WireRouter[source]

Bases: object

Handles routing of wires between components.

__init__()[source]

Initialize the wire router.

route_net(net: Net, placement_nodes: Dict[str, PlacementNode]) List[WireSegment][source]

Route wires for a single net.

class circuit_synth.component_placement.ForceVector(x: float, y: float)[source]

Bases: object

Represents a 2D force vector.

__init__(x: float, y: float) None
magnitude() float[source]
scale(factor: float) ForceVector[source]
x: float
y: float
class circuit_synth.component_placement.ForceDirectedLayout(attractive_force: float = 0.3, repulsive_force: float = 100.0, damping: float = 0.85, min_distance: float = 15.0, max_iterations: int = 100, convergence_threshold: float = 0.1)[source]

Bases: object

Force-directed layout algorithm for component placement.

__init__(attractive_force: float = 0.3, repulsive_force: float = 100.0, damping: float = 0.85, min_distance: float = 15.0, max_iterations: int = 100, convergence_threshold: float = 0.1)[source]

Initialize the force-directed layout algorithm.

Parameters:
  • attractive_force – Strength of attractive forces between connected components

  • repulsive_force – Strength of repulsive forces between all components

  • damping – Damping factor to prevent oscillation (0-1)

  • min_distance – Minimum distance between components

  • max_iterations – Maximum number of iterations

  • convergence_threshold – Stop when max force is below this threshold

layout(circuit: Circuit, initial_positions: Dict[str, Tuple[float, float, float]] | None = None) Dict[str, Tuple[float, float, float]][source]

Apply force-directed layout to position components.

Parameters:
  • circuit – The circuit to layout

  • initial_positions – Optional dict of initial component positions (x, y, rotation)

Returns:

Dict mapping component references to (x, y, rotation) tuples

Interfaces

Exceptions

Exceptions for Circuit Synth.

exception circuit_synth.core.exception.CircuitSynthError[source]

Bases: Exception

Base exception for all Circuit Synth errors.

exception circuit_synth.core.exception.LibraryNotFound[source]

Bases: CircuitSynthError

Raised when a library file cannot be found.

exception circuit_synth.core.exception.SymbolNotFoundError[source]

Bases: CircuitSynthError

Raised when a symbol is not found in a library.

exception circuit_synth.core.exception.ParseError[source]

Bases: CircuitSynthError

Raised when there is an error parsing a file.

exception circuit_synth.core.exception.ValidationError[source]

Bases: CircuitSynthError

Raised when a property validation fails.

exception circuit_synth.core.exception.ComponentError[source]

Bases: CircuitSynthError

Raised when there is an error with a component or its pins.

exception circuit_synth.core.exception.ConnectionError[source]

Bases: CircuitSynthError

Raised when there is an error with connections.