Metadata-Version: 2.5
Name: part2kicad
Version: 0.4.0
Summary: LCSC/EasyEDA parts as a KiCad library, with traceable provenance
Project-URL: Homepage, https://github.com/AltF4Quit/part2kicad
Project-URL: Issues, https://github.com/AltF4Quit/part2kicad/issues
Author: Emil Menzel
License-Expression: GPL-3.0-or-later
License-File: LICENSE
Keywords: easyeda,eda,footprint,jlcpcb,kicad,lcsc,pcb,symbol
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Manufacturing
Classifier: Natural Language :: English
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)
Classifier: Topic :: Utilities
Requires-Python: >=3.11
Requires-Dist: platformdirs>=4.0
Requires-Dist: sexpdata>=1.0.2
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == 'dev'
Description-Content-Type: text/markdown

# part2kicad

Type an LCSC part number and get a KiCad part you can hand to a fab house: the official
KiCad symbol where one exists, a standard footprint that was measured before it was
swapped in, real pin types so KiCad's electrical rule check has something to work with —
and a report that says, field by field, where every value came from.

```
pip install part2kicad
```

```
cd my-board
part2kicad add C93119
```

```
LM393M (C93119) → my-board.kicad_sym
  8 pins in 3 units
  Footprint my-board:SOIC-8_L4.9-W3.9-P1.27-LS6.0-BL, 8 pads
  3D model ${KIPRJMOD}/my-board.3dshapes/SOIC-8_L4.9-W3.9-P1.27-LS6.0-BL.step
  Fetched on 2026-08-15
  14 rules checked, 6 violated, 0 added
  ! Taking the official symbol over also exchanged 8 pin names — the numbers agree on
    both sides, so nothing moves electrically: 1 '1' → '', 2 '2' → '-', 3 '3' → '+', …
```

Open the schematic, press **A**, and the part is in the list. No download folder, no
library table to edit, no files to copy around.

The six violations in that last line are not a failure to hide — they are the reason to
use this tool at all. All six sit on the footprint, which this part keeps because the
official candidate misses its pads by 0.2498 mm and 0.20 mm is what the tolerance allows.
The report says exactly that, with the figures.

## What a part looks like before and after

`C93119` is a plain LM393 comparator, and it is the whole argument in one part. This is
what changes when it goes through `part2kicad` — measured on 2026-08-15, not estimated:

| | as the source delivers it | after `part2kicad add` |
|---|---|---|
| Units | one flat block, 8 pins | **3 units** — two comparators and a supply block |
| Pin types | 8 × `unspecified` | 2 × `power_in`, 4 × `input`, 2 × `open_collector` |
| Rule violations | 10 of 14 | **6 of 14**, all of them on the footprint |
| Electrical rule check | nothing to check | catches a comparator output tied to `+3V3` |

That last row was run by hand in KiCad, in the order that makes it mean something: the
rule check was first made silent with `PWR_FLAG` on both supply nets, and only then was
the deliberate mistake introduced. Without that step the check only proves that KiCad
says *something*.

An RP2040 in a QFN-56 tells the same story from the other end. Its converted footprint
broke 6 of 9 footprint rules; after the official `QFN-56-1EP_7x7mm_P0.4mm` was measured
against it and swapped in, the count is 0. Its symbol arrives with 51 of 57 pins off the
2.54 mm grid, and comes out with none.

## What it does

**It takes the official KiCad part over when there is one.** For the symbol it looks for
the manufacturer part number in your installed KiCad libraries — exact name first, then
the name with a suffix trimmed, then the package family. `extends` is resolved, so you get
the real body and not a stub. The taken-over symbol keeps *your* name and your provenance
fields, and the report names the library, the symbol, whatever suffix was cut off, and the
KiCad version it came from.

**It swaps the footprint only when the geometry agrees.** The package name chooses the
candidate; the measurement vetoes it. Pad hull, lead span, largest pad and pad pitch are
compared, and anything off by more than 0.20 mm keeps the source drawing. Drill diameter
and grid are hard gates with no tolerance at all — a 10 mm electrolytic that matched its
official candidate to 0.0000 mm was still turned away, because the official hole is 0.2 mm
too small for its lead. A footprint that is taken over is copied into your project, not
linked.

