answer from Python too

The engine is the same; only the front door is new. pyo3 behind an optional
feature, maturin for the wheel, and the bindings mirror DayJson rather than Day
because DayJson is already the shape front ends read and the one that has to stay
still. to_dict() therefore gives exactly what --json gives, and a test holds the
two to that.

That test earned its place immediately. The bindings were calling
jurisdictions::resolve(None), which answers with a built-in default that knows
nothing about the machine, so Python was reckoning by the New Calendar while the
command line on the same box reckoned by ROCOR — every field downstream of the
calendar differed, including the fast. They now take the same two steps main.rs
takes, and agree field for field on Pascha, the Nativity and Theophany.

The wheel is abi3, so one build serves 3.9 upward rather than one per
interpreter. That costs the datetime C API, which the limited API does not
expose, so a date is read through its attributes instead — which incidentally
makes datetime.datetime work as well as datetime.date. Constructing dates is
unaffected, pyo3 calls Python's own constructor for that.

panic = "abort" comes out of the release profile. An extension module has to be
able to unwind a panic into a Python exception; aborting would take the
interpreter down with it. The CLI keeps the same behaviour it always had, a
panic there just unwinds now.

Mixed layout so the wheel can carry py.typed and a stub file, which a bare
extension module has nowhere to put. mypy --strict is clean against the stubs
and catches a deliberate error, so the Typing :: Typed classifier is not a claim
about nothing.
This commit is contained in:
funkpower
2026-09-18 11:28:08 -04:00
parent 227abac6f6
commit 5d7e02cffb
11 changed files with 763 additions and 3 deletions
+40
View File
@@ -40,6 +40,15 @@ $ install -Dm755 target/release/typikon ~/.local/bin/typikon
The menologion is compiled in, so the binary answers for every day of the year
on its own: no data file to find, no network, nothing to go stale.
For Python, there is a wheel:
```console
$ pip install typikon
```
The menologion is compiled into that too, so it carries no data files and needs
no network either.
## Which calendar?
Three things get conflated constantly, and keeping them apart is most of what
@@ -165,6 +174,37 @@ Level::Xerophagy.relaxed_to(Level::Fish, Level::WineOil) // WineOil: no saint
Level::Fish.relaxed_to(Level::WineOil, Level::WineOil) // Fish: never tightens
```
## From Python
```python
import datetime, typikon
day = typikon.describe(datetime.date(2026, 9, 18))
day.church_date # '5 September'
day.fast # 'xerophagy'
day.title # 'The Holy Prophet Zachariah, father of the Forerunner'
day.is_obligatory() # False
typikon.today()
typikon.pascha(2027) # datetime.date(2027, 5, 2)
typikon.week(datetime.date(2026, 4, 12), 7) # seven consecutive days
typikon.describe(d, jurisdiction='rocor')
[j['key'] for j in typikon.jurisdictions()]
```
Dates in and out are `datetime.date`. `to_dict()` gives exactly the fields
`--json` gives, so anything already reading the CLI's output can read a `Day`
without changing shape — there is a test that holds the two to that.
Given no `jurisdiction=`, the library reads the same configured default the
command line does. Calling the engine's own resolver instead would have quietly
answered for a different church than `typikon --set-default` selected, which is
the kind of disagreement nobody notices until a fast day is wrong.
The wheel is abi3, so one build serves every Python from 3.9 up. It ships
`py.typed` and a stub file, and `mypy --strict` is clean against it.
## The menologion
The fixed calendar ships as data and is indexed by **ecclesiastical** date, so