ducktools-classbuilder 0.8.1__py3-none-any.whl → 0.8.2__py3-none-any.whl
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.
Potentially problematic release.
This version of ducktools-classbuilder might be problematic. Click here for more details.
- ducktools/classbuilder/__init__.py +15 -5
- ducktools/classbuilder/__init__.pyi +3 -0
- ducktools/classbuilder/_version.py +2 -2
- ducktools/classbuilder/annotations.py +47 -10
- ducktools/classbuilder/prefab.py +6 -1
- ducktools/classbuilder/prefab.pyi +5 -2
- {ducktools_classbuilder-0.8.1.dist-info → ducktools_classbuilder-0.8.2.dist-info}/METADATA +1 -1
- ducktools_classbuilder-0.8.2.dist-info/RECORD +13 -0
- ducktools_classbuilder-0.8.1.dist-info/RECORD +0 -13
- {ducktools_classbuilder-0.8.1.dist-info → ducktools_classbuilder-0.8.2.dist-info}/LICENSE +0 -0
- {ducktools_classbuilder-0.8.1.dist-info → ducktools_classbuilder-0.8.2.dist-info}/WHEEL +0 -0
- {ducktools_classbuilder-0.8.1.dist-info → ducktools_classbuilder-0.8.2.dist-info}/top_level.txt +0 -0
|
@@ -99,11 +99,16 @@ def _get_inst_fields(inst):
|
|
|
99
99
|
# As 'None' can be a meaningful value we need a sentinel value
|
|
100
100
|
# to use to show no value has been provided.
|
|
101
101
|
class _NothingType:
|
|
102
|
+
def __init__(self, custom=None):
|
|
103
|
+
self.custom = custom
|
|
102
104
|
def __repr__(self):
|
|
105
|
+
if self.custom:
|
|
106
|
+
return f"<{self.custom} NOTHING OBJECT>"
|
|
103
107
|
return "<NOTHING OBJECT>"
|
|
104
108
|
|
|
105
109
|
|
|
106
110
|
NOTHING = _NothingType()
|
|
111
|
+
FIELD_NOTHING = _NothingType("FIELD")
|
|
107
112
|
|
|
108
113
|
|
|
109
114
|
# KW_ONLY sentinel 'type' to use to indicate all subsequent attributes are
|
|
@@ -432,11 +437,11 @@ frozen_setattr_maker = MethodMaker("__setattr__", frozen_setattr_generator)
|
|
|
432
437
|
frozen_delattr_maker = MethodMaker("__delattr__", frozen_delattr_generator)
|
|
433
438
|
default_methods = frozenset({init_maker, repr_maker, eq_maker})
|
|
434
439
|
|
|
435
|
-
# Special `__init__` maker for 'Field' subclasses
|
|
440
|
+
# Special `__init__` maker for 'Field' subclasses - needs its own NOTHING option
|
|
436
441
|
_field_init_maker = MethodMaker(
|
|
437
442
|
funcname="__init__",
|
|
438
443
|
code_generator=get_init_generator(
|
|
439
|
-
null=
|
|
444
|
+
null=FIELD_NOTHING,
|
|
440
445
|
extra_code=["self.validate_field()"],
|
|
441
446
|
)
|
|
442
447
|
)
|
|
@@ -649,7 +654,7 @@ class Field(metaclass=SlotMakerMeta):
|
|
|
649
654
|
|
|
650
655
|
def validate_field(self):
|
|
651
656
|
cls_name = self.__class__.__name__
|
|
652
|
-
if self.default is not
|
|
657
|
+
if type(self.default) is not _NothingType and type(self.default_factory) is not _NothingType:
|
|
653
658
|
raise AttributeError(
|
|
654
659
|
f"{cls_name} cannot define both a default value and a default factory."
|
|
655
660
|
)
|
|
@@ -807,6 +812,7 @@ def make_annotation_gatherer(
|
|
|
807
812
|
def make_field_gatherer(
|
|
808
813
|
field_type=Field,
|
|
809
814
|
leave_default_values=False,
|
|
815
|
+
assign_types=True,
|
|
810
816
|
):
|
|
811
817
|
def field_attribute_gatherer(cls_or_ns):
|
|
812
818
|
if isinstance(cls_or_ns, (_MappingProxyType, dict)):
|
|
@@ -819,7 +825,11 @@ def make_field_gatherer(
|
|
|
819
825
|
for k, v in cls_dict.items()
|
|
820
826
|
if isinstance(v, field_type)
|
|
821
827
|
}
|
|
822
|
-
|
|
828
|
+
|
|
829
|
+
if assign_types:
|
|
830
|
+
cls_annotations = get_ns_annotations(cls_dict)
|
|
831
|
+
else:
|
|
832
|
+
cls_annotations = {}
|
|
823
833
|
|
|
824
834
|
cls_modifications = {}
|
|
825
835
|
|
|
@@ -830,7 +840,7 @@ def make_field_gatherer(
|
|
|
830
840
|
else:
|
|
831
841
|
cls_modifications[name] = NOTHING
|
|
832
842
|
|
|
833
|
-
if (anno := cls_annotations.get(name, NOTHING)) is not NOTHING:
|
|
843
|
+
if assign_types and (anno := cls_annotations.get(name, NOTHING)) is not NOTHING:
|
|
834
844
|
cls_attributes[name] = field_type.from_field(attrib, type=anno)
|
|
835
845
|
|
|
836
846
|
return cls_attributes, cls_modifications
|
|
@@ -24,8 +24,10 @@ def get_methods(cls: type) -> types.MappingProxyType[str, MethodMaker]: ...
|
|
|
24
24
|
def _get_inst_fields(inst: typing.Any) -> dict[str, typing.Any]: ...
|
|
25
25
|
|
|
26
26
|
class _NothingType:
|
|
27
|
+
def __init__(self, custom: str | None = ...) -> None: ...
|
|
27
28
|
def __repr__(self) -> str: ...
|
|
28
29
|
NOTHING: _NothingType
|
|
30
|
+
FIELD_NOTHING: _NothingType
|
|
29
31
|
|
|
30
32
|
# noinspection PyPep8Naming
|
|
31
33
|
class _KW_ONLY_TYPE:
|
|
@@ -191,6 +193,7 @@ def make_annotation_gatherer(
|
|
|
191
193
|
def make_field_gatherer(
|
|
192
194
|
field_type: type[_FieldType],
|
|
193
195
|
leave_default_values: bool = False,
|
|
196
|
+
assign_types: bool = True,
|
|
194
197
|
) -> Callable[[type | _CopiableMappings], tuple[dict[str, _FieldType], dict[str, typing.Any]]]: ...
|
|
195
198
|
|
|
196
199
|
@typing.overload
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
__version__ = "0.8.
|
|
2
|
-
__version_tuple__ = (0, 8,
|
|
1
|
+
__version__ = "0.8.2"
|
|
2
|
+
__version_tuple__ = (0, 8, 2)
|
|
@@ -22,6 +22,44 @@
|
|
|
22
22
|
import sys
|
|
23
23
|
|
|
24
24
|
|
|
25
|
+
class _LazyAnnotationLib:
|
|
26
|
+
def __init__(self):
|
|
27
|
+
if sys.version_info < (3, 14):
|
|
28
|
+
self.annotationlib_unavailable = True
|
|
29
|
+
else:
|
|
30
|
+
self.annotationlib_unavailable = None
|
|
31
|
+
|
|
32
|
+
def __getattr__(self, item):
|
|
33
|
+
if self.annotationlib_unavailable:
|
|
34
|
+
raise ImportError("'annotationlib' is not available")
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
import annotationlib
|
|
38
|
+
except ImportError:
|
|
39
|
+
self.annotationlib_unavailable = True
|
|
40
|
+
raise ImportError("'annotationlib' is not available")
|
|
41
|
+
else:
|
|
42
|
+
self.Format = annotationlib.Format
|
|
43
|
+
self.call_annotate_function = annotationlib.call_annotate_function
|
|
44
|
+
|
|
45
|
+
# This function keeps getting changed and renamed
|
|
46
|
+
get_ns_annotate = getattr(annotationlib, "get_annotate_from_class_namespace", None)
|
|
47
|
+
if get_ns_annotate is None:
|
|
48
|
+
get_ns_annotate = getattr(annotationlib, "get_annotate_function")
|
|
49
|
+
self.get_ns_annotate = get_ns_annotate
|
|
50
|
+
|
|
51
|
+
if item == "Format":
|
|
52
|
+
return self.Format
|
|
53
|
+
elif item == "call_annotate_function":
|
|
54
|
+
return self.call_annotate_function
|
|
55
|
+
elif item == "get_ns_annotate":
|
|
56
|
+
return get_ns_annotate
|
|
57
|
+
|
|
58
|
+
raise AttributeError(f"{item!r} is not available from this lazy importer")
|
|
59
|
+
|
|
60
|
+
_lazy_annotationlib = _LazyAnnotationLib()
|
|
61
|
+
|
|
62
|
+
|
|
25
63
|
def get_ns_annotations(ns):
|
|
26
64
|
"""
|
|
27
65
|
Given a class namespace, attempt to retrieve the
|
|
@@ -35,19 +73,18 @@ def get_ns_annotations(ns):
|
|
|
35
73
|
if annotations is not None:
|
|
36
74
|
annotations = annotations.copy()
|
|
37
75
|
else:
|
|
38
|
-
# See if we're using PEP-649 annotations
|
|
39
|
-
# Guarding this with a try/except instead of a version check
|
|
40
|
-
# In case there's a change and PEP-649 somehow doesn't make 3.14
|
|
41
76
|
try:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
pass
|
|
45
|
-
else:
|
|
46
|
-
annotate = ns.get("__annotate__") # Works in the alphas, but may break
|
|
77
|
+
# See if we're using PEP-649 annotations
|
|
78
|
+
annotate = ns.get("__annotate__") # Works in the early alphas
|
|
47
79
|
if not annotate:
|
|
48
|
-
annotate =
|
|
80
|
+
annotate = _lazy_annotationlib.get_ns_annotate(ns)
|
|
49
81
|
if annotate:
|
|
50
|
-
annotations = call_annotate_function(
|
|
82
|
+
annotations = _lazy_annotationlib.call_annotate_function(
|
|
83
|
+
annotate,
|
|
84
|
+
format=_lazy_annotationlib.Format.FORWARDREF
|
|
85
|
+
)
|
|
86
|
+
except ImportError:
|
|
87
|
+
pass
|
|
51
88
|
|
|
52
89
|
if annotations is None:
|
|
53
90
|
annotations = {}
|
ducktools/classbuilder/prefab.py
CHANGED
|
@@ -26,7 +26,7 @@ A 'prebuilt' implementation of class generation.
|
|
|
26
26
|
Includes pre and post init functions along with other methods.
|
|
27
27
|
"""
|
|
28
28
|
from . import (
|
|
29
|
-
INTERNALS_DICT, NOTHING,
|
|
29
|
+
INTERNALS_DICT, NOTHING, FIELD_NOTHING,
|
|
30
30
|
Field, MethodMaker, GatheredFields, GeneratedCode, SlotMakerMeta,
|
|
31
31
|
builder, get_flags, get_fields,
|
|
32
32
|
make_unified_gatherer,
|
|
@@ -289,10 +289,12 @@ class Attribute(Field):
|
|
|
289
289
|
:param kw_only: Make this argument keyword only in init
|
|
290
290
|
:param serialize: Include this attribute in methods that serialize to dict
|
|
291
291
|
:param doc: Parameter documentation for slotted classes
|
|
292
|
+
:param metadata: Additional non-construction related metadata
|
|
292
293
|
:param type: Type of this attribute (for slotted classes)
|
|
293
294
|
"""
|
|
294
295
|
iter: bool = True
|
|
295
296
|
serialize: bool = True
|
|
297
|
+
metadata: dict = Field(default=FIELD_NOTHING, default_factory=dict)
|
|
296
298
|
|
|
297
299
|
|
|
298
300
|
# noinspection PyShadowingBuiltins
|
|
@@ -309,6 +311,7 @@ def attribute(
|
|
|
309
311
|
exclude_field=False,
|
|
310
312
|
private=False,
|
|
311
313
|
doc=None,
|
|
314
|
+
metadata=None,
|
|
312
315
|
type=NOTHING,
|
|
313
316
|
):
|
|
314
317
|
"""
|
|
@@ -326,6 +329,7 @@ def attribute(
|
|
|
326
329
|
:param exclude_field: Shorthand for setting repr, compare, iter and serialize to False
|
|
327
330
|
:param private: Short for init, repr, compare, iter, serialize = False, must have default or factory
|
|
328
331
|
:param doc: Parameter documentation for slotted classes
|
|
332
|
+
:param metadata: Dictionary for additional non-construction metadata
|
|
329
333
|
:param type: Type of this attribute (for slotted classes)
|
|
330
334
|
|
|
331
335
|
:return: Attribute generated with these parameters.
|
|
@@ -356,6 +360,7 @@ def attribute(
|
|
|
356
360
|
serialize=serialize,
|
|
357
361
|
doc=doc,
|
|
358
362
|
type=type,
|
|
363
|
+
metadata=metadata,
|
|
359
364
|
)
|
|
360
365
|
|
|
361
366
|
|
|
@@ -50,6 +50,7 @@ class Attribute(Field):
|
|
|
50
50
|
|
|
51
51
|
iter: bool
|
|
52
52
|
serialize: bool
|
|
53
|
+
metadata: dict
|
|
53
54
|
|
|
54
55
|
def __init__(
|
|
55
56
|
self,
|
|
@@ -64,6 +65,7 @@ class Attribute(Field):
|
|
|
64
65
|
iter: bool = True,
|
|
65
66
|
kw_only: bool = False,
|
|
66
67
|
serialize: bool = True,
|
|
68
|
+
metadata: dict | None = None,
|
|
67
69
|
) -> None: ...
|
|
68
70
|
|
|
69
71
|
def __repr__(self) -> str: ...
|
|
@@ -74,8 +76,6 @@ def attribute(
|
|
|
74
76
|
*,
|
|
75
77
|
default: typing.Any | _NothingType = NOTHING,
|
|
76
78
|
default_factory: typing.Any | _NothingType = NOTHING,
|
|
77
|
-
type: type | _NothingType = NOTHING,
|
|
78
|
-
doc: str | None = None,
|
|
79
79
|
init: bool = True,
|
|
80
80
|
repr: bool = True,
|
|
81
81
|
compare: bool = True,
|
|
@@ -84,6 +84,9 @@ def attribute(
|
|
|
84
84
|
serialize: bool = True,
|
|
85
85
|
exclude_field: bool = False,
|
|
86
86
|
private: bool = False,
|
|
87
|
+
doc: str | None = None,
|
|
88
|
+
metadata: dict | None = None,
|
|
89
|
+
type: type | _NothingType = NOTHING,
|
|
87
90
|
) -> Attribute: ...
|
|
88
91
|
|
|
89
92
|
def prefab_gatherer(cls_or_ns: type | MappingProxyType) -> tuple[dict[str, Attribute], dict[str, typing.Any]]: ...
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
ducktools/classbuilder/__init__.py,sha256=6rU3erZbq2eNJ8P1zr948J7aHDFqyS8w5b2mKUEKrj8,32731
|
|
2
|
+
ducktools/classbuilder/__init__.pyi,sha256=47rT22Copd8gKiw26sTdAkA99NjMMwX6WsYsN0EEEtY,8060
|
|
3
|
+
ducktools/classbuilder/_version.py,sha256=pvHmMOCIcGaDbNqnUSZZhN0tL0wqoa_J1NwZNNPVaEQ,52
|
|
4
|
+
ducktools/classbuilder/annotations.py,sha256=nh6hWpplaWREvUqo8sFkVq6Y8Jgx-3WtY3rWuzjVQ8c,4853
|
|
5
|
+
ducktools/classbuilder/annotations.pyi,sha256=c5vYtULdDgMYWtkzeYMsHIbmnEuT2Ru-nNZieWvYuQ4,247
|
|
6
|
+
ducktools/classbuilder/prefab.py,sha256=Lg3gBn3-09036vn5PNvbXt4m8srEo0JP3tf7rxdqk1I,24369
|
|
7
|
+
ducktools/classbuilder/prefab.pyi,sha256=qHK6KvV4C5fky4KCqskpoTUHKD4wCVxTdyGaydrATOE,4582
|
|
8
|
+
ducktools/classbuilder/py.typed,sha256=la67KBlbjXN-_-DfGNcdOcjYumVpKG_Tkw-8n5dnGB4,8
|
|
9
|
+
ducktools_classbuilder-0.8.2.dist-info/LICENSE,sha256=6Thz9Dbw8R4fWInl6sGl8Rj3UnKnRbDwrc6jZerpugQ,1070
|
|
10
|
+
ducktools_classbuilder-0.8.2.dist-info/METADATA,sha256=hGg-ZxO2sm9yYk7EWcHGlRn7gtnJrwVlUPmypsvRq8I,9636
|
|
11
|
+
ducktools_classbuilder-0.8.2.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
|
|
12
|
+
ducktools_classbuilder-0.8.2.dist-info/top_level.txt,sha256=uSDLtio3ZFqdwcsMJ2O5yhjB4Q3ytbBWbA8rJREganc,10
|
|
13
|
+
ducktools_classbuilder-0.8.2.dist-info/RECORD,,
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
ducktools/classbuilder/__init__.py,sha256=LfPJ6WgDye3juzSovwicQpFKrEmBbl6dPmhxbVLwRaQ,32381
|
|
2
|
-
ducktools/classbuilder/__init__.pyi,sha256=t2KrEsEhKFbLpl0QeX0CiCy_VTRFRZO_2ABdfndRfZY,7939
|
|
3
|
-
ducktools/classbuilder/_version.py,sha256=6rcSpKS_Wuf2hPZDjzhezNIcky7glqQWxkKJXThFcLc,52
|
|
4
|
-
ducktools/classbuilder/annotations.py,sha256=BJRZnGbsXeoXCpXIgjfEDNVNvtoz65LH2VKZsasj_kw,3601
|
|
5
|
-
ducktools/classbuilder/annotations.pyi,sha256=c5vYtULdDgMYWtkzeYMsHIbmnEuT2Ru-nNZieWvYuQ4,247
|
|
6
|
-
ducktools/classbuilder/prefab.py,sha256=6YOVVYYeuMF5gv9DnK-sCvNKxBaBoqDXNLUQ22-xINk,24097
|
|
7
|
-
ducktools/classbuilder/prefab.pyi,sha256=x2ioTpkhNjQXFeKABFOzE0xNeZ8f_W5vusmuAzE19mc,4491
|
|
8
|
-
ducktools/classbuilder/py.typed,sha256=la67KBlbjXN-_-DfGNcdOcjYumVpKG_Tkw-8n5dnGB4,8
|
|
9
|
-
ducktools_classbuilder-0.8.1.dist-info/LICENSE,sha256=6Thz9Dbw8R4fWInl6sGl8Rj3UnKnRbDwrc6jZerpugQ,1070
|
|
10
|
-
ducktools_classbuilder-0.8.1.dist-info/METADATA,sha256=gTTON4Uwak33DCOq41KpYsJPWyVTMtYEEs4r0si5Egs,9636
|
|
11
|
-
ducktools_classbuilder-0.8.1.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
|
|
12
|
-
ducktools_classbuilder-0.8.1.dist-info/top_level.txt,sha256=uSDLtio3ZFqdwcsMJ2O5yhjB4Q3ytbBWbA8rJREganc,10
|
|
13
|
-
ducktools_classbuilder-0.8.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
{ducktools_classbuilder-0.8.1.dist-info → ducktools_classbuilder-0.8.2.dist-info}/top_level.txt
RENAMED
|
File without changes
|