**It redraws IC symbols.** Where no official symbol exists, the symbol is drawn afresh
from the pin data instead of repairing the source artwork: every pin on the 2.54 mm grid,
one pin length, one text size, inputs left, outputs right, supply up, ground down. Twice
the same input gives twice the same symbol.

**It gives pins real electrical types.** Types come from the taken-over official symbol
first, then from name rules you can edit. Over the parts measured so far, 58 of 81 pin
names are classifiable that way. A pin that cannot be classified is reported as *not
determined* and left `unspecified` — never guessed, because a wrong `power_in` is worse
than an honest blank.

**It writes down what it did.** Every field carries its origin, and the report names the
fourteen KLC rules it checked with each result, plus seven more it does not yet check,
each with the reason why not.

**It merges multi-unit parts properly.** A quad op-amp becomes one symbol with units
`U1A` to `U1D`, not four parts. Shared pins land in unit 0.

**It improves parts you already have.** `part2kicad refine` re-runs the footprint match,
the pin types and the rule check against a part that is already in your library, without
making a single network request.

**It never overwrites by accident.** A part already in the library is reported, not
replaced; `--force` is the only way past that.

## Installation

```
pip install part2kicad
```

That is all it takes on any machine with Python 3.11 or newer. The package is a pure
`py3-none-any` wheel, so nothing is compiled and no build toolchain is needed. If you use
`uv`, `uv pip install part2kicad` does the same thing; `uv tool install part2kicad` or
`pipx install part2kicad` keep it out of your project environment.

You also need **KiCad 10.0 or newer**. The tool calls `kicad-cli`, which ships with KiCad.
On Windows that program is normally not on the search path, so `part2kicad` looks in the
usual installation folders first. If it still cannot find it, say where it is:

```
set PART2KICAD_KICAD_CLI=C:\Program Files\KiCad\10.0\bin\kicad-cli.exe
```

In PowerShell the same line reads:

```
$env:PART2KICAD_KICAD_CLI = "C:\Program Files\KiCad\10.0\bin\kicad-cli.exe"
```

To install from a wheel you build yourself, which is the route while working on the tool:

```
uv build
uv pip install dist/part2kicad-*.whl
```

Publishing to PyPI was a deliberate step of its own rather than a side effect of
development. It cannot be taken back: it claims the name permanently and turns the command
layout into a published interface that scripts and tutorials will anchor themselves to. A
released version can be yanked, but its number can never be reused.

## Your first part

1. **Open a terminal in your KiCad project folder** — the one holding the `.kicad_pro`
   file. `part2kicad` finds the project the way git finds a repository: from the working
   directory upwards. If there is none, it stops instead of writing somewhere arbitrary.

2. **Take a part in.** If you know the LCSC number:

   ```
   part2kicad add C93119
   ```

   If you do not, search by manufacturer part number or by description:

   ```
   part2kicad search lm393
   ```

   You get a numbered list with package, price, stock and whether CAD data exists at all.
   Pick a number and that part is taken straight in — search and import are one command.

3. **Open the schematic in KiCad** and press **A** to place a symbol. The library is named
   after your project and is already registered; you do not have to add anything under
   *Preferences → Manage Symbol Libraries*.

4. **Check the 3D model.** Open the board, press **Alt+3**. The model lives inside the
   project, so the folder still works on a machine that has never seen `part2kicad`.

If KiCad was already open while the part was written, use *Preferences → Manage Symbol
Libraries → OK* or restart it. KiCad reads the library tables once, at start.

## The report

`--report` prints the full report and files a copy next to the part, under
`<project>.reports/`. It is Markdown, so it is still readable in a year without this tool
installed. Here is the middle of the real one for the LM393 above — where the symbol came
from, and what the rule check found, violations included:

