ducktools-classbuilder 0.6.1__tar.gz → 0.6.3__tar.gz

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.

Files changed (71) hide show
  1. {ducktools_classbuilder-0.6.1/src/ducktools_classbuilder.egg-info → ducktools_classbuilder-0.6.3}/PKG-INFO +1 -1
  2. ducktools_classbuilder-0.6.3/docs/approach_vs_tool.md +30 -0
  3. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/src/ducktools/classbuilder/__init__.py +58 -13
  4. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/src/ducktools/classbuilder/__init__.pyi +12 -1
  5. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/src/ducktools/classbuilder/prefab.py +11 -0
  6. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/src/ducktools/classbuilder/prefab.pyi +4 -0
  7. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3/src/ducktools_classbuilder.egg-info}/PKG-INFO +1 -1
  8. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/src/ducktools_classbuilder.egg-info/SOURCES.txt +1 -0
  9. ducktools_classbuilder-0.6.3/tests/prefab/dynamic/test_private.py +24 -0
  10. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/test_core.py +31 -0
  11. ducktools_classbuilder-0.6.1/docs/approach_vs_tool.md +0 -16
  12. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/LICENSE.md +0 -0
  13. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/MANIFEST.in +0 -0
  14. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/README.md +0 -0
  15. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/docs/Makefile +0 -0
  16. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/docs/api.md +0 -0
  17. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/docs/conf.py +0 -0
  18. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/docs/extension_examples.md +0 -0
  19. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/docs/generated_code.md +0 -0
  20. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/docs/index.md +0 -0
  21. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/docs/make.bat +0 -0
  22. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/docs/perf/performance_tests.md +0 -0
  23. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/docs/prefab/index.md +0 -0
  24. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/docs/tutorial.md +0 -0
  25. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/pyproject.toml +0 -0
  26. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/setup.cfg +0 -0
  27. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/src/ducktools/classbuilder/annotations.py +0 -0
  28. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/src/ducktools/classbuilder/annotations.pyi +0 -0
  29. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/src/ducktools/classbuilder/py.typed +0 -0
  30. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/src/ducktools_classbuilder.egg-info/dependency_links.txt +0 -0
  31. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/src/ducktools_classbuilder.egg-info/requires.txt +0 -0
  32. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/src/ducktools_classbuilder.egg-info/top_level.txt +0 -0
  33. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/annotations/test_annotated.py +0 -0
  34. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/annotations/test_annotations_module.py +0 -0
  35. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/conftest.py +0 -0
  36. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/dynamic/test_compare_attrib.py +0 -0
  37. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/dynamic/test_construction.py +0 -0
  38. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/dynamic/test_internals.py +0 -0
  39. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/dynamic/test_pre_post_init.py +0 -0
  40. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/dynamic/test_slots_novalues.py +0 -0
  41. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/dynamic/test_slotted_class.py +0 -0
  42. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/dynamic/test_subclass_implementation.py +0 -0
  43. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/shared/conftest.py +0 -0
  44. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/shared/examples/creation.py +0 -0
  45. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/shared/examples/creation_empty.py +0 -0
  46. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/shared/examples/dunders.py +0 -0
  47. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/shared/examples/fails/creation_1.py +0 -0
  48. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/shared/examples/fails/creation_2.py +0 -0
  49. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/shared/examples/fails/creation_3.py +0 -0
  50. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/shared/examples/fails/creation_5.py +0 -0
  51. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/shared/examples/fails/inheritance_1.py +0 -0
  52. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/shared/examples/fails/inheritance_2.py +0 -0
  53. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/shared/examples/frozen_prefabs.py +0 -0
  54. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/shared/examples/funcs_prefabs.py +0 -0
  55. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/shared/examples/hint_syntax.py +0 -0
  56. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/shared/examples/inheritance.py +0 -0
  57. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/shared/examples/init_ex.py +0 -0
  58. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/shared/examples/kw_only.py +0 -0
  59. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/shared/examples/repr_func.py +0 -0
  60. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/shared/test_creation.py +0 -0
  61. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/shared/test_dunders.py +0 -0
  62. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/shared/test_frozen.py +0 -0
  63. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/shared/test_funcs.py +0 -0
  64. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/shared/test_hint_syntax.py +0 -0
  65. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/shared/test_inheritance.py +0 -0
  66. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/shared/test_init.py +0 -0
  67. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/shared/test_kw_only.py +0 -0
  68. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/prefab/shared/test_repr.py +0 -0
  69. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/py312_tests/test_generic_annotations.py +0 -0
  70. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/test_field_flags.py +0 -0
  71. {ducktools_classbuilder-0.6.1 → ducktools_classbuilder-0.6.3}/tests/test_slotmakermeta.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ducktools-classbuilder
