ducktools-classbuilder 0.1.0__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 +418 -0
- ducktools/classbuilder/__init__.pyi +111 -0
- ducktools/classbuilder/prefab.py +909 -0
- ducktools/classbuilder/prefab.pyi +151 -0
- ducktools/classbuilder/py.typed +1 -0
- ducktools_classbuilder-0.1.0.dist-info/LICENSE.md +21 -0
- ducktools_classbuilder-0.1.0.dist-info/METADATA +179 -0
- ducktools_classbuilder-0.1.0.dist-info/RECORD +10 -0
- ducktools_classbuilder-0.1.0.dist-info/WHEEL +5 -0
- ducktools_classbuilder-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,418 @@
|
|
|
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
|
+
__version__ = "v0.1.0"
|
|
23
|
+
|
|
24
|
+
# Change this name if you make heavy modifications
|
|
25
|
+
INTERNALS_DICT = "__classbuilder_internals__"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def get_internals(cls):
|
|
29
|
+
"""
|
|
30
|
+
Utility function to get the internals dictionary
|
|
31
|
+
or return None.
|
|
32
|
+
|
|
33
|
+
As generated classes will always have 'fields'
|
|
34
|
+
and 'local_fields' attributes this will always
|
|
35
|
+
evaluate as 'truthy' if this is a generated class.
|
|
36
|
+
|
|
37
|
+
Usage:
|
|
38
|
+
if internals := get_internals(cls):
|
|
39
|
+
...
|
|
40
|
+
|
|
41
|
+
:param cls: generated class
|
|
42
|
+
:return: internals dictionary of the class or None
|
|
43
|
+
"""
|
|
44
|
+
return getattr(cls, INTERNALS_DICT, None)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def get_fields(cls):
|
|
48
|
+
"""
|
|
49
|
+
Utility function to gather the fields dictionary
|
|
50
|
+
from the class internals.
|
|
51
|
+
|
|
52
|
+
:param cls: generated class
|
|
53
|
+
:return: dictionary of keys and Field attribute info
|
|
54
|
+
"""
|
|
55
|
+
return getattr(cls, INTERNALS_DICT)["fields"]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def get_inst_fields(inst):
|
|
59
|
+
return {
|
|
60
|
+
k: getattr(inst, k)
|
|
61
|
+
for k in get_fields(type(inst))
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# As 'None' can be a meaningful default we need a sentinel value
|
|
66
|
+
# to use to show no value has been provided.
|
|
67
|
+
class _NothingType:
|
|
68
|
+
def __repr__(self):
|
|
69
|
+
return "<NOTHING OBJECT>"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
NOTHING = _NothingType()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class MethodMaker:
|
|
76
|
+
"""
|
|
77
|
+
The descriptor class to place where methods should be generated.
|
|
78
|
+
This delays the actual generation and `exec` until the method is needed.
|
|
79
|
+
|
|
80
|
+
This is used to convert a code generator that returns code and a globals
|
|
81
|
+
dictionary into a descriptor to assign on a generated class.
|
|
82
|
+
"""
|
|
83
|
+
def __init__(self, funcname, code_generator):
|
|
84
|
+
"""
|
|
85
|
+
:param funcname: name of the generated function eg `__init__`
|
|
86
|
+
:param code_generator: code generator function to operate on a class.
|
|
87
|
+
"""
|
|
88
|
+
self.funcname = funcname
|
|
89
|
+
self.code_generator = code_generator
|
|
90
|
+
|
|
91
|
+
def __repr__(self):
|
|
92
|
+
return f"<MethodMaker for {self.funcname} method>"
|
|
93
|
+
|
|
94
|
+
def __get__(self, instance, cls):
|
|
95
|
+
local_vars = {}
|
|
96
|
+
code, globs = self.code_generator(cls)
|
|
97
|
+
exec(code, globs, local_vars)
|
|
98
|
+
method = local_vars.get(self.funcname)
|
|
99
|
+
method.__qualname__ = f"{cls.__qualname__}.{self.funcname}"
|
|
100
|
+
|
|
101
|
+
# Replace this descriptor on the class with the generated function
|
|
102
|
+
setattr(cls, self.funcname, method)
|
|
103
|
+
|
|
104
|
+
# Use 'get' to return the generated function as a bound method
|
|
105
|
+
# instead of as a regular function for first usage.
|
|
106
|
+
return method.__get__(instance, cls)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def init_maker(cls, *, null=NOTHING, kw_only=False):
|
|
110
|
+
fields = get_fields(cls)
|
|
111
|
+
|
|
112
|
+
arglist = []
|
|
113
|
+
assignments = []
|
|
114
|
+
globs = {}
|
|
115
|
+
|
|
116
|
+
if kw_only:
|
|
117
|
+
arglist.append("*")
|
|
118
|
+
|
|
119
|
+
for k, v in fields.items():
|
|
120
|
+
if v.default is not null:
|
|
121
|
+
globs[f"_{k}_default"] = v.default
|
|
122
|
+
arg = f"{k}=_{k}_default"
|
|
123
|
+
assignment = f"self.{k} = {k}"
|
|
124
|
+
elif v.default_factory is not null:
|
|
125
|
+
globs[f"_{k}_factory"] = v.default_factory
|
|
126
|
+
arg = f"{k}=None"
|
|
127
|
+
assignment = f"self.{k} = _{k}_factory() if {k} is None else {k}"
|
|
128
|
+
else:
|
|
129
|
+
arg = f"{k}"
|
|
130
|
+
assignment = f"self.{k} = {k}"
|
|
131
|
+
|
|
132
|
+
arglist.append(arg)
|
|
133
|
+
assignments.append(assignment)
|
|
134
|
+
|
|
135
|
+
args = ", ".join(arglist)
|
|
136
|
+
assigns = "\n ".join(assignments)
|
|
137
|
+
code = f"def __init__(self, {args}):\n" f" {assigns}\n"
|
|
138
|
+
return code, globs
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def repr_maker(cls):
|
|
142
|
+
fields = get_fields(cls)
|
|
143
|
+
content = ", ".join(
|
|
144
|
+
f"{name}={{self.{name}!r}}"
|
|
145
|
+
for name, attrib in fields.items()
|
|
146
|
+
)
|
|
147
|
+
code = (
|
|
148
|
+
f"def __repr__(self):\n"
|
|
149
|
+
f" return f'{{type(self).__qualname__}}({content})'\n"
|
|
150
|
+
)
|
|
151
|
+
globs = {}
|
|
152
|
+
return code, globs
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def eq_maker(cls):
|
|
156
|
+
class_comparison = "self.__class__ is other.__class__"
|
|
157
|
+
field_names = get_fields(cls)
|
|
158
|
+
|
|
159
|
+
if field_names:
|
|
160
|
+
selfvals = ",".join(f"self.{name}" for name in field_names)
|
|
161
|
+
othervals = ",".join(f"other.{name}" for name in field_names)
|
|
162
|
+
instance_comparison = f"({selfvals},) == ({othervals},)"
|
|
163
|
+
else:
|
|
164
|
+
instance_comparison = "True"
|
|
165
|
+
|
|
166
|
+
code = (
|
|
167
|
+
f"def __eq__(self, other):\n"
|
|
168
|
+
f" return {instance_comparison} if {class_comparison} else NotImplemented\n"
|
|
169
|
+
)
|
|
170
|
+
globs = {}
|
|
171
|
+
|
|
172
|
+
return code, globs
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
# As only the __get__ method refers to the class we can use the same
|
|
176
|
+
# Descriptor instances for every class.
|
|
177
|
+
init_desc = MethodMaker("__init__", init_maker)
|
|
178
|
+
repr_desc = MethodMaker("__repr__", repr_maker)
|
|
179
|
+
eq_desc = MethodMaker("__eq__", eq_maker)
|
|
180
|
+
default_methods = frozenset({init_desc, repr_desc, eq_desc})
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def builder(cls=None, /, *, gatherer, methods):
|
|
184
|
+
"""
|
|
185
|
+
The main builder for class generation
|
|
186
|
+
|
|
187
|
+
:param cls: Class to be analysed and have methods generated
|
|
188
|
+
:param gatherer: Function to gather field information
|
|
189
|
+
:type gatherer: Callable[[type], dict[str, Field]]
|
|
190
|
+
:param methods: MethodMakers to add to the class
|
|
191
|
+
:type methods: set[MethodMaker]
|
|
192
|
+
:return: The modified class (the class itself is modified, but this is expected).
|
|
193
|
+
"""
|
|
194
|
+
# Handle `None` to make wrapping with a decorator easier.
|
|
195
|
+
if cls is None:
|
|
196
|
+
return lambda cls_: builder(
|
|
197
|
+
cls_,
|
|
198
|
+
gatherer=gatherer,
|
|
199
|
+
methods=methods,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
internals = {}
|
|
203
|
+
setattr(cls, INTERNALS_DICT, internals)
|
|
204
|
+
|
|
205
|
+
cls_fields = gatherer(cls)
|
|
206
|
+
internals["local_fields"] = cls_fields
|
|
207
|
+
|
|
208
|
+
mro = cls.__mro__[:-1] # skip 'object' base class
|
|
209
|
+
if mro == (cls,): # special case of no inheritance.
|
|
210
|
+
fields = cls_fields.copy()
|
|
211
|
+
else:
|
|
212
|
+
fields = {}
|
|
213
|
+
for c in reversed(mro):
|
|
214
|
+
try:
|
|
215
|
+
fields.update(get_internals(c)["local_fields"])
|
|
216
|
+
except AttributeError:
|
|
217
|
+
pass
|
|
218
|
+
|
|
219
|
+
internals["fields"] = fields
|
|
220
|
+
|
|
221
|
+
# Assign all of the method generators
|
|
222
|
+
for method in methods:
|
|
223
|
+
setattr(cls, method.funcname, method)
|
|
224
|
+
|
|
225
|
+
return cls
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
# The Field class can finally be defined.
|
|
229
|
+
# The __init__ method has to be written manually so Fields can be created
|
|
230
|
+
# However after this, the other methods can be generated.
|
|
231
|
+
class Field:
|
|
232
|
+
"""
|
|
233
|
+
A basic class to handle the assignment of defaults/factories with
|
|
234
|
+
some metadata.
|
|
235
|
+
|
|
236
|
+
Intended to be extendable by subclasses for additional features.
|
|
237
|
+
"""
|
|
238
|
+
__slots__ = {
|
|
239
|
+
"default": "Standard default value to be used for attributes with"
|
|
240
|
+
"this field.",
|
|
241
|
+
"default_factory": "A 0 argument function to be called to generate "
|
|
242
|
+
"a default value, useful for mutable objects like "
|
|
243
|
+
"lists.",
|
|
244
|
+
"type": "The type of the attribute to be assigned by this field.",
|
|
245
|
+
"doc": "The documentation that appears when calling help(...) on the class."
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
# noinspection PyShadowingBuiltins
|
|
249
|
+
def __init__(
|
|
250
|
+
self,
|
|
251
|
+
*,
|
|
252
|
+
default=NOTHING,
|
|
253
|
+
default_factory=NOTHING,
|
|
254
|
+
type=NOTHING,
|
|
255
|
+
doc=None,
|
|
256
|
+
):
|
|
257
|
+
self.default = default
|
|
258
|
+
self.default_factory = default_factory
|
|
259
|
+
self.type = type
|
|
260
|
+
self.doc = doc
|
|
261
|
+
|
|
262
|
+
self.validate_field()
|
|
263
|
+
|
|
264
|
+
def validate_field(self):
|
|
265
|
+
if self.default is not NOTHING and self.default_factory is not NOTHING:
|
|
266
|
+
raise AttributeError(
|
|
267
|
+
"Cannot define both a default value and a default factory."
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
@classmethod
|
|
271
|
+
def from_field(cls, fld, /, **kwargs):
|
|
272
|
+
"""
|
|
273
|
+
Create an instance of field or subclass from another field.
|
|
274
|
+
|
|
275
|
+
This is intended to be used to convert a base
|
|
276
|
+
Field into a subclass.
|
|
277
|
+
|
|
278
|
+
:param fld: field class to convert
|
|
279
|
+
:param kwargs: Additional keyword arguments for subclasses
|
|
280
|
+
:return: new field subclass instance
|
|
281
|
+
"""
|
|
282
|
+
argument_dict = {**get_inst_fields(fld), **kwargs}
|
|
283
|
+
|
|
284
|
+
return cls(**argument_dict)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
# Use the builder to generate __repr__ and __eq__ methods
|
|
288
|
+
# and pretend `Field` was a built class all along.
|
|
289
|
+
_field_internal = {
|
|
290
|
+
"default": Field(default=NOTHING),
|
|
291
|
+
"default_factory": Field(default=NOTHING),
|
|
292
|
+
"type": Field(default=NOTHING),
|
|
293
|
+
"doc": Field(default=None),
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
builder(
|
|
297
|
+
Field,
|
|
298
|
+
gatherer=lambda cls_: _field_internal,
|
|
299
|
+
methods=frozenset({repr_desc, eq_desc})
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
# Subclass of dict to be identifiable by isinstance checks
|
|
304
|
+
# For anything more complicated this could be made into a Mapping
|
|
305
|
+
class SlotFields(dict):
|
|
306
|
+
"""
|
|
307
|
+
A plain dict subclass.
|
|
308
|
+
|
|
309
|
+
For declaring slotfields there are no additional features required
|
|
310
|
+
other than recognising that this is intended to be used as a class
|
|
311
|
+
generating dict and isn't a regular dictionary that ended up in
|
|
312
|
+
`__slots__`.
|
|
313
|
+
|
|
314
|
+
This should be replaced on `__slots__` after fields have been gathered.
|
|
315
|
+
"""
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def slot_gatherer(cls):
|
|
319
|
+
"""
|
|
320
|
+
Gather field information for class generation based on __slots__
|
|
321
|
+
|
|
322
|
+
:param cls: Class to gather field information from
|
|
323
|
+
:return: dict of field_name: Field(...)
|
|
324
|
+
"""
|
|
325
|
+
cls_slots = cls.__dict__.get("__slots__", None)
|
|
326
|
+
|
|
327
|
+
if not isinstance(cls_slots, SlotFields):
|
|
328
|
+
raise TypeError(
|
|
329
|
+
"__slots__ must be an instance of SlotFields "
|
|
330
|
+
"in order to generate a slotclass"
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
cls_annotations = cls.__dict__.get("__annotations__", {})
|
|
334
|
+
cls_fields = {}
|
|
335
|
+
slot_replacement = {}
|
|
336
|
+
|
|
337
|
+
for k, v in cls_slots.items():
|
|
338
|
+
if isinstance(v, Field):
|
|
339
|
+
attrib = v
|
|
340
|
+
if v.type is not NOTHING:
|
|
341
|
+
cls_annotations[k] = attrib.type
|
|
342
|
+
else:
|
|
343
|
+
# Plain values treated as defaults
|
|
344
|
+
attrib = Field(default=v)
|
|
345
|
+
|
|
346
|
+
slot_replacement[k] = attrib.doc
|
|
347
|
+
cls_fields[k] = attrib
|
|
348
|
+
|
|
349
|
+
# Replace the SlotAttributes instance with a regular dict
|
|
350
|
+
# So that help() works
|
|
351
|
+
setattr(cls, "__slots__", slot_replacement)
|
|
352
|
+
|
|
353
|
+
# Update annotations with any types from the slots assignment
|
|
354
|
+
setattr(cls, "__annotations__", cls_annotations)
|
|
355
|
+
return cls_fields
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def slotclass(cls=None, /, *, methods=default_methods, syntax_check=True):
|
|
359
|
+
"""
|
|
360
|
+
Example of class builder in action using __slots__ to find fields.
|
|
361
|
+
|
|
362
|
+
:param cls: Class to be analysed and modified
|
|
363
|
+
:param methods: MethodMakers to be added to the class
|
|
364
|
+
:param syntax_check: check there are no arguments without defaults
|
|
365
|
+
after arguments with defaults.
|
|
366
|
+
:return: Modified class
|
|
367
|
+
"""
|
|
368
|
+
if not cls:
|
|
369
|
+
return lambda cls_: slotclass(cls_, methods=methods, syntax_check=syntax_check)
|
|
370
|
+
|
|
371
|
+
cls = builder(cls, gatherer=slot_gatherer, methods=methods)
|
|
372
|
+
|
|
373
|
+
if syntax_check:
|
|
374
|
+
fields = get_fields(cls)
|
|
375
|
+
used_default = False
|
|
376
|
+
for k, v in fields.items():
|
|
377
|
+
if v.default is NOTHING and v.default_factory is NOTHING:
|
|
378
|
+
if used_default:
|
|
379
|
+
raise SyntaxError(
|
|
380
|
+
f"non-default argument {k!r} follows default argument"
|
|
381
|
+
)
|
|
382
|
+
else:
|
|
383
|
+
used_default = True
|
|
384
|
+
|
|
385
|
+
return cls
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def fieldclass(cls):
|
|
389
|
+
"""
|
|
390
|
+
This is a special decorator for making Field subclasses using __slots__.
|
|
391
|
+
This works by forcing the __init__ method to treat NOTHING as a regular
|
|
392
|
+
value. This means *all* instance attributes always have defaults.
|
|
393
|
+
|
|
394
|
+
:param cls: Field subclass
|
|
395
|
+
:return: Modified subclass
|
|
396
|
+
"""
|
|
397
|
+
|
|
398
|
+
# Fields need a way to call their validate method
|
|
399
|
+
# So append it to the code from __init__.
|
|
400
|
+
def field_init_func(cls_):
|
|
401
|
+
code, globs = init_maker(cls_, null=field_nothing, kw_only=True)
|
|
402
|
+
code += " self.validate_field()\n"
|
|
403
|
+
return code, globs
|
|
404
|
+
|
|
405
|
+
field_nothing = _NothingType()
|
|
406
|
+
field_init_desc = MethodMaker(
|
|
407
|
+
"__init__",
|
|
408
|
+
field_init_func,
|
|
409
|
+
)
|
|
410
|
+
field_methods = frozenset({field_init_desc, repr_desc, eq_desc})
|
|
411
|
+
|
|
412
|
+
cls = builder(
|
|
413
|
+
cls,
|
|
414
|
+
gatherer=slot_gatherer,
|
|
415
|
+
methods=field_methods
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
return cls
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
from collections.abc import Callable
|
|
3
|
+
|
|
4
|
+
__version__: str
|
|
5
|
+
INTERNALS_DICT: str
|
|
6
|
+
|
|
7
|
+
def get_internals(cls) -> dict[str, typing.Any] | None: ...
|
|
8
|
+
|
|
9
|
+
def get_fields(cls: type) -> dict[str, Field]: ...
|
|
10
|
+
|
|
11
|
+
def get_inst_fields(inst: typing.Any) -> dict[str, typing.Any]: ...
|
|
12
|
+
|
|
13
|
+
class _NothingType:
|
|
14
|
+
...
|
|
15
|
+
NOTHING: _NothingType
|
|
16
|
+
|
|
17
|
+
# Stub Only
|
|
18
|
+
_codegen_type = Callable[[type], tuple[str, dict[str, typing.Any]]]
|
|
19
|
+
|
|
20
|
+
class MethodMaker:
|
|
21
|
+
funcname: str
|
|
22
|
+
code_generator: _codegen_type
|
|
23
|
+
def __init__(self, funcname: str, code_generator: _codegen_type) -> None: ...
|
|
24
|
+
def __repr__(self) -> str: ...
|
|
25
|
+
def __get__(self, instance, cls) -> Callable: ...
|
|
26
|
+
|
|
27
|
+
def init_maker(
|
|
28
|
+
cls: type,
|
|
29
|
+
*,
|
|
30
|
+
null: _NothingType = NOTHING,
|
|
31
|
+
kw_only: bool = False
|
|
32
|
+
) -> tuple[str, dict[str, typing.Any]]: ...
|
|
33
|
+
def repr_maker(cls: type) -> tuple[str, dict[str, typing.Any]]: ...
|
|
34
|
+
def eq_maker(cls: type) -> tuple[str, dict[str, typing.Any]]: ...
|
|
35
|
+
|
|
36
|
+
init_desc: MethodMaker
|
|
37
|
+
repr_desc: MethodMaker
|
|
38
|
+
eq_desc: MethodMaker
|
|
39
|
+
default_methods: frozenset[MethodMaker]
|
|
40
|
+
|
|
41
|
+
@typing.overload
|
|
42
|
+
def builder(
|
|
43
|
+
cls: type,
|
|
44
|
+
/,
|
|
45
|
+
*,
|
|
46
|
+
gatherer: Callable[[type], dict[str, Field]],
|
|
47
|
+
methods: frozenset[MethodMaker] | set[MethodMaker]
|
|
48
|
+
) -> typing.Any: ...
|
|
49
|
+
|
|
50
|
+
@typing.overload
|
|
51
|
+
def builder(
|
|
52
|
+
cls: None = None,
|
|
53
|
+
/,
|
|
54
|
+
*,
|
|
55
|
+
gatherer: Callable[[type], dict[str, Field]],
|
|
56
|
+
methods: frozenset[MethodMaker] | set[MethodMaker]
|
|
57
|
+
) -> Callable[[type], type]: ...
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
_Self = typing.TypeVar("_Self", bound="Field")
|
|
61
|
+
|
|
62
|
+
class Field:
|
|
63
|
+
default: _NothingType | typing.Any
|
|
64
|
+
default_factory: _NothingType | typing.Any
|
|
65
|
+
type: _NothingType | type
|
|
66
|
+
doc: None | str
|
|
67
|
+
|
|
68
|
+
def __init__(
|
|
69
|
+
self,
|
|
70
|
+
*,
|
|
71
|
+
default: _NothingType | typing.Any = NOTHING,
|
|
72
|
+
default_factory: _NothingType | typing.Any = NOTHING,
|
|
73
|
+
type: _NothingType | type = NOTHING,
|
|
74
|
+
doc: None | str = None,
|
|
75
|
+
) -> None: ...
|
|
76
|
+
@property
|
|
77
|
+
def _inherited_slots(self) -> list[str]: ...
|
|
78
|
+
def __repr__(self) -> str: ...
|
|
79
|
+
@typing.overload
|
|
80
|
+
def __eq__(self, other: _Self) -> bool: ...
|
|
81
|
+
@typing.overload
|
|
82
|
+
def __eq__(self, other: object) -> NotImplemented: ...
|
|
83
|
+
def validate_field(self) -> None: ...
|
|
84
|
+
@classmethod
|
|
85
|
+
def from_field(cls, fld: Field, **kwargs: typing.Any) -> _Self: ...
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class SlotFields(dict):
|
|
89
|
+
...
|
|
90
|
+
|
|
91
|
+
def slot_gatherer(cls: type) -> dict[str, Field]:
|
|
92
|
+
...
|
|
93
|
+
|
|
94
|
+
@typing.overload
|
|
95
|
+
def slotclass(
|
|
96
|
+
cls: type,
|
|
97
|
+
/,
|
|
98
|
+
*,
|
|
99
|
+
methods: frozenset[MethodMaker] | set[MethodMaker] = default_methods,
|
|
100
|
+
syntax_check: bool = True
|
|
101
|
+
) -> typing.Any: ...
|
|
102
|
+
|
|
103
|
+
def slotclass(
|
|
104
|
+
cls: None = None,
|
|
105
|
+
/,
|
|
106
|
+
*,
|
|
107
|
+
methods: frozenset[MethodMaker] | set[MethodMaker] = default_methods,
|
|
108
|
+
syntax_check: bool = True
|
|
109
|
+
) -> Callable[[type], type]: ...
|
|
110
|
+
|
|
111
|
+
def fieldclass(cls: type) -> typing.Any: ...
|