dataclassbase 0.1.0__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.
Files changed (30) hide show
  1. dataclassbase-0.1.0/.github/workflows/check_and_deploy.yml +131 -0
  2. dataclassbase-0.1.0/.gitignore +311 -0
  3. dataclassbase-0.1.0/.readthedocs.yaml +21 -0
  4. dataclassbase-0.1.0/LICENSE +7 -0
  5. dataclassbase-0.1.0/PKG-INFO +181 -0
  6. dataclassbase-0.1.0/README.md +156 -0
  7. dataclassbase-0.1.0/dataclassbase/__init__.py +1171 -0
  8. dataclassbase-0.1.0/dataclassbase/_util.py +25 -0
  9. dataclassbase-0.1.0/dataclassbase.egg-info/PKG-INFO +181 -0
  10. dataclassbase-0.1.0/dataclassbase.egg-info/SOURCES.txt +28 -0
  11. dataclassbase-0.1.0/dataclassbase.egg-info/dependency_links.txt +1 -0
  12. dataclassbase-0.1.0/dataclassbase.egg-info/requires.txt +5 -0
  13. dataclassbase-0.1.0/dataclassbase.egg-info/scm_file_list.json +25 -0
  14. dataclassbase-0.1.0/dataclassbase.egg-info/scm_version.json +8 -0
  15. dataclassbase-0.1.0/dataclassbase.egg-info/top_level.txt +1 -0
  16. dataclassbase-0.1.0/docs/Makefile +15 -0
  17. dataclassbase-0.1.0/docs/make.bat +35 -0
  18. dataclassbase-0.1.0/docs/source/conf.py +66 -0
  19. dataclassbase-0.1.0/docs/source/index.rst +9 -0
  20. dataclassbase-0.1.0/pyproject.toml +64 -0
  21. dataclassbase-0.1.0/scripts/build.sh +17 -0
  22. dataclassbase-0.1.0/scripts/doc.sh +9 -0
  23. dataclassbase-0.1.0/scripts/lint_test.sh +13 -0
  24. dataclassbase-0.1.0/setup.cfg +4 -0
  25. dataclassbase-0.1.0/tests/dataclassbase_tests/__init__.py +47 -0
  26. dataclassbase-0.1.0/tests/dataclassbase_tests/fields.py +35 -0
  27. dataclassbase-0.1.0/tests/dataclassbase_tests/test_dataclassmeta.py +229 -0
  28. dataclassbase-0.1.0/tests/dataclassbase_tests/test_field_provider.py +89 -0
  29. dataclassbase-0.1.0/tests/dataclassbase_tests/test_modifiers.py +129 -0
  30. dataclassbase-0.1.0/tests/dataclassbase_tests/test_typeconstrained_field.py +44 -0
