Migrations¶
didactic.api.register_migration ¶
register_migration(
source: type[A],
target: type[B],
migration: Lens[A, B] | Iso[A, B] | Mapping[A, B],
) -> None
Register a migration from source to target.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
type[A]
|
The older Model class. |
required |
target
|
type[B]
|
The newer Model class. |
required |
migration
|
Lens[A, B] | Iso[A, B] | Mapping[A, B]
|
required |
Raises:
| Type | Description |
|---|---|
TypeError
|
If a migration is already registered for the same
|
Notes
The registry is process-global. The lookup key is a fingerprint of the didactic-shape spec, so a structurally-identical class re-imported from a different module shares one registry entry.
Examples:
>>> import didactic.api as dx
>>> class UserV1(dx.Model):
... id: str
... name: str
>>> class UserV2(dx.Model):
... id: str
... given_name: str
... family_name: str
>>> class V1ToV2(dx.Iso[UserV1, UserV2]):
... def forward(self, u: UserV1) -> UserV2:
... first, _, last = u.name.partition(" ")
... return UserV2(id=u.id, given_name=first, family_name=last)
...
... def backward(self, u: UserV2) -> UserV1:
... return UserV1(
... id=u.id,
... name=f"{u.given_name} {u.family_name}".rstrip(),
... )
>>> dx.register_migration(UserV1, UserV2, V1ToV2())
didactic.api.migrate ¶
Migrate a payload to the target Model class.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
payload
|
Model | JsonObject
|
Either an instance of an older Model class or a dict produced
by |
required |
source
|
type[Model] | None
|
The source Model class. Required when |
None
|
target
|
type[B]
|
The Model class to migrate to. |
required |
Returns:
| Type | Description |
|---|---|
Model
|
A |
Raises:
| Type | Description |
|---|---|
LookupError
|
If no path exists in the registry. The error message includes the source and target fingerprints so the user can diagnose which migration is missing. |
TypeError
|
If |
Notes
Path search is breadth-first over fingerprints. Lens composition is associative, so any path produces the same result for round-trip-clean migrations.
didactic.api.save_registry ¶
Persist the registry's metadata to a JSON file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | PathLike[str]
|
Filesystem path to write. The parent directory must already exist; the file is created or truncated. |
required |
Notes
Lenses themselves are Python callables and do not serialise. What
gets written is the metadata that lets a subsequent
load_registry call reconnect Python-side
lenses to their entries: source spec, target spec, source
fingerprint, target fingerprint, and the lens's class
__qualname__ for diagnostic purposes. After loading, the user
must re-register the lens (typically by re-importing the module
that called register_migration),
which is a no-op because the fingerprints already match.
The on-disk shape is a JSON object with one top-level key
"entries", mapping to a list of records:
.. code-block:: json
{
"entries": [
{
"source_fp": "...",
"target_fp": "...",
"source_spec": {...},
"target_spec": {...},
"lens_qualname": "module.path.LensClass"
}
]
}
didactic.api.load_registry ¶
Load registry metadata from a JSON file produced by save_registry.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | PathLike[str]
|
Path to a JSON file written by save_registry. |
required |
Returns:
| Type | Description |
|---|---|
int
|
The number of entries cross-checked against the in-memory registry. The disk-side metadata is informational; it does not re-bind lenses (Python callables don't survive a process boundary). Entries whose fingerprints already exist in the in-memory registry are silently confirmed; entries whose fingerprints are missing are reported via the return value being smaller than the number of records on disk. |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If |
ValueError
|
If the file is not in the expected format. |
Notes
The intended workflow is:
- Application starts.
- Each module that defines migration lenses runs its register_migration calls at import time.
load_registry(path)is called for diagnostic purposes: compare the registry on disk to what's been registered in the current process. The return value is the count of confirmed entries; a smaller-than-expected number is a signal that a migration module wasn't imported.
The on-disk format is intentionally human-readable so that operators can audit a deployment's expected migrations.
didactic.migrations._migrations.lookup_migration ¶
lookup_migration(
source: type[A], target: type[B]
) -> Lens[A, B] | Iso[A, B] | Mapping[A, B] | None
Return the migration registered for (source, target), or None.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
type[A]
|
The source Model class. |
required |
target
|
type[B]
|
The target Model class. |
required |
Returns:
| Type | Description |
|---|---|
Lens or Iso or Mapping or None
|
The registered migration, or |
Notes
Lookup is by fingerprint of the didactic spec; class identity is not used. A structurally-identical class re-imported from a different module finds the same migration.
didactic.migrations._migrations.registered_fingerprints ¶
Return every registered (source_fp, target_fp) pair.
Returns:
| Type | Description |
|---|---|
list of tuples
|
One |
didactic.migrations._migrations.clear_registry ¶
Wipe the migration registry. Test-suite hygiene only.