3
- Version: 0.6.1
3
+ Version: 0.6.3
4
4
  Summary: Toolkit for creating class boilerplate generators
5
5
  Author: David C Ellis
6
6
  License: MIT License
@@ -0,0 +1,30 @@
1
+ # Approaches and tools #
2
+
3
+ As this module's code generation is inspired by the workings of [David Beazley's Cluegen](https://github.com/dabeaz/cluegen)
4
+ I thought it was briefly worth discussing his note on learning an approach vs using a tool.
5
+
6
+ I think that learning an approach is valuable, this module would not exist without the
7
+ example given by `cluegen`.
8
+
9
+ However, what I found was that in essentially every case where I wanted to use
10
+ these generating tools, I needed to modify them - often significantly.
11
+ It quickly became easier to just create my own tool and upload it as a package.
12
+
13
+ For example, `cluegen` has a few subtle "exercises for the reader". It needs extending
14
+ and fixing for some use-cases.
15
+ * Default values that are not builtins need to be passed as part of the globals
16
+ dict to `exec`.
17
+ * No support for mutable defaults.
18
+ * Subclass methods will be overwritten if they call a cluegen method that has not been
19
+ generated via `super().methodname(...)`
20
+ * `inspect.signature(cls)` does not work if `cls.__init__` has not already been generated.
21
+ (I think this is actually a bug in inspect).
22
+ * Need an extra filter to support things like `ClassVar`.
23
+
24
+ In the general spirit though, this module intends to provide some basic tools to help
25
+ create your own customized boilerplate generators.
26
+ The generator included in the base module is intended to be used to help 'bootstrap' a
27
+ modified generator with features that work how **you** want them to work.
28
+
29
+ The `prefab` module is the more fully featured tool that handles the additional cases *I*
30
+ needed.
@@ -34,7 +34,7 @@ import sys
34
34
 
35
35
  from .annotations import get_ns_annotations, is_classvar
36
36
 
37
- __version__ = "v0.6.1"
37
+ __version__ = "v0.6.3"
38
38
 
39
39
  # Change this name if you make heavy modifications
40
40
  INTERNALS_DICT = "__classbuilder_internals__"
@@ -158,32 +158,69 @@ class MethodMaker:
158
158
  def __repr__(self):
159
159
  return f"<MethodMaker for {self.funcname!r} method>"
160
160
 
161
- def __get__(self, obj, objtype=None):
162
- if objtype is None or issubclass(objtype, type):
163
- # Called with get(ourclass, type(ourclass))
164
- cls = obj
161
+ def __get__(self, inst, cls):
162
+ local_vars = {}
163
+
164
+ # This can be called via super().funcname(...) in which case the class
165
+ # may not be the correct one. If this is the correct class
166
+ # it should have this descriptor in the class dict under
167
+ # the correct funcname.
168
+ # Otherwise is should be found in the MRO of the class.
169
+ if cls.__dict__.get(self.funcname) is self:
170
+ gen_cls = cls
165
171
  else:
166
- # Called with get(inst | None, ourclass)
167
- cls = objtype
172
+ for c in cls.__mro__[1:]: # skip 'cls' as special cased
173
+ if c.__dict__.get(self.funcname) is self:
174
+ gen_cls = c
175
+ break
176
+ else: # pragma: no cover
177
+ # This should only be reached if called with incorrect arguments
178
+ # manually
179
+ raise AttributeError(
180
+ f"Could not find {self!r} in class {cls.__name__!r} MRO."
181
+ )
168
182
 
169
- local_vars = {}
170
- gen = self.code_generator(cls, self.funcname)
183
+ gen = self.code_generator(gen_cls, self.funcname)
171
184
  exec(gen.source_code, gen.globs, local_vars)
172
185
  method = local_vars.get(self.funcname)
173
186
 
174
187
  try:
175
- method.__qualname__ = f"{cls.__qualname__}.{self.funcname}"
188
+ method.__qualname__ = f"{gen_cls.__qualname__}.{self.funcname}"
176
189
  except AttributeError:
177
190
  # This might be a property or some other special
178
191
  # descriptor. Don't try to rename.
179
192
  pass
180
193
 
181
194
  # Replace this descriptor on the class with the generated function
182
- setattr(cls, self.funcname, method)
195
+ setattr(gen_cls, self.funcname, method)
183
196
 
184
197
  # Use 'get' to return the generated function as a bound method
185
198
  # instead of as a regular function for first usage.
186
- return method.__get__(obj, objtype)
199
+ return method.__get__(inst, cls)
200
+
201
+
202
+ class _SignatureMaker:
203
+ # 'inspect.signature' calls the `__get__` method of the `__init__` methodmaker with
204
+ # the wrong arguments.
205
+ # Instead of __get__(None, cls) or __get__(inst, type(inst))
206
+ # it uses __get__(cls, type(cls)).
207
+ #
208
+ # If this is done before `__init__` has been generated then
209
+ # help(cls) will fail along with inspect.signature(cls)
210
+ # This signature maker descriptor is placed to override __signature__ and force
211
+ # the `__init__` signature to be generated first if the signature is requested.
212
+ def __get__(self, instance, cls):
213
+ import inspect # Deferred inspect import
214
+ _ = cls.__init__ # force generation of `__init__` function
215
+ # Remove this attribute from the class
216
+ # This prevents recursion back into this __get__ method.
217
+ delattr(cls, "__signature__")
218
+ sig = inspect.signature(cls)
219
+ setattr(cls, "__signature__", sig)
220
+ return sig
221
+
222
+
223
+ signature_maker = _SignatureMaker()
187
224
 
188
225
 
189
226
  def get_init_generator(null=NOTHING, extra_code=None):
@@ -402,7 +439,7 @@ _field_init_maker = MethodMaker(
402
439
  )
403
440
 
404
441
 
405
- def builder(cls=None, /, *, gatherer, methods, flags=None):
442
+ def builder(cls=None, /, *, gatherer, methods, flags=None, fix_signature=True):
406
443
  """
407
444
  The main builder for class generation
408
445
 
@@ -413,6 +450,10 @@ def builder(cls=None, /, *, gatherer, methods, flags=None):
413
450
  :type methods: set[MethodMaker]
414
451
  :param flags: additional flags to store in the internals dictionary
415
452
  for use by method generators.
453
+ :type flags: None | dict[str, bool]
454
+ :param fix_signature: Add a __signature__ attribute to work-around an issue with
455
+ inspect.signature incorrectly handling __init__ descriptors.
456
+ :type fix_signature: bool
416
457
  :return: The modified class (the class itself is modified, but this is expected).
417
458
  """
418
459
  # Handle `None` to make wrapping with a decorator easier.
@@ -459,6 +500,10 @@ def builder(cls=None, /, *, gatherer, methods, flags=None):
459
500
 
460
501
  internals["methods"] = _MappingProxyType(internal_methods)
461
502
 
503
+ # Fix for inspect.signature(cls)
504
+ if fix_signature:
505
+ setattr(cls, "__signature__", signature_maker)
506
+
462
507
  return cls
463
508
 
464
509
 
@@ -1,6 +1,8 @@
1
1
  import types
2
2
  import typing
3
3
 
4
+ import inspect
5
+
4
6
  from collections.abc import Callable
5
7
  from types import MappingProxyType
6
8
  from typing_extensions import dataclass_transform
@@ -49,7 +51,12 @@ class MethodMaker:
49
51
  code_generator: _CodegenType
50
52
  def __init__(self, funcname: str, code_generator: _CodegenType) -> None: ...
51
53
  def __repr__(self) -> str: ...
52
- def __get__(self, instance, cls=None) -> Callable: ...
54
+ def __get__(self, instance, cls) -> Callable: ...
55
+
56
+ class _SignatureMaker:
57
+ def __get__(self, instance, cls) -> inspect.Signature: ...
58
+
59
+ signature_maker: _SignatureMaker
53
60
 
54
61
  def get_init_generator(
55
62
  null: _NothingType = NOTHING,
@@ -86,6 +93,7 @@ def builder(
86
93
  gatherer: Callable[[type], tuple[dict[str, Field], dict[str, typing.Any]]],
87
94
  methods: frozenset[MethodMaker] | set[MethodMaker],
88
95
  flags: dict[str, bool] | None = None,
96
+ fix_signature: bool = ...,
89
97
  ) -> type[_T]: ...
90
98
 
91
99
  @typing.overload
@@ -96,6 +104,7 @@ def builder(
96
104
  gatherer: Callable[[type], tuple[dict[str, Field], dict[str, typing.Any]]],
97
105
  methods: frozenset[MethodMaker] | set[MethodMaker],
98
106
  flags: dict[str, bool] | None = None,
107
+ fix_signature: bool = ...,
99
108
  ) -> Callable[[type[_T]], type[_T]]: ...
100
109
 
101
110
 
@@ -126,6 +135,7 @@ class Field(metaclass=SlotMakerMeta):
126
135
 
127
136
  __slots__: dict[str, str]
128
137
  __classbuilder_internals__: dict
138
+ __signature__: inspect.Signature
129
139
 
130
140
  def __init__(
131
141
  self,
@@ -249,6 +259,7 @@ class GatheredFields:
249
259
  modifications: dict[str, typing.Any]
250
260
 
251
261
  __classbuilder_internals__: dict
262
+ __signature__: inspect.Signature
252
263
 
253
264
  def __init__(
254
265
  self,
@@ -294,6 +294,7 @@ def attribute(
294
294
  kw_only=False,
295
295
  serialize=True,
296
296
  exclude_field=False,
297
+ private=False,
297
298
  doc=None,
298
299
  type=NOTHING,
299
300
  ):
@@ -310,6 +311,7 @@ def attribute(
310
311
  :param kw_only: Make this argument keyword only in init
311
312
  :param serialize: Include this attribute in methods that serialize to dict
312
313
  :param exclude_field: Shorthand for setting repr, compare, iter and serialize to False
314
+ :param private: Short for init, repr, compare, iter, serialize = False, must have default or factory
313
315
  :param doc: Parameter documentation for slotted classes
314
316
  :param type: Type of this attribute (for slotted classes)
315
317
 
@@ -321,6 +323,15 @@ def attribute(
321
323
  iter = False
322
324
  serialize = False
323
325
 
326
+ if private:
327
+ if default is NOTHING and default_factory is NOTHING:
328
+ raise AttributeError("Private attributes must have defaults or factories.")
329
+ init = False
330
+ repr = False
331
+ compare = False
332
+ iter = False
333
+ serialize = False
334
+
324
335
  return Attribute(
325
336
  default=default,
326
337
  default_factory=default_factory,
@@ -2,6 +2,8 @@ import typing
2
2
  from types import MappingProxyType
3
3
  from typing_extensions import dataclass_transform
4
4
 
5
+ import inspect
6
+
5
7
  from collections.abc import Callable
6
8
 
7
9
  from . import (
@@ -42,6 +44,7 @@ asdict_maker: MethodMaker
42
44
 
43
45
  class Attribute(Field):
44
46
  __slots__: dict
47
+ __signature__: inspect.Signature
45
48
 
46
49
  iter: bool
47
50
  serialize: bool
@@ -78,6 +81,7 @@ def attribute(
78
81
  kw_only: bool = False,
79
82
  serialize: bool = True,
80
83
  exclude_field: bool = False,
84
+ private: bool = False,
81
85
  ) -> Attribute: ...
82
86
 
83
87
  def prefab_gatherer(cls_or_ns: type | MappingProxyType) -> tuple[dict[str, Attribute], dict[str, typing.Any]]: ...
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ducktools-classbuilder
3
- Version: 0.6.1
3
+ Version: 0.6.3
4
4
  Summary: Toolkit for creating class boilerplate generators
5
5
  Author: David C Ellis
6
6
  License: MIT License
@@ -35,6 +35,7 @@ tests/prefab/dynamic/test_compare_attrib.py
35
35
  tests/prefab/dynamic/test_construction.py
36
36
  tests/prefab/dynamic/test_internals.py
37
37
  tests/prefab/dynamic/test_pre_post_init.py
38
+ tests/prefab/dynamic/test_private.py
38
39
  tests/prefab/dynamic/test_slots_novalues.py
39
40
  tests/prefab/dynamic/test_slotted_class.py
40
41
  tests/prefab/dynamic/test_subclass_implementation.py
@@ -0,0 +1,24 @@
1
+ from ducktools.classbuilder.prefab import Prefab, attribute, get_attributes
2
+
3
+
4
+ def test_private_attribute():
5
+ class Ex(Prefab):
6
+ _internal: "str | None" = attribute(default=None, private=True)
7
+ a: int
8
+ b: str
9
+
10
+
11
+ ex = Ex(1, "Hello")
12
+
13
+ assert ex.a == 1
14
+ assert ex.b == "Hello"
15
+
16
+ assert ex._internal is None
17
+
18
+ _internal_attrib = get_attributes(Ex)["_internal"]
19
+
20
+ assert _internal_attrib.init is False
21
+ assert _internal_attrib.repr is False
22
+ assert _internal_attrib.iter is False
23
+ assert _internal_attrib.compare is False
24
+ assert _internal_attrib.serialize is False
@@ -8,6 +8,7 @@ from ducktools.classbuilder import (
8
8
 
9
9
  builder,
10
10
  default_methods,
11
+ eq_maker,
11
12
  get_fields,
12
13
  get_flags,
13
14
  get_methods,
@@ -527,3 +528,33 @@ def test_signature():
527
528
  __slots__ = SlotFields(x=42)
528
529
 
529
530
  assert str(inspect.signature(SigClass)) == "(x=42)"
531
+
532
+
533
+ def test_subclass_method_not_overwritten():
534
+ @slotclass
535
+ class X:
536
+ __slots__ = SlotFields(x=Field())
537
+
538
+ class Y(X):
539
+ def __init__(self, x, y):
540
+ self.y = y
541
+ super().__init__(x=x)
542
+
543
+ y_init_func = Y.__init__
544
+
545
+ assert X.__dict__["__eq__"] is eq_maker
546
+
547
+ y_inst = Y(0, 1)
548
+
549
+ # super().__init__ method generated correctly
550
+ assert y_init_func is Y.__init__
551
+ assert X.__dict__["__init__"] is not init_maker
552
+ assert (y_inst.x, y_inst.y) == (0, 1)
553
+
554
+ # Would fail previously as __init__ would be overwritten
555
+ y_inst_2 = Y(0, 2)
556
+
557
+ assert y_inst == y_inst_2
558
+
559
+ assert X.__dict__["__eq__"] is not eq_maker
560
+ assert "__eq__" not in Y.__dict__
@@ -1,16 +0,0 @@
1
- # Approaches and tools #
2
-
3
- As this module's code generation is inspired by the workings of [David Beazley's Cluegen](https://github.com/dabeaz/cluegen)
4
- I thought it was briefly worth discussing his note on learning an approach vs using a tool.
5
-
6
- I think that learning an approach is valuable, this module would not exist without the
7
- example given by `cluegen`. It also wouldn't exist if I hadn't needed to extend `cluegen`
8
- for some basic features (try using `Path` default values with `cluegen`).
9
-
10
- In the general spirit though, this module intends to provide some basic tools to help
11
- build your own custom class generators.
12
- The generator included in the base module is intended to be used to help 'bootstrap' a
13
- modified generator with features that work how **you** want them to work.
14
-
15
- The `prefab` module is the more fully featured powertool *I* built with these tools.
16
- However, much like a prefabricated building, it may not be what you desire.