ducktools-classbuilder 0.5.0__py3-none-any.whl → 0.5.1__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 +33 -3
- ducktools/classbuilder/__init__.pyi +19 -0
- ducktools/classbuilder/prefab.py +40 -9
- ducktools/classbuilder/prefab.pyi +2 -0
- {ducktools_classbuilder-0.5.0.dist-info → ducktools_classbuilder-0.5.1.dist-info}/METADATA +1 -1
- ducktools_classbuilder-0.5.1.dist-info/RECORD +10 -0
- ducktools_classbuilder-0.5.0.dist-info/RECORD +0 -10
- {ducktools_classbuilder-0.5.0.dist-info → ducktools_classbuilder-0.5.1.dist-info}/LICENSE.md +0 -0
- {ducktools_classbuilder-0.5.0.dist-info → ducktools_classbuilder-0.5.1.dist-info}/WHEEL +0 -0
- {ducktools_classbuilder-0.5.0.dist-info → ducktools_classbuilder-0.5.1.dist-info}/top_level.txt +0 -0
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
# SOFTWARE.
|
|
22
22
|
import sys
|
|
23
23
|
|
|
24
|
-
__version__ = "v0.5.
|
|
24
|
+
__version__ = "v0.5.1"
|
|
25
25
|
|
|
26
26
|
# Change this name if you make heavy modifications
|
|
27
27
|
INTERNALS_DICT = "__classbuilder_internals__"
|
|
@@ -359,8 +359,19 @@ class Field:
|
|
|
359
359
|
return cls(**argument_dict)
|
|
360
360
|
|
|
361
361
|
|
|
362
|
+
class GatheredFields:
|
|
363
|
+
__slots__ = ("fields", "modifications")
|
|
364
|
+
|
|
365
|
+
def __init__(self, fields, modifications):
|
|
366
|
+
self.fields = fields
|
|
367
|
+
self.modifications = modifications
|
|
368
|
+
|
|
369
|
+
def __call__(self, cls):
|
|
370
|
+
return self.fields, self.modifications
|
|
371
|
+
|
|
372
|
+
|
|
362
373
|
# Use the builder to generate __repr__ and __eq__ methods
|
|
363
|
-
#
|
|
374
|
+
# for both Field and GatheredFields
|
|
364
375
|
_field_internal = {
|
|
365
376
|
"default": Field(default=NOTHING),
|
|
366
377
|
"default_factory": Field(default=NOTHING),
|
|
@@ -368,17 +379,29 @@ _field_internal = {
|
|
|
368
379
|
"doc": Field(default=None),
|
|
369
380
|
}
|
|
370
381
|
|
|
382
|
+
_gathered_field_internal = {
|
|
383
|
+
"fields": Field(default=NOTHING),
|
|
384
|
+
"modifications": Field(default=NOTHING),
|
|
385
|
+
}
|
|
386
|
+
|
|
371
387
|
_field_methods = {repr_maker, eq_maker}
|
|
372
388
|
if _UNDER_TESTING:
|
|
373
389
|
_field_methods.update({frozen_setattr_maker, frozen_delattr_maker})
|
|
374
390
|
|
|
375
391
|
builder(
|
|
376
392
|
Field,
|
|
377
|
-
gatherer=
|
|
393
|
+
gatherer=GatheredFields(_field_internal, {}),
|
|
378
394
|
methods=_field_methods,
|
|
379
395
|
flags={"slotted": True, "kw_only": True},
|
|
380
396
|
)
|
|
381
397
|
|
|
398
|
+
builder(
|
|
399
|
+
GatheredFields,
|
|
400
|
+
gatherer=GatheredFields(_gathered_field_internal, {}),
|
|
401
|
+
methods={repr_maker, eq_maker},
|
|
402
|
+
flags={"slotted": True, "kw_only": False},
|
|
403
|
+
)
|
|
404
|
+
|
|
382
405
|
|
|
383
406
|
# Slot gathering tools
|
|
384
407
|
# Subclass of dict to be identifiable by isinstance checks
|
|
@@ -430,6 +453,13 @@ def make_slot_gatherer(field_type=Field):
|
|
|
430
453
|
slot_replacement = {}
|
|
431
454
|
|
|
432
455
|
for k, v in cls_slots.items():
|
|
456
|
+
# Special case __dict__ and __weakref__
|
|
457
|
+
# They should be included in the final `__slots__`
|
|
458
|
+
# But ignored as a value.
|
|
459
|
+
if k in {"__dict__", "__weakref__"}:
|
|
460
|
+
slot_replacement[k] = None
|
|
461
|
+
continue
|
|
462
|
+
|
|
433
463
|
if isinstance(v, field_type):
|
|
434
464
|
attrib = v
|
|
435
465
|
if attrib.type is not NOTHING:
|
|
@@ -92,6 +92,25 @@ class Field:
|
|
|
92
92
|
def from_field(cls, fld: Field, /, **kwargs: typing.Any) -> Field: ...
|
|
93
93
|
|
|
94
94
|
|
|
95
|
+
class GatheredFields:
|
|
96
|
+
__slots__ = ("fields", "modifications")
|
|
97
|
+
|
|
98
|
+
fields: dict[str, Field]
|
|
99
|
+
modifications: dict[str, typing.Any]
|
|
100
|
+
|
|
101
|
+
__classbuilder_internals__: dict
|
|
102
|
+
|
|
103
|
+
def __init__(
|
|
104
|
+
self,
|
|
105
|
+
fields: dict[str, Field],
|
|
106
|
+
modifications: dict[str, typing.Any]
|
|
107
|
+
) -> None: ...
|
|
108
|
+
|
|
109
|
+
def __repr__(self) -> str: ...
|
|
110
|
+
def __eq__(self, other) -> bool: ...
|
|
111
|
+
def __call__(self, cls: type) -> tuple[dict[str, Field], dict[str, typing.Any]]: ...
|
|
112
|
+
|
|
113
|
+
|
|
95
114
|
class SlotFields(dict):
|
|
96
115
|
...
|
|
97
116
|
|
ducktools/classbuilder/prefab.py
CHANGED
|
@@ -30,7 +30,7 @@ import sys
|
|
|
30
30
|
|
|
31
31
|
from . import (
|
|
32
32
|
INTERNALS_DICT, NOTHING,
|
|
33
|
-
Field, MethodMaker, SlotFields,
|
|
33
|
+
Field, MethodMaker, SlotFields, GatheredFields,
|
|
34
34
|
builder, fieldclass, get_flags, get_fields, make_slot_gatherer,
|
|
35
35
|
frozen_setattr_maker, frozen_delattr_maker, is_classvar,
|
|
36
36
|
)
|
|
@@ -519,6 +519,7 @@ def _make_prefab(
|
|
|
519
519
|
frozen=False,
|
|
520
520
|
dict_method=False,
|
|
521
521
|
recursive_repr=False,
|
|
522
|
+
gathered_fields=None,
|
|
522
523
|
):
|
|
523
524
|
"""
|
|
524
525
|
Generate boilerplate code for dunder methods in a class.
|
|
@@ -535,6 +536,7 @@ def _make_prefab(
|
|
|
535
536
|
such as lists)
|
|
536
537
|
:param dict_method: Include an as_dict method for faster dictionary creation
|
|
537
538
|
:param recursive_repr: Safely handle repr in case of recursion
|
|
539
|
+
:param gathered_fields: Pre-gathered fields callable, to skip re-collecting attributes
|
|
538
540
|
:return: class with __ methods defined
|
|
539
541
|
"""
|
|
540
542
|
cls_dict = cls.__dict__
|
|
@@ -546,12 +548,16 @@ def _make_prefab(
|
|
|
546
548
|
)
|
|
547
549
|
|
|
548
550
|
slots = cls_dict.get("__slots__")
|
|
549
|
-
if
|
|
550
|
-
|
|
551
|
-
|
|
551
|
+
if gathered_fields is None:
|
|
552
|
+
if isinstance(slots, SlotFields):
|
|
553
|
+
gatherer = slot_prefab_gatherer
|
|
554
|
+
slotted = True
|
|
555
|
+
else:
|
|
556
|
+
gatherer = attribute_gatherer
|
|
557
|
+
slotted = False
|
|
552
558
|
else:
|
|
553
|
-
gatherer =
|
|
554
|
-
slotted = False
|
|
559
|
+
gatherer = gathered_fields
|
|
560
|
+
slotted = False if slots is None else True
|
|
555
561
|
|
|
556
562
|
methods = set()
|
|
557
563
|
|
|
@@ -770,6 +776,7 @@ def build_prefab(
|
|
|
770
776
|
frozen=False,
|
|
771
777
|
dict_method=False,
|
|
772
778
|
recursive_repr=False,
|
|
779
|
+
slots=False,
|
|
773
780
|
):
|
|
774
781
|
"""
|
|
775
782
|
Dynamically construct a (dynamic) prefab.
|
|
@@ -790,12 +797,35 @@ def build_prefab(
|
|
|
790
797
|
(This does not prevent the modification of mutable attributes such as lists)
|
|
791
798
|
:param dict_method: Include an as_dict method for faster dictionary creation
|
|
792
799
|
:param recursive_repr: Safely handle repr in case of recursion
|
|
800
|
+
:param slots: Make the resulting class slotted
|
|
793
801
|
:return: class with __ methods defined
|
|
794
802
|
"""
|
|
795
|
-
class_dict = {} if class_dict is None else class_dict
|
|
796
|
-
|
|
803
|
+
class_dict = {} if class_dict is None else class_dict.copy()
|
|
804
|
+
|
|
805
|
+
class_annotations = {}
|
|
806
|
+
class_slots = {}
|
|
807
|
+
fields = {}
|
|
808
|
+
|
|
797
809
|
for name, attrib in attributes:
|
|
798
|
-
|
|
810
|
+
if isinstance(attrib, Attribute):
|
|
811
|
+
fields[name] = attrib
|
|
812
|
+
elif isinstance(attrib, Field):
|
|
813
|
+
fields[name] = Attribute.from_field(attrib)
|
|
814
|
+
else:
|
|
815
|
+
fields[name] = Attribute(default=attrib)
|
|
816
|
+
|
|
817
|
+
if attrib.type is not NOTHING:
|
|
818
|
+
class_annotations[name] = attrib.type
|
|
819
|
+
|
|
820
|
+
class_slots[name] = attrib.doc
|
|
821
|
+
|
|
822
|
+
if slots:
|
|
823
|
+
class_dict["__slots__"] = class_slots
|
|
824
|
+
|
|
825
|
+
class_dict["__annotations__"] = class_annotations
|
|
826
|
+
cls = type(class_name, bases, class_dict)
|
|
827
|
+
|
|
828
|
+
gathered_fields = GatheredFields(fields, {})
|
|
799
829
|
|
|
800
830
|
cls = _make_prefab(
|
|
801
831
|
cls,
|
|
@@ -808,6 +838,7 @@ def build_prefab(
|
|
|
808
838
|
frozen=frozen,
|
|
809
839
|
dict_method=dict_method,
|
|
810
840
|
recursive_repr=recursive_repr,
|
|
841
|
+
gathered_fields=gathered_fields,
|
|
811
842
|
)
|
|
812
843
|
|
|
813
844
|
return cls
|
|
@@ -109,6 +109,7 @@ def _make_prefab(
|
|
|
109
109
|
frozen: bool = False,
|
|
110
110
|
dict_method: bool = False,
|
|
111
111
|
recursive_repr: bool = False,
|
|
112
|
+
gathered_fields: Callable[[type], tuple[dict[str, Attribute], dict[str, typing.Any]]] | None = None,
|
|
112
113
|
) -> type: ...
|
|
113
114
|
|
|
114
115
|
_T = typing.TypeVar("_T")
|
|
@@ -146,6 +147,7 @@ def build_prefab(
|
|
|
146
147
|
frozen: bool = False,
|
|
147
148
|
dict_method: bool = False,
|
|
148
149
|
recursive_repr: bool = False,
|
|
150
|
+
slots: bool = False,
|
|
149
151
|
) -> type: ...
|
|
150
152
|
|
|
151
153
|
def is_prefab(o: typing.Any) -> bool: ...
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
ducktools/classbuilder/__init__.py,sha256=rl5oc0azae-32EjeCTzhM1DR5JNR-AgOriyH5zkf4WI,20856
|
|
2
|
+
ducktools/classbuilder/__init__.pyi,sha256=5gnHhSVMP5yArA-_cuvIeLnLAlmY_EmPMfVY9dRi0CA,4784
|
|
3
|
+
ducktools/classbuilder/prefab.py,sha256=K9_bKh24OiGJ40BbJWVfwDObArMUxi2JRbB5ZN-t--M,28984
|
|
4
|
+
ducktools/classbuilder/prefab.pyi,sha256=sNladRJM3lcZo8zQWvWCIInBx0wUD7PT6fhdE89OpG0,3960
|
|
5
|
+
ducktools/classbuilder/py.typed,sha256=la67KBlbjXN-_-DfGNcdOcjYumVpKG_Tkw-8n5dnGB4,8
|
|
6
|
+
ducktools_classbuilder-0.5.1.dist-info/LICENSE.md,sha256=6Thz9Dbw8R4fWInl6sGl8Rj3UnKnRbDwrc6jZerpugQ,1070
|
|
7
|
+
ducktools_classbuilder-0.5.1.dist-info/METADATA,sha256=B0KO5vvdZOUZ4o28GA4zlgXuIkA9j4SuplzFByvRhjs,10143
|
|
8
|
+
ducktools_classbuilder-0.5.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
9
|
+
ducktools_classbuilder-0.5.1.dist-info/top_level.txt,sha256=uSDLtio3ZFqdwcsMJ2O5yhjB4Q3ytbBWbA8rJREganc,10
|
|
10
|
+
ducktools_classbuilder-0.5.1.dist-info/RECORD,,
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
ducktools/classbuilder/__init__.py,sha256=aB88mFw6bv5wzlZY88ZoCiWic3YMW3naxXmPhwT_fbs,20045
|
|
2
|
-
ducktools/classbuilder/__init__.pyi,sha256=rFNQYj_TeikQMGV-NCDD4fL9l9od7NkGRoIIyABItoQ,4310
|
|
3
|
-
ducktools/classbuilder/prefab.py,sha256=od-GEAYQokPDUePMOb2zPFGfh5p6zToJ-oEYpZx5CLA,28017
|
|
4
|
-
ducktools/classbuilder/prefab.pyi,sha256=f7GWVTyuQUU6EO6l0eDoiKy4uUq0DxK2ndHhJ0AGeKk,3830
|
|
5
|
-
ducktools/classbuilder/py.typed,sha256=la67KBlbjXN-_-DfGNcdOcjYumVpKG_Tkw-8n5dnGB4,8
|
|
6
|
-
ducktools_classbuilder-0.5.0.dist-info/LICENSE.md,sha256=6Thz9Dbw8R4fWInl6sGl8Rj3UnKnRbDwrc6jZerpugQ,1070
|
|
7
|
-
ducktools_classbuilder-0.5.0.dist-info/METADATA,sha256=50Jn1_0FD2BadATbyBWOQTz3fIxV1cJ36J3frvp8qBY,10143
|
|
8
|
-
ducktools_classbuilder-0.5.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
9
|
-
ducktools_classbuilder-0.5.0.dist-info/top_level.txt,sha256=uSDLtio3ZFqdwcsMJ2O5yhjB4Q3ytbBWbA8rJREganc,10
|
|
10
|
-
ducktools_classbuilder-0.5.0.dist-info/RECORD,,
|
{ducktools_classbuilder-0.5.0.dist-info → ducktools_classbuilder-0.5.1.dist-info}/LICENSE.md
RENAMED
|
File without changes
|
|
File without changes
|
{ducktools_classbuilder-0.5.0.dist-info → ducktools_classbuilder-0.5.1.dist-info}/top_level.txt
RENAMED
|
File without changes
|