← Tilbage til The Vault
Open Sourcelive

KiCad Symbol Tool

CLI-værktøj til at søge og fjerne komponenter fra et JLCImport KiCad-bibliotek. Fjerner atomisk symbol, footprint og 3D-modeller i én operation og advarer ved delte footprints.

#python#kicad#jlcimport

Selvinitieret, selvhostet projekt. Bygget og drevet af mig som arkitekt, produktejer og reviewer, med AI som superviseret "lærling" i udviklingsprocessen.

DEL 1: Resume

symbol_tool er en CLI til at søge i og fjerne komponenter fra et KiCad-bibliotek genereret af JLCImport-pluginet. Pluginet tilføjer gerne dele på tværs af tre adskilte lokationer — skematic-symbolfilen, footprint-mappen og 3D-model-mappen — men har ingen tilsvarende måde at fjerne én renset, så et ubrugt bibliotek vokser kun. symbol_tool parser den rå .kicad_sym S-expression-fil med en håndskrevet scanner (ingen KiCad-biblioteksafhængighed), lader en bruger søge på LCSC-kode eller fritekst, og fjerner et symbol sammen med dets footprint og 3D-modelfiler i én atomisk operation — medmindre det footprint stadig deles af et andet symbol, i hvilket tilfælde det bevidst lades urørt.

Teknologistak

  • Kerne: Python 3.11+, uv, Pydantic-modeller til struktureret symboldata
  • Parsing: en brugerdefineret dybde-trackende tegnscanner over KiCads S-expression-format — intet tredjeparts KiCad-bibliotek
  • CLI: argparse-subkommandoer (list, info, search, remove), tabel-formatteret output
  • Konfigurationsopslag: --lib-root-flag → SYMBOL_LIB_ROOT-env-variabel → nuværende arbejdsmappe, koblet med et shell-alias-mønster så værktøjet opererer på hvilket KiCad-projekt brugeren end er i

Største resultat At skrive en parser præcis nok til at en symbolsletning aldrig rører resten af filen. parser.py vandrer gennem .kicad_sym-teksten tegn for tegn — tracker parentes-dybde, springer korrekt over citerede strenge (inklusive escapede anførselstegn) så en ) inde i et beskrivelsesfelt aldrig fejltolkes som en blokgrænse — og registrerer de eksakte (start, end) byte-offsets for hver symbolblok på øverste niveau. Fjernelse er derefter en ren streng-splice: text[:start] + text[end:]. Ingen reserialisering, ingen risiko for at KiCads egne formateringsparadokser stille bliver omskrevet, ingen biblioteksafhængighed der kunne drive fra hvilken .kicad_sym-version der faktisk er i brug. 80 linjer Python der gør præcis ét job præcist.

DEL 2 — Dybdegående baggrund

Problemet

JLCImport-pluginet er en envejsdør: det tilføjer et symbol, et footprint og 3D-modelfiler for hver del en bruger importerer, men fjerner dem aldrig. Et bibliotek brugt på tværs af flere PCB-projekter akkumulerer symboler for dele der ikke er rørt i måneder, og manuelt at rydde op i ét betyder at finde og slette den rigtige blok i en håndredigeret S-expression-fil, og dernæst opspore de matchende footprint- og 3D-modelfiler i to andre mapper — alt sammen mens man sikrer at ingen af de filer stadig refereres af et andet symbol der tilfældigvis deler samme footprint.

Arkitektur og løsning

Overordnet form. En lille, fokuseret CLI med tre biblioteksmoduler:

ModulRolle
lib/parser.pyScanner .kicad_sym, udtrækker SymbolInfo pr. symbol, registrerer eksakte byte-offsets
lib/searcher.pyFiltrerer parsede symboler på LCSC-kode eller fritekst
lib/remover.pyFjerner et symbols blok via streng-splice; fjerner betinget dets footprint og 3D-filer
main.pyCLI-subkommandoer, tabel-rendering, bekræftelsesprompts, --dry-run / --force

