ducktools-classbuilder 0.10.0__py3-none-any.whl → 0.10.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.

@@ -217,15 +217,23 @@ class _SignatureMaker:
217
217
  # help(cls) will fail along with inspect.signature(cls)
218
218
  # This signature maker descriptor is placed to override __signature__ and force
219
219
  # the `__init__` signature to be generated first if the signature is requested.
220
- def __get__(self, instance, cls):
221
- import inspect # Deferred inspect import
222
- _ = cls.__init__ # force generation of `__init__` function
223
- # Remove this attribute from the class
224
- # This prevents recursion back into this __get__ method.
225
- delattr(cls, "__signature__")
226
- sig = inspect.signature(cls)
227
- setattr(cls, "__signature__", sig)
228
- return sig
220
+ def __get__(self, instance, cls=None):
221
+ if cls is None:
222
+ cls = type(instance)
223
+
224
+ # force generation of `__init__` function
225
+ _ = cls.__init__
226
+
227
+ if instance is None:
228
+ raise AttributeError(
229
+ f"type object {cls.__name__!r} "
230
+ "has no attribute '__signature__'"
231
+ )
232
+ else:
233
+ raise AttributeError(
234
+ f"{cls.__name__!r} object"
235
+ "has no attribute '__signature__'"
236
+ )
229
237
 
230
238
 
231
239
  signature_maker = _SignatureMaker()
@@ -393,7 +401,7 @@ def replace_generator(cls, funcname="__replace__"):
393
401
  # Generate the replace method for built classes
394
402
  # unlike the dataclasses implementation this is generated
395
403
  attribs = get_fields(cls)
396
-
404
+
397
405
  # This is essentially the as_dict generator for prefabs
398
406
  # except based on attrib.init instead of .serialize
