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.

@@ -21,7 +21,7 @@
21
21
  # SOFTWARE.
22
22
  import sys
23
23
 
24
- __version__ = "v0.4.0"
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 default we need a sentinel value
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 get_init_maker(null=NOTHING, extra_code=None):
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
- init_maker = get_init_maker()
157
+ init_generator = get_init_generator()
158
158
 
159
159
 
160
- def repr_maker(cls):
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 eq_maker(cls):
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 frozen_setattr_maker(cls):
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 frozen_delattr_maker(cls):
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
- init_desc = MethodMaker("__init__", init_maker)
238
- repr_desc = MethodMaker("__repr__", repr_maker)
239
- eq_desc = MethodMaker("__eq__", eq_maker)
240
- frozen_setattr_desc = MethodMaker("__setattr__", frozen_setattr_maker)
241
- frozen_delattr_desc = MethodMaker("__delattr__", frozen_delattr_maker)
242
- default_methods = frozenset({init_desc, repr_desc, eq_desc})
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 = {repr_desc, eq_desc}
371
+ _field_methods = {repr_maker, eq_maker}
365
372
  if _UNDER_TESTING:
366
- _field_methods.update({frozen_setattr_desc, frozen_delattr_desc})
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
- cls_annotations = cls.__dict__.get("__annotations__", {})
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
- # Replace the SlotAttributes instance with a regular dict
425
- # So that help() works
426
- setattr(cls, "__slots__", slot_replacement)
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
- # Update annotations with any types from the slots assignment
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
- setattr(cls, k, attrib.default)
521
+ modifications[k] = attrib.default
498
522
  else:
499
- delattr(cls, k)
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
- delattr(cls, k)
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=get_init_maker(
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, repr_desc, eq_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({frozen_setattr_desc, frozen_delattr_desc})
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 get_init_maker(
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 init_maker(cls: type) -> tuple[str, dict[str, typing.Any]]: ...
35
- def repr_maker(cls: type) -> tuple[str, dict[str, typing.Any]]: ...
36
- def eq_maker(cls: type) -> tuple[str, dict[str, typing.Any]]: ...
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 frozen_setattr_maker(cls: type) -> tuple[str, dict[str, typing.Any]]: ...
38
+ def frozen_setattr_generator(cls: type) -> tuple[str, dict[str, typing.Any]]: ...
39
39
 
40
- def frozen_delattr_maker(cls: type) -> tuple[str, dict[str, typing.Any]]: ...
40
+ def frozen_delattr_generator(cls: type) -> tuple[str, dict[str, typing.Any]]: ...
41
41
 
42
- init_desc: MethodMaker
43
- repr_desc: MethodMaker
44
- eq_desc: MethodMaker
45
- frozen_setattr_desc: MethodMaker
46
- frozen_delattr_desc: MethodMaker
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(field_type: type[Field] = Field) -> Callable[[type], dict[str, Field]]: ...
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
 
@@ -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
- frozen_setattr_desc, frozen_delattr_desc, is_classvar,
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
- init_desc = get_init_maker()
348
- prefab_init_desc = get_init_maker(init_name=PREFAB_INIT_FUNC)
349
- repr_desc = get_repr_maker()
350
- recursive_repr_desc = get_repr_maker(recursion_safe=True)
351
- eq_desc = get_eq_maker()
352
- iter_desc = get_iter_maker()
353
- asdict_desc = get_asdict_maker()
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
- delattr(cls, name)
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
- delattr(cls, name) # clear attrib from class
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(init_desc)
559
+ methods.add(init_maker)
558
560
  else:
559
- methods.add(prefab_init_desc)
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(recursive_repr_desc)
565
+ methods.add(recursive_repr_maker)
564
566
  else:
565
- methods.add(repr_desc)
567
+ methods.add(repr_maker)
566
568
  if eq and "__eq__" not in cls_dict:
567
- methods.add(eq_desc)
569
+ methods.add(eq_maker)
568
570
  if iter and "__iter__" not in cls_dict:
569
- methods.add(iter_desc)
571
+ methods.add(iter_maker)
570
572
  if frozen:
571
- methods.add(frozen_setattr_desc)
572
- methods.add(frozen_delattr_desc)
573
+ methods.add(frozen_setattr_maker)
574
+ methods.add(frozen_delattr_maker)
573
575
  if dict_method:
574
- methods.add(asdict_desc)
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
- cls = type(o)
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, slot_gatherer
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
- init_desc: MethodMaker
43
- prefab_init_desc: MethodMaker
44
- repr_desc: MethodMaker
45
- recursive_repr_desc: MethodMaker
46
- eq_desc: MethodMaker
47
- iter_desc: MethodMaker
48
- asdict_desc: MethodMaker
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.4.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 `init_maker`, `repr_maker` and `eq_maker` generators are included.
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: `init_desc = MethodMaker("__init__", init_maker)`
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
- init_maker, eq_maker, repr_maker,
102
+ builder,
103
+ slot_gatherer,
104
+ init_generator, eq_generator, repr_generator,
104
105
  MethodMaker,
105
106
  )
106
107
 
107
- init_desc = MethodMaker("__init__", init_maker)
108
- repr_desc = MethodMaker("__repr__", repr_maker)
109
- eq_desc = MethodMaker("__eq__", eq_maker)
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={init_desc, repr_desc, eq_desc})
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
- in `extras.py` which can be used to generate classbuilders that rely on
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,,