ducktools-classbuilder 0.8.4__py3-none-any.whl → 0.9.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.
- ducktools/classbuilder/__init__.py +32 -19
- ducktools/classbuilder/__init__.pyi +2 -13
- ducktools/classbuilder/_version.py +2 -2
- ducktools/classbuilder/annotations.py +1 -1
- ducktools/classbuilder/prefab.py +10 -3
- ducktools/classbuilder/prefab.pyi +65 -24
- {ducktools_classbuilder-0.8.4.dist-info → ducktools_classbuilder-0.9.1.dist-info}/METADATA +2 -1
- ducktools_classbuilder-0.9.1.dist-info/RECORD +13 -0
- ducktools_classbuilder-0.8.4.dist-info/RECORD +0 -13
- {ducktools_classbuilder-0.8.4.dist-info → ducktools_classbuilder-0.9.1.dist-info}/LICENSE +0 -0
- {ducktools_classbuilder-0.8.4.dist-info → ducktools_classbuilder-0.9.1.dist-info}/WHEEL +0 -0
- {ducktools_classbuilder-0.8.4.dist-info → ducktools_classbuilder-0.9.1.dist-info}/top_level.txt +0 -0
|
@@ -47,7 +47,7 @@ _UNDER_TESTING = os.environ.get("PYTEST_VERSION") is not None
|
|
|
47
47
|
# Obtain types the same way types.py does in pypy
|
|
48
48
|
# See: https://github.com/pypy/pypy/blob/19d9fa6be11165116dd0839b9144d969ab426ae7/lib-python/3/types.py#L61-L73
|
|
49
49
|
class _C: __slots__ = 's' # noqa
|
|
50
|
-
_MemberDescriptorType = type(_C.s) #
|
|
50
|
+
_MemberDescriptorType = type(_C.s) # type: ignore
|
|
51
51
|
_MappingProxyType = type(type.__dict__)
|
|
52
52
|
del _C
|
|
53
53
|
|
|
@@ -385,6 +385,36 @@ def eq_generator(cls, funcname="__eq__"):
|
|
|
385
385
|
return GeneratedCode(code, globs)
|
|
386
386
|
|
|
387
387
|
|
|
388
|
+
def replace_generator(cls, funcname="__replace__"):
|
|
389
|
+
# Generate the replace method for built classes
|
|
390
|
+
# unlike the dataclasses implementation this is generated
|
|
391
|
+
attribs = get_fields(cls)
|
|
392
|
+
|
|
393
|
+
# This is essentially the as_dict generator for prefabs
|
|
394
|
+
# except based on attrib.init instead of .serialize
|
|
395
|
+
vals = ", ".join(
|
|
396
|
+
f"'{name}': self.{name}"
|
|
397
|
+
for name, attrib in attribs.items()
|
|
398
|
+
if attrib.init
|
|
399
|
+
)
|
|
400
|
+
init_dict = f"{{{vals}}}"
|
|
401
|
+
|
|
402
|
+
code = (
|
|
403
|
+
f"def {funcname}(self, /, **changes):\n"
|
|
404
|
+
f" new_kwargs = {init_dict}\n"
|
|
405
|
+
f" for name, value in changes.items():\n"
|
|
406
|
+
f" if name not in new_kwargs:\n"
|
|
407
|
+
f" raise TypeError(\n"
|
|
408
|
+
f" f\"{{name!r}} is not a valid replacable \"\n"
|
|
409
|
+
f" f\"field on {{self.__class__.__name__!r}}\"\n"
|
|
410
|
+
f" )\n"
|
|
411
|
+
f" new_kwargs[name] = value\n"
|
|
412
|
+
f" return self.__class__(**new_kwargs)\n"
|
|
413
|
+
)
|
|
414
|
+
globs = {}
|
|
415
|
+
return GeneratedCode(code, globs)
|
|
416
|
+
|
|
417
|
+
|
|
388
418
|
def frozen_setattr_generator(cls, funcname="__setattr__"):
|
|
389
419
|
globs = {}
|
|
390
420
|
field_names = set(get_fields(cls))
|
|
@@ -433,6 +463,7 @@ def frozen_delattr_generator(cls, funcname="__delattr__"):
|
|
|
433
463
|
init_maker = MethodMaker("__init__", init_generator)
|
|
434
464
|
repr_maker = MethodMaker("__repr__", repr_generator)
|
|
435
465
|
eq_maker = MethodMaker("__eq__", eq_generator)
|
|
466
|
+
replace_maker = MethodMaker("__replace__", replace_generator)
|
|
436
467
|
frozen_setattr_maker = MethodMaker("__setattr__", frozen_setattr_generator)
|
|
437
468
|
frozen_delattr_maker = MethodMaker("__delattr__", frozen_delattr_generator)
|
|
438
469
|
default_methods = frozenset({init_maker, repr_maker, eq_maker})
|
|
@@ -961,24 +992,6 @@ def slotclass(cls=None, /, *, methods=default_methods, syntax_check=True):
|
|
|
961
992
|
return cls
|
|
962
993
|
|
|
963
994
|
|
|
964
|
-
class AnnotationClass(metaclass=SlotMakerMeta):
|
|
965
|
-
__slots__ = {}
|
|
966
|
-
|
|
967
|
-
def __init_subclass__(
|
|
968
|
-
cls,
|
|
969
|
-
methods=default_methods,
|
|
970
|
-
gatherer=unified_gatherer,
|
|
971
|
-
**kwargs
|
|
972
|
-
):
|
|
973
|
-
# Check class dict otherwise this will always be True as this base
|
|
974
|
-
# class uses slots.
|
|
975
|
-
slots = "__slots__" in cls.__dict__
|
|
976
|
-
|
|
977
|
-
builder(cls, gatherer=gatherer, methods=methods, flags={"slotted": slots})
|
|
978
|
-
check_argument_order(cls)
|
|
979
|
-
super().__init_subclass__(**kwargs)
|
|
980
|
-
|
|
981
|
-
|
|
982
995
|
@slotclass
|
|
983
996
|
class GatheredFields:
|
|
984
997
|
"""
|
|
@@ -5,7 +5,6 @@ import inspect
|
|
|
5
5
|
|
|
6
6
|
from collections.abc import Callable
|
|
7
7
|
from types import MappingProxyType
|
|
8
|
-
from typing_extensions import dataclass_transform
|
|
9
8
|
|
|
10
9
|
_py_type = type | str # Alias for type hint values
|
|
11
10
|
_CopiableMappings = dict[str, typing.Any] | MappingProxyType[str, typing.Any]
|
|
@@ -74,6 +73,7 @@ def get_repr_generator(
|
|
|
74
73
|
) -> _CodegenType: ...
|
|
75
74
|
def repr_generator(cls: type, funcname: str = "__repr__") -> GeneratedCode: ...
|
|
76
75
|
def eq_generator(cls: type, funcname: str = "__eq__") -> GeneratedCode: ...
|
|
76
|
+
def replace_generator(cls: type, funcname: str = "__replace__") -> GeneratedCode: ...
|
|
77
77
|
|
|
78
78
|
def frozen_setattr_generator(cls: type, funcname: str = "__setattr__") -> GeneratedCode: ...
|
|
79
79
|
|
|
@@ -82,6 +82,7 @@ def frozen_delattr_generator(cls: type, funcname: str = "__delattr__") -> Genera
|
|
|
82
82
|
init_maker: MethodMaker
|
|
83
83
|
repr_maker: MethodMaker
|
|
84
84
|
eq_maker: MethodMaker
|
|
85
|
+
replace_maker: MethodMaker
|
|
85
86
|
frozen_setattr_maker: MethodMaker
|
|
86
87
|
frozen_delattr_maker: MethodMaker
|
|
87
88
|
default_methods: frozenset[MethodMaker]
|
|
@@ -244,18 +245,6 @@ def slotclass(
|
|
|
244
245
|
|
|
245
246
|
_gatherer_type = Callable[[type | _CopiableMappings], tuple[dict[str, Field], dict[str, typing.Any]]]
|
|
246
247
|
|
|
247
|
-
|
|
248
|
-
@dataclass_transform(field_specifiers=(Field,))
|
|
249
|
-
class AnnotationClass(metaclass=SlotMakerMeta):
|
|
250
|
-
__slots__: dict
|
|
251
|
-
|
|
252
|
-
def __init_subclass__(
|
|
253
|
-
cls,
|
|
254
|
-
methods: frozenset[MethodMaker] | set[MethodMaker] = default_methods,
|
|
255
|
-
gatherer: _gatherer_type = unified_gatherer,
|
|
256
|
-
**kwargs,
|
|
257
|
-
) -> None: ...
|
|
258
|
-
|
|
259
248
|
class GatheredFields:
|
|
260
249
|
__slots__: dict[str, None]
|
|
261
250
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
__version__ = "0.
|
|
2
|
-
__version_tuple__ = (0,
|
|
1
|
+
__version__ = "0.9.1"
|
|
2
|
+
__version_tuple__ = (0, 9, 1)
|
|
@@ -25,7 +25,7 @@ import sys
|
|
|
25
25
|
class _LazyAnnotationLib:
|
|
26
26
|
def __getattr__(self, item):
|
|
27
27
|
global _lazyannotationlib
|
|
28
|
-
import annotationlib # type: ignore
|
|
28
|
+
import annotationlib # type: ignore
|
|
29
29
|
_lazyannotationlib = annotationlib
|
|
30
30
|
return getattr(annotationlib, item)
|
|
31
31
|
|
ducktools/classbuilder/prefab.py
CHANGED
|
@@ -30,7 +30,7 @@ from . import (
|
|
|
30
30
|
Field, MethodMaker, GatheredFields, GeneratedCode, SlotMakerMeta,
|
|
31
31
|
builder, get_flags, get_fields,
|
|
32
32
|
make_unified_gatherer,
|
|
33
|
-
frozen_setattr_maker, frozen_delattr_maker,
|
|
33
|
+
eq_maker, frozen_setattr_maker, frozen_delattr_maker, replace_maker,
|
|
34
34
|
get_repr_generator,
|
|
35
35
|
)
|
|
36
36
|
|
|
@@ -294,7 +294,7 @@ class Attribute(Field):
|
|
|
294
294
|
"""
|
|
295
295
|
iter: bool = True
|
|
296
296
|
serialize: bool = True
|
|
297
|
-
metadata: dict = Field(default=FIELD_NOTHING, default_factory=dict)
|
|
297
|
+
metadata: dict = Field(default=FIELD_NOTHING, default_factory=dict) # type: ignore
|
|
298
298
|
|
|
299
299
|
|
|
300
300
|
# noinspection PyShadowingBuiltins
|
|
@@ -441,6 +441,8 @@ def _make_prefab(
|
|
|
441
441
|
if dict_method:
|
|
442
442
|
methods.add(asdict_maker)
|
|
443
443
|
|
|
444
|
+
methods.add(replace_maker)
|
|
445
|
+
|
|
444
446
|
flags = {
|
|
445
447
|
"kw_only": kw_only,
|
|
446
448
|
"slotted": slotted,
|
|
@@ -551,7 +553,7 @@ def _make_prefab(
|
|
|
551
553
|
|
|
552
554
|
class Prefab(metaclass=SlotMakerMeta):
|
|
553
555
|
_meta_gatherer = prefab_gatherer
|
|
554
|
-
__slots__ = {}
|
|
556
|
+
__slots__ = {} # type: ignore
|
|
555
557
|
|
|
556
558
|
# noinspection PyShadowingBuiltins
|
|
557
559
|
def __init_subclass__(
|
|
@@ -779,3 +781,8 @@ def as_dict(o):
|
|
|
779
781
|
for name, attrib in flds.items()
|
|
780
782
|
if attrib.serialize
|
|
781
783
|
}
|
|
784
|
+
|
|
785
|
+
def replace(obj, /, **changes):
|
|
786
|
+
if not is_prefab_instance(obj):
|
|
787
|
+
raise TypeError("replace() should be called on prefab instances")
|
|
788
|
+
return obj.__replace__(**changes)
|
|
@@ -4,7 +4,8 @@ from typing_extensions import dataclass_transform
|
|
|
4
4
|
|
|
5
5
|
import inspect
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
# Suppress weird pylance error
|
|
8
|
+
from collections.abc import Callable # type: ignore
|
|
8
9
|
|
|
9
10
|
from . import (
|
|
10
11
|
NOTHING,
|
|
@@ -58,36 +59,73 @@ class Attribute(Field):
|
|
|
58
59
|
default: typing.Any | _NothingType = NOTHING,
|
|
59
60
|
default_factory: typing.Any | _NothingType = NOTHING,
|
|
60
61
|
type: type | _NothingType = NOTHING,
|
|
61
|
-
doc: str | None =
|
|
62
|
-
init: bool =
|
|
63
|
-
repr: bool =
|
|
64
|
-
compare: bool =
|
|
65
|
-
iter: bool =
|
|
66
|
-
kw_only: bool =
|
|
67
|
-
serialize: bool =
|
|
68
|
-
metadata: dict | None =
|
|
62
|
+
doc: str | None = ...,
|
|
63
|
+
init: bool = ...,
|
|
64
|
+
repr: bool = ...,
|
|
65
|
+
compare: bool = ...,
|
|
66
|
+
iter: bool = ...,
|
|
67
|
+
kw_only: bool = ...,
|
|
68
|
+
serialize: bool = ...,
|
|
69
|
+
metadata: dict | None = ...,
|
|
69
70
|
) -> None: ...
|
|
70
71
|
|
|
71
72
|
def __repr__(self) -> str: ...
|
|
72
73
|
def __eq__(self, other: Attribute | object) -> bool: ...
|
|
73
74
|
def validate_field(self) -> None: ...
|
|
74
75
|
|
|
76
|
+
@typing.overload
|
|
75
77
|
def attribute(
|
|
76
78
|
*,
|
|
77
|
-
default:
|
|
78
|
-
default_factory:
|
|
79
|
-
init: bool =
|
|
80
|
-
repr: bool =
|
|
81
|
-
compare: bool =
|
|
82
|
-
iter: bool =
|
|
83
|
-
kw_only: bool =
|
|
84
|
-
serialize: bool =
|
|
85
|
-
exclude_field: bool =
|
|
86
|
-
private: bool =
|
|
87
|
-
doc: str | None =
|
|
88
|
-
metadata: dict | None =
|
|
89
|
-
type: type | _NothingType =
|
|
90
|
-
) ->
|
|
79
|
+
default: _T,
|
|
80
|
+
default_factory: _NothingType = NOTHING,
|
|
81
|
+
init: bool = ...,
|
|
82
|
+
repr: bool = ...,
|
|
83
|
+
compare: bool = ...,
|
|
84
|
+
iter: bool = ...,
|
|
85
|
+
kw_only: bool = ...,
|
|
86
|
+
serialize: bool = ...,
|
|
87
|
+
exclude_field: bool = ...,
|
|
88
|
+
private: bool = ...,
|
|
89
|
+
doc: str | None = ...,
|
|
90
|
+
metadata: dict | None = ...,
|
|
91
|
+
type: type | _NothingType = ...,
|
|
92
|
+
) -> _T: ...
|
|
93
|
+
|
|
94
|
+
@typing.overload
|
|
95
|
+
def attribute(
|
|
96
|
+
*,
|
|
97
|
+
default: _NothingType = NOTHING,
|
|
98
|
+
default_factory: Callable[[], _T],
|
|
99
|
+
init: bool = ...,
|
|
100
|
+
repr: bool = ...,
|
|
101
|
+
compare: bool = ...,
|
|
102
|
+
iter: bool = ...,
|
|
103
|
+
kw_only: bool = ...,
|
|
104
|
+
serialize: bool = ...,
|
|
105
|
+
exclude_field: bool = ...,
|
|
106
|
+
private: bool = ...,
|
|
107
|
+
doc: str | None = ...,
|
|
108
|
+
metadata: dict | None = ...,
|
|
109
|
+
type: type | _NothingType = ...,
|
|
110
|
+
) -> _T: ...
|
|
111
|
+
|
|
112
|
+
@typing.overload
|
|
113
|
+
def attribute(
|
|
114
|
+
*,
|
|
115
|
+
default: _NothingType = NOTHING,
|
|
116
|
+
default_factory: _NothingType = NOTHING,
|
|
117
|
+
init: bool = ...,
|
|
118
|
+
repr: bool = ...,
|
|
119
|
+
compare: bool = ...,
|
|
120
|
+
iter: bool = ...,
|
|
121
|
+
kw_only: bool = ...,
|
|
122
|
+
serialize: bool = ...,
|
|
123
|
+
exclude_field: bool = ...,
|
|
124
|
+
private: bool = ...,
|
|
125
|
+
doc: str | None = ...,
|
|
126
|
+
metadata: dict | None = ...,
|
|
127
|
+
type: type | _NothingType = ...,
|
|
128
|
+
) -> typing.Any: ...
|
|
91
129
|
|
|
92
130
|
def prefab_gatherer(cls_or_ns: type | MappingProxyType) -> tuple[dict[str, Attribute], dict[str, typing.Any]]: ...
|
|
93
131
|
|
|
@@ -111,7 +149,8 @@ _T = typing.TypeVar("_T")
|
|
|
111
149
|
# noinspection PyUnresolvedReferences
|
|
112
150
|
@dataclass_transform(field_specifiers=(Attribute, attribute))
|
|
113
151
|
class Prefab(metaclass=SlotMakerMeta):
|
|
114
|
-
_meta_gatherer: Callable[[type | _CopiableMappings], tuple[dict[str, Field], dict[str, typing.Any]]]
|
|
152
|
+
_meta_gatherer: Callable[[type | _CopiableMappings], tuple[dict[str, Field], dict[str, typing.Any]]] = ...
|
|
153
|
+
__slots__: dict[str, typing.Any] = ...
|
|
115
154
|
def __init_subclass__(
|
|
116
155
|
cls,
|
|
117
156
|
init: bool = True,
|
|
@@ -199,3 +238,5 @@ def is_prefab(o: typing.Any) -> bool: ...
|
|
|
199
238
|
def is_prefab_instance(o: object) -> bool: ...
|
|
200
239
|
|
|
201
240
|
def as_dict(o) -> dict[str, typing.Any]: ...
|
|
241
|
+
|
|
242
|
+
def replace(obj: _T, /, **changes: typing.Any) -> _T: ...
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ducktools-classbuilder
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.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
|
|
@@ -11,6 +11,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.11
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
15
|
Classifier: Operating System :: OS Independent
|
|
15
16
|
Classifier: License :: OSI Approved :: MIT License
|
|
16
17
|
Requires-Python: >=3.8
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
ducktools/classbuilder/__init__.py,sha256=5vagbDiErapPudNG5-gP3yarlA6-q8IriMcRrAzBBr4,33433
|
|
2
|
+
ducktools/classbuilder/__init__.pyi,sha256=6nEfVyeF1HE4YlvcNkFetACqMd1EObu3hYwoLYBbRNo,7796
|
|
3
|
+
ducktools/classbuilder/_version.py,sha256=Ti7IekFg9SsQHXFYa9yzblMN29C0yL2OG6aLOfnSQOc,52
|
|
4
|
+
ducktools/classbuilder/annotations.py,sha256=VEZsCM8lwfhaWrQi8dUOAkicYHxUHaSAyM-FzL34wXI,3583
|
|
5
|
+
ducktools/classbuilder/annotations.pyi,sha256=c5vYtULdDgMYWtkzeYMsHIbmnEuT2Ru-nNZieWvYuQ4,247
|
|
6
|
+
ducktools/classbuilder/prefab.py,sha256=RiVbESwFiPv3Y4PpclSZAwdwBomILbhGEF8XHAaDzW4,24643
|
|
7
|
+
ducktools/classbuilder/prefab.pyi,sha256=_Tm2V97Udt3-WlbBRuOSt11PPYdw1TrFeSd4Mcs2EKM,6422
|
|
8
|
+
ducktools/classbuilder/py.typed,sha256=la67KBlbjXN-_-DfGNcdOcjYumVpKG_Tkw-8n5dnGB4,8
|
|
9
|
+
ducktools_classbuilder-0.9.1.dist-info/LICENSE,sha256=6Thz9Dbw8R4fWInl6sGl8Rj3UnKnRbDwrc6jZerpugQ,1070
|
|
10
|
+
ducktools_classbuilder-0.9.1.dist-info/METADATA,sha256=45E7CeSoBEii1Tra1U16WUvcBepEQ4vCsay_y_2UinY,9687
|
|
11
|
+
ducktools_classbuilder-0.9.1.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
|
|
12
|
+
ducktools_classbuilder-0.9.1.dist-info/top_level.txt,sha256=uSDLtio3ZFqdwcsMJ2O5yhjB4Q3ytbBWbA8rJREganc,10
|
|
13
|
+
ducktools_classbuilder-0.9.1.dist-info/RECORD,,
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
ducktools/classbuilder/__init__.py,sha256=J0Tyf-IS7fCuNhccsVnLqnId83hHqffw5CW-gbHJza4,32831
|
|
2
|
-
ducktools/classbuilder/__init__.pyi,sha256=47rT22Copd8gKiw26sTdAkA99NjMMwX6WsYsN0EEEtY,8060
|
|
3
|
-
ducktools/classbuilder/_version.py,sha256=1lfD4M7j7FGjNEGzWx3WAlgJCS0G0L-UuzDObMBwmDo,52
|
|
4
|
-
ducktools/classbuilder/annotations.py,sha256=kuL2ZJR3-mrN9XnWx88FC0rXgSwtT-C1EKbdZ_ijNzg,3615
|
|
5
|
-
ducktools/classbuilder/annotations.pyi,sha256=c5vYtULdDgMYWtkzeYMsHIbmnEuT2Ru-nNZieWvYuQ4,247
|
|
6
|
-
ducktools/classbuilder/prefab.py,sha256=GVoYmMVEBXWTwJRQsEUG1VfP_Kc3neHYFdwi7OCyOtg,24383
|
|
7
|
-
ducktools/classbuilder/prefab.pyi,sha256=NoGeBiPvm3VismyOyVsz1mrKFUex90BU3fRcxh6o76E,5465
|
|
8
|
-
ducktools/classbuilder/py.typed,sha256=la67KBlbjXN-_-DfGNcdOcjYumVpKG_Tkw-8n5dnGB4,8
|
|
9
|
-
ducktools_classbuilder-0.8.4.dist-info/LICENSE,sha256=6Thz9Dbw8R4fWInl6sGl8Rj3UnKnRbDwrc6jZerpugQ,1070
|
|
10
|
-
ducktools_classbuilder-0.8.4.dist-info/METADATA,sha256=4M21VREGfntX2daQyaalQBrehQIuSsOwsJwlRR_geYE,9636
|
|
11
|
-
ducktools_classbuilder-0.8.4.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
|
|
12
|
-
ducktools_classbuilder-0.8.4.dist-info/top_level.txt,sha256=uSDLtio3ZFqdwcsMJ2O5yhjB4Q3ytbBWbA8rJREganc,10
|
|
13
|
-
ducktools_classbuilder-0.8.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
{ducktools_classbuilder-0.8.4.dist-info → ducktools_classbuilder-0.9.1.dist-info}/top_level.txt
RENAMED
|
File without changes
|