Designbeslutninger værd at fremhæve

  • Byte-offset-udtræk frem for en tur-retur-parser. Størstedelen af kompleksiteten i parser.py — den citat-bevidste scanner, parentes-dybde-tracking — eksisterer udelukkende for at svare på "hvor præcist starter og slutter denne blok," fordi en fjernelse der reserialiserer hele filen risikerer at omformatere alt andet KiCad nogensinde har skrevet til den.
  • Delt-footprint-sikkerhed tjekkes før noget slettes. remover.py beregner _footprints_in_use() på tværs af hvert andet symbol før filsystemet overhovedet røres; hvis footprintet stadig refereres andetsteds, bevares footprint- og 3D-filerne eksplicit og en advarsel printes — at fjerne én del kan aldrig stille i stykker en anden.
  • 3D-modelnavnet læses fra footprint-filen, gættes ikke. _extract_3d_model_name() trækker model-stammen ud af .kicad_mod'ens egen (model ...)-klausul før nogen fil slettes, fordi modelfilnavnet ikke er garanteret at matche footprint- eller symbolnavnet.
  • --dry-run beskriver den rigtige plan, ikke en simulation. Preview-stien kører præcis de samme opslag (delt-footprint-tjek, model-navn-udtræk) som den reelle fjernelse og printer hvad der ville ske — der er ingen separat, forenklet dry-run-kodesti der kunne drive fra hvad remove faktisk gør.
  • Manglende filer er advarsler, ikke afbrydelser. Et footprint eller en 3D-fil der allerede er væk rapporteres og springes over frem for at standse hele operationen — biblioteket får lov at allerede være en smule inkonsistent uden at værktøjet nægter at hjælpe med at rydde op.
  • Værktøjet finder sit projekt via konvention, ikke ved at få det at vide hver gang. --lib-root → SYMBOL_LIB_ROOT → CWD, koblet til et en-linjes shell-alias, betyder at symbol-tool list bare virker inde fra et hvilket som helst KiCad-projekt uden et flag.

AI som lærling

AI inde i produktet. Ingen. Parsing, søgning og fjernelse er alle deterministiske tekst- og filsystemoperationer.

AI i udviklingsprocessen (human-in-the-loop). SPEC.md definerer bibliotekets tre-lokations-struktur, datamodellen udtrukket fra hvert symbol, og fjernelses-semantikken (inklusive delt-footprint-reglen) før noget kode blev skrevet. En AI-kodeassistent implementerede mod den over ti commits på én dag; jeg reviewede specifikt parseren og remover'en, da dette værktøj redigerer en håndvedligeholdt biblioteksfil direkte, og en parsingfejl der er en stille datakorruptionsrisiko, ikke bare en bug.

Lederens refleksion

  1. Vid præcis hvad du må røre før du rører det. At udtrække præcise byte-offsets så en fjernelse er en splice, ikke en omskrivning, er den slags begrænsning der er let at springe over under tidspres og dyr at fortryde senere i en fil ingen anden genererer for dig.
  2. Et delt-ressource-tjek hører til før sletningen, ikke som et try/except omkring den. At beregne _footprints_in_use() på forhånd gør "ødelagde jeg lige et andet dels footprint" fra et spørgsmål til noget der er strukturelt umuligt.
  3. --dry-run er kun troværdig hvis den kører den reelle logik. En preview-sti der genimplementerer en forenklet udgave af den rigtige vil før eller siden lyve; at køre identiske tjek og bare ikke skrive er det der holder den ærlig.
  4. Specificér datamodellen før det formatspecifikke kode. SPEC.md's felttabel (name, footprint, footprint_name, lcsc, ...) eksisterede før scanneren der udtrækker den, hvilket er det der gjorde scannerens job entydigt.
  5. Lad værktøjet være belejligt om hvor det kører, ikke kun hvad det gør. SYMBOL_LIB_ROOT-plus-shell-alias-mønsteret er en lille smule ergonomi, men det er forskellen mellem et værktøj der bruges dagligt og ét der undgås pga. tasteoverheadet.

Ærlige begrænsninger. Bygget på én dag (ti commits, 9. april 2026), uden automatiseret testsuite — for en håndskrevet offset-baseret parser er det præcis den slags kode der ville have mest gavn af én, og det er et ærligt hul frem for en forglemmelse jeg ville forsvare. Værktøjet er specifikt til JLCImport-pluginets tre-lokations-layout; et anderledes organiseret bibliotek ville kræve reelle ændringer, ikke konfiguration. Flere listede forbedringer (en check-kommando til at validere bibliotekskonsistens, --json/--csv-output, en projekt-niveau konfigurationsfil i stedet for env-var-plus-alias) er dokumenteret i README'en som kendt fremtidigt arbejde frem for implementeret.