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.

@@ -21,7 +21,7 @@
21
21
  # SOFTWARE.
22
22
  import sys
23
23
 
24
- __version__ = "v0.5.0"
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
- # and pretend `Field` was a built class all along.
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=lambda cls_: (_field_internal, {}),
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
 
@@ -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 isinstance(slots, SlotFields):
550
- gatherer = slot_prefab_gatherer
551
- slotted = True
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 = attribute_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
- cls = type(class_name, bases, class_dict)
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
- setattr(cls, name, attrib)
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: ...
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ducktools-classbuilder
3
- Version: 0.5.0
3
+ Version: 0.5.1
4
4
  Summary: Toolkit for creating class boilerplate generators
5
5
  Author: David C Ellis
6
6
  License: MIT License
@@ -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,,