ducktools-classbuilder 0.1.0__py3-none-any.whl → 0.1.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 +2 -2
- ducktools/classbuilder/__init__.pyi +18 -18
- ducktools/classbuilder/prefab.py +18 -7
- ducktools/classbuilder/prefab.pyi +23 -13
- {ducktools_classbuilder-0.1.0.dist-info → ducktools_classbuilder-0.1.1.dist-info}/METADATA +89 -23
- ducktools_classbuilder-0.1.1.dist-info/RECORD +10 -0
- ducktools_classbuilder-0.1.0.dist-info/RECORD +0 -10
- {ducktools_classbuilder-0.1.0.dist-info → ducktools_classbuilder-0.1.1.dist-info}/LICENSE.md +0 -0
- {ducktools_classbuilder-0.1.0.dist-info → ducktools_classbuilder-0.1.1.dist-info}/WHEEL +0 -0
- {ducktools_classbuilder-0.1.0.dist-info → ducktools_classbuilder-0.1.1.dist-info}/top_level.txt +0 -0
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
20
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
# SOFTWARE.
|
|
22
|
-
__version__ = "v0.1.
|
|
22
|
+
__version__ = "v0.1.1"
|
|
23
23
|
|
|
24
24
|
# Change this name if you make heavy modifications
|
|
25
25
|
INTERNALS_DICT = "__classbuilder_internals__"
|
|
@@ -415,4 +415,4 @@ def fieldclass(cls):
|
|
|
415
415
|
methods=field_methods
|
|
416
416
|
)
|
|
417
417
|
|
|
418
|
-
return cls
|
|
418
|
+
return cls
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import typing
|
|
2
2
|
from collections.abc import Callable
|
|
3
3
|
|
|
4
|
+
_py_type = type # Alias for type where it is used as a name
|
|
5
|
+
|
|
4
6
|
__version__: str
|
|
5
7
|
INTERNALS_DICT: str
|
|
6
8
|
|
|
@@ -38,14 +40,16 @@ repr_desc: MethodMaker
|
|
|
38
40
|
eq_desc: MethodMaker
|
|
39
41
|
default_methods: frozenset[MethodMaker]
|
|
40
42
|
|
|
43
|
+
_T = typing.TypeVar("_T")
|
|
44
|
+
|
|
41
45
|
@typing.overload
|
|
42
46
|
def builder(
|
|
43
|
-
cls: type,
|
|
47
|
+
cls: type[_T],
|
|
44
48
|
/,
|
|
45
49
|
*,
|
|
46
50
|
gatherer: Callable[[type], dict[str, Field]],
|
|
47
51
|
methods: frozenset[MethodMaker] | set[MethodMaker]
|
|
48
|
-
) ->
|
|
52
|
+
) -> type[_T]: ...
|
|
49
53
|
|
|
50
54
|
@typing.overload
|
|
51
55
|
def builder(
|
|
@@ -54,35 +58,30 @@ def builder(
|
|
|
54
58
|
*,
|
|
55
59
|
gatherer: Callable[[type], dict[str, Field]],
|
|
56
60
|
methods: frozenset[MethodMaker] | set[MethodMaker]
|
|
57
|
-
) -> Callable[[type], type]: ...
|
|
58
|
-
|
|
61
|
+
) -> Callable[[type[_T]], type[_T]]: ...
|
|
59
62
|
|
|
60
|
-
_Self = typing.TypeVar("_Self", bound="Field")
|
|
61
63
|
|
|
62
64
|
class Field:
|
|
63
65
|
default: _NothingType | typing.Any
|
|
64
66
|
default_factory: _NothingType | typing.Any
|
|
65
|
-
type: _NothingType |
|
|
67
|
+
type: _NothingType | _py_type
|
|
66
68
|
doc: None | str
|
|
67
69
|
|
|
70
|
+
__classbuilder_internals__: dict
|
|
71
|
+
|
|
68
72
|
def __init__(
|
|
69
73
|
self,
|
|
70
74
|
*,
|
|
71
75
|
default: _NothingType | typing.Any = NOTHING,
|
|
72
76
|
default_factory: _NothingType | typing.Any = NOTHING,
|
|
73
|
-
type: _NothingType |
|
|
77
|
+
type: _NothingType | _py_type = NOTHING,
|
|
74
78
|
doc: None | str = None,
|
|
75
79
|
) -> None: ...
|
|
76
|
-
@property
|
|
77
|
-
def _inherited_slots(self) -> list[str]: ...
|
|
78
80
|
def __repr__(self) -> str: ...
|
|
79
|
-
|
|
80
|
-
def __eq__(self, other: _Self) -> bool: ...
|
|
81
|
-
@typing.overload
|
|
82
|
-
def __eq__(self, other: object) -> NotImplemented: ...
|
|
81
|
+
def __eq__(self, other: Field | object) -> bool: ...
|
|
83
82
|
def validate_field(self) -> None: ...
|
|
84
83
|
@classmethod
|
|
85
|
-
def from_field(cls, fld: Field, **kwargs: typing.Any) ->
|
|
84
|
+
def from_field(cls, fld: Field, /, **kwargs: typing.Any) -> Field: ...
|
|
86
85
|
|
|
87
86
|
|
|
88
87
|
class SlotFields(dict):
|
|
@@ -93,19 +92,20 @@ def slot_gatherer(cls: type) -> dict[str, Field]:
|
|
|
93
92
|
|
|
94
93
|
@typing.overload
|
|
95
94
|
def slotclass(
|
|
96
|
-
cls: type,
|
|
95
|
+
cls: type[_T],
|
|
97
96
|
/,
|
|
98
97
|
*,
|
|
99
98
|
methods: frozenset[MethodMaker] | set[MethodMaker] = default_methods,
|
|
100
99
|
syntax_check: bool = True
|
|
101
|
-
) ->
|
|
100
|
+
) -> type[_T]: ...
|
|
102
101
|
|
|
102
|
+
@typing.overload
|
|
103
103
|
def slotclass(
|
|
104
104
|
cls: None = None,
|
|
105
105
|
/,
|
|
106
106
|
*,
|
|
107
107
|
methods: frozenset[MethodMaker] | set[MethodMaker] = default_methods,
|
|
108
108
|
syntax_check: bool = True
|
|
109
|
-
) -> Callable[[type], type]: ...
|
|
109
|
+
) -> Callable[[type[_T]], type[_T]]: ...
|
|
110
110
|
|
|
111
|
-
def fieldclass(cls: type) ->
|
|
111
|
+
def fieldclass(cls: type[_T]) -> type[_T]: ...
|
ducktools/classbuilder/prefab.py
CHANGED
|
@@ -319,12 +319,19 @@ def get_eq_maker():
|
|
|
319
319
|
|
|
320
320
|
def get_iter_maker():
|
|
321
321
|
def __iter__(cls: "type") -> "tuple[str, dict]":
|
|
322
|
-
|
|
322
|
+
fields = get_attributes(cls)
|
|
323
323
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
324
|
+
valid_fields = (
|
|
325
|
+
name for name, attrib in fields.items()
|
|
326
|
+
if attrib.iter and not attrib.exclude_field
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
values = "\n".join(f" yield self.{name}" for name in valid_fields)
|
|
330
|
+
|
|
331
|
+
# if values is an empty string
|
|
332
|
+
if not values:
|
|
327
333
|
values = " yield from ()"
|
|
334
|
+
|
|
328
335
|
code = f"def __iter__(self):\n{values}"
|
|
329
336
|
globs = {}
|
|
330
337
|
return code, globs
|
|
@@ -366,6 +373,7 @@ def get_frozen_setattr_maker():
|
|
|
366
373
|
|
|
367
374
|
|
|
368
375
|
def get_frozen_delattr_maker():
|
|
376
|
+
# noinspection PyUnusedLocal
|
|
369
377
|
def __delattr__(cls: "type") -> "tuple[str, dict]":
|
|
370
378
|
body = (
|
|
371
379
|
' raise TypeError(\n'
|
|
@@ -415,6 +423,7 @@ class Attribute(Field):
|
|
|
415
423
|
init=True,
|
|
416
424
|
repr=True,
|
|
417
425
|
compare=True,
|
|
426
|
+
iter=True,
|
|
418
427
|
kw_only=False,
|
|
419
428
|
in_dict=True,
|
|
420
429
|
exclude_field=False,
|
|
@@ -436,6 +445,7 @@ def attribute(
|
|
|
436
445
|
init=True,
|
|
437
446
|
repr=True,
|
|
438
447
|
compare=True,
|
|
448
|
+
iter=True,
|
|
439
449
|
kw_only=False,
|
|
440
450
|
in_dict=True,
|
|
441
451
|
exclude_field=False,
|
|
@@ -443,8 +453,7 @@ def attribute(
|
|
|
443
453
|
type=NOTHING,
|
|
444
454
|
):
|
|
445
455
|
"""
|
|
446
|
-
|
|
447
|
-
for an instance attribute.
|
|
456
|
+
Get an object to define a prefab Attribute
|
|
448
457
|
|
|
449
458
|
:param default: Default value for this attribute
|
|
450
459
|
:param default_factory: 0 argument callable to give a default value
|
|
@@ -452,6 +461,7 @@ def attribute(
|
|
|
452
461
|
:param init: Include this attribute in the __init__ parameters
|
|
453
462
|
:param repr: Include this attribute in the class __repr__
|
|
454
463
|
:param compare: Include this attribute in the class __eq__
|
|
464
|
+
:param iter: Include this attribute in the class __iter__ if generated
|
|
455
465
|
:param kw_only: Make this argument keyword only in init
|
|
456
466
|
:param in_dict: Include this attribute in methods that serialise to dict
|
|
457
467
|
:param exclude_field: Exclude this field from all magic method generation
|
|
@@ -469,6 +479,7 @@ def attribute(
|
|
|
469
479
|
init=init,
|
|
470
480
|
repr=repr,
|
|
471
481
|
compare=compare,
|
|
482
|
+
iter=iter,
|
|
472
483
|
kw_only=kw_only,
|
|
473
484
|
in_dict=in_dict,
|
|
474
485
|
exclude_field=exclude_field,
|
|
@@ -706,7 +717,7 @@ def _make_prefab(
|
|
|
706
717
|
if attrib.exclude_field:
|
|
707
718
|
if name not in post_init_args:
|
|
708
719
|
raise PrefabError(
|
|
709
|
-
f"{name} is an excluded attribute but is not passed to post_init"
|
|
720
|
+
f"{name!r} is an excluded attribute but is not passed to post_init"
|
|
710
721
|
)
|
|
711
722
|
else:
|
|
712
723
|
valid_args.append(name)
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import typing
|
|
2
|
+
from typing_extensions import dataclass_transform
|
|
3
|
+
|
|
2
4
|
from collections.abc import Callable
|
|
3
5
|
|
|
4
6
|
from . import (
|
|
@@ -7,6 +9,9 @@ from . import (
|
|
|
7
9
|
builder, fieldclass, get_internals, slot_gatherer
|
|
8
10
|
)
|
|
9
11
|
|
|
12
|
+
# noinspection PyUnresolvedReferences
|
|
13
|
+
from . import _NothingType
|
|
14
|
+
|
|
10
15
|
PREFAB_FIELDS: str
|
|
11
16
|
PREFAB_INIT_FUNC: str
|
|
12
17
|
PRE_INIT_FUNC: str
|
|
@@ -56,6 +61,7 @@ class Attribute(Field):
|
|
|
56
61
|
init: bool
|
|
57
62
|
repr: bool
|
|
58
63
|
compare: bool
|
|
64
|
+
iter: bool
|
|
59
65
|
kw_only: bool
|
|
60
66
|
in_dict: bool
|
|
61
67
|
exclude_field: bool
|
|
@@ -63,34 +69,33 @@ class Attribute(Field):
|
|
|
63
69
|
def __init__(
|
|
64
70
|
self,
|
|
65
71
|
*,
|
|
66
|
-
default: typing.Any |
|
|
67
|
-
default_factory: typing.Any |
|
|
68
|
-
type: type |
|
|
72
|
+
default: typing.Any | _NothingType = NOTHING,
|
|
73
|
+
default_factory: typing.Any | _NothingType = NOTHING,
|
|
74
|
+
type: type | _NothingType = NOTHING,
|
|
69
75
|
doc: str | None = None,
|
|
70
76
|
init: bool = True,
|
|
71
77
|
repr: bool = True,
|
|
72
78
|
compare: bool = True,
|
|
79
|
+
iter: bool = True,
|
|
73
80
|
kw_only: bool = False,
|
|
74
81
|
in_dict: bool = True,
|
|
75
82
|
exclude_field: bool = False,
|
|
76
83
|
) -> None: ...
|
|
77
84
|
|
|
78
85
|
def __repr__(self) -> str: ...
|
|
79
|
-
|
|
80
|
-
def __eq__(self, other: Attribute) -> bool: ...
|
|
81
|
-
def __eq__(self, other: object) -> NotImplemented: ...
|
|
82
|
-
|
|
86
|
+
def __eq__(self, other: Attribute | object) -> bool: ...
|
|
83
87
|
def validate_field(self) -> None: ...
|
|
84
88
|
|
|
85
89
|
def attribute(
|
|
86
90
|
*,
|
|
87
|
-
default: typing.Any |
|
|
88
|
-
default_factory: typing.Any |
|
|
89
|
-
type: type |
|
|
91
|
+
default: typing.Any | _NothingType = NOTHING,
|
|
92
|
+
default_factory: typing.Any | _NothingType = NOTHING,
|
|
93
|
+
type: type | _NothingType = NOTHING,
|
|
90
94
|
doc: str | None = None,
|
|
91
95
|
init: bool = True,
|
|
92
96
|
repr: bool = True,
|
|
93
97
|
compare: bool = True,
|
|
98
|
+
iter: bool = True,
|
|
94
99
|
kw_only: bool = False,
|
|
95
100
|
in_dict: bool = True,
|
|
96
101
|
exclude_field: bool = False,
|
|
@@ -112,9 +117,14 @@ def _make_prefab(
|
|
|
112
117
|
recursive_repr: bool = False,
|
|
113
118
|
) -> type: ...
|
|
114
119
|
|
|
115
|
-
|
|
120
|
+
_T = typing.TypeVar("_T")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
# For some reason PyCharm can't see 'attribute'?!?
|
|
124
|
+
# noinspection PyUnresolvedReferences
|
|
125
|
+
@dataclass_transform(field_specifiers=(Attribute, attribute))
|
|
116
126
|
def prefab(
|
|
117
|
-
cls: type | None = None,
|
|
127
|
+
cls: type[_T] | None = None,
|
|
118
128
|
*,
|
|
119
129
|
init: bool = True,
|
|
120
130
|
repr: bool = True,
|
|
@@ -125,7 +135,7 @@ def prefab(
|
|
|
125
135
|
frozen: bool = False,
|
|
126
136
|
dict_method: bool = False,
|
|
127
137
|
recursive_repr: bool = False,
|
|
128
|
-
) -> type | Callable[[type], type]: ...
|
|
138
|
+
) -> type[_T] | Callable[[type[_T]], type[_T]]: ...
|
|
129
139
|
|
|
130
140
|
def build_prefab(
|
|
131
141
|
class_name: str,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ducktools-classbuilder
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: Toolkit for creating class boilerplate generators
|
|
5
5
|
Author: David C Ellis
|
|
6
6
|
License: MIT License
|
|
@@ -44,6 +44,7 @@ Requires-Dist: sphinx-rtd-theme ; extra == 'docs'
|
|
|
44
44
|
Provides-Extra: testing
|
|
45
45
|
Requires-Dist: pytest ; extra == 'testing'
|
|
46
46
|
Requires-Dist: pytest-cov ; extra == 'testing'
|
|
47
|
+
Requires-Dist: mypy ; extra == 'testing'
|
|
47
48
|
|
|
48
49
|
# Ducktools: Class Builder #
|
|
49
50
|
|
|
@@ -53,19 +54,74 @@ of writing... functions... that will bring back the **joy** of writing classes.
|
|
|
53
54
|
Maybe.
|
|
54
55
|
|
|
55
56
|
While `attrs` and `dataclasses` are class boilerplate generators,
|
|
56
|
-
`ducktools.classbuilder` is intended to be a
|
|
57
|
+
`ducktools.classbuilder` is intended to be a `@dataclass`-like generator.
|
|
57
58
|
The goal is to handle some of the basic functions and to allow for flexible
|
|
58
59
|
customization of both the field collection and the method generation.
|
|
59
60
|
|
|
60
61
|
`ducktools.classbuilder.prefab` includes a prebuilt implementation using these tools.
|
|
61
62
|
|
|
63
|
+
Install from PyPI with:
|
|
64
|
+
`python -m pip install ducktools-classbuilder`
|
|
65
|
+
|
|
66
|
+
## Usage: building a class decorator ##
|
|
67
|
+
|
|
68
|
+
In order to create a class decorator using `ducktools.classbuilder` there are
|
|
69
|
+
a few things you need to prepare.
|
|
70
|
+
|
|
71
|
+
1. A field gathering function to analyse the class and collect valid `Field`s.
|
|
72
|
+
* An example `slot_gatherer` is included.
|
|
73
|
+
2. Code generators that can make use of the gathered `Field`s to create magic method
|
|
74
|
+
source code.
|
|
75
|
+
* Example `init_maker`, `repr_maker` and `eq_maker` generators are included.
|
|
76
|
+
3. A function that calls the `builder` function to apply both of these steps.
|
|
77
|
+
|
|
78
|
+
A field gathering function needs to take the original class as an argument and
|
|
79
|
+
return a dictionary of `{key: Field(...)}` pairs.
|
|
80
|
+
|
|
81
|
+
> [!NOTE]
|
|
82
|
+
> The `builder` will handle inheritance so do not collect fields from parent classes.
|
|
83
|
+
|
|
84
|
+
The code generators take the class as the only argument and return a tuple
|
|
85
|
+
of method source code and globals to be provided to `exec(code, globs)` in order
|
|
86
|
+
to generate the actual method.
|
|
87
|
+
|
|
88
|
+
The provided `slot_gatherer` looks for `__slots__` being assigned a `SlotFields`
|
|
89
|
+
class[^1] where keyword arguments define the names and values for the fields.
|
|
90
|
+
|
|
91
|
+
Code generator functions need to be converted to descriptors before being used.
|
|
92
|
+
This is done using the provided `MethodMaker` descriptor class.
|
|
93
|
+
ex: `init_desc = MethodMaker("__init__", init_maker)`
|
|
94
|
+
|
|
95
|
+
These parts can then be used to make a basic class boilerplate generator by
|
|
96
|
+
providing them to the `builder` function.
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
from ducktools.classbuilder import (
|
|
100
|
+
builder,
|
|
101
|
+
slot_gatherer,
|
|
102
|
+
init_maker, eq_maker, repr_maker,
|
|
103
|
+
MethodMaker,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
init_desc = MethodMaker("__init__", init_maker)
|
|
107
|
+
repr_desc = MethodMaker("__repr__", repr_maker)
|
|
108
|
+
eq_desc = MethodMaker("__eq__", eq_maker)
|
|
109
|
+
|
|
110
|
+
def slotclass(cls):
|
|
111
|
+
return builder(cls, gatherer=slot_gatherer, methods={init_desc, repr_desc, eq_desc})
|
|
112
|
+
```
|
|
113
|
+
|
|
62
114
|
## Slot Class Usage ##
|
|
63
115
|
|
|
64
|
-
|
|
65
|
-
|
|
116
|
+
This created `slotclass` function can then be used as a decorator to generate classes in
|
|
117
|
+
a similar manner to the `@dataclass` decorator from `dataclasses`.
|
|
118
|
+
|
|
119
|
+
> [!NOTE]
|
|
120
|
+
> `ducktools.classbuilder` includes a premade version of `slotclass` that can
|
|
121
|
+
> be used directly. (The included version has some extra features).
|
|
66
122
|
|
|
67
123
|
```python
|
|
68
|
-
from ducktools.classbuilder import
|
|
124
|
+
from ducktools.classbuilder import Field, SlotFields
|
|
69
125
|
|
|
70
126
|
@slotclass
|
|
71
127
|
class SlottedDC:
|
|
@@ -81,28 +137,36 @@ ex = SlottedDC()
|
|
|
81
137
|
print(ex)
|
|
82
138
|
```
|
|
83
139
|
|
|
84
|
-
|
|
140
|
+
> [!TIP]
|
|
141
|
+
> For more information and examples of creating class generators with additional
|
|
142
|
+
> features using the builder see
|
|
143
|
+
> [the docs](https://ducktools-classbuilder.readthedocs.io/en/latest/extension_examples.html)
|
|
144
|
+
|
|
145
|
+
## Why does your example use `__slots__` instead of annotations? ##
|
|
85
146
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
`__slots__` and decorators work.
|
|
147
|
+
If you want to use `__slots__` in order to save memory you have to declare
|
|
148
|
+
them when the class is originally created as you can't add them later.
|
|
89
149
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
150
|
+
When you use `@dataclass(slots=True)`[^2] with `dataclasses` in order for
|
|
151
|
+
this to work, `dataclasses` has to make a new class and attempt to
|
|
152
|
+
copy over everything from the original.
|
|
153
|
+
This is because decorators operate on classes *after they have been created*
|
|
154
|
+
while slots need to be declared beforehand.
|
|
155
|
+
While you can change the value of `__slots__` after a class has been created,
|
|
156
|
+
this will have no effect on the internal structure of the class.
|
|
96
157
|
|
|
97
158
|
By declaring the class using `__slots__` on the other hand, we can take
|
|
98
159
|
advantage of the fact that it accepts a mapping, where the keys will be
|
|
99
160
|
used as the attributes to create as slots. The values can then be used as
|
|
100
|
-
the default values equivalently to how type hints are used in dataclasses
|
|
161
|
+
the default values equivalently to how type hints are used in `dataclasses`.
|
|
101
162
|
|
|
102
|
-
For example these two classes would be roughly equivalent, except
|
|
163
|
+
For example these two classes would be roughly equivalent, except that
|
|
103
164
|
`@dataclass` has had to recreate the class from scratch while `@slotclass`
|
|
104
|
-
has
|
|
105
|
-
|
|
165
|
+
has added the methods on to the original class.
|
|
166
|
+
This means that any references stored to the original class *before*
|
|
167
|
+
`@dataclass` has rebuilt the class will not be pointing towards the
|
|
168
|
+
correct class.
|
|
169
|
+
This can be demonstrated using a simple class register decorator.
|
|
106
170
|
|
|
107
171
|
> This example requires Python 3.10 as earlier versions of
|
|
108
172
|
> `dataclasses` did not support the `slots` argument.
|
|
@@ -140,7 +204,6 @@ print(SlotCoords())
|
|
|
140
204
|
|
|
141
205
|
print(f"{DataCoords is class_register[DataCoords.__name__] = }")
|
|
142
206
|
print(f"{SlotCoords is class_register[SlotCoords.__name__] = }")
|
|
143
|
-
|
|
144
207
|
```
|
|
145
208
|
|
|
146
209
|
## What features does this have? ##
|
|
@@ -158,9 +221,6 @@ field so they are present on the class if `help(...)` is called.
|
|
|
158
221
|
If you want something with more features you can look at the `prefab.py`
|
|
159
222
|
implementation which provides a 'prebuilt' implementation.
|
|
160
223
|
|
|
161
|
-
For more information on creating class generators using the builder
|
|
162
|
-
see [the docs](https://ducktools-classbuilder.readthedocs.io/en/latest/extension_examples.html)
|
|
163
|
-
|
|
164
224
|
## Will you add \<feature\> to `classbuilder.prefab`? ##
|
|
165
225
|
|
|
166
226
|
No. Not unless it's something I need or find interesting.
|
|
@@ -177,3 +237,9 @@ with a specific feature, you can create or add it yourself.
|
|
|
177
237
|
## Credit ##
|
|
178
238
|
|
|
179
239
|
Heavily inspired by [David Beazley's Cluegen](https://github.com/dabeaz/cluegen)
|
|
240
|
+
|
|
241
|
+
[^1]: `SlotFields` is actually just a subclassed `dict` with no changes. `__slots__`
|
|
242
|
+
works with dictionaries using the values of the keys, while fields are normally
|
|
243
|
+
used for documentation.
|
|
244
|
+
|
|
245
|
+
[^2]: or `@attrs.define`.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
ducktools/classbuilder/__init__.py,sha256=EgrlCh0ue3DkIhafEC0YCanuCiE9t47XrIsk09GOUFk,12951
|
|
2
|
+
ducktools/classbuilder/__init__.pyi,sha256=Oy2c-yBT3E7UNECjpeqmnv2VJdlgu_MfIDmdjJYXIFo,2744
|
|
3
|
+
ducktools/classbuilder/prefab.py,sha256=KPovgZte2hfOuCOCZwQ5_3v9Y7O_vk2JpQTNG6EgJbE,29675
|
|
4
|
+
ducktools/classbuilder/prefab.pyi,sha256=TIT9OxYJXPbqcAiUELaWoSNIQks4pbeQy9KWYphm1YA,3897
|
|
5
|
+
ducktools/classbuilder/py.typed,sha256=la67KBlbjXN-_-DfGNcdOcjYumVpKG_Tkw-8n5dnGB4,8
|
|
6
|
+
ducktools_classbuilder-0.1.1.dist-info/LICENSE.md,sha256=6Thz9Dbw8R4fWInl6sGl8Rj3UnKnRbDwrc6jZerpugQ,1070
|
|
7
|
+
ducktools_classbuilder-0.1.1.dist-info/METADATA,sha256=oCGG72wG2iT_hOr8ezdbbyinzBXf95WXmB9n9rul-Iw,9280
|
|
8
|
+
ducktools_classbuilder-0.1.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
9
|
+
ducktools_classbuilder-0.1.1.dist-info/top_level.txt,sha256=uSDLtio3ZFqdwcsMJ2O5yhjB4Q3ytbBWbA8rJREganc,10
|
|
10
|
+
ducktools_classbuilder-0.1.1.dist-info/RECORD,,
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
ducktools/classbuilder/__init__.py,sha256=oEdcQdMEqaQjUfbPkHpe55iCiPssDQxf4zxoAMdsTt8,12950
|
|
2
|
-
ducktools/classbuilder/__init__.pyi,sha256=BlpHmxIL4dAiVKgqElRR3wVs7aw73qyJRaSvr-Tuyc0,2770
|
|
3
|
-
ducktools/classbuilder/prefab.py,sha256=qQJzN4ys6Av6s9NaabzSqRKE5BXDfhidt3EtGZ0HAxQ,29405
|
|
4
|
-
ducktools/classbuilder/prefab.pyi,sha256=wrq8NKwy9TsQ6fpnMyTf4DaGtCOK_90NHeU61CfNOo4,3589
|
|
5
|
-
ducktools/classbuilder/py.typed,sha256=la67KBlbjXN-_-DfGNcdOcjYumVpKG_Tkw-8n5dnGB4,8
|
|
6
|
-
ducktools_classbuilder-0.1.0.dist-info/LICENSE.md,sha256=6Thz9Dbw8R4fWInl6sGl8Rj3UnKnRbDwrc6jZerpugQ,1070
|
|
7
|
-
ducktools_classbuilder-0.1.0.dist-info/METADATA,sha256=FHgeLZx-RfcvY2ty1l6O19z449So-UQZIcBinJ8n5xU,6675
|
|
8
|
-
ducktools_classbuilder-0.1.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
9
|
-
ducktools_classbuilder-0.1.0.dist-info/top_level.txt,sha256=uSDLtio3ZFqdwcsMJ2O5yhjB4Q3ytbBWbA8rJREganc,10
|
|
10
|
-
ducktools_classbuilder-0.1.0.dist-info/RECORD,,
|
{ducktools_classbuilder-0.1.0.dist-info → ducktools_classbuilder-0.1.1.dist-info}/LICENSE.md
RENAMED
|
File without changes
|
|
File without changes
|
{ducktools_classbuilder-0.1.0.dist-info → ducktools_classbuilder-0.1.1.dist-info}/top_level.txt
RENAMED
|
File without changes
|