Reference¶
Versionable Class Parameters¶
Full signature:
@dataclass
class MyClass(
Versionable,
version=1,
hash="abc123",
name="MyClass",
old_names=["OldName"],
register=True,
skip_defaults=False,
unknown="ignore",
):
...
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
|
required |
Schema version. Increment on breaking field changes. |
|
|
recommended |
6-char hash of field names/types. Checked at import. |
|
|
class name |
Serialization name for output metadata and registry. |
|
|
|
Previous names; allows loading old files. |
|
|
|
Add class to global registry (for |
|
|
|
Omit fields equal to their class default from output. |
|
|
|
Unrecognised fields: |
Opting Out of Registration¶
By default, every Versionable subclass is added to a global registry keyed by its serialization name. This registry
powers loadDynamic(), which looks up the class from the __OBJECT__ metadata in a file.
Set register=False when a class shouldn’t participate in this registry:
# Abstract base — never serialized directly
@dataclass
class SensorBase(Versionable, version=1, register=False):
timestamp: datetime
# Test fixture — avoid name collisions with production classes
@dataclass
class Sample(Versionable, version=1, register=False):
value: int
Common use cases:
Test classes — test suites often define throwaway classes with generic names like
Sample. Withoutregister=False, these collide with each other across tests.Abstract base classes — intermediate classes that define shared fields but are never saved to disk.
Multiple versions in one process — if you need two definitions of the same schema (e.g. for migration testing), only one can be registered.
Duplicate name detection: if two registered classes share the same serialization name, versionable raises a
VersionableError at class definition time with a suggested fix:
VersionableError: Versionable name 'MyClass' is already registered to
mypackage.models.MyClass. Give one of the classes an explicit name to
disambiguate, e.g.: class MyClass(Versionable, ..., name="other.MyClass")
Field Serialization Rules¶
A field is included in serialization if it:
Has a type annotation
Does not have a leading underscore in its name
Is not a
ClassVar
@dataclass
class Example(Versionable, version=1, hash="..."):
included: int # serialized
_private: int = 0 # excluded — underscore prefix
class_var = "constant" # excluded — no annotation
CONSTANT: ClassVar[int] = 42 # excluded — ClassVar
metadata()¶
Inspect the schema metadata registered for a class:
from versionable import metadata
meta = metadata(SensorConfig)
meta.version # int — current version
meta.hash # str — 6-char hash
meta.name # str — serialization name
meta.fields # list[str] — serialized field names
Versionable.hash()¶
Compute the schema hash for a Versionable subclass. Use this to get the value to put in the hash= parameter:
from dataclasses import dataclass
from versionable import Versionable
@dataclass
class MyConfig(Versionable, version=1):
name: str
value: float
print(MyConfig.hash()) # e.g. "4b7866"
Then add the result to the class definition:
@dataclass
class MyConfig(Versionable, version=1, hash="4b7866"):
name: str
value: float
Reserved Keys¶
The following dunder keys are used internally by versionable and must not be used as field names or as keys in user-provided dict values:
Key |
Purpose |
|---|---|
|
Marks a dict as a serialized numpy array (JSON/YAML/TOML) |
|
YAML-only wrapper for values with no native YAML encoding |
|
Versionable metadata envelope |
|
Serialization class name (stored in metadata) |
|
Schema version (stored in metadata) |
|
Schema hash (stored in metadata) |
|
Reserved for future versionable versioning |
|
Reserved for future backend versioning |
⚠️ Warning: Using any of these as a field name or dict key may cause incorrect serialization or deserialization.