399
407
  vals = ", ".join(
@@ -407,7 +415,7 @@ def replace_generator(cls, funcname="__replace__"):
407
415
  f"def {funcname}(self, /, **changes):\n"
408
416
  f" new_kwargs = {init_dict}\n"
409
417
  f" for name, value in changes.items():\n"
410
- f" if name not in new_kwargs:\n"
418
+ f" if name not in new_kwargs:\n"
411
419
  f" raise TypeError(\n"
412
420
  f" f\"{{name!r}} is not a valid replacable \"\n"
413
421
  f" f\"field on {{self.__class__.__name__!r}}\"\n"
@@ -578,7 +586,7 @@ class SlotMakerMeta(type):
578
586
  def __new__(cls, name, bases, ns, slots=True, gatherer=None, **kwargs):
579
587
  # This should only run if slots=True is declared
580
588
  # and __slots__ have not already been defined
581
- if slots and "__slots__" not in ns:
589
+ if slots and "__slots__" not in ns:
582
590
  # Check if a different gatherer has been set in any base classes
583
591
  # Default to unified gatherer
584
592
  if gatherer is None:
@@ -617,7 +625,7 @@ class SlotMakerMeta(type):
617
625
  # Place pre-gathered field data - modifications are already applied
618
626
  modifications = {}
619
627
  ns[GATHERED_DATA] = fields, modifications
620
-
628
+
621
629
  else:
622
630
  if gatherer is not None:
623
631
  ns[META_GATHERER_NAME] = gatherer
@@ -642,7 +650,7 @@ class GatheredFields:
642
650
  def __eq__(self, other):
643
651
  if type(self) is type(other):
644
652
  return self.fields == other.fields and self.modifications == other.modifications
645
-
653
+
646
654
  def __repr__(self):
647
655
  return f"{type(self).__name__}(fields={self.fields!r}, modifications={self.modifications!r})"
648
656
 
@@ -761,11 +769,11 @@ def _build_field():
761
769
  # Complete the construction of the Field class
762
770
  field_docs = {
763
771
  "default": "Standard default value to be used for attributes with this field.",
764
- "default_factory":
772
+ "default_factory":
765
773
  "A zero-argument function to be called to generate a default value, "
766
774
  "useful for mutable obects like lists.",
767
775
  "type": "The type of the attribute to be assigned by this field.",
768
- "doc":
776
+ "doc":
769
777
  "The documentation for the attribute that appears when calling "
770
778
  "help(...) on the class. (Only in slotted classes).",
771
779
  "init": "Include this attribute in the class __init__ parameters.",
@@ -813,7 +821,7 @@ def pre_gathered_gatherer(cls_or_ns):
813
821
  cls_dict = cls_or_ns
814
822
  else:
815
823
  cls_dict = cls_or_ns.__dict__
816
-
824
+
817
825
  return cls_dict[GATHERED_DATA]
818
826
 
819
827
 
@@ -1043,7 +1051,7 @@ def make_unified_gatherer(
1043
1051
  return anno_g(cls_dict)
1044
1052
 
1045
1053
  return attrib_g(cls_dict)
1046
-
1054
+
1047
1055
  return field_unified_gatherer
1048
1056
 
1049
1057
 
@@ -1,5 +1,6 @@
1
1
  import types
2
2
  import typing
3
+ import typing_extensions
3
4
 
4
5
  import inspect
5
6
 
@@ -57,7 +58,7 @@ class MethodMaker:
57
58
  def __get__(self, instance, cls) -> Callable: ...
58
59
 
59
60
  class _SignatureMaker:
60
- def __get__(self, instance, cls) -> inspect.Signature: ...
61
+ def __get__(self, instance, cls=None) -> typing_extensions.Never: ...
61
62
 
62
63
  signature_maker: _SignatureMaker
63
64
 
@@ -141,7 +142,7 @@ class Field(metaclass=SlotMakerMeta):
141
142
 
142
143
  __slots__: dict[str, str]
143
144
  __classbuilder_internals__: dict
144
- __signature__: inspect.Signature
145
+ __signature__: _SignatureMaker
145
146
 
146
147
  def __init__(
147
148
  self,
@@ -1,2 +1,2 @@
1
- __version__ = "0.10.0"
2
- __version_tuple__ = (0, 10, 0)
1
+ __version__ = "0.10.1"
2
+ __version_tuple__ = (0, 10, 1)
@@ -33,6 +33,29 @@ class _LazyAnnotationLib:
33
33
  _lazy_annotationlib = _LazyAnnotationLib()
34
34
 
35
35
 
36
+ def get_func_annotations(func):
37
+ """
38
+ Given a function, return the annotations dictionary
39
+
40
+ :param func: function object
41
+ :return: dictionary of annotations
42
+ """
43
+ # This method exists for use by prefab in getting annotations from
44
+ # the __prefab_post_init__ function
45
+ try:
46
+ annotations = func.__annotations__
47
+ except Exception:
48
+ if sys.version_info >= (3, 14):
49
+ annotations = _lazy_annotationlib.get_annotations(
50
+ func,
51
+ format=_lazy_annotationlib.Format.FORWARDREF,
52
+ )
53
+ else:
54
+ raise
55
+
56
+ return annotations
57
+
58
+
36
59
  def get_ns_annotations(ns):
37
60
  """
38
61
  Given a class namespace, attempt to retrieve the
@@ -3,6 +3,10 @@ import types
3
3
 
4
4
  _CopiableMappings = dict[str, typing.Any] | types.MappingProxyType[str, typing.Any]
5
5
 
6
+ def get_func_annotations(
7
+ func: types.FunctionType,
8
+ ) -> dict[str, typing.Any]: ...
9
+
6
10
  def get_ns_annotations(
7
11
  ns: _CopiableMappings,
8
12
  ) -> dict[str, typing.Any]: ...
@@ -34,6 +34,8 @@ from . import (
34
34
  get_repr_generator,
35
35
  )
36
36
 
37
+ from .annotations import get_func_annotations
38
+
37
39
  # These aren't used but are re-exported for ease of use
38
40
  # noinspection PyUnresolvedReferences
39
41
  from . import SlotFields, KW_ONLY # noqa: F401
@@ -98,7 +100,7 @@ def init_generator(cls, funcname="__init__"):
98
100
  func_arglist.extend(arglist)
99
101
 
100
102
  if extra_funcname == POST_INIT_FUNC:
101
- post_init_annotations.update(func.__annotations__)
103
+ post_init_annotations.update(get_func_annotations(func))
102
104
 
103
105
  pos_arglist = []
104
106
  kw_only_arglist = []
@@ -204,11 +206,11 @@ def init_generator(cls, funcname="__init__"):
204
206
  post_init_call = ""
205
207
 
206
208
  code = (
207
- f"def {funcname}(self, {args}):\n"
209
+ f"def {funcname}(self, {args}) -> None:\n"
208
210
  f"{pre_init_call}\n"
209
211
  f"{body}\n"
210
212
  f"{post_init_call}\n"
211
- )
213
+ )
212
214
 
213
215
  return GeneratedCode(code, globs)
214
216
 
@@ -13,6 +13,7 @@ from . import (
13
13
  GeneratedCode,
14
14
  MethodMaker,
15
15
  SlotMakerMeta,
16
+ _SignatureMaker
16
17
  )
17
18
 
18
19
  from . import SlotFields as SlotFields, KW_ONLY as KW_ONLY
@@ -47,7 +48,7 @@ hash_maker: MethodMaker
47
48
 
48
49
  class Attribute(Field):
49
50
  __slots__: dict
50
- __signature__: inspect.Signature
51
+ __signature__: _SignatureMaker
51
52
  __classbuilder_gathered_fields__: tuple[dict[str, Field], dict[str, typing.Any]]
52
53
 
53
54
  iter: bool
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ducktools-classbuilder
3
- Version: 0.10.0
3
+ Version: 0.10.1
4
4
  Summary: Toolkit for creating class boilerplate generators
5
5
  Author: David C Ellis
6
6
  Project-URL: Homepage, https://github.com/davidcellis/ducktools-classbuilder
@@ -0,0 +1,13 @@
1
+ ducktools/classbuilder/__init__.py,sha256=zJuGJ8_1EPEYJqO0XXDuknwGD1MxPJSZXS5_wFAW6B0,37299
2
+ ducktools/classbuilder/__init__.pyi,sha256=NazFRfLcMpU533jGixaWHV_kARtc9J4-xXX-gkD8rFY,8177
3
+ ducktools/classbuilder/_version.py,sha256=kdUPXlUQWcTv2wnqFkwrCx7Ue7WU2JVPnG1UU0gmLck,54
4
+ ducktools/classbuilder/annotations.py,sha256=ImEEuzEFUrGNRMlAl7YN-H8PZT1FOK8D_yL1LygNdmo,3626
5
+ ducktools/classbuilder/annotations.pyi,sha256=zAKJbEN1klvZu71ShuvKbf6OE7ddKb5USC6MbOjRPfY,336
6
+ ducktools/classbuilder/prefab.py,sha256=RJfOv9yzJsySaBz6w4e5ZP8Q7K3LWX6_Buuh2zD9YDQ,24689
7
+ ducktools/classbuilder/prefab.pyi,sha256=eZLjFhM-oHeRBONpCWSb1wLJKGs5Xiq0w_Jeor8Go-w,6525
8
+ ducktools/classbuilder/py.typed,sha256=la67KBlbjXN-_-DfGNcdOcjYumVpKG_Tkw-8n5dnGB4,8
9
+ ducktools_classbuilder-0.10.1.dist-info/licenses/LICENSE,sha256=6Thz9Dbw8R4fWInl6sGl8Rj3UnKnRbDwrc6jZerpugQ,1070
10
+ ducktools_classbuilder-0.10.1.dist-info/METADATA,sha256=LkjNjXyb49v3vYBEvbldkThl9xAgCHhVc8dPNgiMsxw,9231
11
+ ducktools_classbuilder-0.10.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
12
+ ducktools_classbuilder-0.10.1.dist-info/top_level.txt,sha256=uSDLtio3ZFqdwcsMJ2O5yhjB4Q3ytbBWbA8rJREganc,10
13
+ ducktools_classbuilder-0.10.1.dist-info/RECORD,,
@@ -1,13 +0,0 @@
1
- ducktools/classbuilder/__init__.py,sha256=nZz_pQM6CN7yCT71oOclih4uls73a6SlW-HY2gxzYIs,37244
2
- ducktools/classbuilder/__init__.pyi,sha256=v9QDO8A0AdPlAPNOXZGYXu1FaAq3yelwWNucdfWudrs,8143
3
- ducktools/classbuilder/_version.py,sha256=MdjuHfU3W8hJQpD3kGrPvowqtRDz5JMifDCCjNbrskE,54
4
- ducktools/classbuilder/annotations.py,sha256=GgBvNthDSRvKKZ9R_qxEVtCV3_vGuEBfuqQJFcDAe1s,3005
5
- ducktools/classbuilder/annotations.pyi,sha256=c5vYtULdDgMYWtkzeYMsHIbmnEuT2Ru-nNZieWvYuQ4,247
6
- ducktools/classbuilder/prefab.py,sha256=2GldQTRvOcFzB68qXbymeHKiXoyHx7eOjGxeilFIcn0,24632
7
- ducktools/classbuilder/prefab.pyi,sha256=q5Zca5wAN80GE4uICY6v3EIB6LvIcjOEhzD1pVDK44U,6507
8
- ducktools/classbuilder/py.typed,sha256=la67KBlbjXN-_-DfGNcdOcjYumVpKG_Tkw-8n5dnGB4,8
9
- ducktools_classbuilder-0.10.0.dist-info/licenses/LICENSE,sha256=6Thz9Dbw8R4fWInl6sGl8Rj3UnKnRbDwrc6jZerpugQ,1070
10
- ducktools_classbuilder-0.10.0.dist-info/METADATA,sha256=zrp9Q-klxkuTdsGSGz87rEZ7nxM8FCEppJfiGwqRh-0,9231
11
- ducktools_classbuilder-0.10.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
12
- ducktools_classbuilder-0.10.0.dist-info/top_level.txt,sha256=uSDLtio3ZFqdwcsMJ2O5yhjB4Q3ytbBWbA8rJREganc,10
13
- ducktools_classbuilder-0.10.0.dist-info/RECORD,,