ducktools-classbuilder 0.8.0__tar.gz → 0.8.1__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 (92) hide show
  1. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/MANIFEST.in +1 -0
  2. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/PKG-INFO +2 -24
  3. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs/index.md +4 -5
  4. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/pyproject.toml +0 -1
  5. ducktools_classbuilder-0.8.1/src/ducktools/classbuilder/_version.py +2 -0
  6. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/src/ducktools_classbuilder.egg-info/PKG-INFO +2 -24
  7. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/src/ducktools_classbuilder.egg-info/SOURCES.txt +1 -5
  8. ducktools_classbuilder-0.8.0/perf/cluegen.py +0 -127
  9. ducktools_classbuilder-0.8.0/perf/dataklasses.py +0 -102
  10. ducktools_classbuilder-0.8.0/perf/hyperfine_testmaker.py +0 -311
  11. ducktools_classbuilder-0.8.0/perf/perf_profile.py +0 -291
  12. ducktools_classbuilder-0.8.0/src/ducktools/classbuilder/_version.py +0 -2
  13. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/.github/dependabot.yml +0 -0
  14. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/.github/workflows/auto_test.yml +0 -0
  15. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/.github/workflows/publish_to_pypi.yml +0 -0
  16. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/.github/workflows/publish_to_testpypi.yml +0 -0
  17. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/.gitignore +0 -0
  18. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/.readthedocs.yaml +0 -0
  19. /ducktools_classbuilder-0.8.0/LICENSE.md → /ducktools_classbuilder-0.8.1/LICENSE +0 -0
  20. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/README.md +0 -0
  21. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs/Makefile +0 -0
  22. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs/api.md +0 -0
  23. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs/approach_vs_tool.md +0 -0
  24. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs/conf.py +0 -0
  25. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs/extension_examples.md +0 -0
  26. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs/generated_code.md +0 -0
  27. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs/make.bat +0 -0
  28. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs/perf/performance_tests.md +0 -0
  29. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs/prefab/index.md +0 -0
  30. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs/tutorial.md +0 -0
  31. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs_code/docs_ex10_frozen_attributes.py +0 -0
  32. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs_code/docs_ex1_basic.py +0 -0
  33. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs_code/docs_ex2_register.py +0 -0
  34. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs_code/docs_ex3_iterable.py +0 -0
  35. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs_code/docs_ex5_frozen.py +0 -0
  36. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs_code/docs_ex7_posonly.py +0 -0
  37. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs_code/docs_ex8_converters.py +0 -0
  38. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs_code/docs_ex9_annotated.py +0 -0
  39. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs_code/index_example.py +0 -0
  40. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs_code/tutorial_code.py +0 -0
  41. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/setup.cfg +0 -0
  42. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/src/ducktools/classbuilder/__init__.py +0 -0
  43. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/src/ducktools/classbuilder/__init__.pyi +0 -0
  44. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/src/ducktools/classbuilder/annotations.py +0 -0
  45. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/src/ducktools/classbuilder/annotations.pyi +0 -0
  46. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/src/ducktools/classbuilder/prefab.py +0 -0
  47. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/src/ducktools/classbuilder/prefab.pyi +0 -0
  48. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/src/ducktools/classbuilder/py.typed +0 -0
  49. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/src/ducktools_classbuilder.egg-info/dependency_links.txt +0 -0
  50. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/src/ducktools_classbuilder.egg-info/requires.txt +0 -0
  51. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/src/ducktools_classbuilder.egg-info/top_level.txt +0 -0
  52. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/annotations/test_annotated.py +0 -0
  53. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/annotations/test_annotations_module.py +0 -0
  54. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/annotations/test_future_annotations.py +0 -0
  55. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/conftest.py +0 -0
  56. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/dynamic/test_compare_attrib.py +0 -0
  57. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/dynamic/test_construction.py +0 -0
  58. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/dynamic/test_frozen.py +0 -0
  59. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/dynamic/test_internals.py +0 -0
  60. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/dynamic/test_pre_post_init.py +0 -0
  61. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/dynamic/test_private.py +0 -0
  62. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/dynamic/test_slots_novalues.py +0 -0
  63. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/dynamic/test_slotted_class.py +0 -0
  64. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/dynamic/test_subclass_implementation.py +0 -0
  65. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/conftest.py +0 -0
  66. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/creation.py +0 -0
  67. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/creation_empty.py +0 -0
  68. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/dunders.py +0 -0
  69. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/fails/creation_2.py +0 -0
  70. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/fails/creation_3.py +0 -0
  71. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/fails/creation_5.py +0 -0
  72. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/fails/inheritance_1.py +0 -0
  73. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/fails/inheritance_2.py +0 -0
  74. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/funcs_prefabs.py +0 -0
  75. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/hint_syntax.py +0 -0
  76. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/inheritance.py +0 -0
  77. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/init_ex.py +0 -0
  78. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/kw_only.py +0 -0
  79. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/repr_func.py +0 -0
  80. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/test_creation.py +0 -0
  81. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/test_dunders.py +0 -0
  82. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/test_funcs.py +0 -0
  83. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/test_hint_syntax.py +0 -0
  84. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/test_inheritance.py +0 -0
  85. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/test_init.py +0 -0
  86. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/test_kw_only.py +0 -0
  87. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/test_repr.py +0 -0
  88. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/py312_tests/test_generic_annotations.py +0 -0
  89. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/py314_tests/test_forwardref_annotations.py +0 -0
  90. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/test_core.py +0 -0
  91. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/test_field_flags.py +0 -0
  92. {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/test_slotmakermeta.py +0 -0
@@ -5,5 +5,6 @@ graft docs
5
5
  prune */build
6
6
  prune */dist
7
7
  prune */.pytest_cache
8
+ prune perf
8
9
 
9
10
  global-exclude *~ *.py[cod] *.so
@@ -1,30 +1,8 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ducktools-classbuilder
3
- Version: 0.8.0
3
+ Version: 0.8.1
4
4
  Summary: Toolkit for creating class boilerplate generators
5
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
6
  Project-URL: Homepage, https://github.com/davidcellis/ducktools-classbuilder
29
7
  Classifier: Development Status :: 4 - Beta
30
8
  Classifier: Programming Language :: Python :: 3.8
@@ -37,7 +15,7 @@ Classifier: Operating System :: OS Independent
37
15
  Classifier: License :: OSI Approved :: MIT License
38
16
  Requires-Python: >=3.8
39
17
  Description-Content-Type: text/markdown
40
- License-File: LICENSE.md
18
+ License-File: LICENSE
41
19
  Provides-Extra: testing
42
20
  Requires-Dist: pytest>=8.2; extra == "testing"
43
21
  Requires-Dist: pytest-cov; extra == "testing"
@@ -77,11 +77,11 @@ is created.
77
77
 
78
78
  ```python
79
79
  from ducktools.classbuilder import (
80
- SlotMakerMeta,
81
- annotation_gatherer,
80
+ SlotMakerMeta,
82
81
  builder,
83
82
  check_argument_order,
84
83
  default_methods,
84
+ unified_gatherer,
85
85
  )
86
86
 
87
87
 
@@ -91,7 +91,7 @@ class AnnotationClass(metaclass=SlotMakerMeta):
91
91
  def __init_subclass__(
92
92
  cls,
93
93
  methods=default_methods,
94
- gatherer=annotation_gatherer,
94
+ gatherer=unified_gatherer,
95
95
  **kwargs
96
96
  ):
97
97
  # Check class dict otherwise this will always be True as this base
@@ -103,12 +103,11 @@ class AnnotationClass(metaclass=SlotMakerMeta):
103
103
  super().__init_subclass__(**kwargs)
104
104
 
105
105
 
106
-
107
106
  class AnnotatedDC(AnnotationClass):
108
107
  the_answer: int = 42
109
108
  the_question: str = "What do you get if you multiply six by nine?"
110
109
 
111
-
110
+
112
111
  ex = AnnotatedDC()
113
112
  print(ex)
114
113
  ```
@@ -25,7 +25,6 @@ classifiers = [
25
25
  "License :: OSI Approved :: MIT License",
26
26
  ]
27
27
  dynamic = ['version']
28
- license = {file = "LICENSE.md"}
29
28
 
30
29
  [project.optional-dependencies]
31
30
  testing = ["pytest>=8.2", "pytest-cov", "typing_extensions"]
@@ -0,0 +1,2 @@
1
+ __version__ = "0.8.1"
2
+ __version_tuple__ = (0, 8, 1)
@@ -1,30 +1,8 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ducktools-classbuilder
3
- Version: 0.8.0
3
+ Version: 0.8.1
4
4
  Summary: Toolkit for creating class boilerplate generators
5
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
6
  Project-URL: Homepage, https://github.com/davidcellis/ducktools-classbuilder
29
7
  Classifier: Development Status :: 4 - Beta
30
8
  Classifier: Programming Language :: Python :: 3.8
@@ -37,7 +15,7 @@ Classifier: Operating System :: OS Independent
37
15
  Classifier: License :: OSI Approved :: MIT License
38
16
  Requires-Python: >=3.8
39
17
  Description-Content-Type: text/markdown
40
- License-File: LICENSE.md
18
+ License-File: LICENSE
41
19
  Provides-Extra: testing
42
20
  Requires-Dist: pytest>=8.2; extra == "testing"
43
21
  Requires-Dist: pytest-cov; extra == "testing"
@@ -1,6 +1,6 @@
1
1
  .gitignore
2
2
  .readthedocs.yaml
3
- LICENSE.md
3
+ LICENSE
4
4
  MANIFEST.in
5
5
  README.md
6
6
  pyproject.toml
@@ -29,10 +29,6 @@ docs_code/docs_ex8_converters.py
29
29
  docs_code/docs_ex9_annotated.py
30
30
  docs_code/index_example.py
31
31
  docs_code/tutorial_code.py
32
- perf/cluegen.py
33
- perf/dataklasses.py
34
- perf/hyperfine_testmaker.py
35
- perf/perf_profile.py
36
32
  src/ducktools/classbuilder/__init__.py
37
33
  src/ducktools/classbuilder/__init__.pyi
38
34
  src/ducktools/classbuilder/_version.py
@@ -1,127 +0,0 @@
1
- # cluegen.py
2
- #
3
- # Classes generated from type clues.
4
- #
5
- # https://github.com/dabeaz/cluegen
6
- #
7
- # Author: David Beazley (@dabeaz).
8
- # http://www.dabeaz.com
9
- #
10
- # Copyright (C) 2018-2021.
11
- #
12
- # Permission is granted to use, copy, and modify this code in any
13
- # manner as long as this copyright message and disclaimer remain in
14
- # the source code. There is no warranty. Try to use the code for the
15
- # greater good.
16
-
17
- import types
18
-
19
-
20
- # Collect all type clues from a class and base classes.
21
- def all_clues(cls):
22
- clues = {}
23
- for c in reversed(cls.__mro__):
24
- clues.update(getattr(c, '__annotations__', {}))
25
- return clues
26
-
27
-
28
- # Decorator to define methods of a class as a code generator.
29
- def cluegen(func):
30
- def __get__(self, instance, cls):
31
- locs = {}
32
- code = func(cls)
33
- exec(code, locs)
34
- meth = locs[func.__name__]
35
- setattr(cls, func.__name__, meth)
36
- return meth.__get__(instance, cls)
37
-
38
- def __set_name__(self, cls, name):
39
- methods = cls.__dict__.get('_methods', list(cls._methods))
40
- if '_methods' not in cls.__dict__:
41
- cls._methods = methods
42
- cls._methods.append((name, self))
43
-
44
- return type(f'ClueGen_{func.__name__}', (), dict(__get__=__get__,
45
- __set_name__=__set_name__))()
46
-
47
-
48
- # Base class for defining data structures
49
- class DatumBase:
50
- __slots__ = ()
51
- _methods = []
52
-
53
- @classmethod
54
- def __init_subclass__(cls):
55
- submethods = []
56
- for name, val in cls._methods:
57
- if name not in cls.__dict__:
58
- setattr(cls, name, val)
59
- submethods.append((name, val))
60
- elif val is cls.__dict__[name]:
61
- submethods.append((name, val))
62
-
63
- if submethods != cls._methods:
64
- cls._methods = submethods
65
-
66
-
67
- class Datum(DatumBase):
68
- __slots__ = ()
69
-
70
- @classmethod
71
- def __init_subclass__(cls):
72
- super().__init_subclass__()
73
- cls.__match_args__ = tuple(all_clues(cls))
74
-
75
- @cluegen
76
- def __init__(cls):
77
- clues = all_clues(cls)
78
- args = ', '.join(f'{name}={getattr(cls, name)!r}'
79
- if hasattr(cls, name) and not isinstance(getattr(cls, name),
80
- types.MemberDescriptorType) else name
81
- for name in clues)
82
- body = '\n'.join(f' self.{name} = {name}'
83
- for name in clues)
84
- return f'def __init__(self, {args}):\n{body}\n'
85
-
86
- @cluegen
87
- def __repr__(cls):
88
- clues = all_clues(cls)
89
- fmt = ', '.join('%s={self.%s!r}' % (name, name) for name in clues)
90
- return 'def __repr__(self):\n' \
91
- ' return f"{type(self).__name__}(%s)"' % fmt
92
-
93
- @cluegen
94
- def __iter__(cls):
95
- clues = all_clues(cls)
96
- values = '\n'.join(f' yield self.{name}' for name in clues)
97
- return 'def __iter__(self):\n' + values
98
-
99
- @cluegen
100
- def __eq__(cls):
101
- clues = all_clues(cls)
102
- selfvals = ','.join(f'self.{name}' for name in clues)
103
- othervals = ','.join(f'other.{name}' for name in clues)
104
- return 'def __eq__(self, other):\n' \
105
- ' if self.__class__ is other.__class__:\n' \
106
- f' return ({selfvals},) == ({othervals},)\n' \
107
- ' else:\n' \
108
- ' return NotImplemented\n'
109
-
110
- @cluegen
111
- def __hash__(cls):
112
- clues = all_clues(cls)
113
- if clues:
114
- self_tuple = '(' + ','.join(f'self.{name}' for name in clues) + ',)'
115
- else:
116
- self_tuple = '()'
117
- return 'def __hash__(self):\n' \
118
- f' return hash({self_tuple})\n'
119
-
120
-
121
- # Example use
122
- if __name__ == '__main__':
123
- # Start defining classes
124
- class Coordinates(Datum):
125
- x: int
126
- y: int
127
-
@@ -1,102 +0,0 @@
1
- # dataklasses.py
2
- #
3
- # https://github.com/dabeaz/dataklasses
4
- #
5
- # Author: David Beazley (@dabeaz).
6
- # http://www.dabeaz.com
7
- #
8
- # Copyright (C) 2021-2022.
9
- #
10
- # Permission is granted to use, copy, and modify this code in any
11
- # manner as long as this copyright message and disclaimer remain in
12
- # the source code. There is no warranty. Try to use the code for the
13
- # greater good.
14
-
15
- __all__ = ['dataklass']
16
-
17
- from functools import lru_cache, reduce
18
-
19
-
20
- def codegen(func):
21
- @lru_cache
22
- def make_func_code(numfields):
23
- names = [f'_{n}' for n in range(numfields)]
24
- exec(func(names), globals(), d := {})
25
- return d.popitem()[1]
26
-
27
- return make_func_code
28
-
29
-
30
- def patch_args_and_attributes(func, fields, start=0):
31
- return type(func)(func.__code__.replace(
32
- co_names=(*func.__code__.co_names[:start], *fields),
33
- co_varnames=('self', *fields),
34
- ), func.__globals__)
35
-
36
-
37
- def patch_attributes(func, fields, start=0):
38
- return type(func)(func.__code__.replace(
39
- co_names=(*func.__code__.co_names[:start], *fields)
40
- ), func.__globals__)
41
-
42
-
43
- def all_hints(cls):
44
- return reduce(lambda x, y: getattr(y, '__annotations__', {}) | x, cls.__mro__, {})
45
-
46
-
47
- @codegen
48
- def make__init__(fields):
49
- code = 'def __init__(self, ' + ','.join(fields) + '):\n'
50
- return code + '\n'.join(f' self.{name} = {name}\n' for name in fields)
51
-
52
-
53
- @codegen
54
- def make__repr__(fields):
55
- return 'def __repr__(self):\n' \
56
- ' return f"{type(self).__name__}(' + \
57
- ', '.join('{self.' + name + '!r}' for name in fields) + ')"\n'
58
-
59
-
60
- @codegen
61
- def make__eq__(fields):
62
- selfvals = ','.join(f'self.{name}' for name in fields)
63
- othervals = ','.join(f'other.{name}' for name in fields)
64
- return 'def __eq__(self, other):\n' \
65
- ' if self.__class__ is other.__class__:\n' \
66
- f' return ({selfvals},) == ({othervals},)\n' \
67
- ' else:\n' \
68
- ' return NotImplemented\n'
69
-
70
-
71
- @codegen
72
- def make__iter__(fields):
73
- return 'def __iter__(self):\n' + '\n'.join(f' yield self.{name}' for name in fields)
74
-
75
-
76
- @codegen
77
- def make__hash__(fields):
78
- self_tuple = '(' + ','.join(f'self.{name}' for name in fields) + ',)'
79
- return 'def __hash__(self):\n' \
80
- f' return hash({self_tuple})\n'
81
-
82
-
83
- def dataklass(cls):
84
- fields = all_hints(cls)
85
- nfields = len(fields)
86
- clsdict = vars(cls)
87
- if not '__init__' in clsdict: cls.__init__ = patch_args_and_attributes(make__init__(nfields), fields)
88
- if not '__repr__' in clsdict: cls.__repr__ = patch_attributes(make__repr__(nfields), fields, 2)
89
- if not '__eq__' in clsdict: cls.__eq__ = patch_attributes(make__eq__(nfields), fields, 1)
90
- # if not '__iter__' in clsdict: cls.__iter__ = patch_attributes(make__iter__(nfields), fields)
91
- # if not '__hash__' in clsdict: cls.__hash__ = patch_attributes(make__hash__(nfields), fields, 1)
92
- cls.__match_args__ = tuple(fields)
93
- return cls
94
-
95
-
96
- # Example use
97
- if __name__ == '__main__':
98
- @dataklass
99
- class Coordinates:
100
- x: int
101
- y: int
102
-
@@ -1,311 +0,0 @@
1
- """
2
- This script makes all of the hyperfine tests used in this project
3
- """
4
- import sys
5
- from pathlib import Path
6
- from ducktools.classbuilder.prefab import prefab
7
-
8
- base_dir = Path(__file__).parent / 'hyperfine_testfiles'
9
- importer_dir = base_dir / 'hyperfine_importers'
10
- classdef_dir = importer_dir / 'class_definitions'
11
- results_dir = base_dir / 'hyperfine_results'
12
-
13
- # Template Body Files #
14
-
15
- standard_template = '''
16
- class C{n}:
17
- def __init__(self, a, b, c, d, e):
18
- self.a = a
19
- self.b = b
20
- self.c = c
21
- self.d = d
22
- self.e = e
23
-
24
- def __repr__(self):
25
- return f'C{n}({{self.a!r}}, {{self.b!r}}, {{self.c!r}}, {{self.d!r}}, {{self.e!r}})'
26
-
27
- def __eq__(self, other):
28
- if self.__class__ is other.__class__:
29
- return (self.a, self.b, self.c, self.d, self.e) == (other.a, other.b, other.c, other.d, other.e)
30
- else:
31
- return NotImplemented
32
-
33
- '''
34
-
35
- namedtuple_template = '''
36
- C{n} = namedtuple('C{n}', ['a', 'b', 'c', 'd', 'e'])
37
- '''
38
-
39
- NamedTuple_template = '''
40
- class C{n}(NamedTuple):
41
- a : int
42
- b : int
43
- c : int
44
- d : int
45
- e : int
46
- '''
47
-
48
- dataclass_template = '''
49
- @dataclass
50
- class C{n}:
51
- a: int
52
- b: int
53
- c: int
54
- d: int
55
- e: int
56
- '''
57
-
58
- attr_noslots_template = '''
59
- @define(slots=False)
60
- class C{n}:
61
- a: int
62
- b: int
63
- c: int
64
- d: int
65
- e: int
66
- '''
67
-
68
- attr_slots_template = '''
69
- @define(slots=True)
70
- class C{n}:
71
- a: int
72
- b: int
73
- c: int
74
- d: int
75
- e: int
76
- '''
77
-
78
- pydantic_template = '''
79
- class C{n}(BaseModel):
80
- a: int
81
- b: int
82
- c: int
83
- d: int
84
- e: int
85
- '''
86
-
87
- cluegen_template = '''
88
- class C{n}(Datum):
89
- a: int
90
- b: int
91
- c: int
92
- d: int
93
- e: int
94
- '''
95
-
96
- # cluegen, but same default methods as dataclasses generated
97
- cluegen_eval_template = '''
98
- class C{n}(Datum):
99
- a: int
100
- b: int
101
- c: int
102
- d: int
103
- e: int
104
-
105
- C{n}.__init__, C{n}.__repr__, C{n}.__eq__
106
- '''
107
-
108
- dataklass_template = '''
109
- @dataklass
110
- class C{n}:
111
- a: int
112
- b: int
113
- c: int
114
- d: int
115
- e: int
116
- '''
117
-
118
- dataklass_eval_template = '''
119
- @dataklass
120
- class C{n}:
121
- a: int
122
- b: int
123
- c: int
124
- d: int
125
- e: int
126
-
127
- C{n}.__init__, C{n}.__repr__, C{n}.__eq__
128
- '''
129
-
130
- slotclass_template = '''
131
- @slotclass
132
- class C{n}:
133
- __slots__ = SlotFields(
134
- a=Field(),
135
- b=Field(),
136
- c=Field(),
137
- d=Field(),
138
- e=Field(),
139
- )
140
- '''
141
-
142
- prefab_template = '''
143
- @prefab
144
- class C{n}:
145
- a: int
146
- b: int
147
- c: int
148
- d: int
149
- e: int
150
- '''
151
-
152
- prefab_attribute_template = '''
153
- @prefab
154
- class C{n}:
155
- a = attribute()
156
- b = attribute()
157
- c = attribute()
158
- d = attribute()
159
- e = attribute()
160
- '''
161
-
162
- prefab_slots_template = '''
163
- @prefab
164
- class C{n}:
165
- __slots__ = SlotFields(
166
- a=attribute(),
167
- b=attribute(),
168
- c=attribute(),
169
- d=attribute(),
170
- e=attribute(),
171
- )
172
- '''
173
-
174
- prefab_eval_template = '''
175
- @prefab
176
- class C{n}:
177
- a: int
178
- b: int
179
- c: int
180
- d: int
181
- e: int
182
-
183
- C{n}.__init__, C{n}.__repr__, C{n}.__eq__
184
- '''
185
-
186
-
187
- # Import Headings #
188
-
189
- namedtuple_header = "from collections import namedtuple"
190
- NamedTuple_header = "from typing import NamedTuple"
191
- dataclass_header = "from dataclasses import dataclass"
192
- attr_header = "from attrs import define"
193
- pydantic_header = "from pydantic import BaseModel"
194
- cluegen_header = "from cluegen import Datum"
195
- dataklass_header = "from dataklasses import dataklass"
196
- slotclass_header = "from ducktools.classbuilder import slotclass, SlotFields, Field"
197
- prefab_header = "from ducktools.classbuilder.prefab import prefab, attribute, SlotFields"
198
-
199
-
200
- def write_perf_file(outpath, count, template, setup):
201
- with open(outpath, 'w') as f:
202
- f.write(setup)
203
- for n in range(count):
204
- f.write(template.format(n=n))
205
-
206
-
207
- @prefab
208
- class TestData:
209
- import_name: str
210
- import_header: str
211
- class_template: str
212
-
213
- @property
214
- def importer_file(self):
215
- importer_name = f"{self.import_name}_timer.py"
216
- return importer_dir / importer_name
217
-
218
- @property
219
- def def_file(self):
220
- def_name = f"{self.import_name}_data.py"
221
- return classdef_dir / def_name
222
-
223
- def write_perf_importer(self):
224
- data = f"import class_definitions.{self.import_name}_data\n"
225
- self.importer_file.write_text(data)
226
-
227
- def write_classdef_file(self, count=100):
228
- with self.def_file.open(mode='w') as f:
229
- f.write(self.import_header)
230
- for n in range(count):
231
- f.write(self.class_template.format(n=n))
232
-
233
-
234
- datasets = [
235
- TestData('native_classes', '', standard_template),
236
- TestData('slotclasses', slotclass_header, slotclass_template),
237
- TestData('prefab', prefab_header, prefab_template),
238
- TestData('prefab_slots', prefab_header, prefab_slots_template),
239
- TestData('prefab_eval', prefab_header, prefab_eval_template),
240
- TestData('namedtuples', namedtuple_header, namedtuple_template),
241
- TestData('typed_namedtuples', NamedTuple_header, NamedTuple_template),
242
- TestData('dataclasses', dataclass_header, dataclass_template),
243
- TestData('attrs_noslots', attr_header, attr_noslots_template),
244
- TestData('attrs_slots', attr_header, attr_slots_template),
245
- TestData('pydantic', pydantic_header, pydantic_template),
246
- # TestData('cluegen', cluegen_header, cluegen_template),
247
- # TestData('cluegen_eval', cluegen_header, cluegen_eval_template),
248
- ]
249
-
250
-
251
- def write_tests(*, runs=100, warmup=20, includes_pass=True):
252
- base_dir.mkdir(exist_ok=True)
253
- importer_dir.mkdir(exist_ok=True)
254
- classdef_dir.mkdir(exist_ok=True)
255
- results_dir.mkdir(exist_ok=True)
256
-
257
- script_ext = "bat" if sys.platform == "win32" else "sh"
258
-
259
- for data in datasets:
260
- data.write_perf_importer()
261
- data.write_classdef_file(count=100)
262
-
263
- tests = " ".join(
264
- f'"python hyperfine_importers/{data.import_name}_timer.py"'
265
- for data in datasets
266
- )
267
-
268
- versions = (
269
- "import attrs, pydantic, ducktools.classbuilder;"
270
- "print(f'attrs {attrs.__version__}');"
271
- "print(f'pydantic {pydantic.__version__}');"
272
- "print(f'ducktools-classbuilder {ducktools.classbuilder.__version__}\\n')"
273
- )
274
-
275
- if includes_pass:
276
- tests = f'"python -c \\"pass\\"" {tests}'
277
-
278
- outpath = results_dir / 'hyperfine_result.md'
279
-
280
- zsh_script = (
281
- "python -VV\n"
282
- f"python -c \"{versions}\"\n"
283
- f"hyperfine --export-markdown {outpath} --shell=none --runs {runs} --warmup {warmup} {tests}"
284
- )
285
-
286
- shell_pth = base_dir / f"hyperfine_runner.{script_ext}"
287
- shell_pth.write_text(zsh_script)
288
-
289
- outpath = results_dir / 'hyperfine_importtimes.md'
290
-
291
- tests = (
292
- '''"python -c \\"pass\\"" '''
293
- '''"python -c \\"from ducktools.classbuilder import slotclass\\"" '''
294
- '''"python -c \\"from ducktools.classbuilder.prefab import prefab\\"" '''
295
- '''"python -c \\"from collections import namedtuple\\"" '''
296
- '''"python -c \\"from typing import NamedTuple\\"" '''
297
- '''"python -c \\"from dataclasses import dataclass\\"" '''
298
- '''"python -c \\"from attrs import define\\"" '''
299
- '''"python -c \\"from pydantic import BaseModel\\"" '''
300
- )
301
-
302
- import_script = (
303
- f"hyperfine --export-markdown {outpath} --shell=none --runs {runs} --warmup {warmup} {tests}"
304
- )
305
-
306
- shell_pth = base_dir / f'hyperfine_importtimes.{script_ext}'
307
- shell_pth.write_text(import_script)
308
-
309
-
310
- if __name__ == '__main__':
311
- write_tests(includes_pass=True, runs=30, warmup=10)