```
## Where the symbol comes from (QUAL-04)

| Fact | Value |
| --- | --- |
| Way | taken over from the official KiCad library |
| Why | the official symbol name is the manufacturer part number with a suffix trimmed |
| Official library | Comparator |
| Official symbol | LM393 |
| Trimmed off the part number | M |
| Resolved from | LM2903 |
| KiCad version | 10.0.5 |

## Checked rules

| Rule | Subject | Demands | Result | Details |
| --- | --- | --- | --- | --- |
| KLC F5.1 (reference text) | footprint | the reference text of the footprint is 'REF**' | violated | found '', expected 'REF**' |
| KLC F5.2 (drawing on the fabrication layer) | footprint | at least one graphic lies on F.Fab or B.Fab | violated | no graphic lies on a fabrication layer; the 4 entries there are text only: fp_text (1x), property (3x) |
| KLC F5.3 (courtyard on the grid) | footprint | every courtyard coordinate is a multiple of the courtyard grid | violated | 4 of 4 courtyard coordinates are not multiples of 0.01 mm: -3.7499, -2.449, 2.449, 3.7499 |
| KLC S4.1 (pin grid) | symbol | every pin sits on the 2.54 mm grid | passed | — |
| KLC S4.4 (pin types) | symbol | not every pin carries the electrical type 'unspecified' | passed | — |
```

The full report has seven sections: the facts about the part, where the symbol came from,
which footprint it got and why, the pin types with the source of each one, the origin of
every single field, the rules that were checked, and the rules that were deliberately left
out with the reason for each. Every judgement carries its figures — *"0.2498 mm off, and
0.20 mm is what is allowed"*, not *"close enough"*.

`--json` gives the same content as a machine-readable structure, out of the same source.
There is no second, parallel truth.

## Everyday use

```
part2kicad add C2040                 # take a part in by number
part2kicad search rp2040             # search, pick, take it in — one command
part2kicad add C2040 --report        # print the full report and file it beside the part
part2kicad add C2040 --force         # replace a part that is already there
part2kicad add C2040 --symbol redraw # force one of the four symbol routes
part2kicad add C2040 --project ../my-board   # name the project explicitly
part2kicad add C2040 --json          # machine-readable output, asks nothing
part2kicad add C2040 --verbose       # one line per fetch on stderr
part2kicad refine C93119             # improve a part already in the library
part2kicad refine --all              # go over the whole project library
```

### Choosing how the symbol is made

`--symbol` takes `auto`, `redraw`, `repair` or `keep`. `auto` is the default and decides
for itself: take over the official symbol where there is one, otherwise draw the symbol
afresh from the pin data, otherwise repair the source drawing. The other three force one
route. Whichever way it goes, the report names the route *and the reason* — including,
when it falls back, why the preferred route was not available.

### Refining what you already have

`part2kicad refine` works on a part that is already in your project library and makes no
network request at all — that is not a promise in prose, it is a bolt in the test suite
that fails the build if a fetch ever appears on that path. It re-runs the footprint match,
the pin types and the rule check against the files on disk.

Like `add`, it reports before it writes:

```
refine my-board.kicad_sym
  0 would change · 1 unchanged · 0 skipped · 0 failed
  LM393M (C93119) unchanged
  ! Price and stock are neither added nor changed by refine: both would need exactly the
    fetch this command avoids.
```

`--force` performs the change. `--all` walks the whole project library; it can be
interrupted, a single failing part does not stop the run, and symbols without a `Source`
field — anything you drew yourself — are left alone and named as left alone.

### Taking the same part in twice

If the part is already in the project library, `part2kicad` says so along with the date of
the first fetch and **writes nothing**. Anyone who has touched up a part by hand does not
lose that work to a careless second command.

`--force` replaces it. A symbol already placed in the schematic then has to be brought up
to date there, via *Tools → Update Symbols from Library*.

### What ends up on the part

