chipify

Project Briefing: Chipify

1. Project Overview


2. File Architecture & Modules (v0.2 — post-Phase-1 refactor)

Engine (no Tk deps)

Module Purpose
settings.py Project paths (IN_DIR, OUT_DIR, WORK_DIR, TB_DIR, FAST_TMP). The four project folders are configurable via settings.json (keys in_dir/out_dir/work_dir/tb_dir); missing/blank ⇒ default structure. FAST_TMP is fixed.
util.py Domain objects: Stimuli, Test, Value. Delegates YAML parsing to schema.py.
schema.py validate_datasheet() — validates datasheet.yaml against typed schema; safe _parse_range_dsl() for range/linspace/logspace strings.
expression.py SafeEvaluator — sandboxed asteval-backed evaluator with numexpr fast path. Replaces all eval() / df.eval() call-sites.
app_config.py Persistent user preferences (settings.json), application-wide logging setup.
simulator.py Multiprocessing simulation engine (NgspiceSimulator, VacaskSimulator). File-based abort via /tmp/sim_work/abort.flag.
plot_manager.py All Matplotlib logic. Avoids GUI bloat.
data_loader.py Results loading / pass-fail / plot-column classification / history — shared by the engine, exporters, and GUI; headless, no GUI deps.
cli.py Entry point for headless execution or launching the GUI (run_gui() → Qt app).

Toolkit-agnostic GUI-support layer (chipify/uikit/)

uikit/                      – NO GUI-toolkit imports; unit-testable headlessly
├── state.py                – AppState (single source of truth) + Signal pub/sub
├── services/
│   ├── equation_service.py – apply_scalar_equations, apply_transient_equations (via SafeEvaluator)
│   ├── measurements.py     – measurement_rows / equation_rows / worst_cases (stats for the table)
│   ├── transient_loader.py – resolve_analysis_dir, list_analysis_signals, load_analysis_df
│   ├── scatter_hover.py    – matplotlib scatter hover/click manager
│   ├── netlist_export.py   – per-sample SPICE netlist rendering (pure)
│   ├── yaml_editor_service.py – get_params_dict, get_tests_dict, gui_repr_param, sync_form_to_yaml
│   └── plugin_context.py   – PluginContext facade handed to tab plugins (see PLUGINS.md)
└── widgets/
    └── yaml_dumper.py      – QuotedString + inline-list YAML representers

Qt GUI Package (chipify/gui_qt/)

gui_qt/
├── app.py                  – QApplication bootstrap + main() (the `chipify` GUI entry point)
├── main_window.py          – QMainWindow shell: left control panel + QTabWidget + status bar
├── theme.py                – night/dark/light palettes → QSS + QPalette; plot_theme()
├── controllers/            – simulation_controller, history_controller (Qt signals, no after())
├── workers/sim_worker.py   – QThread worker emitting queued progress/chunk/finished signals
├── services/               – throttle, canvas_menu (QMenu), figure_export, latex_export
├── tabs/                   – editor / measurements / histogram / analytics / transient / equations
└── widgets/                – settings_dialog, run_annotation_dialog, mpl_canvas, helpers

Plugins: the Qt GUI loads QtTabPlugins; legacy Tk TabPlugins are detected and skipped with a warning (plugin_loader.warn_unsupported_tab_plugins()).

Tests (tests/)

File Covers
test_expression.py SafeEvaluator sandbox, helpers, SPICE sanitization, security
test_util_range_dsl.py _parse_range_dsl whitelist, validate_parameters
test_equation_service.py apply_scalar/transient equations, NaN propagation
test_yaml_editor_service.py get_params_dict, get_tests_dict, gui_repr_param
test_data_loader_history.py data_loader.list_history_runs
test_netlist_export.py per-sample netlist rendering (pure logic)
test_plugin_context.py PluginContext facade, JSON-serialization
test_gui_qt_smoke.py Qt GUI smoke tests (offscreen): window, tabs, themes, worker, plugins

3. Critical Technical Constraints (DO NOT REVERT)

Process Management & Abort System (simulator.py)

Safe Expression Evaluation (expression.py)

YAML Range Parsing (schema.py)

Matplotlib Ghosting (plot_manager.py)

Sweep vs Output Column Separation

Error Handling in DataFrames


4. Architecture Rules (Phase 1 invariants)

  1. The core and the uikit/ layer (uikit/services/, uikit/state.py, data_loader.py, expression.py, schema.py) never import a GUI toolkit. This keeps them unit-testable without a display; all Qt code lives under gui_qt/.
  2. Tabs never call simulator.* directly. They dispatch through a controller (SimulationController).
  3. State is mutated only through AppState. Subscribers receive notifications via Signal.emit(); the QThread sim worker delivers cross-thread updates as queued Qt signals.

5. Development Commands

# Install (engine only)
pip install -e .

# Install with optional fast vectorised evaluation
pip install -e ".[fast]"

# Run tests
pytest

# mypy strict check on typed modules
python -m mypy chipify/expression.py chipify/schema.py chipify/uikit/state.py \
    chipify/uikit/services/ chipify/uikit/widgets/ chipify/data_loader.py \
    chipify/util.py chipify/app_config.py --strict

# Launch GUI (PySide6/Qt)
chipify