ducktools-classbuilder 0.6.0__tar.gz → 0.6.1__tar.gz
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-0.6.0/src/ducktools_classbuilder.egg-info → ducktools_classbuilder-0.6.1}/PKG-INFO +1 -1
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/docs/extension_examples.md +8 -8
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/docs/tutorial.md +2 -2
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/src/ducktools/classbuilder/__init__.py +14 -14
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/src/ducktools/classbuilder/__init__.pyi +13 -10
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/src/ducktools/classbuilder/annotations.pyi +1 -1
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/src/ducktools/classbuilder/prefab.py +160 -167
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/src/ducktools/classbuilder/prefab.pyi +4 -6
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1/src/ducktools_classbuilder.egg-info}/PKG-INFO +1 -1
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/test_core.py +2 -2
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/LICENSE.md +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/MANIFEST.in +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/README.md +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/docs/Makefile +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/docs/api.md +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/docs/approach_vs_tool.md +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/docs/conf.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/docs/generated_code.md +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/docs/index.md +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/docs/make.bat +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/docs/perf/performance_tests.md +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/docs/prefab/index.md +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/pyproject.toml +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/setup.cfg +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/src/ducktools/classbuilder/annotations.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/src/ducktools/classbuilder/py.typed +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/src/ducktools_classbuilder.egg-info/SOURCES.txt +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/src/ducktools_classbuilder.egg-info/dependency_links.txt +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/src/ducktools_classbuilder.egg-info/requires.txt +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/src/ducktools_classbuilder.egg-info/top_level.txt +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/annotations/test_annotated.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/annotations/test_annotations_module.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/conftest.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/dynamic/test_compare_attrib.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/dynamic/test_construction.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/dynamic/test_internals.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/dynamic/test_pre_post_init.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/dynamic/test_slots_novalues.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/dynamic/test_slotted_class.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/dynamic/test_subclass_implementation.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/conftest.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/examples/creation.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/examples/creation_empty.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/examples/dunders.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/examples/fails/creation_1.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/examples/fails/creation_2.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/examples/fails/creation_3.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/examples/fails/creation_5.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/examples/fails/inheritance_1.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/examples/fails/inheritance_2.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/examples/frozen_prefabs.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/examples/funcs_prefabs.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/examples/hint_syntax.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/examples/inheritance.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/examples/init_ex.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/examples/kw_only.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/examples/repr_func.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/test_creation.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/test_dunders.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/test_frozen.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/test_funcs.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/test_hint_syntax.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/test_inheritance.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/test_init.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/test_kw_only.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/test_repr.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/py312_tests/test_generic_annotations.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/test_field_flags.py +0 -0
- {ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/test_slotmakermeta.py +0 -0
|
@@ -79,12 +79,12 @@ from ducktools.classbuilder import (
|
|
|
79
79
|
)
|
|
80
80
|
|
|
81
81
|
|
|
82
|
-
def iter_generator(cls):
|
|
82
|
+
def iter_generator(cls, funcname="__iter__"):
|
|
83
83
|
field_names = get_fields(cls).keys()
|
|
84
84
|
field_yield = "\n".join(f" yield self.{f}" for f in field_names)
|
|
85
85
|
if not field_yield:
|
|
86
86
|
field_yield = " yield from ()"
|
|
87
|
-
code = f"def
|
|
87
|
+
code = f"def {funcname}(self):\n" f"{field_yield}"
|
|
88
88
|
globs = {}
|
|
89
89
|
return GeneratedCode(code, globs)
|
|
90
90
|
|
|
@@ -143,7 +143,7 @@ class PosOnlyField(Field):
|
|
|
143
143
|
__slots__ = SlotFields(pos_only=True)
|
|
144
144
|
|
|
145
145
|
|
|
146
|
-
def init_generator(cls):
|
|
146
|
+
def init_generator(cls, funcname="__init__"):
|
|
147
147
|
fields = get_fields(cls)
|
|
148
148
|
|
|
149
149
|
arglist = []
|
|
@@ -177,11 +177,11 @@ def init_generator(cls):
|
|
|
177
177
|
|
|
178
178
|
args = ", ".join(arglist)
|
|
179
179
|
assigns = "\n ".join(assignments)
|
|
180
|
-
code = f"def
|
|
180
|
+
code = f"def {funcname}(self, {args}):\n" f" {assigns}\n"
|
|
181
181
|
return GeneratedCode(code, globs)
|
|
182
182
|
|
|
183
183
|
|
|
184
|
-
def repr_generator(cls):
|
|
184
|
+
def repr_generator(cls, funcname="__repr__"):
|
|
185
185
|
fields = get_fields(cls)
|
|
186
186
|
content_list = []
|
|
187
187
|
for name, field in fields.items():
|
|
@@ -193,7 +193,7 @@ def repr_generator(cls):
|
|
|
193
193
|
|
|
194
194
|
content = ", ".join(content_list)
|
|
195
195
|
code = (
|
|
196
|
-
f"def
|
|
196
|
+
f"def {funcname}(self):\n"
|
|
197
197
|
f" return f'{{type(self).__qualname__}}({content})'\n"
|
|
198
198
|
)
|
|
199
199
|
globs = {}
|
|
@@ -281,7 +281,7 @@ class ConverterField(Field):
|
|
|
281
281
|
converter = Field(default=None)
|
|
282
282
|
|
|
283
283
|
|
|
284
|
-
def setattr_generator(cls):
|
|
284
|
+
def setattr_generator(cls, funcname="__setattr__"):
|
|
285
285
|
fields = get_fields(cls)
|
|
286
286
|
converters = {}
|
|
287
287
|
for k, v in fields.items():
|
|
@@ -294,7 +294,7 @@ def setattr_generator(cls):
|
|
|
294
294
|
}
|
|
295
295
|
|
|
296
296
|
code = (
|
|
297
|
-
f"def
|
|
297
|
+
f"def {funcname}(self, name, value):\n"
|
|
298
298
|
f" if conv := _converters.get(name):\n"
|
|
299
299
|
f" _object_setattr(self, name, conv(value))\n"
|
|
300
300
|
f" else:\n"
|
|
@@ -120,7 +120,7 @@ field_3: <HIDDEN>
|
|
|
120
120
|
```
|
|
121
121
|
|
|
122
122
|
```python
|
|
123
|
-
def report_generator(cls):
|
|
123
|
+
def report_generator(cls, funcname="report"):
|
|
124
124
|
fields = dtbuild.get_fields(cls)
|
|
125
125
|
|
|
126
126
|
field_reports = []
|
|
@@ -135,7 +135,7 @@ def report_generator(cls):
|
|
|
135
135
|
|
|
136
136
|
code = (
|
|
137
137
|
"@property\n"
|
|
138
|
-
"def
|
|
138
|
+
f"def {funcname}(self):\n"
|
|
139
139
|
f" return f\"{class_str}\\n{reports_str}\""
|
|
140
140
|
)
|
|
141
141
|
globs = {}
|
{ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/src/ducktools/classbuilder/__init__.py
RENAMED
|
@@ -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.1"
|
|
38
38
|
|
|
39
39
|
# Change this name if you make heavy modifications
|
|
40
40
|
INTERNALS_DICT = "__classbuilder_internals__"
|
|
@@ -167,7 +167,7 @@ class MethodMaker:
|
|
|
167
167
|
cls = objtype
|
|
168
168
|
|
|
169
169
|
local_vars = {}
|
|
170
|
-
gen = self.code_generator(cls)
|
|
170
|
+
gen = self.code_generator(cls, self.funcname)
|
|
171
171
|
exec(gen.source_code, gen.globs, local_vars)
|
|
172
172
|
method = local_vars.get(self.funcname)
|
|
173
173
|
|
|
@@ -187,7 +187,7 @@ class MethodMaker:
|
|
|
187
187
|
|
|
188
188
|
|
|
189
189
|
def get_init_generator(null=NOTHING, extra_code=None):
|
|
190
|
-
def cls_init_maker(cls):
|
|
190
|
+
def cls_init_maker(cls, funcname="__init__"):
|
|
191
191
|
fields = get_fields(cls)
|
|
192
192
|
flags = get_flags(cls)
|
|
193
193
|
|
|
@@ -239,7 +239,7 @@ def get_init_generator(null=NOTHING, extra_code=None):
|
|
|
239
239
|
|
|
240
240
|
assigns = "\n ".join(assignments) if assignments else "pass\n"
|
|
241
241
|
code = (
|
|
242
|
-
f"def
|
|
242
|
+
f"def {funcname}(self, {args}):\n"
|
|
243
243
|
f" {assigns}\n"
|
|
244
244
|
)
|
|
245
245
|
# Handle additional function calls
|
|
@@ -265,7 +265,7 @@ def get_repr_generator(recursion_safe=False, eval_safe=False):
|
|
|
265
265
|
not evaluate.
|
|
266
266
|
:return:
|
|
267
267
|
"""
|
|
268
|
-
def cls_repr_generator(cls):
|
|
268
|
+
def cls_repr_generator(cls, funcname="__repr__"):
|
|
269
269
|
fields = get_fields(cls)
|
|
270
270
|
|
|
271
271
|
globs = {}
|
|
@@ -295,19 +295,19 @@ def get_repr_generator(recursion_safe=False, eval_safe=False):
|
|
|
295
295
|
if content:
|
|
296
296
|
code = (
|
|
297
297
|
f"{recursion_func}"
|
|
298
|
-
f"def
|
|
298
|
+
f"def {funcname}(self):\n"
|
|
299
299
|
f" return f'<generated class {{type(self).__qualname__}}; {content}>'\n"
|
|
300
300
|
)
|
|
301
301
|
else:
|
|
302
302
|
code = (
|
|
303
303
|
f"{recursion_func}"
|
|
304
|
-
f"def
|
|
304
|
+
f"def {funcname}(self):\n"
|
|
305
305
|
f" return f'<generated class {{type(self).__qualname__}}>'\n"
|
|
306
306
|
)
|
|
307
307
|
else:
|
|
308
308
|
code = (
|
|
309
309
|
f"{recursion_func}"
|
|
310
|
-
f"def
|
|
310
|
+
f"def {funcname}(self):\n"
|
|
311
311
|
f" return f'{{type(self).__qualname__}}({content})'\n"
|
|
312
312
|
)
|
|
313
313
|
|
|
@@ -318,7 +318,7 @@ def get_repr_generator(recursion_safe=False, eval_safe=False):
|
|
|
318
318
|
repr_generator = get_repr_generator()
|
|
319
319
|
|
|
320
320
|
|
|
321
|
-
def eq_generator(cls):
|
|
321
|
+
def eq_generator(cls, funcname="__eq__"):
|
|
322
322
|
class_comparison = "self.__class__ is other.__class__"
|
|
323
323
|
field_names = [
|
|
324
324
|
name
|
|
@@ -334,7 +334,7 @@ def eq_generator(cls):
|
|
|
334
334
|
instance_comparison = "True"
|
|
335
335
|
|
|
336
336
|
code = (
|
|
337
|
-
f"def
|
|
337
|
+
f"def {funcname}(self, other):\n"
|
|
338
338
|
f" return {instance_comparison} if {class_comparison} else NotImplemented\n"
|
|
339
339
|
)
|
|
340
340
|
globs = {}
|
|
@@ -342,7 +342,7 @@ def eq_generator(cls):
|
|
|
342
342
|
return GeneratedCode(code, globs)
|
|
343
343
|
|
|
344
344
|
|
|
345
|
-
def frozen_setattr_generator(cls):
|
|
345
|
+
def frozen_setattr_generator(cls, funcname="__setattr__"):
|
|
346
346
|
globs = {}
|
|
347
347
|
field_names = set(get_fields(cls))
|
|
348
348
|
flags = get_flags(cls)
|
|
@@ -366,19 +366,19 @@ def frozen_setattr_generator(cls):
|
|
|
366
366
|
f" else:\n"
|
|
367
367
|
f" {setattr_method}\n"
|
|
368
368
|
)
|
|
369
|
-
code = f"def
|
|
369
|
+
code = f"def {funcname}(self, name, value):\n{body}"
|
|
370
370
|
|
|
371
371
|
return GeneratedCode(code, globs)
|
|
372
372
|
|
|
373
373
|
|
|
374
|
-
def frozen_delattr_generator(cls):
|
|
374
|
+
def frozen_delattr_generator(cls, funcname="__delattr__"):
|
|
375
375
|
body = (
|
|
376
376
|
' raise TypeError(\n'
|
|
377
377
|
' f"{type(self).__name__!r} object "\n'
|
|
378
378
|
' f"does not support attribute deletion"\n'
|
|
379
379
|
' )\n'
|
|
380
380
|
)
|
|
381
|
-
code = f"def
|
|
381
|
+
code = f"def {funcname}(self, name):\n{body}"
|
|
382
382
|
globs = {}
|
|
383
383
|
return GeneratedCode(code, globs)
|
|
384
384
|
|
|
@@ -30,7 +30,10 @@ class _KW_ONLY_TYPE:
|
|
|
30
30
|
|
|
31
31
|
KW_ONLY: _KW_ONLY_TYPE
|
|
32
32
|
# Stub Only
|
|
33
|
-
|
|
33
|
+
@typing.type_check_only
|
|
34
|
+
class _CodegenType(typing.Protocol):
|
|
35
|
+
def __call__(self, cls: type, funcname: str = ...) -> GeneratedCode: ...
|
|
36
|
+
|
|
34
37
|
|
|
35
38
|
class GeneratedCode:
|
|
36
39
|
__slots__: tuple[str, str]
|
|
@@ -43,28 +46,28 @@ class GeneratedCode:
|
|
|
43
46
|
|
|
44
47
|
class MethodMaker:
|
|
45
48
|
funcname: str
|
|
46
|
-
code_generator:
|
|
47
|
-
def __init__(self, funcname: str, code_generator:
|
|
49
|
+
code_generator: _CodegenType
|
|
50
|
+
def __init__(self, funcname: str, code_generator: _CodegenType) -> None: ...
|
|
48
51
|
def __repr__(self) -> str: ...
|
|
49
52
|
def __get__(self, instance, cls=None) -> Callable: ...
|
|
50
53
|
|
|
51
54
|
def get_init_generator(
|
|
52
55
|
null: _NothingType = NOTHING,
|
|
53
56
|
extra_code: None | list[str] = None
|
|
54
|
-
) ->
|
|
57
|
+
) -> _CodegenType: ...
|
|
55
58
|
|
|
56
|
-
def init_generator(cls: type) -> GeneratedCode: ...
|
|
59
|
+
def init_generator(cls: type, funcname: str="__init__") -> GeneratedCode: ...
|
|
57
60
|
|
|
58
61
|
def get_repr_generator(
|
|
59
62
|
recursion_safe: bool = False,
|
|
60
63
|
eval_safe: bool = False
|
|
61
|
-
) ->
|
|
62
|
-
def repr_generator(cls: type) -> GeneratedCode: ...
|
|
63
|
-
def eq_generator(cls: type) -> GeneratedCode: ...
|
|
64
|
+
) -> _CodegenType: ...
|
|
65
|
+
def repr_generator(cls: type, funcname: str = "__repr__") -> GeneratedCode: ...
|
|
66
|
+
def eq_generator(cls: type, funcname: str = "__eq__") -> GeneratedCode: ...
|
|
64
67
|
|
|
65
|
-
def frozen_setattr_generator(cls: type) -> GeneratedCode: ...
|
|
68
|
+
def frozen_setattr_generator(cls: type, funcname: str = "__setattr__") -> GeneratedCode: ...
|
|
66
69
|
|
|
67
|
-
def frozen_delattr_generator(cls: type) -> GeneratedCode: ...
|
|
70
|
+
def frozen_delattr_generator(cls: type, funcname: str = "__delattr__") -> GeneratedCode: ...
|
|
68
71
|
|
|
69
72
|
init_maker: MethodMaker
|
|
70
73
|
repr_maker: MethodMaker
|
{ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/src/ducktools/classbuilder/prefab.py
RENAMED
|
@@ -60,202 +60,195 @@ def get_attributes(cls):
|
|
|
60
60
|
|
|
61
61
|
|
|
62
62
|
# Method Generators
|
|
63
|
-
def
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
flags = get_flags(cls)
|
|
69
|
-
|
|
70
|
-
kw_only = flags.get("kw_only", False)
|
|
71
|
-
|
|
72
|
-
# Handle pre/post init first - post_init can change types for __init__
|
|
73
|
-
# Get pre and post init arguments
|
|
74
|
-
pre_init_args = []
|
|
75
|
-
post_init_args = []
|
|
76
|
-
post_init_annotations = {}
|
|
77
|
-
|
|
78
|
-
for func_name, func_arglist in [
|
|
79
|
-
(PRE_INIT_FUNC, pre_init_args),
|
|
80
|
-
(POST_INIT_FUNC, post_init_args),
|
|
81
|
-
]:
|
|
82
|
-
try:
|
|
83
|
-
func = getattr(cls, func_name)
|
|
84
|
-
func_code = func.__code__
|
|
85
|
-
except AttributeError:
|
|
86
|
-
pass
|
|
87
|
-
else:
|
|
88
|
-
argcount = func_code.co_argcount + func_code.co_kwonlyargcount
|
|
63
|
+
def init_generator(cls, funcname="__init__"):
|
|
64
|
+
globs = {}
|
|
65
|
+
# Get the internals dictionary and prepare attributes
|
|
66
|
+
attributes = get_attributes(cls)
|
|
67
|
+
flags = get_flags(cls)
|
|
89
68
|
|
|
90
|
-
|
|
91
|
-
is_static = type(cls.__dict__.get(func_name)) is staticmethod
|
|
69
|
+
kw_only = flags.get("kw_only", False)
|
|
92
70
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
71
|
+
# Handle pre/post init first - post_init can change types for __init__
|
|
72
|
+
# Get pre and post init arguments
|
|
73
|
+
pre_init_args = []
|
|
74
|
+
post_init_args = []
|
|
75
|
+
post_init_annotations = {}
|
|
76
|
+
|
|
77
|
+
for extra_funcname, func_arglist in [
|
|
78
|
+
(PRE_INIT_FUNC, pre_init_args),
|
|
79
|
+
(POST_INIT_FUNC, post_init_args),
|
|
80
|
+
]:
|
|
81
|
+
try:
|
|
82
|
+
func = getattr(cls, extra_funcname)
|
|
83
|
+
func_code = func.__code__
|
|
84
|
+
except AttributeError:
|
|
85
|
+
pass
|
|
86
|
+
else:
|
|
87
|
+
argcount = func_code.co_argcount + func_code.co_kwonlyargcount
|
|
98
88
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
arg = f"{name}=_{name}_default"
|
|
127
|
-
else:
|
|
128
|
-
arg = f"{name}: _{name}_type = _{name}_default"
|
|
129
|
-
globs[f"_{name}_default"] = attrib.default
|
|
130
|
-
elif attrib.default_factory is not NOTHING:
|
|
131
|
-
# Use NONE here and call the factory later
|
|
132
|
-
# This matches the behaviour of compiled
|
|
89
|
+
# Identify if method is static, if so include first arg, otherwise skip
|
|
90
|
+
is_static = type(cls.__dict__.get(extra_funcname)) is staticmethod
|
|
91
|
+
|
|
92
|
+
arglist = (
|
|
93
|
+
func_code.co_varnames[:argcount]
|
|
94
|
+
if is_static
|
|
95
|
+
else func_code.co_varnames[1:argcount]
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
func_arglist.extend(arglist)
|
|
99
|
+
|
|
100
|
+
if extra_funcname == POST_INIT_FUNC:
|
|
101
|
+
post_init_annotations.update(func.__annotations__)
|
|
102
|
+
|
|
103
|
+
pos_arglist = []
|
|
104
|
+
kw_only_arglist = []
|
|
105
|
+
for name, attrib in attributes.items():
|
|
106
|
+
# post_init annotations can be used to broaden types.
|
|
107
|
+
if name in post_init_annotations:
|
|
108
|
+
globs[f"_{name}_type"] = post_init_annotations[name]
|
|
109
|
+
elif attrib.type is not NOTHING:
|
|
110
|
+
globs[f"_{name}_type"] = attrib.type
|
|
111
|
+
|
|
112
|
+
if attrib.init:
|
|
113
|
+
if attrib.default is not NOTHING:
|
|
114
|
+
if isinstance(attrib.default, (str, int, float, bool)):
|
|
115
|
+
# Just use the literal in these cases
|
|
133
116
|
if attrib.type is NOTHING:
|
|
134
|
-
arg = f"{name}=
|
|
117
|
+
arg = f"{name}={attrib.default!r}"
|
|
135
118
|
else:
|
|
136
|
-
arg = f"{name}: _{name}_type =
|
|
137
|
-
globs[f"_{name}_factory"] = attrib.default_factory
|
|
119
|
+
arg = f"{name}: _{name}_type = {attrib.default!r}"
|
|
138
120
|
else:
|
|
121
|
+
# No guarantee repr will work for other objects
|
|
122
|
+
# so store the value in a variable and put it
|
|
123
|
+
# in the globals dict for eval
|
|
139
124
|
if attrib.type is NOTHING:
|
|
140
|
-
arg = name
|
|
125
|
+
arg = f"{name}=_{name}_default"
|
|
141
126
|
else:
|
|
142
|
-
arg = f"{name}: _{name}_type"
|
|
143
|
-
if attrib.kw_only or kw_only:
|
|
144
|
-
kw_only_arglist.append(arg)
|
|
145
|
-
else:
|
|
146
|
-
pos_arglist.append(arg)
|
|
147
|
-
# Not in init, but need to set defaults
|
|
148
|
-
else:
|
|
149
|
-
if attrib.default is not NOTHING:
|
|
127
|
+
arg = f"{name}: _{name}_type = _{name}_default"
|
|
150
128
|
globs[f"_{name}_default"] = attrib.default
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if pos_args and kw_args:
|
|
157
|
-
args = f"{pos_args}, *, {kw_args}"
|
|
158
|
-
elif kw_args:
|
|
159
|
-
args = f"*, {kw_args}"
|
|
160
|
-
else:
|
|
161
|
-
args = pos_args
|
|
162
|
-
|
|
163
|
-
assignments = []
|
|
164
|
-
processes = [] # post_init values still need default factories to be called.
|
|
165
|
-
for name, attrib in attributes.items():
|
|
166
|
-
if attrib.init:
|
|
167
|
-
if attrib.default_factory is not NOTHING:
|
|
168
|
-
value = f"{name} if {name} is not None else _{name}_factory()"
|
|
129
|
+
elif attrib.default_factory is not NOTHING:
|
|
130
|
+
# Use NONE here and call the factory later
|
|
131
|
+
# This matches the behaviour of compiled
|
|
132
|
+
if attrib.type is NOTHING:
|
|
133
|
+
arg = f"{name}=None"
|
|
169
134
|
else:
|
|
170
|
-
|
|
135
|
+
arg = f"{name}: _{name}_type = None"
|
|
136
|
+
globs[f"_{name}_factory"] = attrib.default_factory
|
|
171
137
|
else:
|
|
172
|
-
if attrib.
|
|
173
|
-
|
|
174
|
-
elif attrib.default is not NOTHING:
|
|
175
|
-
value = f"_{name}_default"
|
|
138
|
+
if attrib.type is NOTHING:
|
|
139
|
+
arg = name
|
|
176
140
|
else:
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
assignments.append((name, value))
|
|
184
|
-
|
|
185
|
-
if hasattr(cls, PRE_INIT_FUNC):
|
|
186
|
-
pre_init_arg_call = ", ".join(f"{name}={name}" for name in pre_init_args)
|
|
187
|
-
pre_init_call = f" self.{PRE_INIT_FUNC}({pre_init_arg_call})\n"
|
|
141
|
+
arg = f"{name}: _{name}_type"
|
|
142
|
+
if attrib.kw_only or kw_only:
|
|
143
|
+
kw_only_arglist.append(arg)
|
|
144
|
+
else:
|
|
145
|
+
pos_arglist.append(arg)
|
|
146
|
+
# Not in init, but need to set defaults
|
|
188
147
|
else:
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
148
|
+
if attrib.default is not NOTHING:
|
|
149
|
+
globs[f"_{name}_default"] = attrib.default
|
|
150
|
+
elif attrib.default_factory is not NOTHING:
|
|
151
|
+
globs[f"_{name}_factory"] = attrib.default_factory
|
|
152
|
+
|
|
153
|
+
pos_args = ", ".join(pos_arglist)
|
|
154
|
+
kw_args = ", ".join(kw_only_arglist)
|
|
155
|
+
if pos_args and kw_args:
|
|
156
|
+
args = f"{pos_args}, *, {kw_args}"
|
|
157
|
+
elif kw_args:
|
|
158
|
+
args = f"*, {kw_args}"
|
|
159
|
+
else:
|
|
160
|
+
args = pos_args
|
|
161
|
+
|
|
162
|
+
assignments = []
|
|
163
|
+
processes = [] # post_init values still need default factories to be called.
|
|
164
|
+
for name, attrib in attributes.items():
|
|
165
|
+
if attrib.init:
|
|
166
|
+
if attrib.default_factory is not NOTHING:
|
|
167
|
+
value = f"{name} if {name} is not None else _{name}_factory()"
|
|
168
|
+
else:
|
|
169
|
+
value = name
|
|
198
170
|
else:
|
|
199
|
-
|
|
171
|
+
if attrib.default_factory is not NOTHING:
|
|
172
|
+
value = f"_{name}_factory()"
|
|
173
|
+
elif attrib.default is not NOTHING:
|
|
174
|
+
value = f"_{name}_default"
|
|
175
|
+
else:
|
|
176
|
+
value = None
|
|
200
177
|
|
|
201
|
-
if
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
178
|
+
if name in post_init_args:
|
|
179
|
+
if attrib.default_factory is not NOTHING:
|
|
180
|
+
processes.append((name, value))
|
|
181
|
+
elif value is not None:
|
|
182
|
+
assignments.append((name, value))
|
|
183
|
+
|
|
184
|
+
if hasattr(cls, PRE_INIT_FUNC):
|
|
185
|
+
pre_init_arg_call = ", ".join(f"{name}={name}" for name in pre_init_args)
|
|
186
|
+
pre_init_call = f" self.{PRE_INIT_FUNC}({pre_init_arg_call})\n"
|
|
187
|
+
else:
|
|
188
|
+
pre_init_call = ""
|
|
206
189
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
f"{
|
|
211
|
-
f"{post_init_call}\n"
|
|
190
|
+
if assignments or processes:
|
|
191
|
+
body = ""
|
|
192
|
+
body += "\n".join(
|
|
193
|
+
f" self.{name} = {value}" for name, value in assignments
|
|
212
194
|
)
|
|
213
|
-
|
|
195
|
+
body += "\n"
|
|
196
|
+
body += "\n".join(f" {name} = {value}" for name, value in processes)
|
|
197
|
+
else:
|
|
198
|
+
body = " pass"
|
|
214
199
|
|
|
215
|
-
|
|
200
|
+
if hasattr(cls, POST_INIT_FUNC):
|
|
201
|
+
post_init_arg_call = ", ".join(f"{name}={name}" for name in post_init_args)
|
|
202
|
+
post_init_call = f" self.{POST_INIT_FUNC}({post_init_arg_call})\n"
|
|
203
|
+
else:
|
|
204
|
+
post_init_call = ""
|
|
205
|
+
|
|
206
|
+
code = (
|
|
207
|
+
f"def {funcname}(self, {args}):\n"
|
|
208
|
+
f"{pre_init_call}\n"
|
|
209
|
+
f"{body}\n"
|
|
210
|
+
f"{post_init_call}\n"
|
|
211
|
+
)
|
|
216
212
|
|
|
213
|
+
return GeneratedCode(code, globs)
|
|
217
214
|
|
|
218
|
-
def get_iter_maker():
|
|
219
|
-
def __iter__(cls: type) -> GeneratedCode:
|
|
220
|
-
fields = get_attributes(cls)
|
|
221
215
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if attrib.iter
|
|
225
|
-
)
|
|
216
|
+
def iter_generator(cls, funcname="__iter__"):
|
|
217
|
+
fields = get_attributes(cls)
|
|
226
218
|
|
|
227
|
-
|
|
219
|
+
valid_fields = (
|
|
220
|
+
name for name, attrib in fields.items()
|
|
221
|
+
if attrib.iter
|
|
222
|
+
)
|
|
228
223
|
|
|
229
|
-
|
|
230
|
-
if not values:
|
|
231
|
-
values = " yield from ()"
|
|
224
|
+
values = "\n".join(f" yield self.{name}" for name in valid_fields)
|
|
232
225
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
226
|
+
# if values is an empty string
|
|
227
|
+
if not values:
|
|
228
|
+
values = " yield from ()"
|
|
236
229
|
|
|
237
|
-
|
|
230
|
+
code = f"def {funcname}(self):\n{values}"
|
|
231
|
+
globs = {}
|
|
232
|
+
return GeneratedCode(code, globs)
|
|
238
233
|
|
|
239
234
|
|
|
240
|
-
def
|
|
241
|
-
|
|
242
|
-
fields = get_attributes(cls)
|
|
235
|
+
def as_dict_generator(cls, funcname="as_dict"):
|
|
236
|
+
fields = get_attributes(cls)
|
|
243
237
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
238
|
+
vals = ", ".join(
|
|
239
|
+
f"'{name}': self.{name}"
|
|
240
|
+
for name, attrib in fields.items()
|
|
241
|
+
if attrib.serialize
|
|
242
|
+
)
|
|
243
|
+
out_dict = f"{{{vals}}}"
|
|
244
|
+
code = f"def {funcname}(self): return {out_dict}"
|
|
251
245
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
return MethodMaker("as_dict", as_dict_gen)
|
|
246
|
+
globs = {}
|
|
247
|
+
return GeneratedCode(code, globs)
|
|
255
248
|
|
|
256
249
|
|
|
257
|
-
init_maker =
|
|
258
|
-
prefab_init_maker =
|
|
250
|
+
init_maker = MethodMaker("__init__", init_generator)
|
|
251
|
+
prefab_init_maker = MethodMaker(PREFAB_INIT_FUNC, init_generator)
|
|
259
252
|
repr_maker = MethodMaker(
|
|
260
253
|
"__repr__",
|
|
261
254
|
get_repr_generator(recursion_safe=False, eval_safe=True)
|
|
@@ -264,8 +257,8 @@ recursive_repr_maker = MethodMaker(
|
|
|
264
257
|
"__repr__",
|
|
265
258
|
get_repr_generator(recursion_safe=True, eval_safe=True)
|
|
266
259
|
)
|
|
267
|
-
iter_maker =
|
|
268
|
-
asdict_maker =
|
|
260
|
+
iter_maker = MethodMaker("__iter__", iter_generator)
|
|
261
|
+
asdict_maker = MethodMaker("as_dict", as_dict_generator)
|
|
269
262
|
|
|
270
263
|
|
|
271
264
|
# Updated field with additional attributes
|
{ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/src/ducktools/classbuilder/prefab.pyi
RENAMED
|
@@ -7,6 +7,7 @@ from collections.abc import Callable
|
|
|
7
7
|
from . import (
|
|
8
8
|
NOTHING,
|
|
9
9
|
Field,
|
|
10
|
+
GeneratedCode,
|
|
10
11
|
MethodMaker,
|
|
11
12
|
SlotMakerMeta,
|
|
12
13
|
)
|
|
@@ -27,12 +28,9 @@ class PrefabError(Exception): ...
|
|
|
27
28
|
|
|
28
29
|
def get_attributes(cls: type) -> dict[str, Attribute]: ...
|
|
29
30
|
|
|
30
|
-
def
|
|
31
|
-
|
|
32
|
-
def
|
|
33
|
-
|
|
34
|
-
def get_asdict_maker() -> MethodMaker: ...
|
|
35
|
-
|
|
31
|
+
def init_generator(cls: type, funcname: str = "__init__") -> GeneratedCode: ...
|
|
32
|
+
def iter_generator(cls: type, funcname: str = "__iter__") -> GeneratedCode: ...
|
|
33
|
+
def as_dict_generator(cls: type, funcname: str = "as_dict") -> GeneratedCode: ...
|
|
36
34
|
|
|
37
35
|
init_maker: MethodMaker
|
|
38
36
|
prefab_init_maker: MethodMaker
|
|
@@ -50,8 +50,8 @@ def test_get_fields_flags_methods():
|
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
def test_method_maker():
|
|
53
|
-
def generator(cls):
|
|
54
|
-
code = "def
|
|
53
|
+
def generator(cls, funcname="demo"):
|
|
54
|
+
code = f"def {funcname}(self): return self.x"
|
|
55
55
|
globs = {}
|
|
56
56
|
return GeneratedCode(code, globs)
|
|
57
57
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/docs/perf/performance_tests.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/src/ducktools/classbuilder/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/annotations/test_annotated.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/dynamic/test_internals.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/conftest.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/test_creation.py
RENAMED
|
File without changes
|
{ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/test_dunders.py
RENAMED
|
File without changes
|
{ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/test_frozen.py
RENAMED
|
File without changes
|
{ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/test_funcs.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/test_init.py
RENAMED
|
File without changes
|
{ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/test_kw_only.py
RENAMED
|
File without changes
|
{ducktools_classbuilder-0.6.0 → ducktools_classbuilder-0.6.1}/tests/prefab/shared/test_repr.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|