| Group | Fields |
|---|---|
| Provenance | `Supplier Part`, `Manufacturer`, `Manufacturer Part`, `Datasheet`, `Source`, `Fetch Date` |
| Commercial data | `Price` and `Stock`, each with the date it was fetched |
| Technical parameters | whatever the source delivers, under its **unchanged** field names |
| Assembly | `JLCPCB Part Class` (Basic/Extended) — it decides the assembly cost |

Every field except `Reference` and `Value` is created hidden, so the schematic stays
readable. Price and stock only come with `part2kicad search`; a fetch by part number does
not carry them, and `part2kicad` does not invent a price.

The `Datasheet` link is checked before it is written: one HEAD request asks the server
what it would actually deliver. Many links that end in `.pdf` return a web page, and the
field says so rather than pretending. Where an official KiCad symbol was taken over, its
datasheet link wins and the source link is kept beside it as `Source Datasheet`.

## When something goes wrong

| What you see | What it means | What to do |
|---|---|---|
| `kicad-cli not found` | KiCad is not installed, or not where the tool looked | Set `PART2KICAD_KICAD_CLI` to the full path, see [Installation](#installation) |
| `no KiCad project found` | You are not inside a project folder | `cd` into the folder holding the `.kicad_pro` file, or pass `--project` |
| `this part has no CAD data` | The supplier has the part but no symbol or footprint for it | The message names the part and links its datasheet; draw it by hand or pick another part |
| `already in the library` | The part was taken in before | That is the safeguard, not an error. `--force` replaces it |
| The library does not show in KiCad | KiCad read its library tables at start | *Preferences → Manage Symbol Libraries → OK*, or restart KiCad |
| Everything hangs for two minutes | The supplier is throttling you | Leave it running; it waits and retries. **Ctrl-C** stops it cleanly at any time |

## What it cannot do yet

The list stays here, and it stays complete, because a tool whose whole point is honest
reporting cannot be coy about itself:

- **A local result store does not exist yet**, so a second `add … --force` fetches the same
  geometry again. It arrives with phase 5, together with offline search and filtering by
  value and price.
- **`refine` does not take over an official symbol.** On an existing part it keeps the
  symbol it finds and improves footprint, pin types and report around it. That is
  deliberate — an earlier version redrew a taken-over symbol and threw the good one away —
  but it means the official-symbol route is available on import only.
- **`refine` says nothing about a footprint candidate it rejected.** The import report
  names the candidate and the deviation in figures; the refine report only lists changes,
  so a rejection leaves no line at all.
- **The field names of the technical parameters stay as the source delivers them** — so,
  inconsistent. Unifying them is a step of its own and has not been taken.
- **The symbol naming scheme and the target library name are not configurable.** Everything
  else about the drawing and matching lives in `recipe.toml`; these two are still hard-wired
  to the part number and the project name.
- **macOS is untested, and nobody has run this on Linux yet.** Development and testing
  happen on Windows. For Linux the platform assumptions are pinned down statically — no
  hard-wired path, no shell invocation, no file access without a stated encoding,
  configuration folder from `platformdirs` — but a run is still outstanding.
- **Two concurrent invocations on the same library can lose a symbol.** The swap of the
  finished file is atomic; the read-modify-write before it is not. For an interactive
  single-workstation tool, a file lock has deliberately not been built in.
- **Price and stock are only available via `part2kicad search`**, see above.

## How this tool treats the data sources

`part2kicad` queries foreign servers that have no contract with us to serve it. What it
does while doing so is written out here in full, because a user is entitled to know what
runs in their name.

### Endpoints used

All the addresses live in `src/part2kicad/recipe.toml` and **not** in the program code. If
the counterpart changes, the repair is an edit there — not a program update.

| Purpose | Address | When |
|---|---|---|
| Search | `https://pro.easyeda.com/api/eda/product/search` | once per `search` command |
| Geometry (symbol **and** footprint) | `https://easyeda.com/api/products/{lcsc}/components` | once per part taken in |
| 3D model (STEP) | `https://modules.easyeda.com/…/{uuid}` | once per part taken in, if one is on file |
| Datasheet check | whichever address the response names — measured, `atta.`, `item.` and `so.szlcsc.com` | one HEAD request per part taken in |

Symbol and footprint come from **one** fetch; there is no second one for the footprint.
The datasheet check is a HEAD request: it asks what the server would deliver and reads no
body. If a part is already in the library, no fetch happens at all without `--force`. And
`part2kicad refine` touches none of these addresses — it is the one command that is
guaranteed to stay silent on the wire.

### Identification

`part2kicad` identifies itself as

```
part2kicad/<version> (+https://pypi.org/project/part2kicad/)
```

An honest identification that names the product. Explicitly **not**: a faked browser user
agent, or bending name resolution to get around a block. This is not decoration — measured,
the honest route gets through, and the dishonest one would not be acceptable even if it
were necessary.

The identification can be changed without touching the program: your own `recipe.toml` in
the platform's configuration folder (Windows `%LOCALAPPDATA%\part2kicad\`, Linux
`~/.config/part2kicad/`) overrides individual values **selectively**:

