didactic 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- didactic-0.1.0/.gitignore +48 -0
- didactic-0.1.0/PKG-INFO +68 -0
- didactic-0.1.0/README.md +48 -0
- didactic-0.1.0/pyproject.toml +40 -0
- didactic-0.1.0/src/didactic/_self_describing.py +206 -0
- didactic-0.1.0/src/didactic/api.py +126 -0
- didactic-0.1.0/src/didactic/axioms/__init__.py +11 -0
- didactic-0.1.0/src/didactic/axioms/_axiom_enforcement.py +236 -0
- didactic-0.1.0/src/didactic/axioms/_axioms.py +137 -0
- didactic-0.1.0/src/didactic/cli/__init__.py +7 -0
- didactic-0.1.0/src/didactic/cli/_cli.py +241 -0
- didactic-0.1.0/src/didactic/codegen/__init__.py +54 -0
- didactic-0.1.0/src/didactic/codegen/_emitter.py +286 -0
- didactic-0.1.0/src/didactic/codegen/_json_schema.py +215 -0
- didactic-0.1.0/src/didactic/codegen/_write.py +128 -0
- didactic-0.1.0/src/didactic/codegen/io.py +278 -0
- didactic-0.1.0/src/didactic/codegen/source.py +198 -0
- didactic-0.1.0/src/didactic/fields/__init__.py +28 -0
- didactic-0.1.0/src/didactic/fields/_computed.py +149 -0
- didactic-0.1.0/src/didactic/fields/_derived.py +142 -0
- didactic-0.1.0/src/didactic/fields/_fields.py +543 -0
- didactic-0.1.0/src/didactic/fields/_refs.py +246 -0
- didactic-0.1.0/src/didactic/fields/_unions.py +255 -0
- didactic-0.1.0/src/didactic/fields/_validators.py +164 -0
- didactic-0.1.0/src/didactic/lenses/__init__.py +16 -0
- didactic-0.1.0/src/didactic/lenses/_dependent_lens.py +315 -0
- didactic-0.1.0/src/didactic/lenses/_lens.py +444 -0
- didactic-0.1.0/src/didactic/lenses/_testing.py +302 -0
- didactic-0.1.0/src/didactic/migrations/__init__.py +38 -0
- didactic-0.1.0/src/didactic/migrations/_diff.py +128 -0
- didactic-0.1.0/src/didactic/migrations/_fingerprint.py +220 -0
- didactic-0.1.0/src/didactic/migrations/_migrations.py +499 -0
- didactic-0.1.0/src/didactic/migrations/_synthesis.py +138 -0
- didactic-0.1.0/src/didactic/models/__init__.py +19 -0
- didactic-0.1.0/src/didactic/models/_config.py +104 -0
- didactic-0.1.0/src/didactic/models/_meta.py +469 -0
- didactic-0.1.0/src/didactic/models/_model.py +790 -0
- didactic-0.1.0/src/didactic/models/_root.py +124 -0
- didactic-0.1.0/src/didactic/models/_storage.py +151 -0
- didactic-0.1.0/src/didactic/py.typed +0 -0
- didactic-0.1.0/src/didactic/theory/__init__.py +8 -0
- didactic-0.1.0/src/didactic/theory/_theory.py +477 -0
- didactic-0.1.0/src/didactic/types/__init__.py +16 -0
- didactic-0.1.0/src/didactic/types/_types.py +866 -0
- didactic-0.1.0/src/didactic/types/_types_lib.py +147 -0
- didactic-0.1.0/src/didactic/types/_typing.py +136 -0
- didactic-0.1.0/src/didactic/vcs/__init__.py +10 -0
- didactic-0.1.0/src/didactic/vcs/_backref.py +232 -0
- didactic-0.1.0/src/didactic/vcs/_repo.py +328 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# dev-only working notes, design drafts, scratch
|
|
2
|
+
notes/
|
|
3
|
+
|
|
4
|
+
# python
|
|
5
|
+
__pycache__/
|
|
6
|
+
*.py[cod]
|
|
7
|
+
*$py.class
|
|
8
|
+
*.so
|
|
9
|
+
.Python
|
|
10
|
+
build/
|
|
11
|
+
dist/
|
|
12
|
+
*.egg-info/
|
|
13
|
+
.eggs/
|
|
14
|
+
*.egg
|
|
15
|
+
|
|
16
|
+
# virtualenvs
|
|
17
|
+
.venv/
|
|
18
|
+
venv/
|
|
19
|
+
env/
|
|
20
|
+
|
|
21
|
+
# uv
|
|
22
|
+
.uv/
|
|
23
|
+
|
|
24
|
+
# testing / coverage
|
|
25
|
+
.pytest_cache/
|
|
26
|
+
.coverage
|
|
27
|
+
.coverage.*
|
|
28
|
+
htmlcov/
|
|
29
|
+
.tox/
|
|
30
|
+
.nox/
|
|
31
|
+
|
|
32
|
+
# type-checkers / linters
|
|
33
|
+
.mypy_cache/
|
|
34
|
+
.ruff_cache/
|
|
35
|
+
.pyright/
|
|
36
|
+
|
|
37
|
+
# mkdocs build output
|
|
38
|
+
site/
|
|
39
|
+
|
|
40
|
+
# editors
|
|
41
|
+
.vscode/
|
|
42
|
+
.idea/
|
|
43
|
+
*.swp
|
|
44
|
+
*.swo
|
|
45
|
+
|
|
46
|
+
# os
|
|
47
|
+
.DS_Store
|
|
48
|
+
Thumbs.db
|
didactic-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: didactic
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Pydantic-class API on top of panproto: GATs, lenses, and VCS.
|
|
5
|
+
Project-URL: Homepage, https://github.com/panproto/didactic
|
|
6
|
+
Project-URL: Repository, https://github.com/panproto/didactic
|
|
7
|
+
Author-email: Aaron Steven White <aaronstevenwhite@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Keywords: gat,lenses,models,panproto,schema,validation
|
|
10
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
|
+
Classifier: Typing :: Typed
|
|
16
|
+
Requires-Python: >=3.14
|
|
17
|
+
Requires-Dist: annotated-types>=0.7
|
|
18
|
+
Requires-Dist: panproto>=0.43
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# didactic
|
|
22
|
+
|
|
23
|
+
A typed-data library for Python that uses
|
|
24
|
+
[panproto](https://github.com/panproto/panproto) as its substrate.
|
|
25
|
+
Authoring is class-based and looks like Pydantic. Underneath, every
|
|
26
|
+
Model corresponds to a panproto `Theory`, every value to a panproto
|
|
27
|
+
`Schema`, and every transformation between Models to a panproto
|
|
28
|
+
`Lens`.
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
import didactic.api as dx
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class User(dx.Model):
|
|
35
|
+
"""A user record."""
|
|
36
|
+
|
|
37
|
+
id: str
|
|
38
|
+
email: str
|
|
39
|
+
display_name: str = ""
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
u = User(id="u1", email="a@b.c")
|
|
43
|
+
u2 = u.with_(display_name="Alice")
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
This is the core distribution. Three sibling distributions
|
|
47
|
+
(`didactic-pydantic`, `didactic-settings`, `didactic-fastapi`)
|
|
48
|
+
contribute submodules under `didactic.<name>`.
|
|
49
|
+
|
|
50
|
+
## Install
|
|
51
|
+
|
|
52
|
+
didactic targets Python 3.14 and panproto 0.42+.
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
pip install didactic
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Documentation
|
|
59
|
+
|
|
60
|
+
The full documentation site is at
|
|
61
|
+
[https://panproto.dev/didactic/](https://panproto.dev/didactic/)
|
|
62
|
+
and includes a tutorial, task-oriented guides, conceptual background,
|
|
63
|
+
and per-symbol API reference. Source is in the workspace `docs/`
|
|
64
|
+
directory.
|
|
65
|
+
|
|
66
|
+
## License
|
|
67
|
+
|
|
68
|
+
MIT.
|
didactic-0.1.0/README.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# didactic
|
|
2
|
+
|
|
3
|
+
A typed-data library for Python that uses
|
|
4
|
+
[panproto](https://github.com/panproto/panproto) as its substrate.
|
|
5
|
+
Authoring is class-based and looks like Pydantic. Underneath, every
|
|
6
|
+
Model corresponds to a panproto `Theory`, every value to a panproto
|
|
7
|
+
`Schema`, and every transformation between Models to a panproto
|
|
8
|
+
`Lens`.
|
|
9
|
+
|
|
10
|
+
```python
|
|
11
|
+
import didactic.api as dx
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class User(dx.Model):
|
|
15
|
+
"""A user record."""
|
|
16
|
+
|
|
17
|
+
id: str
|
|
18
|
+
email: str
|
|
19
|
+
display_name: str = ""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
u = User(id="u1", email="a@b.c")
|
|
23
|
+
u2 = u.with_(display_name="Alice")
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
This is the core distribution. Three sibling distributions
|
|
27
|
+
(`didactic-pydantic`, `didactic-settings`, `didactic-fastapi`)
|
|
28
|
+
contribute submodules under `didactic.<name>`.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
didactic targets Python 3.14 and panproto 0.42+.
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
pip install didactic
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Documentation
|
|
39
|
+
|
|
40
|
+
The full documentation site is at
|
|
41
|
+
[https://panproto.dev/didactic/](https://panproto.dev/didactic/)
|
|
42
|
+
and includes a tutorial, task-oriented guides, conceptual background,
|
|
43
|
+
and per-symbol API reference. Source is in the workspace `docs/`
|
|
44
|
+
directory.
|
|
45
|
+
|
|
46
|
+
## License
|
|
47
|
+
|
|
48
|
+
MIT.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.27"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "didactic"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Pydantic-class API on top of panproto: GATs, lenses, and VCS."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.14"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Aaron Steven White", email = "aaronstevenwhite@gmail.com" },
|
|
14
|
+
]
|
|
15
|
+
keywords = ["schema", "models", "validation", "lenses", "gat", "panproto"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 2 - Pre-Alpha",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.14",
|
|
22
|
+
"Typing :: Typed",
|
|
23
|
+
]
|
|
24
|
+
dependencies = [
|
|
25
|
+
"panproto>=0.43",
|
|
26
|
+
"annotated-types>=0.7",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://github.com/panproto/didactic"
|
|
31
|
+
Repository = "https://github.com/panproto/didactic"
|
|
32
|
+
|
|
33
|
+
[project.scripts]
|
|
34
|
+
didactic = "didactic.cli._cli:main"
|
|
35
|
+
|
|
36
|
+
[tool.hatch.build.targets.sdist]
|
|
37
|
+
include = ["src/didactic"]
|
|
38
|
+
|
|
39
|
+
[tool.hatch.build.targets.wheel]
|
|
40
|
+
packages = ["src/didactic"]
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"""Self-describing JSON via fingerprint URIs.
|
|
2
|
+
|
|
3
|
+
A panproto-native form of self-describing data: an emitted JSON
|
|
4
|
+
payload carries a ``$schema`` URI of the form
|
|
5
|
+
``didactic://v1/<structural-fingerprint>``. A consumer that holds a
|
|
6
|
+
registry of known Theories (by fingerprint) can validate an unknown
|
|
7
|
+
payload by looking the URI up.
|
|
8
|
+
|
|
9
|
+
This is something Pydantic structurally cannot do, because Pydantic
|
|
10
|
+
has no content-addressed schema identity. didactic's structural
|
|
11
|
+
fingerprint already gives every Model a stable address; this module
|
|
12
|
+
just plumbs it through to JSON.
|
|
13
|
+
|
|
14
|
+
Examples
|
|
15
|
+
--------
|
|
16
|
+
>>> import didactic.api as dx
|
|
17
|
+
>>>
|
|
18
|
+
>>> class User(dx.Model):
|
|
19
|
+
... id: str
|
|
20
|
+
... email: str
|
|
21
|
+
>>>
|
|
22
|
+
>>> u = User(id="u1", email="ada@example.org")
|
|
23
|
+
>>> payload = dx.embed_schema_uri(u)
|
|
24
|
+
>>> payload["$schema"].startswith("didactic://v1/")
|
|
25
|
+
True
|
|
26
|
+
|
|
27
|
+
See Also
|
|
28
|
+
--------
|
|
29
|
+
didactic.migrations._fingerprint.structural_fingerprint : the underlying address.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
# Cross-translation between ``FieldValue`` and ``JsonValue`` shapes
|
|
33
|
+
# in the schema-URI registry layer.
|
|
34
|
+
# Tracked in panproto/didactic#1.
|
|
35
|
+
# pyright: reportArgumentType=false, reportReturnType=false
|
|
36
|
+
|
|
37
|
+
from __future__ import annotations
|
|
38
|
+
|
|
39
|
+
from typing import TYPE_CHECKING
|
|
40
|
+
|
|
41
|
+
if TYPE_CHECKING:
|
|
42
|
+
from didactic.models._model import Model
|
|
43
|
+
from didactic.types._typing import JsonObject
|
|
44
|
+
|
|
45
|
+
#: URI scheme prefix didactic uses for self-describing payloads.
|
|
46
|
+
URI_PREFIX = "didactic://v1/"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def schema_uri(cls: type[Model]) -> str:
|
|
50
|
+
"""Return the canonical schema URI for a Model class.
|
|
51
|
+
|
|
52
|
+
Parameters
|
|
53
|
+
----------
|
|
54
|
+
cls
|
|
55
|
+
A [Model][didactic.api.Model] subclass.
|
|
56
|
+
|
|
57
|
+
Returns
|
|
58
|
+
-------
|
|
59
|
+
str
|
|
60
|
+
``"didactic://v1/<fingerprint>"`` where ``<fingerprint>`` is
|
|
61
|
+
the Model's structural fingerprint.
|
|
62
|
+
"""
|
|
63
|
+
from didactic.migrations._fingerprint import structural_fingerprint # noqa: PLC0415
|
|
64
|
+
from didactic.theory._theory import build_theory_spec # noqa: PLC0415
|
|
65
|
+
|
|
66
|
+
return f"{URI_PREFIX}{structural_fingerprint(build_theory_spec(cls))}"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def embed_schema_uri(instance: Model) -> JsonObject:
|
|
70
|
+
"""Return ``instance.model_dump()`` with a ``$schema`` URI prepended.
|
|
71
|
+
|
|
72
|
+
Parameters
|
|
73
|
+
----------
|
|
74
|
+
instance
|
|
75
|
+
A Model instance.
|
|
76
|
+
|
|
77
|
+
Returns
|
|
78
|
+
-------
|
|
79
|
+
dict
|
|
80
|
+
The dump dict, with ``"$schema"`` set to the Model's
|
|
81
|
+
canonical URI as the first key.
|
|
82
|
+
|
|
83
|
+
Notes
|
|
84
|
+
-----
|
|
85
|
+
A consumer that knows how to resolve ``didactic://v1/<fp>`` URIs
|
|
86
|
+
can fetch the Theory by fingerprint and validate the payload
|
|
87
|
+
without knowing the original Python class.
|
|
88
|
+
"""
|
|
89
|
+
payload = instance.model_dump()
|
|
90
|
+
return {"$schema": schema_uri(type(instance)), **payload}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class FingerprintRegistry:
|
|
94
|
+
"""An in-memory mapping of structural fingerprint to Model class.
|
|
95
|
+
|
|
96
|
+
Use as the lookup side of a self-describing JSON pipeline:
|
|
97
|
+
register every Model your application understands, then
|
|
98
|
+
[validate_with_uri_lookup][didactic.api.validate_with_uri_lookup]
|
|
99
|
+
can resolve an unknown payload's ``$schema`` URI back to a class.
|
|
100
|
+
|
|
101
|
+
Examples
|
|
102
|
+
--------
|
|
103
|
+
>>> import didactic.api as dx
|
|
104
|
+
>>> class User(dx.Model):
|
|
105
|
+
... id: str
|
|
106
|
+
>>>
|
|
107
|
+
>>> reg = dx.FingerprintRegistry()
|
|
108
|
+
>>> reg.register(User)
|
|
109
|
+
>>>
|
|
110
|
+
>>> u = User(id="u1")
|
|
111
|
+
>>> payload = dx.embed_schema_uri(u)
|
|
112
|
+
>>> back = dx.validate_with_uri_lookup(payload, reg)
|
|
113
|
+
>>> back == u
|
|
114
|
+
True
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
__slots__ = ("_by_fingerprint",)
|
|
118
|
+
|
|
119
|
+
def __init__(self) -> None:
|
|
120
|
+
self._by_fingerprint: dict[str, type[Model]] = {}
|
|
121
|
+
|
|
122
|
+
def register[M: Model](self, cls: type[M]) -> type[M]:
|
|
123
|
+
"""Register ``cls`` under its structural fingerprint."""
|
|
124
|
+
from didactic.migrations._fingerprint import ( # noqa: PLC0415
|
|
125
|
+
structural_fingerprint,
|
|
126
|
+
)
|
|
127
|
+
from didactic.theory._theory import build_theory_spec # noqa: PLC0415
|
|
128
|
+
|
|
129
|
+
fp = structural_fingerprint(build_theory_spec(cls))
|
|
130
|
+
self._by_fingerprint[fp] = cls
|
|
131
|
+
return cls
|
|
132
|
+
|
|
133
|
+
def lookup(self, uri: str) -> type[Model] | None:
|
|
134
|
+
"""Resolve a ``didactic://v1/<fp>`` URI to a registered class."""
|
|
135
|
+
if not uri.startswith(URI_PREFIX):
|
|
136
|
+
return None
|
|
137
|
+
fp = uri[len(URI_PREFIX) :]
|
|
138
|
+
return self._by_fingerprint.get(fp)
|
|
139
|
+
|
|
140
|
+
def __contains__(self, cls_or_uri: object) -> bool:
|
|
141
|
+
if isinstance(cls_or_uri, str):
|
|
142
|
+
return self.lookup(cls_or_uri) is not None
|
|
143
|
+
if isinstance(cls_or_uri, type):
|
|
144
|
+
from didactic.migrations._fingerprint import ( # noqa: PLC0415
|
|
145
|
+
structural_fingerprint,
|
|
146
|
+
)
|
|
147
|
+
from didactic.theory._theory import build_theory_spec # noqa: PLC0415
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
structural_fingerprint(build_theory_spec(cls_or_uri))
|
|
151
|
+
in self._by_fingerprint
|
|
152
|
+
)
|
|
153
|
+
return False
|
|
154
|
+
|
|
155
|
+
def __len__(self) -> int:
|
|
156
|
+
return len(self._by_fingerprint)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def validate_with_uri_lookup(
|
|
160
|
+
payload: JsonObject,
|
|
161
|
+
registry: FingerprintRegistry,
|
|
162
|
+
) -> Model:
|
|
163
|
+
"""Validate ``payload`` against the Model named by its ``$schema`` URI.
|
|
164
|
+
|
|
165
|
+
Parameters
|
|
166
|
+
----------
|
|
167
|
+
payload
|
|
168
|
+
A dict that includes a ``$schema`` key.
|
|
169
|
+
registry
|
|
170
|
+
A [FingerprintRegistry][didactic.api.FingerprintRegistry] mapping
|
|
171
|
+
URIs to Model classes.
|
|
172
|
+
|
|
173
|
+
Returns
|
|
174
|
+
-------
|
|
175
|
+
Model
|
|
176
|
+
A validated Model instance whose class came from the
|
|
177
|
+
registry.
|
|
178
|
+
|
|
179
|
+
Raises
|
|
180
|
+
------
|
|
181
|
+
LookupError
|
|
182
|
+
If the URI is not registered.
|
|
183
|
+
KeyError
|
|
184
|
+
If the payload has no ``$schema`` key.
|
|
185
|
+
"""
|
|
186
|
+
if "$schema" not in payload:
|
|
187
|
+
msg = "validate_with_uri_lookup: payload has no $schema key"
|
|
188
|
+
raise KeyError(msg)
|
|
189
|
+
|
|
190
|
+
uri = payload["$schema"]
|
|
191
|
+
cls = registry.lookup(uri)
|
|
192
|
+
if cls is None:
|
|
193
|
+
msg = f"validate_with_uri_lookup: no registered model for URI {uri!r}"
|
|
194
|
+
raise LookupError(msg)
|
|
195
|
+
|
|
196
|
+
body = {k: v for k, v in payload.items() if k != "$schema"}
|
|
197
|
+
return cls.model_validate(body)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
__all__ = [
|
|
201
|
+
"URI_PREFIX",
|
|
202
|
+
"FingerprintRegistry",
|
|
203
|
+
"embed_schema_uri",
|
|
204
|
+
"schema_uri",
|
|
205
|
+
"validate_with_uri_lookup",
|
|
206
|
+
]
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""Aggregator module for the didactic API.
|
|
2
|
+
|
|
3
|
+
`didactic` lets you author class-based, declarative data models the way
|
|
4
|
+
[Pydantic][pydantic] does, while the underlying values are
|
|
5
|
+
[panproto][panproto] [Theory][panproto.Theory] and
|
|
6
|
+
[Schema][panproto.Schema] instances rather than ad hoc Python objects with
|
|
7
|
+
bolt-on validation. The selling point is everything you get *for free*
|
|
8
|
+
once your data is panproto-native: lenses, dependent optics, theory
|
|
9
|
+
colimits, schema migrations as data, vertex-level VCS, and cross-language
|
|
10
|
+
semantic export.
|
|
11
|
+
|
|
12
|
+
[pydantic]: https://docs.pydantic.dev/
|
|
13
|
+
[panproto]: https://github.com/panproto/panproto
|
|
14
|
+
|
|
15
|
+
Notes
|
|
16
|
+
-----
|
|
17
|
+
The conventional alias for this module is ``dx``::
|
|
18
|
+
|
|
19
|
+
import didactic.api as dx
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class User(dx.Model):
|
|
23
|
+
id: str
|
|
24
|
+
email: str
|
|
25
|
+
|
|
26
|
+
The ``didactic`` namespace itself is a PEP 420 implicit namespace
|
|
27
|
+
package; the four distributions (``didactic`` / ``didactic-pydantic`` /
|
|
28
|
+
``didactic-settings`` / ``didactic-fastapi``) each contribute a
|
|
29
|
+
sub-package (``didactic.api``, ``didactic.pydantic``,
|
|
30
|
+
``didactic.settings``, ``didactic.fastapi``) without an
|
|
31
|
+
``__init__.py`` at the namespace root. This lets static type checkers
|
|
32
|
+
resolve cross-distribution imports without a ``pkgutil`` workaround.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
from didactic import codegen
|
|
36
|
+
from didactic._self_describing import (
|
|
37
|
+
FingerprintRegistry,
|
|
38
|
+
embed_schema_uri,
|
|
39
|
+
schema_uri,
|
|
40
|
+
validate_with_uri_lookup,
|
|
41
|
+
)
|
|
42
|
+
from didactic.axioms._axioms import Axiom, axiom
|
|
43
|
+
from didactic.fields._computed import computed
|
|
44
|
+
from didactic.fields._derived import derived
|
|
45
|
+
from didactic.fields._fields import Field, FieldSpec, field
|
|
46
|
+
from didactic.fields._refs import Backref, Embed, Ref
|
|
47
|
+
from didactic.fields._unions import TaggedUnion
|
|
48
|
+
from didactic.fields._validators import (
|
|
49
|
+
ValidationError,
|
|
50
|
+
ValidationErrorEntry,
|
|
51
|
+
validates,
|
|
52
|
+
)
|
|
53
|
+
from didactic.lenses import _testing as testing
|
|
54
|
+
from didactic.lenses._dependent_lens import DependentLens
|
|
55
|
+
from didactic.lenses._lens import Iso, Lens, Mapping, lens
|
|
56
|
+
from didactic.migrations._diff import classify_change, diff, is_breaking_change
|
|
57
|
+
from didactic.migrations._migrations import (
|
|
58
|
+
load_registry,
|
|
59
|
+
migrate,
|
|
60
|
+
register_migration,
|
|
61
|
+
save_registry,
|
|
62
|
+
)
|
|
63
|
+
from didactic.migrations._synthesis import SynthesisResult, synthesise_migration
|
|
64
|
+
from didactic.models._config import DEFAULT_CONFIG, ExtraPolicy, ModelConfig
|
|
65
|
+
from didactic.models._model import BaseModel, Model
|
|
66
|
+
from didactic.models._root import RootModel, TypeAdapter
|
|
67
|
+
from didactic.types import _types_lib as types
|
|
68
|
+
from didactic.vcs._backref import ModelPool, resolve_backrefs
|
|
69
|
+
from didactic.vcs._repo import Repository
|
|
70
|
+
|
|
71
|
+
__version__ = "0.1.0"
|
|
72
|
+
|
|
73
|
+
#: Conventional namespace for lens utilities (`dx.lens.identity(...)`,
|
|
74
|
+
#: `dx.lens.Lens`, etc.). The ``lens`` name doubles as a decorator
|
|
75
|
+
#: (``@dx.lens(A, B)``) and as a module-style namespace. The attributes
|
|
76
|
+
#: are bound by ``_LensNamespace.__init__`` in :mod:`didactic.lenses._lens`.
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
__all__ = [
|
|
80
|
+
"DEFAULT_CONFIG",
|
|
81
|
+
"Axiom",
|
|
82
|
+
"Backref",
|
|
83
|
+
"BaseModel",
|
|
84
|
+
"DependentLens",
|
|
85
|
+
"Embed",
|
|
86
|
+
"ExtraPolicy",
|
|
87
|
+
"Field",
|
|
88
|
+
"FieldSpec",
|
|
89
|
+
"FingerprintRegistry",
|
|
90
|
+
"Iso",
|
|
91
|
+
"Lens",
|
|
92
|
+
"Mapping",
|
|
93
|
+
"Model",
|
|
94
|
+
"ModelConfig",
|
|
95
|
+
"ModelPool",
|
|
96
|
+
"Ref",
|
|
97
|
+
"Repository",
|
|
98
|
+
"RootModel",
|
|
99
|
+
"SynthesisResult",
|
|
100
|
+
"TaggedUnion",
|
|
101
|
+
"TypeAdapter",
|
|
102
|
+
"ValidationError",
|
|
103
|
+
"ValidationErrorEntry",
|
|
104
|
+
"__version__",
|
|
105
|
+
"axiom",
|
|
106
|
+
"classify_change",
|
|
107
|
+
"codegen",
|
|
108
|
+
"computed",
|
|
109
|
+
"derived",
|
|
110
|
+
"diff",
|
|
111
|
+
"embed_schema_uri",
|
|
112
|
+
"field",
|
|
113
|
+
"is_breaking_change",
|
|
114
|
+
"lens",
|
|
115
|
+
"load_registry",
|
|
116
|
+
"migrate",
|
|
117
|
+
"register_migration",
|
|
118
|
+
"resolve_backrefs",
|
|
119
|
+
"save_registry",
|
|
120
|
+
"schema_uri",
|
|
121
|
+
"synthesise_migration",
|
|
122
|
+
"testing",
|
|
123
|
+
"types",
|
|
124
|
+
"validate_with_uri_lookup",
|
|
125
|
+
"validates",
|
|
126
|
+
]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Class-level axioms and their construction-time enforcement."""
|
|
2
|
+
|
|
3
|
+
from didactic.axioms._axiom_enforcement import check_class_axioms
|
|
4
|
+
from didactic.axioms._axioms import Axiom, axiom, collect_class_axioms
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"Axiom",
|
|
8
|
+
"axiom",
|
|
9
|
+
"check_class_axioms",
|
|
10
|
+
"collect_class_axioms",
|
|
11
|
+
]
|