Cookbook contract¶
The Cookbook (recipes/) is the executable source of runnable
examples for the SDK. The recipe contract is enforced by the
verification suite (tests/test_recipes_verification.py and
per-recipe tests). Every recipe MUST comply with CB-001 through
CB-008.
The contract¶
CB-001 — public-SDK only¶
Every recipe MUST import only public SDK symbols. A recipe that
imports a private module (un_comtrade._foo,
un_comtrade.storage._base, un_comtrade.analytics._query_engine)
is rejected at review.
# ALLOWED — public SDK only
from un_comtrade import ComtradeClient
from un_comtrade.config import Configuration
from un_comtrade.models import Country
# NOT ALLOWED — these would fail CB-001:
# from un_comtrade._internal import _foo
# from un_comtrade.storage import _base
CB-002 — frontmatter¶
Every recipe MUST declare a recipe_id, title, category,
difficulty, sdk_version, requires_api_key, and
estimated_runtime in the frontmatter.
---
recipe_id: RECIPE-NNN
title: Human-readable title
category: metadata | trade | analytics | storage | cli | end_to_end
difficulty: beginner | intermediate | advanced
sdk_version: ">=1.0.0"
requires_api_key: yes | no
estimated_runtime: <1s | 1-2s | 2-4s | 4-8s | 8-15s
---
CB-003 — deterministic output¶
Every recipe MUST produce deterministic output under mock mode. The regression tests inject a mock transport; the recipe MUST work without any network call.
CB-004 — demo(client) + main() pattern¶
The recipe body MUST follow the demo + main pattern:
def demo(client: ComtradeClient) -> SomeResult:
"""The actual demonstration."""
...
def main() -> int:
"""Build a real client and run the demo."""
config = Configuration(api_key=os.environ.get("UN_COMTRADE_KEY") or None)
with ComtradeClient(config) as client:
demo(client)
return 0
if __name__ == "__main__":
print("== Recipe NNN: Title ==")
raise SystemExit(main())
The demo(client) function is what the regression test calls; the
main() function is what python recipes/<file>.py runs.
CB-005 — expected output documented¶
The recipe docstring MUST include a documented "Expected output" section showing the mock-mode output.
CB-006 — recipe_id matches the filename¶
The recipe_id in the frontmatter MUST match the canonical RECIPE
identifier (RECIPE-NNN). The numbering is dense per category:
- 001–099: metadata + trade + analytics + storage + CLI
- 100+: end-to-end recipes
CB-007 — referenced by documentation pages¶
Every recipe referenced from a documentation page (website/docs/)
MUST exist in recipes/. The link checker
(scripts/build_docs.py Step 6) enforces this.
CB-008 — example compilation¶
Every fenced Python block in every documentation page MUST parse
without errors. The example-compile step
(scripts/build_docs.py Step 7) extracts the blocks, writes them
to a temporary file, and runs python -m py_compile.
The verification suite¶
The verification suite is tests/test_recipes_verification.py
plus per-category modules. It enforces:
- CB-001 via AST scan of every recipe's imports.
- CB-002 via frontmatter parsing.
- CB-003 via mock-mode execution.
- CB-004 via
inspect.signature(demo)matching the expected signature. - CB-005 via doctest-style comparison.
- CB-006 via regex match against the recipe filename.
Run the suite:
Adding a new recipe¶
- Copy
recipes/_TEMPLATE.pytorecipes/<category>/<NN>_<slug>.py. - Fill in the frontmatter (CB-002).
- Implement
demo(client)andmain()(CB-004). - Document the expected output (CB-005).
- Run
pytest tests/test_recipes_verification.py -vto verify. - Add a regression test in the per-category module if the recipe has any non-trivial behaviour.
Related Recipes¶
- RECIPE template — the canonical recipe scaffold.
Related Guides¶
- Testing — the testing standard.
- Contributing — the PR checklist.
Next steps¶
- Testing — the quality bar.