| name | nbdev-patterns |
| description | nbdev cell directives, module export conventions, editing workflow, and testing patterns for notebook-first development. MANDATORY reading before editing any kreview Python module. |
nbdev Patterns for kreview
⚠️ THE GOLDEN RULE
Never manually edit .py files and consider the job done.
All kreview/*.py files (except standalone files like scoreboard.py) are auto-generated from nbs/*.ipynb.
If you edit a .py file, you MUST sync it back to the notebook using nbdev_update.
When to use this skill
- Creating or editing any notebook in
nbs/
- Editing any
kreview/*.py file that has an AUTOGENERATED header
- Running
nbdev_export, nbdev_update, nbdev_test, or nbdev_docs
- Troubleshooting export, import, or drift issues
- Before committing any changes that touch
.py or .ipynb files
Editing Workflow
There are two valid workflows. Both end with the same verification step.
Workflow A: Notebook-First (preferred for new code)
1. Edit the notebook in nbs/*.ipynb
2. Run: python3 -m nbdev.export
3. Verify: python3 -m pytest tests/ -x -q
When to use: Creating new modules, writing documentation, adding test cells.
Workflow B: Python-First (preferred for refactoring)
1. Edit the .py file directly (kreview/*.py)
2. Sync: python3 -m nbdev.sync --fname kreview/<module>.py
3. Verify: python3 -m nbdev.export (should produce NO changes)
4. Test: python3 -m pytest tests/ -x -q
When to use: Large refactors, IDE-driven edits, complex multi-file changes.
[!CAUTION]
Step 3 is critical. If nbdev_export produces changes after nbdev_update,
it means the sync lost something. Investigate before committing.
Verification: Idempotency Check
After ANY edit workflow, run this sequence to confirm consistency:
python3 -m nbdev.export
cp kreview/<module>.py /tmp/check.py
python3 -m nbdev.export
diff kreview/<module>.py /tmp/check.py && echo "IDEMPOTENT ✓"
Notebook ↔ Python Module Mapping
| Notebook | Python Module | Key Exports |
|---|
nbs/00_core.ipynb | kreview/core.py | LABEL_META_COLS, Paths, LabelConfig |
nbs/01_labels.ipynb | kreview/labels.py | Labeler, label hierarchy |
nbs/02_eval_engine.ipynb | kreview/eval_engine.py | cpu_models(), gpu_models(), evaluate_feature() |
nbs/03_registry.ipynb | kreview/registry.py | get_all_evaluators() |
nbs/04_selection.ipynb | kreview/selection.py | score_features(), select_features(), build_binary_target() |
nbs/05_report.ipynb | kreview/report.py | Report rendering |
nbs/90_cli.ipynb | kreview/cli.py | run, label, extract, fuse, report commands |
nbs/91_cli_eval.ipynb | kreview/cli_eval.py | kreview eval cpu|gpu|multimodal |
nbs/92_cli_select.ipynb | kreview/cli_select.py | kreview select command |
Standalone files (no notebook, edit directly):
kreview/scoreboard.py
kreview/feature_cards.py
Cell Directive Reference
| Directive | Effect |
|---|
#| default_exp core | Sets module export target for this notebook |
#| export | Exports the cell's code to the Python module |
#| exporti | Exports but marks as internal (not in __all__) |
#| hide | Hides cell from docs |
#| test | Marks cell as a test (run by nbdev_test) |
[!NOTE]
Both # | export (with space) and #| export (without space) are valid.
The codebase uses both conventions. Be consistent within a single notebook.
Import Convention
from kreview.core import *
from kreview.core import LabelConfig, Paths, LABEL_META_COLS
CLI Registration Patterns
from kreview.cli_eval import eval_app
app.add_typer(eval_app, name="eval")
from kreview.cli_select import select
app.command(name="select")(select)
[!IMPORTANT]
When adding a new CLI command, the registration MUST go in Cell 1 of
nbs/90_cli.ipynb (the cell that creates app = typer.Typer(...)).
Deduplication Rules
When logic is shared between kreview run (monolithic) and a standalone
command (e.g. kreview select), follow this pattern:
- Define the logic in a library module (e.g.
selection.py)
- Import and call from both
cli.py (the run function) and the
standalone CLI module (e.g. cli_select.py)
- Never copy-paste logic inline — use the shared function
Current shared functions (selection.py → used by cli.py + cli_select.py):
score_features() — univariate AUC + mutual information scoring
select_features() — mRMR (default) or hybrid-union feature selection + variance guard
build_binary_target() — label filtering + binary target construction
_impute() — NaN imputation (zero/mean/median)
Common Errors
| Error | Cause | Fix |
|---|
ModuleNotFoundError | Notebook edited but not exported | python3 -m nbdev.export |
AttributeError: module has no attribute | Cell missing #| export | Add directive to cell |
| Tests fail silently | Cell missing #| test | Add directive to test cell |
.py changes lost after export | Edited .py without nbdev_update | python3 -m nbdev.sync --fname <file> |
| Export produces unexpected changes | Notebook and .py out of sync | Run nbdev_update then nbdev_export |
kreview <cmd> --help not found | Command not registered in Cell 1 | Add import + app.command() registration |
Pre-Commit Checklist
Before committing changes to any nbdev-managed file:
python3 -m nbdev.export
python3 -m nbdev.export
python3 -m nbdev.doclinks
python3 -m pytest tests/ -x -q
git diff --name-only kreview/*.py