cooklang-bindings¶
Python bindings for cooklang-rs, the official Rust implementation of the Cooklang recipe markup language.
This is a binding, not a fork
The package contains no parser logic. Everything under
cooklang/_generated/ is produced by UniFFI from the interface
upstream already maintains — the same interface behind their Swift and
Kotlin bindings — and the rest is a thin Pythonic layer over that output.
See Relationship to cooklang-rs.
Install as cooklang-bindings, import as cooklang:
In one screen¶
>>> import cooklang
>>> recipe = cooklang.parse("""
... ---
... title: Test
... servings: 4
... ---
...
... == Prep ==
...
... Chop the @onion{1}.
...
... > A note that is not a step.
...
... Fry it in a #pan{} for ~{5%minutes}.
... """)
>>> recipe.title
'Test'
>>> recipe.servings
4
>>> recipe.method
('Chop the onion.', 'Fry it in a pan for 5 minutes.')
>>> recipe.sections[0].name
'Prep'
>>> recipe.notes[0].text
'A note that is not a step.'
>>> recipe.ingredients[0]
Ingredient(name='onion', quantity=Quantity(value=1, unit=None, text='1'), note=None, recipe_reference=None)
>>> recipe.cookware[0].name
'pan'
>>> str(recipe.timers[0])
'5 minutes'
-
Install it
Wheels carry a prebuilt native library, so no Rust toolchain is needed.
-
Use it
Parse a recipe, walk its steps, total its ingredients.
-
Look things up
Every exported name, with the type it returns.
-
Build it
The native library is generated, not committed.
What is covered¶
The package wraps the whole of upstream's exported UniFFI surface — all 28 functions — not just the recipe parser.
| Area | Entry point | Guide |
|---|---|---|
| Recipes | parse |
Working with recipes |
| Totalling ingredients | combine_ingredients |
Working with recipes |
| Aisle configuration and common names | parse_aisle_config |
Aisle configuration |
| Shopping lists | parse_shopping_list |
Shopping lists |
| The checked log | parse_checked_log |
Shopping lists |
| Quantity values | parse_value, format_value |
Quantity values |
Canonical Cooklang only¶
cooklang-rs parses a superset of canonical Cooklang. These bindings
parse the canonical spec only, and the extensions cannot be switched on —
upstream's UniFFI layer hardcodes the canonical parser. Because Cooklang reads
unrecognised syntax as text rather than rejecting it, extended markup ends up
inside your data rather than raising. If you ingest recipes you did not write,
read Syntax extensions first.
Design notes¶
-
The model is plain data. Every type in
cooklang.modelsis a frozen dataclass with no FFI object inside it, so instances compare, hash, pickle and hand to a template without surprises. -
Display text comes from upstream. A
Quantitycarries both avaluefor arithmetic and atextthat preserves how the recipe wrote it —1/2stays1/2rather than becoming0.5. -
Sections and notes are first class. They are types upstream models directly, not something reconstructed from rendered text.
Licence¶
MIT, matching upstream. Distributed wheels contain a compiled copy of the MIT-licensed cooklang-rs. See Relationship to cooklang-rs.