didactic 0.4.2__tar.gz → 0.4.3__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.4.2 → didactic-0.4.3}/PKG-INFO +1 -1
- {didactic-0.4.2 → didactic-0.4.3}/pyproject.toml +1 -1
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/api.py +1 -1
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/fields/_unions.py +56 -3
- {didactic-0.4.2 → didactic-0.4.3}/.gitignore +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/README.md +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/_self_describing.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/axioms/__init__.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/axioms/_axiom_enforcement.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/axioms/_axioms.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/cli/__init__.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/cli/_cli.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/codegen/__init__.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/codegen/_emitter.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/codegen/_json_schema.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/codegen/_write.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/codegen/io.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/codegen/source.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/fields/__init__.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/fields/_computed.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/fields/_derived.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/fields/_fields.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/fields/_refs.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/fields/_validators.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/lenses/__init__.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/lenses/_dependent_lens.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/lenses/_lens.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/lenses/_testing.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/migrations/__init__.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/migrations/_diff.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/migrations/_fingerprint.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/migrations/_migrations.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/migrations/_synthesis.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/models/__init__.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/models/_config.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/models/_meta.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/models/_model.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/models/_root.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/models/_storage.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/py.typed +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/theory/__init__.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/theory/_theory.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/types/__init__.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/types/_types.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/types/_types_lib.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/types/_typing.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/vcs/__init__.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/vcs/_backref.py +0 -0
- {didactic-0.4.2 → didactic-0.4.3}/src/didactic/vcs/_repo.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: didactic
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.3
|
|
4
4
|
Summary: Pydantic-class API on top of panproto: GATs, lenses, and VCS.
|
|
5
5
|
Project-URL: Homepage, https://github.com/panproto/didactic
|
|
6
6
|
Project-URL: Repository, https://github.com/panproto/didactic
|
|
@@ -68,7 +68,7 @@ from didactic.types import _types_lib as types
|
|
|
68
68
|
from didactic.vcs._backref import ModelPool, resolve_backrefs
|
|
69
69
|
from didactic.vcs._repo import Repository
|
|
70
70
|
|
|
71
|
-
__version__ = "0.4.
|
|
71
|
+
__version__ = "0.4.3"
|
|
72
72
|
|
|
73
73
|
#: Conventional namespace for lens utilities (`dx.lens.identity(...)`,
|
|
74
74
|
#: `dx.lens.Lens`, etc.). The ``lens`` name doubles as a decorator
|
|
@@ -44,7 +44,16 @@ didactic.models._meta.ModelMeta : the metaclass; unchanged.
|
|
|
44
44
|
from __future__ import annotations
|
|
45
45
|
|
|
46
46
|
import annotationlib
|
|
47
|
-
|
|
47
|
+
import sys
|
|
48
|
+
from typing import (
|
|
49
|
+
TYPE_CHECKING,
|
|
50
|
+
ClassVar,
|
|
51
|
+
Literal,
|
|
52
|
+
Self,
|
|
53
|
+
cast,
|
|
54
|
+
get_args,
|
|
55
|
+
get_origin,
|
|
56
|
+
)
|
|
48
57
|
|
|
49
58
|
from didactic.fields._validators import ValidationError, ValidationErrorEntry
|
|
50
59
|
from didactic.models._model import Model
|
|
@@ -139,6 +148,12 @@ class TaggedUnion(Model):
|
|
|
139
148
|
# NOTE: we read annotations directly here rather than through the
|
|
140
149
|
# metaclass's `__field_specs__` because `__init_subclass__` fires
|
|
141
150
|
# *during* the metaclass's __new__, before __field_specs__ is set.
|
|
151
|
+
# FORWARDREF gives us un-evaluated forms when the class uses
|
|
152
|
+
# ``from __future__ import annotations``; the discriminator field
|
|
153
|
+
# is then re-resolved in the class's defining module so qualified
|
|
154
|
+
# spellings (``typing.Literal[...]``) and aliased imports
|
|
155
|
+
# (``from typing import Literal as L``) are handled the same as
|
|
156
|
+
# the bare ``Literal[...]`` form.
|
|
142
157
|
annotations = annotationlib.get_annotations(
|
|
143
158
|
cls,
|
|
144
159
|
format=annotationlib.Format.FORWARDREF,
|
|
@@ -150,12 +165,16 @@ class TaggedUnion(Model):
|
|
|
150
165
|
)
|
|
151
166
|
raise TypeError(msg)
|
|
152
167
|
|
|
168
|
+
disc_annotation = _resolve_discriminator_annotation(
|
|
169
|
+
cls, annotations[disc_field]
|
|
170
|
+
)
|
|
171
|
+
|
|
153
172
|
# extract the Literal value(s)
|
|
154
|
-
values = _literal_values(
|
|
173
|
+
values = _literal_values(disc_annotation)
|
|
155
174
|
if not values:
|
|
156
175
|
msg = (
|
|
157
176
|
f"variant {cls.__name__}.{disc_field} must be annotated as "
|
|
158
|
-
f"Literal[...] with at least one value; got {
|
|
177
|
+
f"Literal[...] with at least one value; got {disc_annotation!r}"
|
|
159
178
|
)
|
|
160
179
|
raise TypeError(msg)
|
|
161
180
|
|
|
@@ -252,6 +271,40 @@ def _literal_values(annotation: Opaque) -> tuple[FieldValue, ...]:
|
|
|
252
271
|
return ()
|
|
253
272
|
|
|
254
273
|
|
|
274
|
+
def _resolve_discriminator_annotation(cls: type, raw: Opaque) -> Opaque:
|
|
275
|
+
"""Return the live annotation object for ``cls.disc_field``.
|
|
276
|
+
|
|
277
|
+
Under ``from __future__ import annotations`` (or any context where
|
|
278
|
+
the class's ``__annotations__`` carries strings), the raw entry
|
|
279
|
+
will not satisfy ``get_origin(...) is Literal``. Evaluate the
|
|
280
|
+
string in the class's defining module so qualified spellings
|
|
281
|
+
(``typing.Literal[...]``), aliased imports
|
|
282
|
+
(``from typing import Literal as L``), and bare ``Literal[...]``
|
|
283
|
+
all reach the same live ``Literal`` object.
|
|
284
|
+
|
|
285
|
+
Only the discriminator's own annotation is evaluated; other
|
|
286
|
+
fields on the class may legitimately carry forward references
|
|
287
|
+
that don't resolve at class-creation time, and resolving them
|
|
288
|
+
here would mask those into spurious failures of this check.
|
|
289
|
+
|
|
290
|
+
The raw annotation is returned unchanged when evaluation fails;
|
|
291
|
+
the caller surfaces the original value in the error message,
|
|
292
|
+
which is friendlier than a stray ``NameError``.
|
|
293
|
+
"""
|
|
294
|
+
if get_origin(raw) is Literal:
|
|
295
|
+
return raw
|
|
296
|
+
if not isinstance(raw, str):
|
|
297
|
+
return raw
|
|
298
|
+
module = sys.modules.get(getattr(cls, "__module__", ""))
|
|
299
|
+
if module is None:
|
|
300
|
+
return raw
|
|
301
|
+
try:
|
|
302
|
+
evaluated = eval(raw, vars(module))
|
|
303
|
+
except Exception:
|
|
304
|
+
return raw
|
|
305
|
+
return cast("Opaque", evaluated)
|
|
306
|
+
|
|
307
|
+
|
|
255
308
|
__all__ = [
|
|
256
309
|
"TaggedUnion",
|
|
257
310
|
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|