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,251 @@
1
+ import typing
2
+ from types import MappingProxyType
3
+ from typing_extensions import dataclass_transform
4
+
5
+
6
+ # Suppress weird pylance error
7
+ from collections.abc import Callable # type: ignore
8
+
9
+ from . import (
10
+ NOTHING,
11
+ Field,
12
+ GeneratedCode,
13
+ MethodMaker,
14
+ SlotMakerMeta,
15
+ _SignatureMaker
16
+ )
17
+
18
+ from . import SlotFields as SlotFields, KW_ONLY as KW_ONLY
19
+
20
+ # noinspection PyUnresolvedReferences
21
+ from . import _NothingType
22
+
23
+ PREFAB_FIELDS: str
24
+ PREFAB_INIT_FUNC: str
25
+ PRE_INIT_FUNC: str
26
+ POST_INIT_FUNC: str
27
+
28
+ _CopiableMappings = dict[str, typing.Any] | MappingProxyType[str, typing.Any]
29
+
30
+ class PrefabError(Exception): ...
31
+
32
+ def get_attributes(cls: type) -> dict[str, Attribute]: ...
33
+
34
+ def init_generator(cls: type, funcname: str = "__init__") -> GeneratedCode: ...
35
+ def iter_generator(cls: type, funcname: str = "__iter__") -> GeneratedCode: ...
36
+ def as_dict_generator(cls: type, funcname: str = "as_dict") -> GeneratedCode: ...
37
+ def hash_generator(cls: type, funcname: str = "__hash__") -> GeneratedCode: ...
38
+
39
+ init_maker: MethodMaker
40
+ prefab_init_maker: MethodMaker
41
+ repr_maker: MethodMaker
42
+ recursive_repr_maker: MethodMaker
43
+ eq_maker: MethodMaker
44
+ iter_maker: MethodMaker
45
+ asdict_maker: MethodMaker
46
+ hash_maker: MethodMaker
47
+
48
+ class Attribute(Field):
49
+ __slots__: dict
50
+ __signature__: _SignatureMaker
51
+ __classbuilder_gathered_fields__: tuple[dict[str, Field], dict[str, typing.Any]]
52
+
53
+ iter: bool
54
+ serialize: bool
55
+ metadata: dict
56
+
57
+ def __init__(
58
+ self,
59
+ *,
60
+ default: typing.Any | _NothingType = NOTHING,
61
+ default_factory: typing.Any | _NothingType = NOTHING,
62
+ type: type | _NothingType = NOTHING,
63
+ doc: str | None = ...,
64
+ init: bool = ...,
65
+ repr: bool = ...,
66
+ compare: bool = ...,
67
+ iter: bool = ...,
68
+ kw_only: bool = ...,
69
+ serialize: bool = ...,
70
+ metadata: dict | None = ...,
71
+ ) -> None: ...
72
+
73
+ def __repr__(self) -> str: ...
74
+ def __eq__(self, other: Attribute | object) -> bool: ...
75
+ def validate_field(self) -> None: ...
76
+
77
+ @typing.overload
78
+ def attribute(
79
+ *,
80
+ default: _T,
81
+ default_factory: _NothingType = NOTHING,
82
+ init: bool = ...,
83
+ repr: bool = ...,
84
+ compare: bool = ...,
85
+ iter: bool = ...,
86
+ kw_only: bool = ...,
87
+ serialize: bool = ...,
88
+ exclude_field: bool = ...,
89
+ private: bool = ...,
90
+ doc: str | None = ...,
91
+ metadata: dict | None = ...,
92
+ type: type | _NothingType = ...,
93
+ ) -> _T: ...
94
+
95
+ @typing.overload
96
+ def attribute(
97
+ *,
98
+ default: _NothingType = NOTHING,
99
+ default_factory: Callable[[], _T],
100
+ init: bool = ...,
101
+ repr: bool = ...,
102
+ compare: bool = ...,
103
+ iter: bool = ...,
104
+ kw_only: bool = ...,
105
+ serialize: bool = ...,
106
+ exclude_field: bool = ...,
107
+ private: bool = ...,
108
+ doc: str | None = ...,
109
+ metadata: dict | None = ...,
110
+ type: type | _NothingType = ...,
111
+ ) -> _T: ...
112
+
113
+ @typing.overload
114
+ def attribute(
115
+ *,
116
+ default: _NothingType = NOTHING,
117
+ default_factory: _NothingType = NOTHING,
118
+ init: bool = ...,
119
+ repr: bool = ...,
120
+ compare: bool = ...,
121
+ iter: bool = ...,
122
+ kw_only: bool = ...,
123
+ serialize: bool = ...,
124
+ exclude_field: bool = ...,
125
+ private: bool = ...,
126
+ doc: str | None = ...,
127
+ metadata: dict | None = ...,
128
+ type: type | _NothingType = ...,
129
+ ) -> typing.Any: ...
130
+
131
+ def prefab_gatherer(cls_or_ns: type | MappingProxyType) -> tuple[dict[str, Attribute], dict[str, typing.Any]]: ...
132
+
133
+ def _make_prefab(
134
+ cls: type,
135
+ *,
136
+ init: bool = True,
137
+ repr: bool = True,
138
+ eq: bool = True,
139
+ iter: bool = False,
140
+ match_args: bool = True,
141
+ kw_only: bool = False,
142
+ frozen: bool = False,
143
+ replace: bool = True,
144
+ dict_method: bool = False,
145
+ recursive_repr: bool = False,
146
+ gathered_fields: Callable[[type], tuple[dict[str, Attribute], dict[str, typing.Any]]] | None = None,
147
+ ignore_annotations: bool = False,
148
+ ) -> type: ...
149
+
150
+ _T = typing.TypeVar("_T")
151
+
152
+ # noinspection PyUnresolvedReferences
153
+ @dataclass_transform(field_specifiers=(Attribute, attribute))
154
+ class Prefab(metaclass=SlotMakerMeta):
155
+ __classbuilder_internals__: dict[str, typing.Any]
156
+ _meta_gatherer: Callable[[type | _CopiableMappings], tuple[dict[str, Field], dict[str, typing.Any]]] = ...
157
+ __slots__: dict[str, typing.Any] = ...
158
+ def __init_subclass__(
159
+ cls,
160
+ *,
161
+ init: bool = True,
162
+ repr: bool = True,
163
+ eq: bool = True,
164
+ iter: bool = False,
165
+ match_args: bool = True,
166
+ kw_only: bool = False,
167
+ frozen: bool = False,
168
+ replace: bool = True,
169
+ dict_method: bool = False,
170
+ recursive_repr: bool = False,
171
+ ) -> None: ...
172
+
173
+ # As far as I can tell these are the correct types
174
+ # But mypy.stubtest crashes trying to analyse them
175
+ # Due to the combination of overload and dataclass_transform
176
+ # @typing.overload
177
+ # def prefab(
178
+ # cls: None = None,
179
+ # *,
180
+ # init: bool = ...,
181
+ # repr: bool = ...,
182
+ # eq: bool = ...,
183
+ # iter: bool = ...,
184
+ # match_args: bool = ...,
185
+ # kw_only: bool = ...,
186
+ # frozen: bool = ...,
187
+ # dict_method: bool = ...,
188
+ # recursive_repr: bool = ...,
189
+ # ) -> Callable[[type[_T]], type[_T]]: ...
190
+
191
+ # @dataclass_transform(field_specifiers=(Attribute, attribute))
192
+ # @typing.overload
193
+ # def prefab(
194
+ # cls: type[_T],
195
+ # *,
196
+ # init: bool = ...,
197
+ # repr: bool = ...,
198
+ # eq: bool = ...,
199
+ # iter: bool = ...,
200
+ # match_args: bool = ...,
201
+ # kw_only: bool = ...,
202
+ # frozen: bool = ...,
203
+ # dict_method: bool = ...,
204
+ # recursive_repr: bool = ...,
205
+ # ) -> type[_T]: ...
206
+
207
+ # As mypy crashes, and the only difference is the return type
208
+ # just return `Any` for now to avoid the overload.
209
+ @dataclass_transform(field_specifiers=(Attribute, attribute))
210
+ def prefab(
211
+ cls: type[_T] | None = ...,
212
+ *,
213
+ init: bool = ...,
214
+ repr: bool = ...,
215
+ eq: bool = ...,
216
+ iter: bool = ...,
217
+ match_args: bool = ...,
218
+ kw_only: bool = ...,
219
+ frozen: bool = ...,
220
+ replace: bool = ...,
221
+ dict_method: bool = ...,
222
+ recursive_repr: bool = ...,
223
+ ignore_annotations: bool = ...,
224
+ ) -> typing.Any: ...
225
+
226
+ def build_prefab(
227
+ class_name: str,
228
+ attributes: list[tuple[str, Attribute]],
229
+ *,
230
+ bases: tuple[type, ...] = (),
231
+ class_dict: dict[str, typing.Any] | None = None,
232
+ init: bool = True,
233
+ repr: bool = True,
234
+ eq: bool = True,
235
+ iter: bool = False,
236
+ match_args: bool = True,
237
+ kw_only: bool = False,
238
+ frozen: bool = False,
239
+ replace: bool = True,
240
+ dict_method: bool = False,
241
+ recursive_repr: bool = False,
242
+ slots: bool = False,
243
+ ) -> type: ...
244
+
245
+ def is_prefab(o: typing.Any) -> bool: ...
246
+
247
+ def is_prefab_instance(o: object) -> bool: ...
248
+
249
+ def as_dict(o) -> dict[str, typing.Any]: ...
250
+
251
+ def replace(obj: _T, /, **changes: typing.Any) -> _T: ...
@@ -0,0 +1 @@
1
+ partial
@@ -0,0 +1,335 @@
1
+ Metadata-Version: 2.4
2
+ Name: ducktools-classbuilder
3
+ Version: 0.12.1
4
+ Summary: Toolkit for creating class boilerplate generators
5
+ Author: David C Ellis
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/davidcellis/ducktools-classbuilder
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Programming Language :: Python :: 3.14
14
+ Classifier: Operating System :: OS Independent
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Provides-Extra: docs
19
+ Requires-Dist: sphinx>=8.1; extra == "docs"
20
+ Requires-Dist: myst-parser>=4.0; extra == "docs"
21
+ Requires-Dist: sphinx_rtd_theme>=3.0; extra == "docs"
22
+ Dynamic: license-file
23
+
24
+ # Ducktools: Class Builder #
25
+
26
+ `ducktools-classbuilder` is *the* Python package that will bring you the **joy**
27
+ of writing... functions... that will bring back the **joy** of writing classes.
28
+
29
+ Maybe.
30
+
31
+ While `attrs` and `dataclasses` are class boilerplate generators,
32
+ `ducktools.classbuilder` is intended to provide the tools to help make a customized
33
+ version of the same concept.
34
+
35
+ Install from PyPI with:
36
+ `python -m pip install ducktools-classbuilder`
37
+
38
+ ## Included Implementations ##
39
+
40
+ The classbuilder tools make up the core of this module and there is an implementation
41
+ using these tools in the `prefab` submodule.
42
+
43
+ There is also a minimal `@slotclass` example that can construct classes from a special
44
+ mapping used in `__slots__`.
45
+
46
+ ```python
47
+ from ducktools.classbuilder import Field, SlotFields, slotclass
48
+
49
+ @slotclass
50
+ class SlottedDC:
51
+ __slots__ = SlotFields(
52
+ the_answer=42,
53
+ the_question=Field(
54
+ default="What do you get if you multiply six by nine?",
55
+ doc="Life, the Universe, and Everything",
56
+ ),
57
+ )
58
+
59
+ ex = SlottedDC()
60
+ print(ex)
61
+ ```
62
+
63
+ ### Core ###
64
+
65
+ The core of the module provides tools for creating a customized version of the `dataclass` concept.
66
+
67
+ * `MethodMaker`
68
+ * This tool takes a function that generates source code and converts it into a descriptor
69
+ that will execute the source code and attach the gemerated method to a class on demand.
70
+ * `Field`
71
+ * This defines a basic dataclass-like field with some basic arguments
72
+ * This class itself is a dataclass-like of sorts
73
+ * Additional arguments can be added by subclassing and using annotations
74
+ * See `ducktools.classbuilder.prefab.Attribute` for an example of this
75
+ * Gatherers
76
+ * These collect field information and return both the gathered fields and any modifications
77
+ that will need to be made to the class when built to support them.
78
+ * `builder`
79
+ * This is the main tool used for constructing decorators and base classes to provide
80
+ generated methods.
81
+ * Other than the required changes to a class for `__slots__` that are done by `SlotMakerMeta`
82
+ this is where all class mutations should be applied.
83
+ * `SlotMakerMeta`
84
+ * When given a gatherer, this metaclass will create `__slots__` automatically.
85
+
86
+ > [!TIP]
87
+ > For more information on using these tools to create your own implementations
88
+ > using the builder see
89
+ > [the tutorial](https://ducktools-classbuilder.readthedocs.io/en/latest/tutorial.html)
90
+ > for a full tutorial and
91
+ > [extension_examples](https://ducktools-classbuilder.readthedocs.io/en/latest/extension_examples.html)
92
+ > for other customizations.
93
+
94
+ ### Prefab ###
95
+
96
+ This prebuilt implementation is available from the `ducktools.classbuilder.prefab` submodule.
97
+
98
+ This includes more customization including `__prefab_pre_init__` and `__prefab_post_init__`
99
+ functions for subclass customization.
100
+
101
+ A `@prefab` decorator and `Prefab` base class are provided.
102
+
103
+ `Prefab` will generate `__slots__` by default.
104
+ decorated classes with `@prefab` that do not declare fields using `__slots__`
105
+ will **not** be slotted and there is no `slots` argument to apply this.
106
+
107
+ Here is an example of applying a conversion in `__post_init__`:
108
+ ```python
109
+ from pathlib import Path
110
+ from ducktools.classbuilder.prefab import Prefab
111
+
112
+ class AppDetails(Prefab, frozen=True):
113
+ app_name: str
114
+ app_path: Path
115
+
116
+ def __prefab_post_init__(self, app_path: str | Path):
117
+ # frozen in `Prefab` is implemented as a 'set-once' __setattr__ function.
118
+ # So we do not need to use `object.__setattr__` here
119
+ self.app_path = Path(app_path)
120
+
121
+ steam = AppDetails(
122
+ "Steam",
123
+ r"C:\Program Files (x86)\Steam\steam.exe"
124
+ )
125
+
126
+ print(steam)
127
+ ```
128
+
129
+ #### Features ####
130
+
131
+ `Prefab` and `@prefab` support many standard dataclass features along with
132
+ a few extras.
133
+
134
+ * All standard methods are generated on-demand
135
+ * This makes the construction of classes much faster in general
136
+ * Generation is done and then cached on first access
137
+ * Standard `__init__`, `__eq__` and `__repr__` methods are generated by default
138
+ - The `__repr__` implementation does not automatically protect against recursion,
139
+ but there is a `recursive_repr` argument that will do so if needed
140
+ * `repr`, `eq` and `kw_only` arguments work as they do in `dataclasses`
141
+ * There is an optional `iter` argument that will make the class iterable
142
+ * `__prefab_post_init__` will take any field name as an argument and can
143
+ be used to write a 'partial' `__init__` function for only non-standard attributes
144
+ * The `frozen` argument will make the dataclass a 'write once' object
145
+ * This is to make the partial `__prefab_post_init__` function more natural
146
+ to write for frozen classes
147
+ * `dict_method=True` will generate an `as_dict` method that gives a dictionary of
148
+ attributes that have `serialize=True` (the default)
149
+ * `ignore_annotations` can be used to only use the presence of `attribute` values
150
+ to decide how the class is constructed
151
+ * This is intended for cases where evaluating the annotations may trigger imports
152
+ which could be slow and unnecessary for the function of class generation
153
+ * `replace=False` can be used to avoid defining the `__replace__` method
154
+ * `attribute` has additional options over dataclasses' `Field`
155
+ * `iter=True` will include the attribute in the iterable if `__iter__` is generated
156
+ * `serialize=True` decides if the attribute is include in `as_dict`
157
+ * `exclude_field` is short for `repr=False`, `compare=False`, `iter=False`, `serialize=False`
158
+ * `private` is short for `exclude_field=True` and `init=False` and requires a default/factory
159
+ * `doc` will add this string as the value in slotted classes, which appears in `help()`
160
+ * `build_prefab` can be used to dynamically create classes and *does* support a slots argument
161
+ * Unlike dataclasses, this does not create the class twice in order to provide slots
162
+
163
+ There are also some intentionally missing features:
164
+
165
+ * The `@prefab` decorator does not and will not support a `slots` argument
166
+ * Use `Prefab` for slots.
167
+ * `as_dict` and the generated `.as_dict` method **do not** recurse or deep copy
168
+ * `unsafe_hash` is not provided
169
+ * `weakref_slot` is not available as an argument
170
+ * `__weakref__` can be added to slots by declaring it as if it were an attribute
171
+ * There is no check for mutable defaults
172
+ * You should still use `default_factory` as you would for dataclasses, not doing so
173
+ is still incorrect
174
+ * `dataclasses` uses hashability as a proxy for mutability, but technically this is
175
+ inaccurate as you can be unhashable but immutable and mutable but hashable
176
+ * This may change in a future version, but I haven't felt the need to add this check so far
177
+ * In Python 3.14 Annotations are gathered as `VALUE` if possible and `STRING` if this fails
178
+ * `VALUE` annotations are used as they are faster in most cases
179
+ * As the `__init__` method gets `__annotations__` these need to be either values or strings
180
+ to match the behaviour of previous Python versions
181
+
182
+
183
+ ## What is the issue with generating `__slots__` with a decorator ##
184
+
185
+ If you want to use `__slots__` in order to save memory you have to declare
186
+ them when the class is originally created as you can't add them later.
187
+
188
+ When you use `@dataclass(slots=True)`[^2] with `dataclasses`, the function
189
+ has to make a new class and attempt to copy over everything from the original.
190
+
191
+ This is because decorators operate on classes *after they have been created*
192
+ while slots need to be declared beforehand.
193
+ While you can change the value of `__slots__` after a class has been created,
194
+ this will have no effect on the internal structure of the class.
195
+
196
+ By using a metaclass or by declaring fields using `__slots__` however,
197
+ the fields can be set *before* the class is constructed, so the class
198
+ will work correctly without needing to be rebuilt.
199
+
200
+ For example these two classes would be roughly equivalent, except that
201
+ `@dataclass` has had to recreate the class from scratch while `Prefab`
202
+ has created `__slots__` and added the methods on to the original class.
203
+ This means that any references stored to the original class *before*
204
+ `@dataclass` has rebuilt the class will not be pointing towards the
205
+ correct class.
206
+
207
+ Here's a demonstration of the issue using a registry for serialization
208
+ functions.
209
+
210
+ > This example requires Python 3.10 or later as earlier versions of
211
+ > `dataclasses` did not support the `slots` argument.
212
+
213
+ ```python
214
+ import json
215
+ from dataclasses import dataclass
216
+ from ducktools.classbuilder.prefab import Prefab, attribute
217
+
218
+
219
+ class _RegisterDescriptor:
220
+ def __init__(self, func, registry):
221
+ self.func = func
222
+ self.registry = registry
223
+
224
+ def __set_name__(self, owner, name):
225
+ self.registry.register(owner, self.func)
226
+ setattr(owner, name, self.func)
227
+
228
+
229
+ class SerializeRegister:
230
+ def __init__(self):
231
+ self.serializers = {}
232
+
233
+ def register(self, cls, func):
234
+ self.serializers[cls] = func
235
+
236
+ def register_method(self, method):
237
+ return _RegisterDescriptor(method, self)
238
+
239
+ def default(self, o):
240
+ try:
241
+ return self.serializers[type(o)](o)
242
+ except KeyError:
243
+ raise TypeError(f"Object of type {type(o).__name__} is not JSON serializable")
244
+
245
+
246
+ register = SerializeRegister()
247
+
248
+
249
+ @dataclass(slots=True)
250
+ class DataCoords:
251
+ x: float = 0.0
252
+ y: float = 0.0
253
+
254
+ @register.register_method
255
+ def to_json(self):
256
+ return {"x": self.x, "y": self.y}
257
+
258
+
259
+ # slots=True is the default for Prefab
260
+ class BuilderCoords(Prefab):
261
+ x: float = 0.0
262
+ y: float = attribute(default=0.0, doc="y coordinate")
263
+
264
+ @register.register_method
265
+ def to_json(self):
266
+ return {"x": self.x, "y": self.y}
267
+
268
+
269
+ # In both cases __slots__ have been defined
270
+ print(f"{DataCoords.__slots__ = }")
271
+ print(f"{BuilderCoords.__slots__ = }\n")
272
+
273
+ data_ex = DataCoords()
274
+ builder_ex = BuilderCoords()
275
+
276
+ objs = [data_ex, builder_ex]
277
+
278
+ print(data_ex)
279
+ print(builder_ex)
280
+ print()
281
+
282
+ # Demonstrate you can not set values not defined in slots
283
+ for obj in objs:
284
+ try:
285
+ obj.z = 1.0
286
+ except AttributeError as e:
287
+ print(e)
288
+ print()
289
+
290
+ print("Attempt to serialize:")
291
+ for obj in objs:
292
+ try:
293
+ print(f"{type(obj).__name__}: {json.dumps(obj, default=register.default)}")
294
+ except TypeError as e:
295
+ print(f"{type(obj).__name__}: {e!r}")
296
+ ```
297
+
298
+ Output (Python 3.12):
299
+ ```
300
+ DataCoords.__slots__ = ('x', 'y')
301
+ BuilderCoords.__slots__ = {'x': None, 'y': 'y coordinate'}
302
+
303
+ DataCoords(x=0.0, y=0.0)
304
+ BuilderCoords(x=0.0, y=0.0)
305
+
306
+ 'DataCoords' object has no attribute 'z'
307
+ 'BuilderCoords' object has no attribute 'z'
308
+
309
+ Attempt to serialize:
310
+ DataCoords: TypeError('Object of type DataCoords is not JSON serializable')
311
+ BuilderCoords: {"x": 0.0, "y": 0.0}
312
+ ```
313
+
314
+ ## Will you add \<feature\> to `classbuilder.prefab`? ##
315
+
316
+ No. Not unless it's something I need or find interesting.
317
+
318
+ The original version of `prefab_classes` was intended to have every feature
319
+ anybody could possibly require, but this is no longer the case with this
320
+ rebuilt version.
321
+
322
+ I will fix bugs (assuming they're not actually intended behaviour).
323
+
324
+ However the whole goal of this module is if you want to have a class generator
325
+ with a specific feature, you can create or add it yourself.
326
+
327
+ ## Credit ##
328
+
329
+ Heavily inspired by [David Beazley's Cluegen](https://github.com/dabeaz/cluegen)
330
+
331
+ [^1]: `SlotFields` is actually just a subclassed `dict` with no changes. `__slots__`
332
+ works with dictionaries using the values of the keys, while fields are normally
333
+ used for documentation.
334
+
335
+ [^2]: or `@attrs.define`.
@@ -0,0 +1,15 @@
1
+ ducktools/classbuilder/__init__.py,sha256=QtrTaV0nKA6qcZxBveucyj8UaO-q7_Mhv5Q8ek2LO6c,41551
2
+ ducktools/classbuilder/__init__.pyi,sha256=RBU83QZLZSp62XnPDcxTIAHu6U9o6vJs_Rq0XBhqE68,8377
3
+ ducktools/classbuilder/_version.py,sha256=8kcVJoc2ezPl65T7kB6_NJXlTK8tPiz-ZU6sBevSt8s,54
4
+ ducktools/classbuilder/annotations.pyi,sha256=bKTwQlPydbwrbVGaDu_PoSYOhuaqv8I_tMHf-g4aT0M,476
5
+ ducktools/classbuilder/prefab.py,sha256=XXH_NeDk6DhMfXHmr0nGmT_7zTbTg3RTcnSCg0Ujnvs,28375
6
+ ducktools/classbuilder/prefab.pyi,sha256=_cg9WsZqwkgOy0iowHtzGB2E6BSNJdKcdk0IC_Wz_qU,6756
7
+ ducktools/classbuilder/py.typed,sha256=la67KBlbjXN-_-DfGNcdOcjYumVpKG_Tkw-8n5dnGB4,8
8
+ ducktools/classbuilder/annotations/__init__.py,sha256=n8VFmOItEbsMuq5gothp5yi6H3m3JJdVys04DVb0r-4,2145
9
+ ducktools/classbuilder/annotations/annotations_314.py,sha256=Yeng_kke1dcE6RarOzYN7SczWc9TSQEEZFEODzSj4uo,3692
10
+ ducktools/classbuilder/annotations/annotations_pre_314.py,sha256=Et-TYQYNVtMssShsS62PTu2mou2kr-ZJldYAAayhjAU,1651
11
+ ducktools_classbuilder-0.12.1.dist-info/licenses/LICENSE,sha256=6Thz9Dbw8R4fWInl6sGl8Rj3UnKnRbDwrc6jZerpugQ,1070
12
+ ducktools_classbuilder-0.12.1.dist-info/METADATA,sha256=uWB4nU2Gju2MOXTY30Mc-YsfwhLWEGGDuRfkQa-xJao,12347
13
+ ducktools_classbuilder-0.12.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
14
+ ducktools_classbuilder-0.12.1.dist-info/top_level.txt,sha256=uSDLtio3ZFqdwcsMJ2O5yhjB4Q3ytbBWbA8rJREganc,10
15
+ ducktools_classbuilder-0.12.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
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.
@@ -0,0 +1 @@
1
+ ducktools