ducktools-classbuilder 0.12.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.
@@ -0,0 +1,283 @@
1
+ import sys
2
+ import types
3
+ import typing
4
+ import typing_extensions
5
+
6
+
7
+ from collections.abc import Callable
8
+ from types import MappingProxyType
9
+
10
+ if sys.version_info >= (3, 14):
11
+ import annotationlib
12
+
13
+ _py_type = annotationlib.ForwardRef | type | str
14
+ else:
15
+ _py_type = type | str
16
+
17
+ _CopiableMappings = dict[str, typing.Any] | MappingProxyType[str, typing.Any]
18
+
19
+ __version__: str
20
+ __version_tuple__: tuple[str | int, ...]
21
+ INTERNALS_DICT: str
22
+ META_GATHERER_NAME: str
23
+ GATHERED_DATA: str
24
+
25
+ def get_fields(cls: type, *, local: bool = False) -> dict[str, Field]: ...
26
+
27
+ def get_flags(cls: type) -> dict[str, bool]: ...
28
+
29
+ def get_methods(cls: type) -> types.MappingProxyType[str, MethodMaker]: ...
30
+
31
+ def get_generated_code(cls: type) -> dict[str, GeneratedCode]: ...
32
+
33
+ def print_generated_code(cls: type) -> None: ...
34
+
35
+ def build_completed(ns: _CopiableMappings) -> bool: ...
36
+
37
+ def _get_inst_fields(inst: typing.Any) -> dict[str, typing.Any]: ...
38
+
39
+ class _NothingType:
40
+ def __init__(self, custom: str | None = ...) -> None: ...
41
+ def __repr__(self) -> str: ...
42
+ NOTHING: _NothingType
43
+ FIELD_NOTHING: _NothingType
44
+
45
+ class _KW_ONLY_META(type):
46
+ def __repr__(self) -> str: ...
47
+
48
+ class KW_ONLY(metaclass=_KW_ONLY_META): ...
49
+
50
+ # Stub Only
51
+ @typing.type_check_only
52
+ class _CodegenType(typing.Protocol):
53
+ def __call__(self, cls: type, funcname: str = ...) -> GeneratedCode: ...
54
+
55
+ class GeneratedCode:
56
+ __slots__: tuple[str, ...]
57
+ source_code: str
58
+ globs: dict[str, typing.Any]
59
+ annotations: dict[str, typing.Any]
60
+
61
+ def __init__(
62
+ self,
63
+ source_code: str,
64
+ globs: dict[str, typing.Any],
65
+ annotations: dict[str, typing.Any] | None = ...,
66
+ ) -> None: ...
67
+ def __repr__(self) -> str: ...
68
+
69
+ class MethodMaker:
70
+ funcname: str
71
+ code_generator: _CodegenType
72
+ def __init__(self, funcname: str, code_generator: _CodegenType) -> None: ...
73
+ def __repr__(self) -> str: ...
74
+ def __get__(self, instance, cls) -> Callable: ...
75
+
76
+ class _SignatureMaker:
77
+ def __get__(self, instance, cls=None) -> typing_extensions.Never: ...
78
+
79
+ signature_maker: _SignatureMaker
80
+
81
+ def get_init_generator(
82
+ null: _NothingType = NOTHING,
83
+ extra_code: None | list[str] = None
84
+ ) -> _CodegenType: ...
85
+
86
+ def init_generator(cls: type, funcname: str="__init__") -> GeneratedCode: ...
87
+
88
+ def get_repr_generator(
89
+ recursion_safe: bool = False,
90
+ eval_safe: bool = False
91
+ ) -> _CodegenType: ...
92
+ def repr_generator(cls: type, funcname: str = "__repr__") -> GeneratedCode: ...
93
+ def eq_generator(cls: type, funcname: str = "__eq__") -> GeneratedCode: ...
94
+ def replace_generator(cls: type, funcname: str = "__replace__") -> GeneratedCode: ...
95
+
96
+ def frozen_setattr_generator(cls: type, funcname: str = "__setattr__") -> GeneratedCode: ...
97
+
98
+ def frozen_delattr_generator(cls: type, funcname: str = "__delattr__") -> GeneratedCode: ...
99
+
100
+ init_maker: MethodMaker
101
+ repr_maker: MethodMaker
102
+ eq_maker: MethodMaker
103
+ replace_maker: MethodMaker
104
+ frozen_setattr_maker: MethodMaker
105
+ frozen_delattr_maker: MethodMaker
106
+ default_methods: frozenset[MethodMaker]
107
+
108
+ _T = typing.TypeVar("_T")
109
+
110
+ @typing.overload
111
+ def builder(
112
+ cls: type[_T],
113
+ /,
114
+ *,
115
+ gatherer: Callable[[type], tuple[dict[str, Field], dict[str, typing.Any]]],
116
+ methods: frozenset[MethodMaker] | set[MethodMaker],
117
+ flags: dict[str, bool] | None = None,
118
+ fix_signature: bool = ...,
119
+ ) -> type[_T]: ...
120
+
121
+ @typing.overload
122
+ def builder(
123
+ cls: None = None,
124
+ /,
125
+ *,
126
+ gatherer: Callable[[type], tuple[dict[str, Field], dict[str, typing.Any]]],
127
+ methods: frozenset[MethodMaker] | set[MethodMaker],
128
+ flags: dict[str, bool] | None = None,
129
+ fix_signature: bool = ...,
130
+ ) -> Callable[[type[_T]], type[_T]]: ...
131
+
132
+
133
+ class SlotFields(dict):
134
+ ...
135
+
136
+
137
+ class SlotMakerMeta(type):
138
+ def __new__(
139
+ cls: type[_T],
140
+ name: str,
141
+ bases: tuple[type, ...],
142
+ ns: dict[str, typing.Any],
143
+ slots: bool = ...,
144
+ gatherer: Callable[[type], tuple[dict[str, Field], dict[str, typing.Any]]] | None = ...,
145
+ ignore_annotations: bool | None = ...,
146
+ **kwargs: typing.Any,
147
+ ) -> _T: ...
148
+
149
+
150
+ class Field(metaclass=SlotMakerMeta):
151
+ default: _NothingType | typing.Any
152
+ default_factory: _NothingType | typing.Any
153
+ type: _NothingType | _py_type
154
+ doc: None | str
155
+ init: bool
156
+ repr: bool
157
+ compare: bool
158
+ kw_only: bool
159
+
160
+ __slots__: dict[str, str]
161
+ __classbuilder_internals__: dict
162
+ __signature__: _SignatureMaker
163
+
164
+ def __init__(
165
+ self,
166
+ *,
167
+ default: _NothingType | typing.Any = ...,
168
+ default_factory: _NothingType | typing.Any = ...,
169
+ type: _NothingType | _py_type = ...,
170
+ doc: None | str = ...,
171
+ init: bool = ...,
172
+ repr: bool = ...,
173
+ compare: bool = ...,
174
+ kw_only: bool = ...,
175
+ ) -> None: ...
176
+
177
+ def __init_subclass__(cls, frozen: bool = ..., ignore_annotations: bool = ...): ...
178
+ def __repr__(self) -> str: ...
179
+ def __eq__(self, other: Field | object) -> bool: ...
180
+ def validate_field(self) -> None: ...
181
+ @classmethod
182
+ def from_field(cls, fld: Field, /, **kwargs: typing.Any) -> Field: ...
183
+
184
+ # type[Field] doesn't work due to metaclass
185
+ # This is not really precise enough because isinstance is used
186
+ _ReturnsField = Callable[..., Field]
187
+ _FieldType = typing.TypeVar("_FieldType", bound=Field)
188
+
189
+ @typing.overload
190
+ def make_slot_gatherer(
191
+ field_type: type[_FieldType]
192
+ ) -> Callable[[type | _CopiableMappings], tuple[dict[str, _FieldType], dict[str, typing.Any]]]: ...
193
+
194
+ @typing.overload
195
+ def make_slot_gatherer(
196
+ field_type: _ReturnsField = Field
197
+ ) -> Callable[[type | _CopiableMappings], tuple[dict[str, Field], dict[str, typing.Any]]]: ...
198
+
199
+ @typing.overload
200
+ def make_annotation_gatherer(
201
+ field_type: type[_FieldType],
202
+ leave_default_values: bool = False,
203
+ ) -> Callable[[type | _CopiableMappings], tuple[dict[str, _FieldType], dict[str, typing.Any]]]: ...
204
+
205
+ @typing.overload
206
+ def make_annotation_gatherer(
207
+ field_type: _ReturnsField = Field,
208
+ leave_default_values: bool = False,
209
+ ) -> Callable[[type | _CopiableMappings], tuple[dict[str, Field], dict[str, typing.Any]]]: ...
210
+
211
+ @typing.overload
212
+ def make_field_gatherer(
213
+ field_type: type[_FieldType],
214
+ leave_default_values: bool = False,
215
+ ) -> Callable[[type | _CopiableMappings], tuple[dict[str, _FieldType], dict[str, typing.Any]]]: ...
216
+
217
+ @typing.overload
218
+ def make_field_gatherer(
219
+ field_type: _ReturnsField = Field,
220
+ leave_default_values: bool = False,
221
+ ) -> Callable[[type | _CopiableMappings], tuple[dict[str, Field], dict[str, typing.Any]]]: ...
222
+
223
+ @typing.overload
224
+ def make_unified_gatherer(
225
+ field_type: type[_FieldType],
226
+ leave_default_values: bool = ...,
227
+ ) -> Callable[[type | _CopiableMappings], tuple[dict[str, _FieldType], dict[str, typing.Any]]]: ...
228
+
229
+ @typing.overload
230
+ def make_unified_gatherer(
231
+ field_type: _ReturnsField = ...,
232
+ leave_default_values: bool = ...,
233
+ ) -> Callable[[type | _CopiableMappings], tuple[dict[str, Field], dict[str, typing.Any]]]: ...
234
+
235
+
236
+ def slot_gatherer(cls_or_ns: type | _CopiableMappings) -> tuple[dict[str, Field], dict[str, typing.Any]]: ...
237
+ def annotation_gatherer(
238
+ cls_or_ns: type | _CopiableMappings,
239
+ *,
240
+ cls_annotations: None | dict[str, typing.Any] = ...
241
+ ) -> tuple[dict[str, Field], dict[str, typing.Any]]: ...
242
+
243
+ def unified_gatherer(cls_or_ns: type | _CopiableMappings) -> tuple[dict[str, Field], dict[str, typing.Any]]: ...
244
+
245
+
246
+ def check_argument_order(cls: type) -> None: ...
247
+
248
+ @typing.overload
249
+ def slotclass(
250
+ cls: type[_T],
251
+ /,
252
+ *,
253
+ methods: frozenset[MethodMaker] | set[MethodMaker] = default_methods,
254
+ syntax_check: bool = True
255
+ ) -> type[_T]: ...
256
+
257
+ @typing.overload
258
+ def slotclass(
259
+ cls: None = None,
260
+ /,
261
+ *,
262
+ methods: frozenset[MethodMaker] | set[MethodMaker] = default_methods,
263
+ syntax_check: bool = True
264
+ ) -> Callable[[type[_T]], type[_T]]: ...
265
+
266
+
267
+ _gatherer_type = Callable[[type | _CopiableMappings], tuple[dict[str, Field], dict[str, typing.Any]]]
268
+
269
+ class GatheredFields:
270
+ __slots__: tuple[str, ...]
271
+
272
+ fields: dict[str, Field]
273
+ modifications: dict[str, typing.Any]
274
+
275
+ def __init__(
276
+ self,
277
+ fields: dict[str, Field],
278
+ modifications: dict[str, typing.Any]
279
+ ) -> None: ...
280
+
281
+ def __repr__(self) -> str: ...
282
+ def __eq__(self, other) -> bool: ...
283
+ def __call__(self, cls_dict: type | dict[str, typing.Any]) -> tuple[dict[str, Field], dict[str, typing.Any]]: ...
@@ -0,0 +1,2 @@
1
+ __version__ = "0.12.1"
2
+ __version_tuple__ = (0, 12, 1)
@@ -0,0 +1,63 @@
1
+ # MIT License
2
+ #
3
+ # Copyright (c) 2024 David C Ellis
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ import sys
24
+
25
+ if sys.version_info >= (3, 14):
26
+ from .annotations_314 import (
27
+ get_func_annotations,
28
+ get_ns_annotations,
29
+ )
30
+ else:
31
+ from .annotations_pre_314 import (
32
+ get_func_annotations,
33
+ get_ns_annotations,
34
+ )
35
+
36
+
37
+ __all__ = [
38
+ "get_func_annotations",
39
+ "get_ns_annotations",
40
+ "is_classvar",
41
+ ]
42
+
43
+
44
+ def is_classvar(hint):
45
+ if isinstance(hint, str):
46
+ # String annotations, just check if the string 'ClassVar' is in there
47
+ # This is overly broad and could be smarter.
48
+ return "ClassVar" in hint
49
+ else:
50
+ _typing = sys.modules.get("typing")
51
+ if _typing:
52
+ _Annotated = _typing.Annotated
53
+ _get_origin = _typing.get_origin
54
+
55
+ if _Annotated and _get_origin(hint) is _Annotated:
56
+ hint = getattr(hint, "__origin__", None)
57
+
58
+ if (
59
+ hint is _typing.ClassVar
60
+ or getattr(hint, "__origin__", None) is _typing.ClassVar
61
+ ):
62
+ return True
63
+ return False
@@ -0,0 +1,104 @@
1
+ # MIT License
2
+ #
3
+ # Copyright (c) 2024 David C Ellis
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ """
24
+ Python 3.14 has new annotations methods, but does not provide any correct way to handle VALUE
25
+ annotation generation for new __init__ methods.
26
+
27
+ The approach taken here is to try to use VALUE annotations, if those fail it falls back to
28
+ STRING annotations, as if __future__ annotations actually arrived.
29
+
30
+ Hopefully in a future version of Python we will have complete, correct, performant annotations
31
+ so we can use a more standard format.
32
+ """
33
+
34
+ class _LazyAnnotationLib:
35
+ def __getattr__(self, item):
36
+ global _lazy_annotationlib
37
+ import annotationlib # type: ignore
38
+ _lazy_annotationlib = annotationlib
39
+ return getattr(annotationlib, item)
40
+
41
+
42
+ _lazy_annotationlib = _LazyAnnotationLib()
43
+
44
+
45
+ def get_func_annotations(func, use_forwardref=False):
46
+ """
47
+ Given a function, return the annotations dictionary
48
+
49
+ :param func: function object
50
+ :return: dictionary of annotations
51
+ """
52
+ # Try to get `__annotations__` for VALUE annotations first
53
+ try:
54
+ raw_annotations = func.__annotations__
55
+ except Exception:
56
+ fmt = (
57
+ _lazy_annotationlib.Format.FORWARDREF
58
+ if use_forwardref
59
+ else _lazy_annotationlib.Format.STRING
60
+ )
61
+ annotations = _lazy_annotationlib.get_annotations(func, format=fmt)
62
+ else:
63
+ annotations = raw_annotations.copy()
64
+
65
+ return annotations
66
+
67
+
68
+ def get_ns_annotations(ns, cls=None, use_forwardref=False):
69
+ """
70
+ Given a class namespace, attempt to retrieve the
71
+ annotations dictionary.
72
+
73
+ :param ns: Class namespace (eg cls.__dict__)
74
+ :param cls: Class if available
75
+ :param use_forwardref: Use FORWARDREF instead of STRING if VALUE fails
76
+ :return: dictionary of annotations
77
+ """
78
+
79
+ annotations = ns.get("__annotations__")
80
+ if annotations is not None:
81
+ annotations = annotations.copy()
82
+ else:
83
+ # See if we're using PEP-649 annotations
84
+ annotate = _lazy_annotationlib.get_annotate_from_class_namespace(ns)
85
+ if annotate:
86
+ try:
87
+ annotations = annotate(_lazy_annotationlib.Format.VALUE)
88
+ except Exception:
89
+ fmt = (
90
+ _lazy_annotationlib.Format.FORWARDREF
91
+ if use_forwardref
92
+ else _lazy_annotationlib.Format.STRING
93
+ )
94
+
95
+ annotations = _lazy_annotationlib.call_annotate_function(
96
+ annotate,
97
+ format=fmt,
98
+ owner=cls
99
+ )
100
+
101
+ if annotations is None:
102
+ annotations = {}
103
+
104
+ return annotations
@@ -0,0 +1,42 @@
1
+ # MIT License
2
+ #
3
+ # Copyright (c) 2024 David C Ellis
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ def get_func_annotations(func, use_forwardref=False):
24
+ """
25
+ Given a function, return the annotations dictionary
26
+
27
+ :param func: function object
28
+ :return: dictionary of annotations
29
+ """
30
+ annotations = func.__annotations__
31
+ return annotations
32
+
33
+
34
+ # This is simplified under 3.13 or earlier
35
+ def get_ns_annotations(ns, cls=None, use_forwardref=False):
36
+ annotations = ns.get("__annotations__")
37
+ if annotations is not None:
38
+ annotations = annotations.copy()
39
+ else:
40
+ annotations = {}
41
+ return annotations
42
+
@@ -0,0 +1,21 @@
1
+ from collections.abc import Callable
2
+ import typing
3
+ import types
4
+ import sys
5
+
6
+ _CopiableMappings = dict[str, typing.Any] | types.MappingProxyType[str, typing.Any]
7
+
8
+ def get_func_annotations(
9
+ func: types.FunctionType,
10
+ use_forwardref: bool = ...,
11
+ ) -> dict[str, typing.Any]: ...
12
+
13
+ def get_ns_annotations(
14
+ ns: _CopiableMappings,
15
+ cls: type | None = ...,
16
+ use_forwardref: bool = ...,
17
+ ) -> dict[str, typing.Any]: ...
18
+
19
+ def is_classvar(
20
+ hint: object,
21
+ ) -> bool: ...