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.
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/MANIFEST.in +1 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/PKG-INFO +2 -24
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs/index.md +4 -5
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/pyproject.toml +0 -1
- ducktools_classbuilder-0.8.1/src/ducktools/classbuilder/_version.py +2 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/src/ducktools_classbuilder.egg-info/PKG-INFO +2 -24
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/src/ducktools_classbuilder.egg-info/SOURCES.txt +1 -5
- ducktools_classbuilder-0.8.0/perf/cluegen.py +0 -127
- ducktools_classbuilder-0.8.0/perf/dataklasses.py +0 -102
- ducktools_classbuilder-0.8.0/perf/hyperfine_testmaker.py +0 -311
- ducktools_classbuilder-0.8.0/perf/perf_profile.py +0 -291
- ducktools_classbuilder-0.8.0/src/ducktools/classbuilder/_version.py +0 -2
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/.github/dependabot.yml +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/.github/workflows/auto_test.yml +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/.github/workflows/publish_to_pypi.yml +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/.github/workflows/publish_to_testpypi.yml +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/.gitignore +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/.readthedocs.yaml +0 -0
- /ducktools_classbuilder-0.8.0/LICENSE.md → /ducktools_classbuilder-0.8.1/LICENSE +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/README.md +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs/Makefile +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs/api.md +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs/approach_vs_tool.md +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs/conf.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs/extension_examples.md +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs/generated_code.md +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs/make.bat +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs/perf/performance_tests.md +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs/prefab/index.md +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs/tutorial.md +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs_code/docs_ex10_frozen_attributes.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs_code/docs_ex1_basic.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs_code/docs_ex2_register.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs_code/docs_ex3_iterable.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs_code/docs_ex5_frozen.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs_code/docs_ex7_posonly.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs_code/docs_ex8_converters.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs_code/docs_ex9_annotated.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs_code/index_example.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/docs_code/tutorial_code.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/setup.cfg +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/src/ducktools/classbuilder/__init__.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/src/ducktools/classbuilder/__init__.pyi +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/src/ducktools/classbuilder/annotations.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/src/ducktools/classbuilder/annotations.pyi +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/src/ducktools/classbuilder/prefab.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/src/ducktools/classbuilder/prefab.pyi +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/src/ducktools/classbuilder/py.typed +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/src/ducktools_classbuilder.egg-info/dependency_links.txt +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/src/ducktools_classbuilder.egg-info/requires.txt +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/src/ducktools_classbuilder.egg-info/top_level.txt +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/annotations/test_annotated.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/annotations/test_annotations_module.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/annotations/test_future_annotations.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/conftest.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/dynamic/test_compare_attrib.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/dynamic/test_construction.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/dynamic/test_frozen.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/dynamic/test_internals.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/dynamic/test_pre_post_init.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/dynamic/test_private.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/dynamic/test_slots_novalues.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/dynamic/test_slotted_class.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/dynamic/test_subclass_implementation.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/conftest.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/creation.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/creation_empty.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/dunders.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/fails/creation_2.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/fails/creation_3.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/fails/creation_5.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/fails/inheritance_1.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/fails/inheritance_2.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/funcs_prefabs.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/hint_syntax.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/inheritance.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/init_ex.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/kw_only.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/examples/repr_func.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/test_creation.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/test_dunders.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/test_funcs.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/test_hint_syntax.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/test_inheritance.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/test_init.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/test_kw_only.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/prefab/shared/test_repr.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/py312_tests/test_generic_annotations.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/py314_tests/test_forwardref_annotations.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/test_core.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/test_field_flags.py +0 -0
- {ducktools_classbuilder-0.8.0 → ducktools_classbuilder-0.8.1}/tests/test_slotmakermeta.py +0 -0
|
@@ -1,30 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ducktools-classbuilder
|
|
3
|
-
Version: 0.8.
|
|
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
|
|
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=
|
|
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
|
```
|
|
@@ -1,30 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ducktools-classbuilder
|
|
3
|
-
Version: 0.8.
|
|
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
|
|
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
|
|
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)
|