Part 1 · Why this file even exists
The one job: let old code keep writing
from …spice_sim import simulate_metricseven though the actual ngspice code was moved out into aspice/sub-package.
There is almost no code here to "run." The whole file is a signpost. So the lesson isn't a line of logic — it's a design pattern: the facade.
Picture a company that outgrew a single office. The ngspice verification used to live in one big file, spice_sim.py. It grew — deck building, a testbench rig, a dozen per-metric measurements, DC bias soundness — until it deserved to be split into a proper spice/ package with one file per job. But dozens of callers all over the codebase already say:
from circuitgenome.sizer.shared.spice_sim import simulate_metrics
Move the code and every one of those imports breaks. The facade is the fix: a thin module that forwards the old names to their new home. Callers never notice the furniture moved.
The switchboard analogy
Think of a company receptionist. You call one well-known number and ask for "simulate metrics." The receptionist doesn't do the work — she just connects you to whichever department now handles it. Tomorrow that department could move to a different floor; you still dial the same number. spice_sim.py is that receptionist for the spice/ package.
Looking ahead: Part 2 opens the receptionist's desk and reads the two things she actually contains — an
importthat pulls names in, and an__all__that hands them back out.
Part 2 · The re-export lines, in full
The one job: pull six names in from the sub-package, then declare those same six as this module's public surface so
from spice_sim import Xresolves for every one of them.
Here is the entire body of the file. Two statements, nothing else.
from .spice import (
check_bias_soundness,
ngspice_available,
pdk_netlist,
read_op_operating_point,
simulate_metrics,
sized_netlist,
)
__all__ = [
"check_bias_soundness",
"ngspice_available",
"pdk_netlist",
"read_op_operating_point",
"simulate_metrics",
"sized_netlist",
]
Line 1 — the forwarding import
The mailbox analogy is exact. You moved apartments but filed a change-of-address so mail to the old address still reaches you. from .spice import (…) is that forwarding card: the . means "the package next to me," and each name listed is a piece of mail redirected from spice/ into this module's namespace. After this line runs, simulate_metrics is a real, callable name living inside spice_sim — pointing at the exact same function object defined over in spice/simulate.py.
Why is it called __all__?
I like this name. __all__ is Python's answer to "when someone does from spice_sim import *, what counts as all of me?" Without it, a wildcard import would also drag along anything else that happened to be in scope. Listing the six names makes the public API explicit and intentional — this is the storefront window, and only these six items are on display.
So the two statements are a matched pair: the import brings the goods into the shop, and __all__ puts them in the window.
Notice what the picture reveals: the six names come from three different files (deck, op, simulate), yet callers see one flat menu. That collapsing of internal structure into a single friendly surface is the facade's value.
| Public name | What it does | Real home |
|---|---|---|
simulate_metrics | Entry point: re-simulate a sized circuit, measure every metric | spice/simulate.py |
ngspice_available | Is the ngspice binary usable at all? | spice/deck.py |
pdk_netlist | Emit the base PDK/model netlist | spice/deck.py |
sized_netlist | Stamp chosen W/L into the netlist | spice/deck.py |
check_bias_soundness | DC bias-soundness verdict from the op point | spice/op.py |
read_op_operating_point | Parse ngspice's operating-point dump | spice/op.py |
Part 3 · The hidden idea, the limits, and one sentence
The file never states its purpose as a principle, but this is the idea it quietly implements — the public API is decoupled from the internal package layout:
Because those two are held apart by the facade, the team is free to reshape spice/ — rename files, split deck in two, move check_bias_soundness from op.py to some new bias.py — and not one caller has to change. Only this one 25-line file gets edited. That is the whole payoff, drawn below.
What it honestly does not do
- It adds no behavior — no logic, no wrapping, no validation. Call
simulate_metricshere and you run the identical code as calling it fromspice.simulate. - It forwards only these six names. Anything else in
spice/is not reachable through this shim — by design, so the public surface stays small. - It does not shield you from signature changes. If
simulate_metricsgains a new required argument, the facade forwards that change straight through — it hides location, not contract. - It is a compatibility bridge. New code can import from
…shared.spicedirectly; this path exists chiefly to avoid breaking what already works.
Why it's elegant
The cleverness is in the restraint. A whole subsystem got reorganized, and the blast radius was contained to one tiny file that carries no logic to break. Six names in, six names out, and every existing caller sails on unaware. Cheap to write, nearly impossible to get wrong, and it turns a scary cross-cutting refactor into a one-file edit.
In one sentence: spice_sim.py is a facade that re-exports six names from the spice/ sub-package so the historical import path from …spice_sim import simulate_metrics keeps working — decoupling what callers import from where the code actually lives, so the internals can be refactored without breaking a single caller.