ducktools-classbuilder 0.5.1__py3-none-any.whl → 0.6.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 +494 -210
- ducktools/classbuilder/__init__.pyi +145 -57
- ducktools/classbuilder/annotations.py +173 -0
- ducktools/classbuilder/annotations.pyi +26 -0
- ducktools/classbuilder/prefab.py +237 -385
- ducktools/classbuilder/prefab.pyi +30 -28
- ducktools_classbuilder-0.6.1.dist-info/METADATA +318 -0
- ducktools_classbuilder-0.6.1.dist-info/RECORD +12 -0
- {ducktools_classbuilder-0.5.1.dist-info → ducktools_classbuilder-0.6.1.dist-info}/WHEEL +1 -1
- ducktools_classbuilder-0.5.1.dist-info/METADATA +0 -270
- ducktools_classbuilder-0.5.1.dist-info/RECORD +0 -10
- {ducktools_classbuilder-0.5.1.dist-info → ducktools_classbuilder-0.6.1.dist-info}/LICENSE.md +0 -0
- {ducktools_classbuilder-0.5.1.dist-info → ducktools_classbuilder-0.6.1.dist-info}/top_level.txt +0 -0
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: ducktools-classbuilder
|
|
3
|
-
Version: 0.5.1
|
|
4
|
-
Summary: Toolkit for creating class boilerplate generators
|
|
5
|
-
Author: David C Ellis
|
|
6
|
-
License: MIT License
|
|
7
|
-
|
|
8
|
-
Copyright (c) 2024 David C Ellis
|
|
9
|
-
|
|
10
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
-
in the Software without restriction, including without limitation the rights
|
|
13
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
-
furnished to do so, subject to the following conditions:
|
|
16
|
-
|
|
17
|
-
The above copyright notice and this permission notice shall be included in all
|
|
18
|
-
copies or substantial portions of the Software.
|
|
19
|
-
|
|
20
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
-
SOFTWARE.
|
|
27
|
-
|
|
28
|
-
Project-URL: Homepage, https://github.com/davidcellis/ducktools-classbuilder
|
|
29
|
-
Classifier: Development Status :: 4 - Beta
|
|
30
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
31
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
32
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
33
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
34
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
35
|
-
Classifier: Operating System :: OS Independent
|
|
36
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
37
|
-
Requires-Python: >=3.8
|
|
38
|
-
Description-Content-Type: text/markdown
|
|
39
|
-
License-File: LICENSE.md
|
|
40
|
-
Provides-Extra: docs
|
|
41
|
-
Requires-Dist: sphinx ; extra == 'docs'
|
|
42
|
-
Requires-Dist: myst-parser ; extra == 'docs'
|
|
43
|
-
Requires-Dist: sphinx-rtd-theme ; extra == 'docs'
|
|
44
|
-
Provides-Extra: testing
|
|
45
|
-
Requires-Dist: pytest ; extra == 'testing'
|
|
46
|
-
Requires-Dist: pytest-cov ; extra == 'testing'
|
|
47
|
-
Requires-Dist: mypy ; extra == 'testing'
|
|
48
|
-
Requires-Dist: typing-extensions ; (python_version < "3.10") and extra == 'testing'
|
|
49
|
-
|
|
50
|
-
# Ducktools: Class Builder #
|
|
51
|
-
|
|
52
|
-
`ducktools-classbuilder` is *the* Python package that will bring you the **joy**
|
|
53
|
-
of writing... functions... that will bring back the **joy** of writing classes.
|
|
54
|
-
|
|
55
|
-
Maybe.
|
|
56
|
-
|
|
57
|
-
While `attrs` and `dataclasses` are class boilerplate generators,
|
|
58
|
-
`ducktools.classbuilder` is intended to be a `@dataclass`-like generator.
|
|
59
|
-
The goal is to handle some of the basic functions and to allow for flexible
|
|
60
|
-
customization of both the field collection and the method generation.
|
|
61
|
-
|
|
62
|
-
`ducktools.classbuilder.prefab` includes a prebuilt implementation using these tools.
|
|
63
|
-
|
|
64
|
-
Install from PyPI with:
|
|
65
|
-
`python -m pip install ducktools-classbuilder`
|
|
66
|
-
|
|
67
|
-
## Usage: building a class decorator ##
|
|
68
|
-
|
|
69
|
-
In order to create a class decorator using `ducktools.classbuilder` there are
|
|
70
|
-
a few things you need to prepare.
|
|
71
|
-
|
|
72
|
-
1. A field gathering function to analyse the class and collect valid `Field`s and provide
|
|
73
|
-
any modifications that need to be applied to the class attributes.
|
|
74
|
-
* An example `slot_gatherer` is included.
|
|
75
|
-
2. Code generators that can make use of the gathered `Field`s to create magic method
|
|
76
|
-
source code. To be made into descriptors by `MethodMaker`.
|
|
77
|
-
* Example `init_generator`, `repr_generator` and `eq_generator` generators are included.
|
|
78
|
-
3. A function that calls the `builder` function to apply both of these steps.
|
|
79
|
-
|
|
80
|
-
A field gathering function needs to take the original class as an argument and
|
|
81
|
-
return a dictionary of `{key: Field(...)}` pairs.
|
|
82
|
-
|
|
83
|
-
> [!NOTE]
|
|
84
|
-
> The `builder` will handle inheritance so do not collect fields from parent classes.
|
|
85
|
-
|
|
86
|
-
The code generators take the class as the only argument and return a tuple
|
|
87
|
-
of method source code and globals to be provided to `exec(code, globs)` in order
|
|
88
|
-
to generate the actual method.
|
|
89
|
-
|
|
90
|
-
The provided `slot_gatherer` looks for `__slots__` being assigned a `SlotFields`
|
|
91
|
-
class[^1] where keyword arguments define the names and values for the fields.
|
|
92
|
-
|
|
93
|
-
Code generator functions need to be converted to descriptors before being used.
|
|
94
|
-
This is done using the provided `MethodMaker` descriptor class.
|
|
95
|
-
ex: `init_maker = MethodMaker("__init__", init_generator)`.
|
|
96
|
-
|
|
97
|
-
These parts can then be used to make a basic class boilerplate generator by
|
|
98
|
-
providing them to the `builder` function.
|
|
99
|
-
|
|
100
|
-
```python
|
|
101
|
-
from ducktools.classbuilder import (
|
|
102
|
-
builder,
|
|
103
|
-
slot_gatherer,
|
|
104
|
-
init_generator, eq_generator, repr_generator,
|
|
105
|
-
MethodMaker,
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
init_maker = MethodMaker("__init__", init_generator)
|
|
109
|
-
repr_maker = MethodMaker("__repr__", repr_generator)
|
|
110
|
-
eq_maker = MethodMaker("__eq__", eq_generator)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
def slotclass(cls):
|
|
114
|
-
return builder(cls, gatherer=slot_gatherer, methods={init_maker, repr_maker, eq_maker})
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
## Slot Class Usage ##
|
|
118
|
-
|
|
119
|
-
This created `slotclass` function can then be used as a decorator to generate classes in
|
|
120
|
-
a similar manner to the `@dataclass` decorator from `dataclasses`.
|
|
121
|
-
|
|
122
|
-
> [!NOTE]
|
|
123
|
-
> `ducktools.classbuilder` includes a premade version of `slotclass` that can
|
|
124
|
-
> be used directly. (The included version has some extra features).
|
|
125
|
-
|
|
126
|
-
```python
|
|
127
|
-
from ducktools.classbuilder import Field, SlotFields, slotclass
|
|
128
|
-
|
|
129
|
-
@slotclass
|
|
130
|
-
class SlottedDC:
|
|
131
|
-
__slots__ = SlotFields(
|
|
132
|
-
the_answer=42,
|
|
133
|
-
the_question=Field(
|
|
134
|
-
default="What do you get if you multiply six by nine?",
|
|
135
|
-
doc="Life, the Universe, and Everything",
|
|
136
|
-
),
|
|
137
|
-
)
|
|
138
|
-
|
|
139
|
-
ex = SlottedDC()
|
|
140
|
-
print(ex)
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
> [!TIP]
|
|
144
|
-
> For more information and examples of creating class generators with additional
|
|
145
|
-
> features using the builder see
|
|
146
|
-
> [the docs](https://ducktools-classbuilder.readthedocs.io/en/latest/extension_examples.html)
|
|
147
|
-
|
|
148
|
-
## Why does your example use `__slots__` instead of annotations? ##
|
|
149
|
-
|
|
150
|
-
If you want to use `__slots__` in order to save memory you have to declare
|
|
151
|
-
them when the class is originally created as you can't add them later.
|
|
152
|
-
|
|
153
|
-
When you use `@dataclass(slots=True)`[^2] with `dataclasses` in order for
|
|
154
|
-
this to work, `dataclasses` has to make a new class and attempt to
|
|
155
|
-
copy over everything from the original.
|
|
156
|
-
This is because decorators operate on classes *after they have been created*
|
|
157
|
-
while slots need to be declared beforehand.
|
|
158
|
-
While you can change the value of `__slots__` after a class has been created,
|
|
159
|
-
this will have no effect on the internal structure of the class.
|
|
160
|
-
|
|
161
|
-
By declaring the class using `__slots__` on the other hand, we can take
|
|
162
|
-
advantage of the fact that it accepts a mapping, where the keys will be
|
|
163
|
-
used as the attributes to create as slots. The values can then be used as
|
|
164
|
-
the default values equivalently to how type hints are used in `dataclasses`.
|
|
165
|
-
|
|
166
|
-
For example these two classes would be roughly equivalent, except that
|
|
167
|
-
`@dataclass` has had to recreate the class from scratch while `@slotclass`
|
|
168
|
-
has added the methods on to the original class.
|
|
169
|
-
This means that any references stored to the original class *before*
|
|
170
|
-
`@dataclass` has rebuilt the class will not be pointing towards the
|
|
171
|
-
correct class.
|
|
172
|
-
This can be demonstrated using a simple class register decorator.
|
|
173
|
-
|
|
174
|
-
> This example requires Python 3.10 as earlier versions of
|
|
175
|
-
> `dataclasses` did not support the `slots` argument.
|
|
176
|
-
|
|
177
|
-
```python
|
|
178
|
-
from dataclasses import dataclass
|
|
179
|
-
from ducktools.classbuilder import slotclass, SlotFields
|
|
180
|
-
|
|
181
|
-
class_register = {}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
def register(cls):
|
|
185
|
-
class_register[cls.__name__] = cls
|
|
186
|
-
return cls
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
@dataclass(slots=True)
|
|
190
|
-
@register
|
|
191
|
-
class DataCoords:
|
|
192
|
-
x: float = 0.0
|
|
193
|
-
y: float = 0.0
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
@slotclass
|
|
197
|
-
@register
|
|
198
|
-
class SlotCoords:
|
|
199
|
-
__slots__ = SlotFields(x=0.0, y=0.0)
|
|
200
|
-
# Type hints don't affect class construction, these are optional.
|
|
201
|
-
x: float
|
|
202
|
-
y: float
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
print(DataCoords())
|
|
206
|
-
print(SlotCoords())
|
|
207
|
-
|
|
208
|
-
print(f"{DataCoords is class_register[DataCoords.__name__] = }")
|
|
209
|
-
print(f"{SlotCoords is class_register[SlotCoords.__name__] = }")
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
## Using annotations anyway ##
|
|
213
|
-
|
|
214
|
-
For those that really want to use type annotations a basic `annotation_gatherer`
|
|
215
|
-
function and `@annotationclass` decorator are also included. Slots are not generated
|
|
216
|
-
in this case.
|
|
217
|
-
|
|
218
|
-
```python
|
|
219
|
-
from ducktools.classbuilder import annotationclass
|
|
220
|
-
|
|
221
|
-
@annotationclass
|
|
222
|
-
class AnnotatedDC:
|
|
223
|
-
the_answer: int = 42
|
|
224
|
-
the_question: str = "What do you get if you multiply six by nine?"
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
ex = AnnotatedDC()
|
|
228
|
-
print(ex)
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
## What features does this have? ##
|
|
232
|
-
|
|
233
|
-
Included as an example implementation, the `slotclass` generator supports
|
|
234
|
-
`default_factory` for creating mutable defaults like lists, dicts etc.
|
|
235
|
-
It also supports default values that are not builtins (try this on
|
|
236
|
-
[Cluegen](https://github.com/dabeaz/cluegen)).
|
|
237
|
-
|
|
238
|
-
It will copy values provided as the `type` to `Field` into the
|
|
239
|
-
`__annotations__` dictionary of the class.
|
|
240
|
-
Values provided to `doc` will be placed in the final `__slots__`
|
|
241
|
-
field so they are present on the class if `help(...)` is called.
|
|
242
|
-
|
|
243
|
-
A fairly basic `annotations_gatherer` and `annotationclass` are also included
|
|
244
|
-
and can be used to generate classbuilders that rely on annotations.
|
|
245
|
-
|
|
246
|
-
If you want something with more features you can look at the `prefab.py`
|
|
247
|
-
implementation which provides a 'prebuilt' implementation.
|
|
248
|
-
|
|
249
|
-
## Will you add \<feature\> to `classbuilder.prefab`? ##
|
|
250
|
-
|
|
251
|
-
No. Not unless it's something I need or find interesting.
|
|
252
|
-
|
|
253
|
-
The original version of `prefab_classes` was intended to have every feature
|
|
254
|
-
anybody could possibly require, but this is no longer the case with this
|
|
255
|
-
rebuilt version.
|
|
256
|
-
|
|
257
|
-
I will fix bugs (assuming they're not actually intended behaviour).
|
|
258
|
-
|
|
259
|
-
However the whole goal of this module is if you want to have a class generator
|
|
260
|
-
with a specific feature, you can create or add it yourself.
|
|
261
|
-
|
|
262
|
-
## Credit ##
|
|
263
|
-
|
|
264
|
-
Heavily inspired by [David Beazley's Cluegen](https://github.com/dabeaz/cluegen)
|
|
265
|
-
|
|
266
|
-
[^1]: `SlotFields` is actually just a subclassed `dict` with no changes. `__slots__`
|
|
267
|
-
works with dictionaries using the values of the keys, while fields are normally
|
|
268
|
-
used for documentation.
|
|
269
|
-
|
|
270
|
-
[^2]: or `@attrs.define`.
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
ducktools/classbuilder/__init__.py,sha256=rl5oc0azae-32EjeCTzhM1DR5JNR-AgOriyH5zkf4WI,20856
|
|
2
|
-
ducktools/classbuilder/__init__.pyi,sha256=5gnHhSVMP5yArA-_cuvIeLnLAlmY_EmPMfVY9dRi0CA,4784
|
|
3
|
-
ducktools/classbuilder/prefab.py,sha256=K9_bKh24OiGJ40BbJWVfwDObArMUxi2JRbB5ZN-t--M,28984
|
|
4
|
-
ducktools/classbuilder/prefab.pyi,sha256=sNladRJM3lcZo8zQWvWCIInBx0wUD7PT6fhdE89OpG0,3960
|
|
5
|
-
ducktools/classbuilder/py.typed,sha256=la67KBlbjXN-_-DfGNcdOcjYumVpKG_Tkw-8n5dnGB4,8
|
|
6
|
-
ducktools_classbuilder-0.5.1.dist-info/LICENSE.md,sha256=6Thz9Dbw8R4fWInl6sGl8Rj3UnKnRbDwrc6jZerpugQ,1070
|
|
7
|
-
ducktools_classbuilder-0.5.1.dist-info/METADATA,sha256=B0KO5vvdZOUZ4o28GA4zlgXuIkA9j4SuplzFByvRhjs,10143
|
|
8
|
-
ducktools_classbuilder-0.5.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
9
|
-
ducktools_classbuilder-0.5.1.dist-info/top_level.txt,sha256=uSDLtio3ZFqdwcsMJ2O5yhjB4Q3ytbBWbA8rJREganc,10
|
|
10
|
-
ducktools_classbuilder-0.5.1.dist-info/RECORD,,
|
{ducktools_classbuilder-0.5.1.dist-info → ducktools_classbuilder-0.6.1.dist-info}/LICENSE.md
RENAMED
|
File without changes
|
{ducktools_classbuilder-0.5.1.dist-info → ducktools_classbuilder-0.6.1.dist-info}/top_level.txt
RENAMED
|
File without changes
|