ducktools-classbuilder 0.6.1__py3-none-any.whl → 0.6.2__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 -13
- ducktools/classbuilder/__init__.pyi +12 -1
- ducktools/classbuilder/prefab.pyi +3 -0
- {ducktools_classbuilder-0.6.1.dist-info → ducktools_classbuilder-0.6.2.dist-info}/METADATA +1 -1
- ducktools_classbuilder-0.6.2.dist-info/RECORD +12 -0
- {ducktools_classbuilder-0.6.1.dist-info → ducktools_classbuilder-0.6.2.dist-info}/WHEEL +1 -1
- ducktools_classbuilder-0.6.1.dist-info/RECORD +0 -12
- {ducktools_classbuilder-0.6.1.dist-info → ducktools_classbuilder-0.6.2.dist-info}/LICENSE.md +0 -0
- {ducktools_classbuilder-0.6.1.dist-info → ducktools_classbuilder-0.6.2.dist-info}/top_level.txt +0 -0
|
@@ -34,7 +34,7 @@ import sys
|
|
|
34
34
|
|
|
35
35
|
from .annotations import get_ns_annotations, is_classvar
|
|
36
36
|
|
|
37
|
-
__version__ = "v0.6.
|
|
37
|
+
__version__ = "v0.6.2"
|
|
38
38
|
|
|
39
39
|
# Change this name if you make heavy modifications
|
|
40
40
|
INTERNALS_DICT = "__classbuilder_internals__"
|
|
@@ -158,32 +158,69 @@ class MethodMaker:
|
|
|
158
158
|
def __repr__(self):
|
|
159
159
|
return f"<MethodMaker for {self.funcname!r} method>"
|
|
160
160
|
|
|
161
|
-
def __get__(self,
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
161
|
+
def __get__(self, inst, cls):
|
|
162
|
+
local_vars = {}
|
|
163
|
+
|
|
164
|
+
# This can be called via super().funcname(...) in which case the class
|
|
165
|
+
# may not be the correct one. If this is the correct class
|
|
166
|
+
# it should have this descriptor in the class dict under
|
|
167
|
+
# the correct funcname.
|
|
168
|
+
# Otherwise is should be found in the MRO of the class.
|
|
169
|
+
if cls.__dict__.get(self.funcname) is self:
|
|
170
|
+
gen_cls = cls
|
|
165
171
|
else:
|
|
166
|
-
#
|
|
167
|
-
|
|
172
|
+
for c in cls.__mro__[1:]: # skip 'cls' as special cased
|
|
173
|
+
if c.__dict__.get(self.funcname) is self:
|
|
174
|
+
gen_cls = c
|
|
175
|
+
break
|
|
176
|
+
else: # pragma: no cover
|
|
177
|
+
# This should only be reached if called with incorrect arguments
|
|
178
|
+
# manually
|
|
179
|
+
raise AttributeError(
|
|
180
|
+
f"Could not find {self!r} in class {cls.__name__!r} MRO."
|
|
181
|
+
)
|
|
168
182
|
|
|
169
|
-
|
|
170
|
-
gen = self.code_generator(cls, self.funcname)
|
|
183
|
+
gen = self.code_generator(gen_cls, self.funcname)
|
|
171
184
|
exec(gen.source_code, gen.globs, local_vars)
|
|
172
185
|
method = local_vars.get(self.funcname)
|
|
173
186
|
|
|
174
187
|
try:
|
|
175
|
-
method.__qualname__ = f"{
|
|
188
|
+
method.__qualname__ = f"{gen_cls.__qualname__}.{self.funcname}"
|
|
176
189
|
except AttributeError:
|
|
177
190
|
# This might be a property or some other special
|
|
178
191
|
# descriptor. Don't try to rename.
|
|
179
192
|
pass
|
|
180
193
|
|
|
181
194
|
# Replace this descriptor on the class with the generated function
|
|
182
|
-
setattr(
|
|
195
|
+
setattr(gen_cls, self.funcname, method)
|
|
183
196
|
|
|
184
197
|
# Use 'get' to return the generated function as a bound method
|
|
185
198
|
# instead of as a regular function for first usage.
|
|
186
|
-
return method.__get__(
|
|
199
|
+
return method.__get__(inst, cls)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class _SignatureMaker:
|
|
203
|
+
# 'inspect.signature' calls the `__get__` method of the `__init__` methodmaker with
|
|
204
|
+
# the wrong arguments.
|
|
205
|
+
# Instead of __get__(None, cls) or __get__(inst, type(inst))
|
|
206
|
+
# it uses __get__(cls, type(cls)).
|
|
207
|
+
#
|
|
208
|
+
# If this is done before `__init__` has been generated then
|
|
209
|
+
# help(cls) will fail along with inspect.signature(cls)
|
|
210
|
+
# This signature maker descriptor is placed to override __signature__ and force
|
|
211
|
+
# the `__init__` signature to be generated first if the signature is requested.
|
|
212
|
+
def __get__(self, instance, cls):
|
|
213
|
+
import inspect # Deferred inspect import
|
|
214
|
+
_ = cls.__init__ # force generation of `__init__` function
|
|
215
|
+
# Remove this attribute from the class
|
|
216
|
+
# This prevents recursion back into this __get__ method.
|
|
217
|
+
delattr(cls, "__signature__")
|
|
218
|
+
sig = inspect.signature(cls)
|
|
219
|
+
setattr(cls, "__signature__", sig)
|
|
220
|
+
return sig
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
signature_maker = _SignatureMaker()
|
|
187
224
|
|
|
188
225
|
|
|
189
226
|
def get_init_generator(null=NOTHING, extra_code=None):
|
|
@@ -402,7 +439,7 @@ _field_init_maker = MethodMaker(
|
|
|
402
439
|
)
|
|
403
440
|
|
|
404
441
|
|
|
405
|
-
def builder(cls=None, /, *, gatherer, methods, flags=None):
|
|
442
|
+
def builder(cls=None, /, *, gatherer, methods, flags=None, fix_signature=True):
|
|
406
443
|
"""
|
|
407
444
|
The main builder for class generation
|
|
408
445
|
|
|
@@ -413,6 +450,10 @@ def builder(cls=None, /, *, gatherer, methods, flags=None):
|
|
|
413
450
|
:type methods: set[MethodMaker]
|
|
414
451
|
:param flags: additional flags to store in the internals dictionary
|
|
415
452
|
for use by method generators.
|
|
453
|
+
:type flags: None | dict[str, bool]
|
|
454
|
+
:param fix_signature: Add a __signature__ attribute to work-around an issue with
|
|
455
|
+
inspect.signature incorrectly handling __init__ descriptors.
|
|
456
|
+
:type fix_signature: bool
|
|
416
457
|
:return: The modified class (the class itself is modified, but this is expected).
|
|
417
458
|
"""
|
|
418
459
|
# Handle `None` to make wrapping with a decorator easier.
|
|
@@ -459,6 +500,10 @@ def builder(cls=None, /, *, gatherer, methods, flags=None):
|
|
|
459
500
|
|
|
460
501
|
internals["methods"] = _MappingProxyType(internal_methods)
|
|
461
502
|
|
|
503
|
+
# Fix for inspect.signature(cls)
|
|
504
|
+
if fix_signature:
|
|
505
|
+
setattr(cls, "__signature__", signature_maker)
|
|
506
|
+
|
|
462
507
|
return cls
|
|
463
508
|
|
|
464
509
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import types
|
|
2
2
|
import typing
|
|
3
3
|
|
|
4
|
+
import inspect
|
|
5
|
+
|
|
4
6
|
from collections.abc import Callable
|
|
5
7
|
from types import MappingProxyType
|
|
6
8
|
from typing_extensions import dataclass_transform
|
|
@@ -49,7 +51,12 @@ class MethodMaker:
|
|
|
49
51
|
code_generator: _CodegenType
|
|
50
52
|
def __init__(self, funcname: str, code_generator: _CodegenType) -> None: ...
|
|
51
53
|
def __repr__(self) -> str: ...
|
|
52
|
-
def __get__(self, instance, cls
|
|
54
|
+
def __get__(self, instance, cls) -> Callable: ...
|
|
55
|
+
|
|
56
|
+
class _SignatureMaker:
|
|
57
|
+
def __get__(self, instance, cls) -> inspect.Signature: ...
|
|
58
|
+
|
|
59
|
+
signature_maker: _SignatureMaker
|
|
53
60
|
|
|
54
61
|
def get_init_generator(
|
|
55
62
|
null: _NothingType = NOTHING,
|
|
@@ -86,6 +93,7 @@ def builder(
|
|
|
86
93
|
gatherer: Callable[[type], tuple[dict[str, Field], dict[str, typing.Any]]],
|
|
87
94
|
methods: frozenset[MethodMaker] | set[MethodMaker],
|
|
88
95
|
flags: dict[str, bool] | None = None,
|
|
96
|
+
fix_signature: bool = ...,
|
|
89
97
|
) -> type[_T]: ...
|
|
90
98
|
|
|
91
99
|
@typing.overload
|
|
@@ -96,6 +104,7 @@ def builder(
|
|
|
96
104
|
gatherer: Callable[[type], tuple[dict[str, Field], dict[str, typing.Any]]],
|
|
97
105
|
methods: frozenset[MethodMaker] | set[MethodMaker],
|
|
98
106
|
flags: dict[str, bool] | None = None,
|
|
107
|
+
fix_signature: bool = ...,
|
|
99
108
|
) -> Callable[[type[_T]], type[_T]]: ...
|
|
100
109
|
|
|
101
110
|
|
|
@@ -126,6 +135,7 @@ class Field(metaclass=SlotMakerMeta):
|
|
|
126
135
|
|
|
127
136
|
__slots__: dict[str, str]
|
|
128
137
|
__classbuilder_internals__: dict
|
|
138
|
+
__signature__: inspect.Signature
|
|
129
139
|
|
|
130
140
|
def __init__(
|
|
131
141
|
self,
|
|
@@ -249,6 +259,7 @@ class GatheredFields:
|
|
|
249
259
|
modifications: dict[str, typing.Any]
|
|
250
260
|
|
|
251
261
|
__classbuilder_internals__: dict
|
|
262
|
+
__signature__: inspect.Signature
|
|
252
263
|
|
|
253
264
|
def __init__(
|
|
254
265
|
self,
|
|
@@ -2,6 +2,8 @@ import typing
|
|
|
2
2
|
from types import MappingProxyType
|
|
3
3
|
from typing_extensions import dataclass_transform
|
|
4
4
|
|
|
5
|
+
import inspect
|
|
6
|
+
|
|
5
7
|
from collections.abc import Callable
|
|
6
8
|
|
|
7
9
|
from . import (
|
|
@@ -42,6 +44,7 @@ asdict_maker: MethodMaker
|
|
|
42
44
|
|
|
43
45
|
class Attribute(Field):
|
|
44
46
|
__slots__: dict
|
|
47
|
+
__signature__: inspect.Signature
|
|
45
48
|
|
|
46
49
|
iter: bool
|
|
47
50
|
serialize: bool
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
ducktools/classbuilder/__init__.py,sha256=9IBwSOSXY9SFbbV6msfS9oUx2BneNrQvjZsb-iLSLZ4,32820
|
|
2
|
+
ducktools/classbuilder/__init__.pyi,sha256=JaAfc6y4Jj920y4lm9peduNXeb9XUJwKN-QqFpqG5pw,7924
|
|
3
|
+
ducktools/classbuilder/annotations.py,sha256=17g1WnWfyMWBHKQp2HD_hvV-n7CKXy_zv8NTODpOsuU,5437
|
|
4
|
+
ducktools/classbuilder/annotations.pyi,sha256=cTZQ-pp2IkfdvwHiU3pIySUzzBKxfOtO-e5PkA8TNwo,520
|
|
5
|
+
ducktools/classbuilder/prefab.py,sha256=LCrnS2zKADX5FS0SdzCYLF4n5yR43CIlv2QLL6PDjl4,23292
|
|
6
|
+
ducktools/classbuilder/prefab.pyi,sha256=kmqZcP2aGHEAb3-cLCBlYi6gclzsQAAmY7cnHvKT7jQ,4360
|
|
7
|
+
ducktools/classbuilder/py.typed,sha256=la67KBlbjXN-_-DfGNcdOcjYumVpKG_Tkw-8n5dnGB4,8
|
|
8
|
+
ducktools_classbuilder-0.6.2.dist-info/LICENSE.md,sha256=6Thz9Dbw8R4fWInl6sGl8Rj3UnKnRbDwrc6jZerpugQ,1070
|
|
9
|
+
ducktools_classbuilder-0.6.2.dist-info/METADATA,sha256=fGjzXJdePiVfTkzZozk9-HHFW-zZJakoPnBrTmMepAE,10911
|
|
10
|
+
ducktools_classbuilder-0.6.2.dist-info/WHEEL,sha256=y4mX-SOX4fYIkonsAGA5N0Oy-8_gI4FXw5HNI1xqvWg,91
|
|
11
|
+
ducktools_classbuilder-0.6.2.dist-info/top_level.txt,sha256=uSDLtio3ZFqdwcsMJ2O5yhjB4Q3ytbBWbA8rJREganc,10
|
|
12
|
+
ducktools_classbuilder-0.6.2.dist-info/RECORD,,
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
ducktools/classbuilder/__init__.py,sha256=i0NqVICBsMUBQtxX_2UsnETE9y0X15cwTSoSlE6_fJI,30820
|
|
2
|
-
ducktools/classbuilder/__init__.pyi,sha256=tdYtI5Wqn-iur-MGGSpk4EbQTtl91F_MOGHXwYf4BYo,7656
|
|
3
|
-
ducktools/classbuilder/annotations.py,sha256=17g1WnWfyMWBHKQp2HD_hvV-n7CKXy_zv8NTODpOsuU,5437
|
|
4
|
-
ducktools/classbuilder/annotations.pyi,sha256=cTZQ-pp2IkfdvwHiU3pIySUzzBKxfOtO-e5PkA8TNwo,520
|
|
5
|
-
ducktools/classbuilder/prefab.py,sha256=LCrnS2zKADX5FS0SdzCYLF4n5yR43CIlv2QLL6PDjl4,23292
|
|
6
|
-
ducktools/classbuilder/prefab.pyi,sha256=1YkrSSTcjzhNn_AXSYiGIaX7kLm3DFckCR_zytFp0PE,4307
|
|
7
|
-
ducktools/classbuilder/py.typed,sha256=la67KBlbjXN-_-DfGNcdOcjYumVpKG_Tkw-8n5dnGB4,8
|
|
8
|
-
ducktools_classbuilder-0.6.1.dist-info/LICENSE.md,sha256=6Thz9Dbw8R4fWInl6sGl8Rj3UnKnRbDwrc6jZerpugQ,1070
|
|
9
|
-
ducktools_classbuilder-0.6.1.dist-info/METADATA,sha256=7sRGN_m6jeEKYCdajAu8NN-7cs3U4E7s6YUdEj643m4,10911
|
|
10
|
-
ducktools_classbuilder-0.6.1.dist-info/WHEEL,sha256=cpQTJ5IWu9CdaPViMhC9YzF8gZuS5-vlfoFihTBC86A,91
|
|
11
|
-
ducktools_classbuilder-0.6.1.dist-info/top_level.txt,sha256=uSDLtio3ZFqdwcsMJ2O5yhjB4Q3ytbBWbA8rJREganc,10
|
|
12
|
-
ducktools_classbuilder-0.6.1.dist-info/RECORD,,
|
{ducktools_classbuilder-0.6.1.dist-info → ducktools_classbuilder-0.6.2.dist-info}/LICENSE.md
RENAMED
|
File without changes
|
{ducktools_classbuilder-0.6.1.dist-info → ducktools_classbuilder-0.6.2.dist-info}/top_level.txt
RENAMED
|
File without changes
|