@@ -0,0 +1,131 @@
1
+ name: Python Lint, Format, Test, Deploy
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - "**"
7
+ pull_request:
8
+ release:
9
+ types: [published]
10
+
11
+ jobs:
12
+ test:
13
+ name: Lint, Check formatting, Static Type-Check, Test
14
+ runs-on: ubuntu-latest
15
+
16
+ steps:
17
+ - name: Checkout
18
+ uses: actions/checkout@v7
19
+
20
+ - name: Setup Python
21
+ uses: actions/setup-python@v6
22
+ with:
23
+ python-version: "3.14"
24
+ cache: 'pip'
25
+
26
+ - name: Install dependencies
27
+ run: |
28
+ python -m pip install --upgrade pip
29
+ pip install ruff mypy pytest pytest-cov pytest-markdown-summary
30
+ pip install -e .
31
+
32
+ - name: Install pyproject.toml
33
+ run: pip install .
34
+
35
+ - name: Ruff lint
36
+ run: ruff check --output-format=github --target-version=py314
37
+
38
+ - name: Ruff format
39
+ run: ruff format --diff --target-version=py314
40
+
41
+ - name: MyPy
42
+ run: mypy dataclassbase
43
+
44
+ - name: Run tests
45
+ run: |
46
+ mkdir summary
47
+ pytest --cov=dataclassbase --cov-report=markdown-append:summary/coverage.md --markdown-summary-file=summary/summary.md
48
+ echo -e "# Summary\n" >> $GITHUB_STEP_SUMMARY
49
+ cat summary/summary.md >> $GITHUB_STEP_SUMMARY
50
+ echo -e "\n# Coverage\n" >> $GITHUB_STEP_SUMMARY
51
+ cat summary/coverage.md >> $GITHUB_STEP_SUMMARY
52
+
53
+ build_for_testpypi:
54
+ name: Build project for TestPyPI
55
+ runs-on: ubuntu-latest
56
+ env:
57
+ SETUPTOOLS_SCM_OVERRIDES_FOR_DATACLASSBASE: '{local_scheme = "no-local-version-strict"}'
58
+
59
+ if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')
60
+
61
+ steps:
62
+ - uses: actions/checkout@v7
63
+ with:
64
+ fetch-depth: 0
65
+
66
+ - uses: hynek/build-and-inspect-python-package@v2
67
+
68
+ publish-to-test-pypi:
69
+ name: Publish to TestPyPI
70
+ runs-on: ubuntu-latest
71
+ needs: [build_for_testpypi, test]
72
+
73
+ if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')
74
+
75
+ environment:
76
+ name: testpypi
77
+
78
+ url: https://test.pypi.org/p/dataclassbase
79
+
80
+ permissions:
81
+ id-token: write
82
+
83
+ steps:
84
+ - name: Download dist
85
+ uses: actions/download-artifact@v6
86
+ with:
87
+ name: Packages
88
+ path: dist
89
+
90
+ - name: Publish
91
+ uses: pypa/gh-action-pypi-publish@release/v1
92
+ with:
93
+ repository-url: https://test.pypi.org/legacy/
94
+
95
+ # we may build and publish to PyPI
96
+ build_for_pypi:
97
+ name: Build project for PyPI
98
+ runs-on: ubuntu-latest
99
+
100
+ if: github.event_name == 'release'
101
+
102
+ steps:
103
+ - uses: actions/checkout@v7
104
+ with:
105
+ fetch-depth: 0
106
+
107
+ - uses: hynek/build-and-inspect-python-package@v2
108
+
109
+ publish-to-pypi:
110
+ name: Publish to PyPI
111
+ runs-on: ubuntu-latest
112
+ needs: [build_for_pypi, test]
113
+
114
+ if: github.event_name == 'release'
115
+
116
+ environment:
117
+ name: pypi
118
+
119
+ url: https://pypi.org/p/dataclassbase
120
+ permissions:
121
+ id-token: write
122
+
123
+ steps:
124
+ - name: Download dist
125
+ uses: actions/download-artifact@v6
126
+ with:
127
+ name: Packages
128
+ path: dist
129
+
130
+ - name: Publish
131
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,311 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py.cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/build/doctrees
73
+ docs/source/_autogen
74
+
75
+ # PyBuilder
76
+ .pybuilder/
77
+ target/
78
+
79
+ # Jupyter Notebook
80
+ .ipynb_checkpoints
81
+
82
+ # IPython
83
+ profile_default/
84
+ ipython_config.py
85
+
86
+ # pyenv
87
+ # For a library or package, you might want to ignore these files since the code is
88
+ # intended to run in multiple environments; otherwise, check them in:
89
+ # .python-version
90
+
91
+ # pipenv
92
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
93
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
94
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
95
+ # install all needed dependencies.
96
+ # Pipfile.lock
97
+
98
+ # UV
99
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
100
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
101
+ # commonly ignored for libraries.
102
+ # uv.lock
103
+
104
+ # poetry
105
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
106
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
107
+ # commonly ignored for libraries.
108
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
109
+ # poetry.lock
110
+ # poetry.toml
111
+
112
+ # pdm
113
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
114
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
115
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
116
+ # pdm.lock
117
+ # pdm.toml
118
+ .pdm-python
119
+ .pdm-build/
120
+
121
+ # pixi
122
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
123
+ # pixi.lock
124
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
125
+ # in the .venv directory. It is recommended not to include this directory in version control.
126
+ .pixi
127
+
128
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
129
+ __pypackages__/
130
+
131
+ # Celery stuff
132
+ celerybeat-schedule
133
+ celerybeat.pid
134
+
135
+ # Redis
136
+ *.rdb
137
+ *.aof
138
+ *.pid
139
+
140
+ # RabbitMQ
141
+ mnesia/
142
+ rabbitmq/
143
+ rabbitmq-data/
144
+
145
+ # ActiveMQ
146
+ activemq-data/
147
+
148
+ # SageMath parsed files
149
+ *.sage.py
150
+
151
+ # Environments
152
+ .env
153
+ .envrc
154
+ .venv
155
+ env/
156
+ venv/
157
+ ENV/
158
+ env.bak/
159
+ venv.bak/
160
+
161
+ # Spyder project settings
162
+ .spyderproject
163
+ .spyproject
164
+
165
+ # Rope project settings
166
+ .ropeproject
167
+
168
+ # mkdocs documentation
169
+ /site
170
+
171
+ # mypy
172
+ .mypy_cache/
173
+ .dmypy.json
174
+ dmypy.json
175
+
176
+ # Pyre type checker
177
+ .pyre/
178
+
179
+ # pytype static type analyzer
180
+ .pytype/
181
+
182
+ # Cython debug symbols
183
+ cython_debug/
184
+
185
+ # PyCharm
186
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
187
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
188
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
189
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
190
+ # .idea/
191
+
192
+ # Abstra
193
+ # Abstra is an AI-powered process automation framework.
194
+ # Ignore directories containing user credentials, local state, and settings.
195
+ # Learn more at https://abstra.io/docs
196
+ .abstra/
197
+
198
+ # Visual Studio Code
199
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
200
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
201
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
202
+ # you could uncomment the following to ignore the entire vscode folder
203
+ # .vscode/
204
+ # Temporary file for partial code execution
205
+ tempCodeRunnerFile.py
206
+
207
+ # Ruff stuff:
208
+ .ruff_cache/
209
+
210
+ # PyPI configuration file
211
+ .pypirc
212
+
213
+ # Marimo
214
+ marimo/_static/
215
+ marimo/_lsp/
216
+ __marimo__/
217
+
218
+ # Streamlit
219
+ .streamlit/secrets.toml
220
+
221
+
222
+ # https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
223
+ # Covers JetBrains IDEs: IntelliJ, GoLand, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
224
+ # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
225
+
226
+ # User-specific stuff
227
+ .idea/**/workspace.xml
228
+ .idea/**/tasks.xml
229
+ .idea/**/usage.statistics.xml
230
+ .idea/**/dictionaries
231
+ .idea/**/shelf
232
+
233
+ # AWS User-specific
234
+ .idea/**/aws.xml
235
+
236
+ # Generated files
237
+ .idea/**/contentModel.xml
238
+
239
+ # Sensitive or high-churn files
240
+ .idea/**/dataSources/
241
+ .idea/**/dataSources.ids
242
+ .idea/**/dataSources.local.xml
243
+ .idea/**/sqlDataSources.xml
244
+ .idea/**/dynamic.xml
245
+ .idea/**/uiDesigner.xml
246
+ .idea/**/dbnavigator.xml
247
+
248
+ # Gradle
249
+ .idea/**/gradle.xml
250
+ .idea/**/libraries
251
+
252
+ # Gradle and Maven with auto-import
253
+ # When using Gradle or Maven with auto-import, you should exclude module files,
254
+ # since they will be recreated, and may cause churn. Uncomment if using
255
+ # auto-import.
256
+ # .idea/artifacts
257
+ # .idea/compiler.xml
258
+ # .idea/jarRepositories.xml
259
+ # .idea/modules.xml
260
+ # .idea/*.iml
261
+ # .idea/modules
262
+ # *.iml
263
+ # *.ipr
264
+
265
+ # CMake
266
+ cmake-build-*/
267
+
268
+ # Mongo Explorer plugin
269
+ .idea/**/mongoSettings.xml
270
+
271
+ # File-based project format
272
+ *.iws
273
+
274
+ # IntelliJ
275
+ out/
276
+
277
+ # mpeltonen/sbt-idea plugin
278
+ .idea_modules/
279
+
280
+ # JIRA plugin
281
+ atlassian-ide-plugin.xml
282
+
283
+ # Cursive Clojure plugin
284
+ .idea/replstate.xml
285
+
286
+ # SonarLint plugin
287
+ .idea/sonarlint/
288
+ # see https://community.sonarsource.com/t/is-the-file-idea-idea-idea-sonarlint-xml-intended-to-be-under-source-control/121119
289
+ .idea/sonarlint.xml
290
+
291
+ # Crashlytics plugin (for Android Studio and IntelliJ)
292
+ com_crashlytics_export_strings.xml
293
+ crashlytics.properties
294
+ crashlytics-build.properties
295
+ fabric.properties
296
+
297
+ # Editor-based HTTP Client
298
+ .idea/httpRequests
299
+ http-client.private.env.json
300
+
301
+ # Android studio 3.1+ serialized cache file
302
+ .idea/caches/build_file_checksums.ser
303
+
304
+ # Apifox Helper cache
305
+ .idea/.cache/.Apifox_Helper
306
+ .idea/ApifoxUploaderProjectSetting.xml
307
+
308
+ # Github Copilot persisted session migrations, see: https://github.com/microsoft/copilot-intellij-feedback/issues/712#issuecomment-3322062215
309
+ .idea/**/copilot.data.migration.*.xml
310
+
311
+ .idea
@@ -0,0 +1,21 @@
1
+ version: 2
2
+
3
+ build:
4
+ os: ubuntu-24.04
5
+ tools:
6
+ python: "3"
7
+
8
+ jobs:
9
+ pre_build:
10
+ - sphinx-apidoc -o docs/source/_autogen python_ci_base
11
+
12
+ sphinx:
13
+ configuration: docs/source/conf.py
14
+ fail_on_warning: true
15
+
16
+ python:
17
+ install:
18
+ - method: pip
19
+ path: .
20
+ extra_requirements:
21
+ - docs
@@ -0,0 +1,7 @@
1
+ (c) Copyright 2026 Josef Mayr
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,181 @@
1
+ Metadata-Version: 2.4
2
+ Name: dataclassbase
3
+ Version: 0.1.0
4
+ Summary: Implement your own type of dataclass without the usual boilerplate code.
5
+ License-Expression: MIT
6
+ Classifier: Development Status :: 4 - Beta
7
+ Classifier: Intended Audience :: Developers
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.14
10
+ Classifier: Programming Language :: Python :: 3 :: Only
11
+ Classifier: Topic :: Software Development
12
+ Classifier: Typing :: Typed
13
+ Classifier: Operating System :: Microsoft :: Windows
14
+ Classifier: Operating System :: POSIX
15
+ Classifier: Operating System :: Unix
16
+ Classifier: Operating System :: MacOS
17
+ Requires-Python: >=3.14
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Provides-Extra: docs
21
+ Requires-Dist: pydata_sphinx_theme; extra == "docs"
22
+ Requires-Dist: setuptools; extra == "docs"
23
+ Requires-Dist: setuptools_scm; extra == "docs"
24
+ Dynamic: license-file
25
+
26
+ # dataclassbase: Implement custom dataclasses
27
+
28
+ ## What it is and how to get it
29
+
30
+ **dataclassbase** is a small toolkit for providing your own dataclasses. For dataclasses, attributes are declared via
31
+ annotations, and constructors are created automatically making development fast.
32
+
33
+ However, often data in dataclasses shares common properties (e.g., data shape, etc.) or we might want to have more
34
+ control over the fields in dataclasses. This is where **dataclassbase** comes to help: You can define your own
35
+ dataclass-like behavior using `typing.dataclass_transform` or metaclasses, and control the behavior of your dataclass in
36
+ a fine-grained manner.
37
+
38
+ You can install it into your environment from PyPI via
39
+
40
+ ```shell
41
+ pip install dataclassbase
42
+ ```
43
+
44
+
45
+ ## General note
46
+
47
+ While the project is generally usable, it is not yet production-ready. It is, however, being actively developed since I
48
+ plan to use it as a base for further work.
49
+
50
+ ## License
51
+
52
+ [MIT](./LICENSE)
53
+
54
+ ## The Basics
55
+
56
+ The `dataclasses` module provides very basic dataclasses with little customization. The objective of this package is to
57
+ overcome this limitation by providing customizable dataclasses.
58
+
59
+ A possible definition for the built-in dataclasses is
60
+
61
+ ```python
62
+ from dataclasses import dataclass
63
+
64
+ @dataclass
65
+ class SomeClass:
66
+ some_field: str
67
+ some_defaulting_field: int = 1
68
+ ```
69
+
70
+ In this framework, a similar approach is possible:
71
+
72
+ ```python
73
+ from dataclassbase import dataclass
74
+
75
+ @dataclass
76
+ class SomeClass:
77
+ some_field: str
78
+ some_defaulting_field: int = 1
79
+ ```
80
+
81
+ The most obvious difference above is that we import from `dataclassbase` rather than `dataclasses`. However, looking at
82
+ the signature of `dataclassbase.dataclass`, we note that there are two notable parameters: `field_provider` and
83
+ `object_handler`. `field_provider` allows customization of the field generation and processing (if the user provides a
84
+ `dataclassbase.Field` as a default value), `object_handler` implements the logic for the initialization (`__init__`) and
85
+ attribute assignment (`__setattr__`) of objects of the generated dataclass. We therefore can hook into or override the
86
+ behavior of the dataclass.
87
+
88
+ If you omit the `field_provider` or `object_handler`, the respective default implementations provided in the
89
+ `FieldProvider` and `ObjectHandler` classes, which reproduce the default dataclass behavior, are used instead.
90
+ If you provide a callable matching the signature of `dataclassbase.Field` to `field_provider`, a `FieldProvider` is
91
+ generated. Therefore, you can directly specify the type of field to use, as long as it matches the respective signature.
92
+ You can also replace the `default`-value if it indicates a value you want to process.
93
+
94
+ Note that while it suffices to call `dataclassbase.dataclass` on a class to initialize dataclass-like behavior, it does
95
+ not truly behave like a dataclass to static type checkers since it lacks the `typing.dataclass_transform` decorator.
96
+ My reason for that choice is that I would have to fix the `field_specifiers` parameter.
97
+ (Instances of the types provided in `field_specifiers` are allowed as default values for fields since they are
98
+ assumed to be fields or to be converted into some. We do that conversion in the provided `field_provider`.)
99
+ In doing so, I would constrain proper static type checking on the fields. Therefore, I would rather let you indicate the
100
+ types yourself.
101
+ To achieve true dataclass-like behavior on a type checker, you can call `typing.dataclass_transform` yourself or rely on
102
+ a metaclass derived from `DataclassMetaBase[TField]`:
103
+
104
+ Rather than using `dataclassbase.dataclass` as above, you can define your own dataclass by deriving a metaclass from
105
+ class `DataclassMetaBase[TField]` and attaching a `typing.dataclass_transform` to it. `DataclassMetaBase` invokes
106
+ `make_dataclass` when creating the new type (`dataclass` is the decorator-variant of `make_dataclass`). It provides
107
+ the methods `_field_provider` and `_object_handler` which act as factories for the field provider and object handler for
108
+ the types using the metaclass.
109
+
110
+ ```python
111
+ from dataclassbase import DataclassMetaBase, Field, FieldProvider
112
+ from typing import dataclass_transform
113
+
114
+
115
+ class CustomField(Field):
116
+ # you can override the methods provided by `Field` to control the behavior of the field in derived classes,
117
+ # superclasses, to check the assignments, etc. You can also change the behavior of `__init__` as long as its
118
+ # signature matches the one from the base
119
+ pass
120
+
121
+
122
+ @dataclass_transform
123
+ class CustomDataclassMeta(DataclassMetaBase[Field]):
124
+ @classmethod
125
+ def _field_provider(cls) -> FieldProvider[Field]:
126
+ # we want it to return our custom field. The factory works since the signature of `CustomField.__init__` matches
127
+ # the one of `Field.__init__`
128
+ return FieldProvider(CustomField)
129
+
130
+ class VariantA(metaclass=CustomDataclassMeta):
131
+ field: int
132
+ defaulting: int = 1
133
+ ```
134
+
135
+ `dataclassbase.Field`s themselves can check whether the value assigned to them is valid. Furthermore, they can check
136
+ whether fields in derived classes are valid and, in the opposite direction, whether they are valid overrides of fields
137
+ of superclasses. As an example, you can look at the provided `dataclassbase.TypeConstrainedField` which provides
138
+ simple type checking via `isinstance`.
139
+
140
+ The default implementation is `DataclassMeta` which just uses the default values and allows any
141
+ `dataclasses.Field` as a default value for its fields. While it behaves in the same way as the built-in
142
+ `dataclasses`-dataclasses, it does however not give us any benefits over it.
143
+
144
+ ```python
145
+ from dataclassbase import DataclassMeta
146
+
147
+ class VariantB(metaclass=DataclassMeta):
148
+ field: int
149
+ defaulting: int = 1
150
+ ```
151
+
152
+ ## Fine-tuning the behavior of the dataclasses
153
+
154
+ `dataclasses.dataclass` comes with quite a lot of nice additions: We can freeze the dataclass, equality and unequality
155
+ operators get generated, a hash function may be provided, and so forth. This behavior can be imitated using the classes
156
+ derived from the `BehaviorModifier` type. For now, these are
157
+
158
+ * `Frozen`: Dataclasses cannot be assigned new values and cannot be reinitialized
159
+ * `Representable`: Adds a string representation via `__repr__` and `__str__`
160
+ * `Equatable`: Adds overloads for `__eq__` and `__ne__`, and, optionally, for `__hash__`
161
+ * `PositionalOnly`: The initializer only allows positional arguments
162
+ * `KeywordOnly`: The initializer only allows keyword arguments
163
+
164
+ Add these as `modifiers` when calling `dataclass` or `make_dataclass` or return them as `_modifiers` in metaclasses
165
+ derived from `DataclassMetaBase`.
166
+
167
+ Note that some of the behaviors are "unsafe", and it is up to you to decide whether your use-case is safe. For
168
+ example, hash codes typically change once fields of the dataclass change. Using such a class as a key is unsafe. You
169
+ thus will typically combine `Frozen` and `Equatable`. Note that `Frozen` does not affect nested dataclasses: If these
170
+ are not frozen, the dataclass itself may still be mutable. Another unsafe example is the combination of `PositionalOnly`
171
+ and `KeywordOnly` which does not make any sense, but is not caught for now.
172
+
173
+ I may add such safeguards later, but for now it is obvious that such combinations do not make any sense.
174
+
175
+ # When to use dataclassbase and not ...?
176
+
177
+ ## Pydantic
178
+
179
+ Pydantic's objective is to provide fast data validation and serialization [[1]](https://pydantic.dev/docs/validation/latest/get-started/).
180
+ dataclassbase aims to be much more abstract since it is not constrained to a single use-case such as data validation,
181
+ and thus does (hopefully) not give too strong restrictions on what you can/cannot do.