ducktools-classbuilder 0.4.0__py3-none-any.whl → 0.5.0__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 +58 -33
- ducktools/classbuilder/__init__.pyi +19 -17
- ducktools/classbuilder/prefab.py +27 -25
- ducktools/classbuilder/prefab.pyi +10 -10
- {ducktools_classbuilder-0.4.0.dist-info → ducktools_classbuilder-0.5.0.dist-info}/METADATA +16 -15
- ducktools_classbuilder-0.5.0.dist-info/RECORD +10 -0
- ducktools_classbuilder-0.4.0.dist-info/RECORD +0 -10
- {ducktools_classbuilder-0.4.0.dist-info → ducktools_classbuilder-0.5.0.dist-info}/LICENSE.md +0 -0
- {ducktools_classbuilder-0.4.0.dist-info → ducktools_classbuilder-0.5.0.dist-info}/WHEEL +0 -0
- {ducktools_classbuilder-0.4.0.dist-info → ducktools_classbuilder-0.5.0.dist-info}/top_level.txt +0 -0
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
# SOFTWARE.
|
|
22
22
|
import sys
|
|
23
23
|
|
|
24
|
-
__version__ = "v0.
|
|
24
|
+
__version__ = "v0.5.0"
|
|
25
25
|
|
|
26
26
|
# Change this name if you make heavy modifications
|
|
27
27
|
INTERNALS_DICT = "__classbuilder_internals__"
|
|
@@ -65,7 +65,7 @@ def _get_inst_fields(inst):
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
|
|
68
|
-
# As 'None' can be a meaningful
|
|
68
|
+
# As 'None' can be a meaningful value we need a sentinel value
|
|
69
69
|
# to use to show no value has been provided.
|
|
70
70
|
class _NothingType:
|
|
71
71
|
def __repr__(self):
|
|
@@ -109,7 +109,7 @@ class MethodMaker:
|
|
|
109
109
|
return method.__get__(instance, cls)
|
|
110
110
|
|
|
111
111
|
|
|
112
|
-
def
|
|
112
|
+
def get_init_generator(null=NOTHING, extra_code=None):
|
|
113
113
|
def cls_init_maker(cls):
|
|
114
114
|
fields = get_fields(cls)
|
|
115
115
|
flags = get_flags(cls)
|
|
@@ -154,10 +154,10 @@ def get_init_maker(null=NOTHING, extra_code=None):
|
|
|
154
154
|
return cls_init_maker
|
|
155
155
|
|
|
156
156
|
|
|
157
|
-
|
|
157
|
+
init_generator = get_init_generator()
|
|
158
158
|
|
|
159
159
|
|
|
160
|
-
def
|
|
160
|
+
def repr_generator(cls):
|
|
161
161
|
fields = get_fields(cls)
|
|
162
162
|
content = ", ".join(
|
|
163
163
|
f"{name}={{self.{name}!r}}"
|
|
@@ -171,7 +171,7 @@ def repr_maker(cls):
|
|
|
171
171
|
return code, globs
|
|
172
172
|
|
|
173
173
|
|
|
174
|
-
def
|
|
174
|
+
def eq_generator(cls):
|
|
175
175
|
class_comparison = "self.__class__ is other.__class__"
|
|
176
176
|
field_names = get_fields(cls)
|
|
177
177
|
|
|
@@ -191,7 +191,7 @@ def eq_maker(cls):
|
|
|
191
191
|
return code, globs
|
|
192
192
|
|
|
193
193
|
|
|
194
|
-
def
|
|
194
|
+
def frozen_setattr_generator(cls):
|
|
195
195
|
globs = {}
|
|
196
196
|
field_names = set(get_fields(cls))
|
|
197
197
|
flags = get_flags(cls)
|
|
@@ -220,7 +220,7 @@ def frozen_setattr_maker(cls):
|
|
|
220
220
|
return code, globs
|
|
221
221
|
|
|
222
222
|
|
|
223
|
-
def
|
|
223
|
+
def frozen_delattr_generator(cls):
|
|
224
224
|
body = (
|
|
225
225
|
' raise TypeError(\n'
|
|
226
226
|
' f"{type(self).__name__!r} object "\n'
|
|
@@ -234,12 +234,12 @@ def frozen_delattr_maker(cls):
|
|
|
234
234
|
|
|
235
235
|
# As only the __get__ method refers to the class we can use the same
|
|
236
236
|
# Descriptor instances for every class.
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
default_methods = frozenset({
|
|
237
|
+
init_maker = MethodMaker("__init__", init_generator)
|
|
238
|
+
repr_maker = MethodMaker("__repr__", repr_generator)
|
|
239
|
+
eq_maker = MethodMaker("__eq__", eq_generator)
|
|
240
|
+
frozen_setattr_maker = MethodMaker("__setattr__", frozen_setattr_generator)
|
|
241
|
+
frozen_delattr_maker = MethodMaker("__delattr__", frozen_delattr_generator)
|
|
242
|
+
default_methods = frozenset({init_maker, repr_maker, eq_maker})
|
|
243
243
|
|
|
244
244
|
|
|
245
245
|
def builder(cls=None, /, *, gatherer, methods, flags=None):
|
|
@@ -248,7 +248,7 @@ def builder(cls=None, /, *, gatherer, methods, flags=None):
|
|
|
248
248
|
|
|
249
249
|
:param cls: Class to be analysed and have methods generated
|
|
250
250
|
:param gatherer: Function to gather field information
|
|
251
|
-
:type gatherer: Callable[[type], dict[str, Field]]
|
|
251
|
+
:type gatherer: Callable[[type], tuple[dict[str, Field], dict[str, Any]]]
|
|
252
252
|
:param methods: MethodMakers to add to the class
|
|
253
253
|
:type methods: set[MethodMaker]
|
|
254
254
|
:param flags: additional flags to store in the internals dictionary
|
|
@@ -267,7 +267,14 @@ def builder(cls=None, /, *, gatherer, methods, flags=None):
|
|
|
267
267
|
internals = {}
|
|
268
268
|
setattr(cls, INTERNALS_DICT, internals)
|
|
269
269
|
|
|
270
|
-
cls_fields = gatherer(cls)
|
|
270
|
+
cls_fields, modifications = gatherer(cls)
|
|
271
|
+
|
|
272
|
+
for name, value in modifications.items():
|
|
273
|
+
if value is NOTHING:
|
|
274
|
+
delattr(cls, name)
|
|
275
|
+
else:
|
|
276
|
+
setattr(cls, name, value)
|
|
277
|
+
|
|
271
278
|
internals["local_fields"] = cls_fields
|
|
272
279
|
|
|
273
280
|
mro = cls.__mro__[:-1] # skip 'object' base class
|
|
@@ -361,13 +368,13 @@ _field_internal = {
|
|
|
361
368
|
"doc": Field(default=None),
|
|
362
369
|
}
|
|
363
370
|
|
|
364
|
-
_field_methods = {
|
|
371
|
+
_field_methods = {repr_maker, eq_maker}
|
|
365
372
|
if _UNDER_TESTING:
|
|
366
|
-
_field_methods.update({
|
|
373
|
+
_field_methods.update({frozen_setattr_maker, frozen_delattr_maker})
|
|
367
374
|
|
|
368
375
|
builder(
|
|
369
376
|
Field,
|
|
370
|
-
gatherer=lambda cls_: _field_internal,
|
|
377
|
+
gatherer=lambda cls_: (_field_internal, {}),
|
|
371
378
|
methods=_field_methods,
|
|
372
379
|
flags={"slotted": True, "kw_only": True},
|
|
373
380
|
)
|
|
@@ -390,6 +397,14 @@ class SlotFields(dict):
|
|
|
390
397
|
|
|
391
398
|
|
|
392
399
|
def make_slot_gatherer(field_type=Field):
|
|
400
|
+
"""
|
|
401
|
+
Create a new annotation gatherer that will work with `Field` instances
|
|
402
|
+
of the creators definition.
|
|
403
|
+
|
|
404
|
+
:param field_type: The `Field` classes to be used when gathering fields
|
|
405
|
+
:return: A slot gatherer that will check for and generate Fields of
|
|
406
|
+
the type field_type.
|
|
407
|
+
"""
|
|
393
408
|
def field_slot_gatherer(cls):
|
|
394
409
|
"""
|
|
395
410
|
Gather field information for class generation based on __slots__
|
|
@@ -405,7 +420,12 @@ def make_slot_gatherer(field_type=Field):
|
|
|
405
420
|
"in order to generate a slotclass"
|
|
406
421
|
)
|
|
407
422
|
|
|
408
|
-
|
|
423
|
+
# Don't want to mutate original annotations so make a copy if it exists
|
|
424
|
+
# Looking at the dict is a Python3.9 or earlier requirement
|
|
425
|
+
cls_annotations = {
|
|
426
|
+
**cls.__dict__.get("__annotations__", {})
|
|
427
|
+
}
|
|
428
|
+
|
|
409
429
|
cls_fields = {}
|
|
410
430
|
slot_replacement = {}
|
|
411
431
|
|
|
@@ -421,13 +441,15 @@ def make_slot_gatherer(field_type=Field):
|
|
|
421
441
|
slot_replacement[k] = attrib.doc
|
|
422
442
|
cls_fields[k] = attrib
|
|
423
443
|
|
|
424
|
-
#
|
|
425
|
-
#
|
|
426
|
-
|
|
444
|
+
# Send the modifications to the builder for what should be changed
|
|
445
|
+
# On the class.
|
|
446
|
+
# In this case, slots with documentation and new annotations.
|
|
447
|
+
modifications = {
|
|
448
|
+
"__slots__": slot_replacement,
|
|
449
|
+
"__annotations__": cls_annotations,
|
|
450
|
+
}
|
|
427
451
|
|
|
428
|
-
|
|
429
|
-
setattr(cls, "__annotations__", cls_annotations)
|
|
430
|
-
return cls_fields
|
|
452
|
+
return cls_fields, modifications
|
|
431
453
|
|
|
432
454
|
return field_slot_gatherer
|
|
433
455
|
|
|
@@ -483,6 +505,8 @@ def make_annotation_gatherer(field_type=Field, leave_default_values=True):
|
|
|
483
505
|
|
|
484
506
|
cls_fields: dict[str, field_type] = {}
|
|
485
507
|
|
|
508
|
+
modifications = {}
|
|
509
|
+
|
|
486
510
|
for k, v in cls_annotations.items():
|
|
487
511
|
# Ignore ClassVar
|
|
488
512
|
if is_classvar(v):
|
|
@@ -494,20 +518,21 @@ def make_annotation_gatherer(field_type=Field, leave_default_values=True):
|
|
|
494
518
|
if isinstance(attrib, field_type):
|
|
495
519
|
attrib = field_type.from_field(attrib, type=v)
|
|
496
520
|
if attrib.default is not NOTHING and leave_default_values:
|
|
497
|
-
|
|
521
|
+
modifications[k] = attrib.default
|
|
498
522
|
else:
|
|
499
|
-
|
|
523
|
+
# NOTHING sentinel indicates a value should be removed
|
|
524
|
+
modifications[k] = NOTHING
|
|
500
525
|
else:
|
|
501
526
|
attrib = field_type(default=attrib, type=v)
|
|
502
527
|
if not leave_default_values:
|
|
503
|
-
|
|
528
|
+
modifications[k] = NOTHING
|
|
504
529
|
|
|
505
530
|
else:
|
|
506
531
|
attrib = field_type(type=v)
|
|
507
532
|
|
|
508
533
|
cls_fields[k] = attrib
|
|
509
534
|
|
|
510
|
-
return cls_fields
|
|
535
|
+
return cls_fields, modifications
|
|
511
536
|
|
|
512
537
|
return field_annotation_gatherer
|
|
513
538
|
|
|
@@ -569,7 +594,7 @@ def annotationclass(cls=None, /, *, methods=default_methods):
|
|
|
569
594
|
|
|
570
595
|
_field_init_desc = MethodMaker(
|
|
571
596
|
funcname="__init__",
|
|
572
|
-
code_generator=
|
|
597
|
+
code_generator=get_init_generator(
|
|
573
598
|
null=_NothingType(),
|
|
574
599
|
extra_code=["self.validate_field()"],
|
|
575
600
|
)
|
|
@@ -590,11 +615,11 @@ def fieldclass(cls=None, /, *, frozen=False):
|
|
|
590
615
|
if not cls:
|
|
591
616
|
return lambda cls_: fieldclass(cls_, frozen=frozen)
|
|
592
617
|
|
|
593
|
-
field_methods = {_field_init_desc,
|
|
618
|
+
field_methods = {_field_init_desc, repr_maker, eq_maker}
|
|
594
619
|
|
|
595
620
|
# Always freeze when running tests
|
|
596
621
|
if frozen or _UNDER_TESTING:
|
|
597
|
-
field_methods.update({
|
|
622
|
+
field_methods.update({frozen_setattr_maker, frozen_delattr_maker})
|
|
598
623
|
|
|
599
624
|
cls = builder(
|
|
600
625
|
cls,
|
|
@@ -26,24 +26,24 @@ class MethodMaker:
|
|
|
26
26
|
def __repr__(self) -> str: ...
|
|
27
27
|
def __get__(self, instance, cls) -> Callable: ...
|
|
28
28
|
|
|
29
|
-
def
|
|
29
|
+
def get_init_generator(
|
|
30
30
|
null: _NothingType = NOTHING,
|
|
31
31
|
extra_code: None | list[str] = None
|
|
32
32
|
) -> Callable[[type], tuple[str, dict[str, typing.Any]]]: ...
|
|
33
33
|
|
|
34
|
-
def
|
|
35
|
-
def
|
|
36
|
-
def
|
|
34
|
+
def init_generator(cls: type) -> tuple[str, dict[str, typing.Any]]: ...
|
|
35
|
+
def repr_generator(cls: type) -> tuple[str, dict[str, typing.Any]]: ...
|
|
36
|
+
def eq_generator(cls: type) -> tuple[str, dict[str, typing.Any]]: ...
|
|
37
37
|
|
|
38
|
-
def
|
|
38
|
+
def frozen_setattr_generator(cls: type) -> tuple[str, dict[str, typing.Any]]: ...
|
|
39
39
|
|
|
40
|
-
def
|
|
40
|
+
def frozen_delattr_generator(cls: type) -> tuple[str, dict[str, typing.Any]]: ...
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
init_maker: MethodMaker
|
|
43
|
+
repr_maker: MethodMaker
|
|
44
|
+
eq_maker: MethodMaker
|
|
45
|
+
frozen_setattr_maker: MethodMaker
|
|
46
|
+
frozen_delattr_maker: MethodMaker
|
|
47
47
|
default_methods: frozenset[MethodMaker]
|
|
48
48
|
|
|
49
49
|
_T = typing.TypeVar("_T")
|
|
@@ -53,7 +53,7 @@ def builder(
|
|
|
53
53
|
cls: type[_T],
|
|
54
54
|
/,
|
|
55
55
|
*,
|
|
56
|
-
gatherer: Callable[[type], dict[str, Field]],
|
|
56
|
+
gatherer: Callable[[type], tuple[dict[str, Field], dict[str, typing.Any]]],
|
|
57
57
|
methods: frozenset[MethodMaker] | set[MethodMaker],
|
|
58
58
|
flags: dict[str, bool] | None = None,
|
|
59
59
|
) -> type[_T]: ...
|
|
@@ -63,7 +63,7 @@ def builder(
|
|
|
63
63
|
cls: None = None,
|
|
64
64
|
/,
|
|
65
65
|
*,
|
|
66
|
-
gatherer: Callable[[type], dict[str, Field]],
|
|
66
|
+
gatherer: Callable[[type], tuple[dict[str, Field], dict[str, typing.Any]]],
|
|
67
67
|
methods: frozenset[MethodMaker] | set[MethodMaker],
|
|
68
68
|
flags: dict[str, bool] | None = None,
|
|
69
69
|
) -> Callable[[type[_T]], type[_T]]: ...
|
|
@@ -95,9 +95,11 @@ class Field:
|
|
|
95
95
|
class SlotFields(dict):
|
|
96
96
|
...
|
|
97
97
|
|
|
98
|
-
def make_slot_gatherer(
|
|
98
|
+
def make_slot_gatherer(
|
|
99
|
+
field_type: type[Field] = Field
|
|
100
|
+
) -> Callable[[type], tuple[dict[str, Field], dict[str, typing.Any]]]: ...
|
|
99
101
|
|
|
100
|
-
def slot_gatherer(cls: type) -> dict[str, Field]:
|
|
102
|
+
def slot_gatherer(cls: type) -> tuple[dict[str, Field], dict[str, typing.Any]]:
|
|
101
103
|
...
|
|
102
104
|
|
|
103
105
|
def is_classvar(hint: object) -> bool: ...
|
|
@@ -105,9 +107,9 @@ def is_classvar(hint: object) -> bool: ...
|
|
|
105
107
|
def make_annotation_gatherer(
|
|
106
108
|
field_type: type[Field] = Field,
|
|
107
109
|
leave_default_values: bool = True,
|
|
108
|
-
) -> Callable[[type], dict[str, Field]]: ...
|
|
110
|
+
) -> Callable[[type], tuple[dict[str, Field], dict[str, typing.Any]]]: ...
|
|
109
111
|
|
|
110
|
-
def annotation_gatherer(cls: type) -> dict[str, Field]: ...
|
|
112
|
+
def annotation_gatherer(cls: type) -> tuple[dict[str, Field], dict[str, typing.Any]]: ...
|
|
111
113
|
|
|
112
114
|
def check_argument_order(cls: type) -> None: ...
|
|
113
115
|
|
ducktools/classbuilder/prefab.py
CHANGED
|
@@ -32,7 +32,7 @@ from . import (
|
|
|
32
32
|
INTERNALS_DICT, NOTHING,
|
|
33
33
|
Field, MethodMaker, SlotFields,
|
|
34
34
|
builder, fieldclass, get_flags, get_fields, make_slot_gatherer,
|
|
35
|
-
|
|
35
|
+
frozen_setattr_maker, frozen_delattr_maker, is_classvar,
|
|
36
36
|
)
|
|
37
37
|
|
|
38
38
|
PREFAB_FIELDS = "PREFAB_FIELDS"
|
|
@@ -344,13 +344,13 @@ def get_asdict_maker():
|
|
|
344
344
|
return MethodMaker("as_dict", as_dict_gen)
|
|
345
345
|
|
|
346
346
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
347
|
+
init_maker = get_init_maker()
|
|
348
|
+
prefab_init_maker = get_init_maker(init_name=PREFAB_INIT_FUNC)
|
|
349
|
+
repr_maker = get_repr_maker()
|
|
350
|
+
recursive_repr_maker = get_repr_maker(recursion_safe=True)
|
|
351
|
+
eq_maker = get_eq_maker()
|
|
352
|
+
iter_maker = get_iter_maker()
|
|
353
|
+
asdict_maker = get_asdict_maker()
|
|
354
354
|
|
|
355
355
|
|
|
356
356
|
# Updated field with additional attributes
|
|
@@ -441,6 +441,8 @@ def attribute_gatherer(cls):
|
|
|
441
441
|
|
|
442
442
|
cls_attribute_names = cls_attributes.keys()
|
|
443
443
|
|
|
444
|
+
cls_modifications = {}
|
|
445
|
+
|
|
444
446
|
if set(cls_annotation_names).issuperset(set(cls_attribute_names)):
|
|
445
447
|
# replace the classes' attributes dict with one with the correct
|
|
446
448
|
# order from the annotations.
|
|
@@ -483,7 +485,7 @@ def attribute_gatherer(cls):
|
|
|
483
485
|
|
|
484
486
|
# Clear the attribute from the class after it has been used
|
|
485
487
|
# in the definition.
|
|
486
|
-
|
|
488
|
+
cls_modifications[name] = NOTHING
|
|
487
489
|
else:
|
|
488
490
|
attrib = attribute(**extras)
|
|
489
491
|
|
|
@@ -493,14 +495,14 @@ def attribute_gatherer(cls):
|
|
|
493
495
|
else:
|
|
494
496
|
for name in cls_attributes.keys():
|
|
495
497
|
attrib = cls_attributes[name]
|
|
496
|
-
|
|
498
|
+
cls_modifications[name] = NOTHING
|
|
497
499
|
|
|
498
500
|
# Some items can still be annotated.
|
|
499
501
|
if name in cls_annotations:
|
|
500
502
|
new_attrib = Attribute.from_field(attrib, type=cls_annotations[name])
|
|
501
503
|
cls_attributes[name] = new_attrib
|
|
502
504
|
|
|
503
|
-
return cls_attributes
|
|
505
|
+
return cls_attributes, cls_modifications
|
|
504
506
|
|
|
505
507
|
|
|
506
508
|
# Class Builders
|
|
@@ -554,24 +556,24 @@ def _make_prefab(
|
|
|
554
556
|
methods = set()
|
|
555
557
|
|
|
556
558
|
if init and "__init__" not in cls_dict:
|
|
557
|
-
methods.add(
|
|
559
|
+
methods.add(init_maker)
|
|
558
560
|
else:
|
|
559
|
-
methods.add(
|
|
561
|
+
methods.add(prefab_init_maker)
|
|
560
562
|
|
|
561
563
|
if repr and "__repr__" not in cls_dict:
|
|
562
564
|
if recursive_repr:
|
|
563
|
-
methods.add(
|
|
565
|
+
methods.add(recursive_repr_maker)
|
|
564
566
|
else:
|
|
565
|
-
methods.add(
|
|
567
|
+
methods.add(repr_maker)
|
|
566
568
|
if eq and "__eq__" not in cls_dict:
|
|
567
|
-
methods.add(
|
|
569
|
+
methods.add(eq_maker)
|
|
568
570
|
if iter and "__iter__" not in cls_dict:
|
|
569
|
-
methods.add(
|
|
571
|
+
methods.add(iter_maker)
|
|
570
572
|
if frozen:
|
|
571
|
-
methods.add(
|
|
572
|
-
methods.add(
|
|
573
|
+
methods.add(frozen_setattr_maker)
|
|
574
|
+
methods.add(frozen_delattr_maker)
|
|
573
575
|
if dict_method:
|
|
574
|
-
methods.add(
|
|
576
|
+
methods.add(asdict_maker)
|
|
575
577
|
|
|
576
578
|
flags = {
|
|
577
579
|
"kw_only": kw_only,
|
|
@@ -847,17 +849,17 @@ def as_dict(o):
|
|
|
847
849
|
:param o: instance of a prefab class
|
|
848
850
|
:return: dictionary of {k: v} from fields
|
|
849
851
|
"""
|
|
852
|
+
cls = type(o)
|
|
853
|
+
if not hasattr(cls, PREFAB_FIELDS):
|
|
854
|
+
raise TypeError(f"{o!r} should be a prefab instance, not {cls}")
|
|
855
|
+
|
|
850
856
|
# Attempt to use the generated method if available
|
|
851
857
|
try:
|
|
852
858
|
return o.as_dict()
|
|
853
859
|
except AttributeError:
|
|
854
860
|
pass
|
|
855
861
|
|
|
856
|
-
|
|
857
|
-
try:
|
|
858
|
-
flds = get_attributes(cls)
|
|
859
|
-
except AttributeError:
|
|
860
|
-
raise TypeError(f"inst should be a prefab instance, not {cls}")
|
|
862
|
+
flds = get_attributes(cls)
|
|
861
863
|
|
|
862
864
|
return {
|
|
863
865
|
name: getattr(o, name)
|
|
@@ -6,7 +6,7 @@ from collections.abc import Callable
|
|
|
6
6
|
from . import (
|
|
7
7
|
INTERNALS_DICT, NOTHING,
|
|
8
8
|
Field, MethodMaker, SlotFields as SlotFields,
|
|
9
|
-
builder, fieldclass, get_flags, get_fields,
|
|
9
|
+
builder, fieldclass, get_flags, get_fields, make_slot_gatherer
|
|
10
10
|
)
|
|
11
11
|
|
|
12
12
|
# noinspection PyUnresolvedReferences
|
|
@@ -39,13 +39,13 @@ def get_iter_maker() -> MethodMaker: ...
|
|
|
39
39
|
def get_asdict_maker() -> MethodMaker: ...
|
|
40
40
|
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
init_maker: MethodMaker
|
|
43
|
+
prefab_init_maker: MethodMaker
|
|
44
|
+
repr_maker: MethodMaker
|
|
45
|
+
recursive_repr_maker: MethodMaker
|
|
46
|
+
eq_maker: MethodMaker
|
|
47
|
+
iter_maker: MethodMaker
|
|
48
|
+
asdict_maker: MethodMaker
|
|
49
49
|
|
|
50
50
|
class Attribute(Field):
|
|
51
51
|
__slots__: dict
|
|
@@ -93,9 +93,9 @@ def attribute(
|
|
|
93
93
|
exclude_field: bool = False,
|
|
94
94
|
) -> Attribute: ...
|
|
95
95
|
|
|
96
|
-
def slot_prefab_gatherer(cls: type) -> dict[str, Attribute]: ...
|
|
96
|
+
def slot_prefab_gatherer(cls: type) -> tuple[dict[str, Attribute], dict[str, typing.Any]]: ...
|
|
97
97
|
|
|
98
|
-
def attribute_gatherer(cls: type) -> dict[str, Attribute]: ...
|
|
98
|
+
def attribute_gatherer(cls: type) -> tuple[dict[str, Attribute], dict[str, typing.Any]]: ...
|
|
99
99
|
|
|
100
100
|
def _make_prefab(
|
|
101
101
|
cls: type,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ducktools-classbuilder
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Toolkit for creating class boilerplate generators
|
|
5
5
|
Author: David C Ellis
|
|
6
6
|
License: MIT License
|
|
@@ -69,11 +69,12 @@ Install from PyPI with:
|
|
|
69
69
|
In order to create a class decorator using `ducktools.classbuilder` there are
|
|
70
70
|
a few things you need to prepare.
|
|
71
71
|
|
|
72
|
-
1. A field gathering function to analyse the class and collect valid `Field`s
|
|
72
|
+
1. A field gathering function to analyse the class and collect valid `Field`s and provide
|
|
73
|
+
any modifications that need to be applied to the class attributes.
|
|
73
74
|
* An example `slot_gatherer` is included.
|
|
74
75
|
2. Code generators that can make use of the gathered `Field`s to create magic method
|
|
75
|
-
source code.
|
|
76
|
-
* Example `
|
|
76
|
+
source code. To be made into descriptors by `MethodMaker`.
|
|
77
|
+
* Example `init_generator`, `repr_generator` and `eq_generator` generators are included.
|
|
77
78
|
3. A function that calls the `builder` function to apply both of these steps.
|
|
78
79
|
|
|
79
80
|
A field gathering function needs to take the original class as an argument and
|
|
@@ -91,25 +92,26 @@ class[^1] where keyword arguments define the names and values for the fields.
|
|
|
91
92
|
|
|
92
93
|
Code generator functions need to be converted to descriptors before being used.
|
|
93
94
|
This is done using the provided `MethodMaker` descriptor class.
|
|
94
|
-
ex: `
|
|
95
|
+
ex: `init_maker = MethodMaker("__init__", init_generator)`.
|
|
95
96
|
|
|
96
97
|
These parts can then be used to make a basic class boilerplate generator by
|
|
97
98
|
providing them to the `builder` function.
|
|
98
99
|
|
|
99
100
|
```python
|
|
100
101
|
from ducktools.classbuilder import (
|
|
101
|
-
builder,
|
|
102
|
-
slot_gatherer,
|
|
103
|
-
|
|
102
|
+
builder,
|
|
103
|
+
slot_gatherer,
|
|
104
|
+
init_generator, eq_generator, repr_generator,
|
|
104
105
|
MethodMaker,
|
|
105
106
|
)
|
|
106
107
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
init_maker = MethodMaker("__init__", init_generator)
|
|
109
|
+
repr_maker = MethodMaker("__repr__", repr_generator)
|
|
110
|
+
eq_maker = MethodMaker("__eq__", eq_generator)
|
|
111
|
+
|
|
110
112
|
|
|
111
113
|
def slotclass(cls):
|
|
112
|
-
return builder(cls, gatherer=slot_gatherer, methods={
|
|
114
|
+
return builder(cls, gatherer=slot_gatherer, methods={init_maker, repr_maker, eq_maker})
|
|
113
115
|
```
|
|
114
116
|
|
|
115
117
|
## Slot Class Usage ##
|
|
@@ -238,9 +240,8 @@ It will copy values provided as the `type` to `Field` into the
|
|
|
238
240
|
Values provided to `doc` will be placed in the final `__slots__`
|
|
239
241
|
field so they are present on the class if `help(...)` is called.
|
|
240
242
|
|
|
241
|
-
A fairly basic `annotations_gatherer` and `annotationclass` are included
|
|
242
|
-
|
|
243
|
-
annotations.
|
|
243
|
+
A fairly basic `annotations_gatherer` and `annotationclass` are also included
|
|
244
|
+
and can be used to generate classbuilders that rely on annotations.
|
|
244
245
|
|
|
245
246
|
If you want something with more features you can look at the `prefab.py`
|
|
246
247
|
implementation which provides a 'prebuilt' implementation.
|
|
@@ -0,0 +1,10 @@
|
|
|
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,,
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
ducktools/classbuilder/__init__.py,sha256=7OkqvfHLSSJylkAeiErgZXB9E-lDUK-rvbu3CRWFSdc,19113
|
|
2
|
-
ducktools/classbuilder/__init__.pyi,sha256=64cp1-zpOh2bUVlwcoTTdaampFINIhdFbFDcleE3N0g,4091
|
|
3
|
-
ducktools/classbuilder/prefab.py,sha256=9K_02AJ3-gLR0Cb0RxKwd2n_cwVtE2mf1TE2n_VNp4o,27947
|
|
4
|
-
ducktools/classbuilder/prefab.pyi,sha256=5MiPuuT4hc-Er4xFfrEkBqz-dCjULYJfTM_c0upa0Dc,3758
|
|
5
|
-
ducktools/classbuilder/py.typed,sha256=la67KBlbjXN-_-DfGNcdOcjYumVpKG_Tkw-8n5dnGB4,8
|
|
6
|
-
ducktools_classbuilder-0.4.0.dist-info/LICENSE.md,sha256=6Thz9Dbw8R4fWInl6sGl8Rj3UnKnRbDwrc6jZerpugQ,1070
|
|
7
|
-
ducktools_classbuilder-0.4.0.dist-info/METADATA,sha256=teRrGNNsVoQJ1jLnzctDVFREtSDq9MVCS9GNZr1S01E,9982
|
|
8
|
-
ducktools_classbuilder-0.4.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
9
|
-
ducktools_classbuilder-0.4.0.dist-info/top_level.txt,sha256=uSDLtio3ZFqdwcsMJ2O5yhjB4Q3ytbBWbA8rJREganc,10
|
|
10
|
-
ducktools_classbuilder-0.4.0.dist-info/RECORD,,
|
{ducktools_classbuilder-0.4.0.dist-info → ducktools_classbuilder-0.5.0.dist-info}/LICENSE.md
RENAMED
|
File without changes
|
|
File without changes
|
{ducktools_classbuilder-0.4.0.dist-info → ducktools_classbuilder-0.5.0.dist-info}/top_level.txt
RENAMED
|
File without changes
|