```toml
[identity]
user_agent = "my-identification/1.0 (+https://example.example)"
```

### Minimum intervals and behaviour when blocked

Every endpoint has its **own** time budget — the block we measured is tied to the path, and
a shared budget would slow the search down as soon as a geometry fetch is due.

| Endpoint | Minimum interval | Maximum attempts |
|---|---|---|
| Search | 2.5 s | 3 |
| Geometry | 3.0 s | 3 |
| 3D model | 1.0 s | 3 |
| Datasheet check | 1.0 s | 3 |

The counterpart starts blocking after roughly 45 fetches within a few minutes —
cumulatively, not by peak rate. It then answers with a bare **HTTP 403**, without
`Retry-After` and without HTTP 429; the block therefore cannot be predicted, only
recognised. It clears by itself after about two minutes.

In that case `part2kicad` waits, starting at 120 seconds and doubling per attempt, for at
most three attempts — 120 + 240 = **six minutes** in total. More would be out of
proportion: measured, the block clears after around two minutes, and if it is still there
after three times that, it is not the ordinary rate limit; waiting longer is then no better
a bet than telling the user. The wait is shown as a status line — two minutes of silence
could not be told apart from a crash — and **Ctrl-C** ends it at any time with a message
instead of a stack trace. If the counterpart still will not let up, the command stops with
a message saying that it is not your machine's fault. Nothing is written along the way: as
long as not all the pieces are together, the library stays untouched.

The numbers all live in `recipe.toml` and can be changed in your own user file — a limit
hard-coded in the program would take that option away.

### The counterpart's exclusion list

`easyeda.com/robots.txt` disallows `//api/` paths — and with them the geometry endpoint.
That is named openly here rather than passed over:

- The route is used **deliberately**, because there is no other one that would yield a
  part's CAD data.
- It is **minimised**: one fetch per part, never a full catalogue, none at all for a part
  that is already present, and none at all for `part2kicad refine`.
- A local cache that would also spare the *repeated* fetch of the same part is **still
  missing** — it arrives with phase 5. So a second `part2kicad add C2040 --force` fetches
  the geometry a second time. This is stated here because a disclosure that lists a measure
  not yet taken is not a disclosure.
- The **search traffic** — that is, the volume — goes to `pro.easyeda.com`, whose own
  exclusion list names only `/login` and a set of named SEO bots, not `/api/`.

Anyone who judges this differently can leave `part2kicad add` alone and use the tool for
searching only.

## Development

```
git clone https://github.com/AltF4Quit/part2kicad
uv sync
```

| Run | Command |
|---|---|
| Fast lane, without KiCad | `pytest -m "not kicad and not live"` |
| Full run (default) | `pytest -m "not live"` |
| Live tests against the real counterpart | `pytest -m live --live` |

Live tests **never** run by accident: they need both the marker and the `--live` switch.
Tests that need an installed KiCad skip themselves with a visible reason when none is
found.

## License

GPL-3.0-or-later, see [LICENSE](LICENSE).
