tinydantic 0.1.19__tar.gz → 0.3.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.
- tinydantic-0.3.0/PKG-INFO +96 -0
- tinydantic-0.3.0/README.md +60 -0
- tinydantic-0.3.0/pyproject.toml +236 -0
- tinydantic-0.3.0/src/tinydantic/__init__.py +38 -0
- tinydantic-0.3.0/src/tinydantic/_config.py +137 -0
- tinydantic-0.3.0/src/tinydantic/_errors.py +76 -0
- tinydantic-0.3.0/src/tinydantic/_model.py +654 -0
- {tinydantic-0.1.19/tests → tinydantic-0.3.0/src/tinydantic/tinydb}/__init__.py +1 -1
- tinydantic-0.3.0/src/tinydantic/tinydb/middlewares.py +89 -0
- tinydantic-0.3.0/src/tinydantic/tinydb/operations.py +31 -0
- tinydantic-0.3.0/src/tinydantic/tinydb/storages.py +120 -0
- tinydantic-0.1.19/.editorconfig +0 -31
- tinydantic-0.1.19/.github/workflows/ci.yaml +0 -80
- tinydantic-0.1.19/.github/workflows/docs-dev.yaml +0 -60
- tinydantic-0.1.19/.github/workflows/docs-release.yaml +0 -66
- tinydantic-0.1.19/.github/workflows/publish-python-package.yaml +0 -126
- tinydantic-0.1.19/.gitignore +0 -171
- tinydantic-0.1.19/.linkcheckerrc +0 -8
- tinydantic-0.1.19/.markdownlint.yaml +0 -13
- tinydantic-0.1.19/.pre-commit-config.yaml +0 -84
- tinydantic-0.1.19/.prettierrc.yaml +0 -22
- tinydantic-0.1.19/.vscode/extensions.json +0 -18
- tinydantic-0.1.19/.yamllint.yaml +0 -33
- tinydantic-0.1.19/LICENSE.md +0 -25
- tinydantic-0.1.19/PKG-INFO +0 -82
- tinydantic-0.1.19/README.md +0 -23
- tinydantic-0.1.19/REUSE.toml +0 -25
- tinydantic-0.1.19/docs/.pages +0 -9
- tinydantic-0.1.19/docs/css/code_select.css +0 -10
- tinydantic-0.1.19/docs/index.md +0 -9
- tinydantic-0.1.19/docs/installation.md +0 -7
- tinydantic-0.1.19/docs/reference/.pages +0 -52
- tinydantic-0.1.19/docs/reference/index.md +0 -5
- tinydantic-0.1.19/mkdocs.yaml +0 -126
- tinydantic-0.1.19/pyproject.toml +0 -290
- tinydantic-0.1.19/scripts/gen_api_docs.py +0 -161
- tinydantic-0.1.19/src/tinydantic/__init__.py +0 -17
- tinydantic-0.1.19/taplo.toml +0 -38
- tinydantic-0.1.19/tests/test_version.py +0 -30
- {tinydantic-0.1.19 → tinydantic-0.3.0}/LICENSES/Apache-2.0.txt +0 -0
- {tinydantic-0.1.19 → tinydantic-0.3.0}/LICENSES/CC-BY-4.0.txt +0 -0
- {tinydantic-0.1.19 → tinydantic-0.3.0}/LICENSES/MIT.txt +0 -0
- {tinydantic-0.1.19 → tinydantic-0.3.0}/src/tinydantic/py.typed +0 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tinydantic
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: A Pydantic-powered ODM (object-document mapper) for TinyDB
|
|
5
|
+
Keywords: odm,pydantic,tinydb
|
|
6
|
+
Author: Chris Wilson
|
|
7
|
+
Author-email: Chris Wilson <christopher.david.wilson@gmail.com>
|
|
8
|
+
License-Expression: Apache-2.0 OR MIT
|
|
9
|
+
License-File: LICENSES/Apache-2.0.txt
|
|
10
|
+
License-File: LICENSES/CC-BY-4.0.txt
|
|
11
|
+
License-File: LICENSES/MIT.txt
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Framework :: Pydantic :: 2
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
23
|
+
Classifier: Programming Language :: Python
|
|
24
|
+
Classifier: Topic :: Database
|
|
25
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
26
|
+
Classifier: Typing :: Typed
|
|
27
|
+
Requires-Dist: pyyaml>=6.0
|
|
28
|
+
Requires-Dist: pydantic[email]>=2.11
|
|
29
|
+
Requires-Dist: tinydb>=4.8
|
|
30
|
+
Requires-Python: >=3.10
|
|
31
|
+
Project-URL: Changelog, https://github.com/tinydantic/tinydantic/blob/main/CHANGELOG.md
|
|
32
|
+
Project-URL: Documentation, https://tinydantic.dev
|
|
33
|
+
Project-URL: Issues, https://github.com/tinydantic/tinydantic/issues
|
|
34
|
+
Project-URL: Source, https://github.com/tinydantic/tinydantic
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# tinydantic
|
|
38
|
+
|
|
39
|
+
<!-- overview-start -->
|
|
40
|
+
|
|
41
|
+
[](https://pypi.org/project/tinydantic) [](https://pypi.org/project/tinydantic) [](https://pydantic.dev)
|
|
42
|
+
|
|
43
|
+
`tinydantic` is a simple Python object-document mapper (ODM) for the [TinyDB](https://tinydb.readthedocs.io/en/latest/) document database. It uses [Pydantic](https://docs.pydantic.dev/latest/)—the most widely used data validation library—for document model definition and validation.
|
|
44
|
+
|
|
45
|
+
<!-- prettier-ignore-start -->
|
|
46
|
+
|
|
47
|
+
> [!NOTE]
|
|
48
|
+
> `tinydantic` is an experimental project under active development and should not be considered stable for use in a production environment.
|
|
49
|
+
>
|
|
50
|
+
> Releases follow the [SemVer versioning spec](https://semver.org):
|
|
51
|
+
>
|
|
52
|
+
> > Major version zero (0.y.z) is for initial development. Anything MAY change at any time. The public API SHOULD NOT be considered stable.
|
|
53
|
+
>
|
|
54
|
+
> Minor releases may include breaking changes until v1.0 — check the [changelog](https://github.com/tinydantic/tinydantic/blob/main/CHANGELOG.md) when upgrading.
|
|
55
|
+
>
|
|
56
|
+
> **AI Disclaimer**
|
|
57
|
+
>
|
|
58
|
+
> I started thinking about the idea for this project after discovering TinyDB a couple years ago. However, I never had the time (or a real use case) to finish the implementation. With the release of [Claude Fable 5](https://www.anthropic.com/claude/fable), it seemed like a good opportunity to revisit this idea and try to get an initial prototype of this working with the help of AI. I'm currently experimenting with the implementation generated by Claude to understand what works well and what needs further improvement.
|
|
59
|
+
>
|
|
60
|
+
> I am *not* a NoSQL database expert and it's very possible that there are better implementations and/or interfaces than what Claude has produced in this initial prototype. If you find things that are broken, awkward, missing, or don't follow best practices, please feel free to open an issue or a PR to suggest changes. Feedback is welcome!
|
|
61
|
+
|
|
62
|
+
<!-- prettier-ignore-end -->
|
|
63
|
+
|
|
64
|
+
<!-- overview-end -->
|
|
65
|
+
|
|
66
|
+
Full documentation — including a [quickstart](https://tinydantic.dev/latest/usage/quickstart/) that walks through a complete create-read-update-delete example — is available at [tinydantic.dev](https://tinydantic.dev).
|
|
67
|
+
|
|
68
|
+
## Table of Contents
|
|
69
|
+
|
|
70
|
+
- [Installation](#installation)
|
|
71
|
+
- [Introduction](#introduction)
|
|
72
|
+
- [License](#license)
|
|
73
|
+
|
|
74
|
+
## Introduction
|
|
75
|
+
|
|
76
|
+
<!-- introduction-start -->
|
|
77
|
+
|
|
78
|
+
`tinydantic` is a wrapper library around [TinyDB](https://tinydb.readthedocs.io/en/latest/) that enables using [Pydantic](https://docs.pydantic.dev/) models to manage documents stored in a TinyDB database. By specifying Python type annotations for each field in a document model, data validation is handled automatically by Pydantic when instantiating a model from the database.
|
|
79
|
+
|
|
80
|
+
<!-- introduction-end -->
|
|
81
|
+
|
|
82
|
+
## Installation
|
|
83
|
+
|
|
84
|
+
<!-- installation-start -->
|
|
85
|
+
|
|
86
|
+
`tinydantic` is [available on PyPI](https://pypi.org/project/tinydantic/) and can be installed with [pip](https://github.com/pypa/pip). Easy peasy lemon squeezy 🍋
|
|
87
|
+
|
|
88
|
+
```sh
|
|
89
|
+
pip install tinydantic
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
<!-- installation-end -->
|
|
93
|
+
|
|
94
|
+
## License
|
|
95
|
+
|
|
96
|
+
See the [LICENSE](https://github.com/tinydantic/tinydantic/blob/main/LICENSE.md) file for copyright & license information.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# tinydantic
|
|
2
|
+
|
|
3
|
+
<!-- overview-start -->
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/tinydantic) [](https://pypi.org/project/tinydantic) [](https://pydantic.dev)
|
|
6
|
+
|
|
7
|
+
`tinydantic` is a simple Python object-document mapper (ODM) for the [TinyDB](https://tinydb.readthedocs.io/en/latest/) document database. It uses [Pydantic](https://docs.pydantic.dev/latest/)—the most widely used data validation library—for document model definition and validation.
|
|
8
|
+
|
|
9
|
+
<!-- prettier-ignore-start -->
|
|
10
|
+
|
|
11
|
+
> [!NOTE]
|
|
12
|
+
> `tinydantic` is an experimental project under active development and should not be considered stable for use in a production environment.
|
|
13
|
+
>
|
|
14
|
+
> Releases follow the [SemVer versioning spec](https://semver.org):
|
|
15
|
+
>
|
|
16
|
+
> > Major version zero (0.y.z) is for initial development. Anything MAY change at any time. The public API SHOULD NOT be considered stable.
|
|
17
|
+
>
|
|
18
|
+
> Minor releases may include breaking changes until v1.0 — check the [changelog](https://github.com/tinydantic/tinydantic/blob/main/CHANGELOG.md) when upgrading.
|
|
19
|
+
>
|
|
20
|
+
> **AI Disclaimer**
|
|
21
|
+
>
|
|
22
|
+
> I started thinking about the idea for this project after discovering TinyDB a couple years ago. However, I never had the time (or a real use case) to finish the implementation. With the release of [Claude Fable 5](https://www.anthropic.com/claude/fable), it seemed like a good opportunity to revisit this idea and try to get an initial prototype of this working with the help of AI. I'm currently experimenting with the implementation generated by Claude to understand what works well and what needs further improvement.
|
|
23
|
+
>
|
|
24
|
+
> I am *not* a NoSQL database expert and it's very possible that there are better implementations and/or interfaces than what Claude has produced in this initial prototype. If you find things that are broken, awkward, missing, or don't follow best practices, please feel free to open an issue or a PR to suggest changes. Feedback is welcome!
|
|
25
|
+
|
|
26
|
+
<!-- prettier-ignore-end -->
|
|
27
|
+
|
|
28
|
+
<!-- overview-end -->
|
|
29
|
+
|
|
30
|
+
Full documentation — including a [quickstart](https://tinydantic.dev/latest/usage/quickstart/) that walks through a complete create-read-update-delete example — is available at [tinydantic.dev](https://tinydantic.dev).
|
|
31
|
+
|
|
32
|
+
## Table of Contents
|
|
33
|
+
|
|
34
|
+
- [Installation](#installation)
|
|
35
|
+
- [Introduction](#introduction)
|
|
36
|
+
- [License](#license)
|
|
37
|
+
|
|
38
|
+
## Introduction
|
|
39
|
+
|
|
40
|
+
<!-- introduction-start -->
|
|
41
|
+
|
|
42
|
+
`tinydantic` is a wrapper library around [TinyDB](https://tinydb.readthedocs.io/en/latest/) that enables using [Pydantic](https://docs.pydantic.dev/) models to manage documents stored in a TinyDB database. By specifying Python type annotations for each field in a document model, data validation is handled automatically by Pydantic when instantiating a model from the database.
|
|
43
|
+
|
|
44
|
+
<!-- introduction-end -->
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
<!-- installation-start -->
|
|
49
|
+
|
|
50
|
+
`tinydantic` is [available on PyPI](https://pypi.org/project/tinydantic/) and can be installed with [pip](https://github.com/pypa/pip). Easy peasy lemon squeezy 🍋
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
pip install tinydantic
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
<!-- installation-end -->
|
|
57
|
+
|
|
58
|
+
## License
|
|
59
|
+
|
|
60
|
+
See the [LICENSE](https://github.com/tinydantic/tinydantic/blob/main/LICENSE.md) file for copyright & license information.
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Chris Wilson <christopher.david.wilson@gmail.com>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
4
|
+
|
|
5
|
+
#:schema https://json.schemastore.org/pyproject.json
|
|
6
|
+
|
|
7
|
+
[build-system]
|
|
8
|
+
build-backend = "uv_build"
|
|
9
|
+
requires = [
|
|
10
|
+
"uv_build>=0.11,<0.12",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
[project]
|
|
14
|
+
authors = [
|
|
15
|
+
{ name = "Chris Wilson", email = "christopher.david.wilson@gmail.com" },
|
|
16
|
+
]
|
|
17
|
+
# https://pypi.org/classifiers/
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Development Status :: 3 - Alpha",
|
|
20
|
+
"Framework :: Pydantic :: 2",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"Operating System :: OS Independent",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.10",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"Programming Language :: Python :: 3.13",
|
|
28
|
+
"Programming Language :: Python :: 3.14",
|
|
29
|
+
"Programming Language :: Python :: Implementation :: CPython",
|
|
30
|
+
"Programming Language :: Python",
|
|
31
|
+
"Topic :: Database",
|
|
32
|
+
"Topic :: Software Development :: Libraries",
|
|
33
|
+
"Typing :: Typed",
|
|
34
|
+
]
|
|
35
|
+
dependencies = [
|
|
36
|
+
"PyYAML>=6.0",
|
|
37
|
+
"pydantic[email]>=2.11",
|
|
38
|
+
"tinydb>=4.8",
|
|
39
|
+
]
|
|
40
|
+
description = "A Pydantic-powered ODM (object-document mapper) for TinyDB"
|
|
41
|
+
keywords = [
|
|
42
|
+
"odm",
|
|
43
|
+
"pydantic",
|
|
44
|
+
"tinydb",
|
|
45
|
+
]
|
|
46
|
+
license = "Apache-2.0 OR MIT"
|
|
47
|
+
license-files = [
|
|
48
|
+
"LICENSES/*",
|
|
49
|
+
]
|
|
50
|
+
name = "tinydantic"
|
|
51
|
+
readme = "README.md"
|
|
52
|
+
requires-python = ">=3.10"
|
|
53
|
+
version = "0.3.0"
|
|
54
|
+
|
|
55
|
+
[project.urls]
|
|
56
|
+
Changelog = "https://github.com/tinydantic/tinydantic/blob/main/CHANGELOG.md"
|
|
57
|
+
Documentation = "https://tinydantic.dev"
|
|
58
|
+
Issues = "https://github.com/tinydantic/tinydantic/issues"
|
|
59
|
+
Source = "https://github.com/tinydantic/tinydantic"
|
|
60
|
+
|
|
61
|
+
[dependency-groups]
|
|
62
|
+
dev = [
|
|
63
|
+
"check-jsonschema",
|
|
64
|
+
"commitizen",
|
|
65
|
+
"editorconfig-checker",
|
|
66
|
+
"interrogate",
|
|
67
|
+
"jsonschema[format]",
|
|
68
|
+
"poethepoet",
|
|
69
|
+
"pre-commit",
|
|
70
|
+
"pre-commit-ci-config",
|
|
71
|
+
"pyclean",
|
|
72
|
+
"reuse>=5.0.0",
|
|
73
|
+
{ include-group = "docs" },
|
|
74
|
+
{ include-group = "lint" },
|
|
75
|
+
{ include-group = "test" },
|
|
76
|
+
{ include-group = "types" },
|
|
77
|
+
]
|
|
78
|
+
docs = [
|
|
79
|
+
# black is used by separate_signature option in mkdocstrings-python
|
|
80
|
+
"black",
|
|
81
|
+
"fastapi",
|
|
82
|
+
"griffe",
|
|
83
|
+
"httpx2",
|
|
84
|
+
"linkchecker",
|
|
85
|
+
"markdown-callouts",
|
|
86
|
+
"markdown-gfm-admonition",
|
|
87
|
+
"mdx_truly_sane_lists",
|
|
88
|
+
"mike",
|
|
89
|
+
"mkdocs-awesome-pages-plugin",
|
|
90
|
+
"mkdocs-gen-files",
|
|
91
|
+
"mkdocs-github-admonitions-plugin",
|
|
92
|
+
"mkdocs-include-markdown-plugin",
|
|
93
|
+
"mkdocs-materialx",
|
|
94
|
+
"mkdocstrings",
|
|
95
|
+
"mkdocstrings-python",
|
|
96
|
+
"poethepoet",
|
|
97
|
+
# properdocs is the docs build engine. mike >=2.2.0 auto-detects it and
|
|
98
|
+
# runs properdocs for versioned deploys too. The mkdocs package remains
|
|
99
|
+
# installed only as a transitive dependency (mkdocs-materialx pins it for
|
|
100
|
+
# import compatibility).
|
|
101
|
+
"properdocs",
|
|
102
|
+
]
|
|
103
|
+
lint = [
|
|
104
|
+
"poethepoet",
|
|
105
|
+
"ruff>=0.14",
|
|
106
|
+
]
|
|
107
|
+
test = [
|
|
108
|
+
"fastapi",
|
|
109
|
+
"httpx2",
|
|
110
|
+
"packaging",
|
|
111
|
+
"poethepoet",
|
|
112
|
+
"pytest-cov",
|
|
113
|
+
"pytest-randomly",
|
|
114
|
+
"pytest-rerunfailures",
|
|
115
|
+
"pytest-xdist",
|
|
116
|
+
"pytest>=8",
|
|
117
|
+
]
|
|
118
|
+
types = [
|
|
119
|
+
"mypy>=1.14",
|
|
120
|
+
"packaging",
|
|
121
|
+
"types-PyYAML",
|
|
122
|
+
]
|
|
123
|
+
|
|
124
|
+
[tool.coverage.run]
|
|
125
|
+
branch = true
|
|
126
|
+
parallel = true
|
|
127
|
+
source_pkgs = [
|
|
128
|
+
"tests",
|
|
129
|
+
"tinydantic",
|
|
130
|
+
]
|
|
131
|
+
|
|
132
|
+
[tool.coverage.paths]
|
|
133
|
+
tests = [
|
|
134
|
+
"*/tinydantic/tests",
|
|
135
|
+
"tests",
|
|
136
|
+
]
|
|
137
|
+
tinydantic = [
|
|
138
|
+
"*/tinydantic/src/tinydantic",
|
|
139
|
+
"src/tinydantic",
|
|
140
|
+
]
|
|
141
|
+
|
|
142
|
+
[tool.coverage.report]
|
|
143
|
+
exclude_lines = [
|
|
144
|
+
"if TYPE_CHECKING:",
|
|
145
|
+
"if __name__ == .__main__.:",
|
|
146
|
+
"no cov",
|
|
147
|
+
]
|
|
148
|
+
|
|
149
|
+
[tool.commitizen]
|
|
150
|
+
annotated_tag = true
|
|
151
|
+
major_version_zero = true
|
|
152
|
+
name = "cz_conventional_commits"
|
|
153
|
+
tag_format = "v$version"
|
|
154
|
+
version_provider = "pep621"
|
|
155
|
+
version_scheme = "semver2"
|
|
156
|
+
|
|
157
|
+
[tool.poe.tasks]
|
|
158
|
+
clean = "pyclean . --debris --verbose"
|
|
159
|
+
fmt = [
|
|
160
|
+
"_ruff-fix",
|
|
161
|
+
"_ruff-format",
|
|
162
|
+
]
|
|
163
|
+
lint = [
|
|
164
|
+
"_ruff-check",
|
|
165
|
+
"_ruff-format-check",
|
|
166
|
+
]
|
|
167
|
+
pre-commit = "pre-commit run --all-files --verbose"
|
|
168
|
+
sbom = "reuse spdx -o reuse.spdx"
|
|
169
|
+
sbom-check = "reuse lint"
|
|
170
|
+
spell-check = "npx cspell --gitignore --dot ."
|
|
171
|
+
test = "pytest"
|
|
172
|
+
test-cov = "pytest --cov --cov-branch"
|
|
173
|
+
types = "mypy --install-types --non-interactive src/tinydantic tests"
|
|
174
|
+
update-project-words = { shell = "npx cspell --gitignore --dot --words-only --unique . | sort --ignore-case >> project-words.txt" }
|
|
175
|
+
|
|
176
|
+
_ruff-check = "ruff check ."
|
|
177
|
+
_ruff-fix = "ruff check --fix ."
|
|
178
|
+
_ruff-format = "ruff format ."
|
|
179
|
+
_ruff-format-check = "ruff format --check ."
|
|
180
|
+
|
|
181
|
+
check = [
|
|
182
|
+
"lint",
|
|
183
|
+
"sbom-check",
|
|
184
|
+
"spell-check",
|
|
185
|
+
"types",
|
|
186
|
+
]
|
|
187
|
+
|
|
188
|
+
[tool.poe.tasks.docs-build]
|
|
189
|
+
cmd = "properdocs build --config-file mkdocs.yaml --clean --strict"
|
|
190
|
+
env = { PYTHONUNBUFFERED = "1", SOURCE_DATE_EPOCH = "1580601600" }
|
|
191
|
+
|
|
192
|
+
[tool.poe.tasks.docs-build-check]
|
|
193
|
+
sequence = [
|
|
194
|
+
{ ref = "docs-build --no-directory-urls" },
|
|
195
|
+
{ ref = "docs-check" },
|
|
196
|
+
]
|
|
197
|
+
|
|
198
|
+
[tool.poe.tasks.docs-check]
|
|
199
|
+
cmd = "linkchecker --config .linkcheckerrc site"
|
|
200
|
+
|
|
201
|
+
[tool.poe.tasks.docs-ci-publish]
|
|
202
|
+
cmd = "mike deploy --config-file mkdocs.yaml --update-aliases --push"
|
|
203
|
+
env = { PYTHONUNBUFFERED = "1", SOURCE_DATE_EPOCH = "1580601600" }
|
|
204
|
+
|
|
205
|
+
[tool.poe.tasks.docs-serve]
|
|
206
|
+
cmd = "properdocs serve --config-file mkdocs.yaml --dev-addr localhost:8000"
|
|
207
|
+
env = { PYTHONUNBUFFERED = "1" }
|
|
208
|
+
|
|
209
|
+
[tool.interrogate]
|
|
210
|
+
fail-under = 100
|
|
211
|
+
ignore-overloaded-functions = true
|
|
212
|
+
ignore-private = true
|
|
213
|
+
ignore-semiprivate = true
|
|
214
|
+
omit-covered-files = true
|
|
215
|
+
style = "google"
|
|
216
|
+
verbose = 2
|
|
217
|
+
|
|
218
|
+
[tool.mypy]
|
|
219
|
+
python_version = "3.10"
|
|
220
|
+
|
|
221
|
+
[tool.pytest.ini_options]
|
|
222
|
+
addopts = "--doctest-modules --doctest-glob '*.md' --ignore=docs/superpowers"
|
|
223
|
+
doctest_optionflags = "NORMALIZE_WHITESPACE IGNORE_EXCEPTION_DETAIL"
|
|
224
|
+
filterwarnings = [
|
|
225
|
+
# The queries.md "shadowing" section deliberately defines a field named
|
|
226
|
+
# `search` to demonstrate the pitfall, so pydantic's warning is expected.
|
|
227
|
+
'ignore:Field name "search" in "Command" shadows an attribute in parent "TinydanticModel":UserWarning',
|
|
228
|
+
]
|
|
229
|
+
minversion = "6.0"
|
|
230
|
+
testpaths = [
|
|
231
|
+
"CONTRIBUTING.md",
|
|
232
|
+
"README.md",
|
|
233
|
+
"docs",
|
|
234
|
+
"src",
|
|
235
|
+
"tests",
|
|
236
|
+
]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Chris Wilson <christopher.david.wilson@gmail.com>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
4
|
+
|
|
5
|
+
"""`tinydantic` is a simple Python object-document mapper (ODM) for the
|
|
6
|
+
[TinyDB](https://tinydb.readthedocs.io/en/latest/) document database.
|
|
7
|
+
|
|
8
|
+
Attributes:
|
|
9
|
+
__version__: The `tinydantic` package version.
|
|
10
|
+
""" # noqa: D205
|
|
11
|
+
|
|
12
|
+
from importlib import metadata
|
|
13
|
+
|
|
14
|
+
from tinydantic._config import TinydanticConfig
|
|
15
|
+
from tinydantic._errors import (
|
|
16
|
+
AmbiguousConfigError,
|
|
17
|
+
DatabaseNotBoundError,
|
|
18
|
+
DocumentIDRequiredError,
|
|
19
|
+
DocumentNotFoundError,
|
|
20
|
+
TinydanticError,
|
|
21
|
+
TinydanticUserError,
|
|
22
|
+
)
|
|
23
|
+
from tinydantic._model import TinydanticModel, q
|
|
24
|
+
|
|
25
|
+
__version__: str = metadata.version("tinydantic")
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"AmbiguousConfigError",
|
|
29
|
+
"DatabaseNotBoundError",
|
|
30
|
+
"DocumentIDRequiredError",
|
|
31
|
+
"DocumentNotFoundError",
|
|
32
|
+
"TinydanticConfig",
|
|
33
|
+
"TinydanticError",
|
|
34
|
+
"TinydanticModel",
|
|
35
|
+
"TinydanticUserError",
|
|
36
|
+
"__version__",
|
|
37
|
+
"q",
|
|
38
|
+
]
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Chris Wilson <christopher.david.wilson@gmail.com>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
4
|
+
|
|
5
|
+
"""Configuration machinery for tinydantic models.
|
|
6
|
+
|
|
7
|
+
Design note — why tinydantic config does NOT live in ``model_config``:
|
|
8
|
+
|
|
9
|
+
Pydantic merges ``model_config`` across multiple base classes in
|
|
10
|
+
"last wins" order — the *opposite* of Python's MRO (see
|
|
11
|
+
https://github.com/pydantic/pydantic/issues/9992). Community fixes
|
|
12
|
+
were rejected as breaking changes, and the behavior may change in
|
|
13
|
+
pydantic v3. Storing tinydantic's keys there would (a) inherit those
|
|
14
|
+
surprising semantics, (b) risk future key collisions with
|
|
15
|
+
``ConfigDict``, and (c) couple binding resolution to behavior pydantic
|
|
16
|
+
itself may flip.
|
|
17
|
+
|
|
18
|
+
Instead, every model class stores only the config keys *explicitly set
|
|
19
|
+
on it* in its own ``__tinydantic_config__`` class attribute, and
|
|
20
|
+
lookup walks ``cls.__mro__`` for the first class that provides the
|
|
21
|
+
key — standard Python attribute semantics. For the one scenario where
|
|
22
|
+
pydantic's ordering and Python's ordering could disagree (two
|
|
23
|
+
*unrelated* bases providing conflicting values), tinydantic refuses to
|
|
24
|
+
guess and raises
|
|
25
|
+
[AmbiguousConfigError][tinydantic.AmbiguousConfigError]
|
|
26
|
+
at class-definition time instead.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
from typing import TYPE_CHECKING, Any, TypedDict
|
|
32
|
+
|
|
33
|
+
from tinydantic._errors import AmbiguousConfigError
|
|
34
|
+
|
|
35
|
+
if TYPE_CHECKING:
|
|
36
|
+
from tinydb import TinyDB
|
|
37
|
+
|
|
38
|
+
# Name of the per-class attribute holding explicitly-set config.
|
|
39
|
+
CONFIG_ATTR = "__tinydantic_config__"
|
|
40
|
+
|
|
41
|
+
_CONFIG_KEYS = ("database", "table_name")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class TinydanticConfig(TypedDict, total=False):
|
|
45
|
+
"""Configuration options for tinydantic models.
|
|
46
|
+
|
|
47
|
+
This is a plain [TypedDict][typing.TypedDict] — deliberately NOT a
|
|
48
|
+
``pydantic.ConfigDict`` subclass; see the module docstring for the
|
|
49
|
+
design rationale. Values are provided as class keyword arguments:
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
class User(TinydanticModel, database=db, table_name="users"):
|
|
53
|
+
name: str
|
|
54
|
+
```
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
database: TinyDB
|
|
58
|
+
"""TinyDB database where documents of this model are stored."""
|
|
59
|
+
|
|
60
|
+
table_name: str | None
|
|
61
|
+
"""Database table name for documents of this model.
|
|
62
|
+
|
|
63
|
+
When unset (or falsy), the table name is derived from the model
|
|
64
|
+
class name converted to snake_case — for example, a model class
|
|
65
|
+
named ``AdminUser`` uses the table ``admin_user``.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def get_config_value(
|
|
70
|
+
model_class: type,
|
|
71
|
+
key: str,
|
|
72
|
+
default: Any = None,
|
|
73
|
+
) -> Any:
|
|
74
|
+
"""Resolve a tinydantic config key for a model class.
|
|
75
|
+
|
|
76
|
+
Walks ``model_class.__mro__`` and returns the value from the first
|
|
77
|
+
class whose own ``__tinydantic_config__`` contains ``key`` —
|
|
78
|
+
standard Python attribute-lookup semantics, unlike pydantic's
|
|
79
|
+
``model_config`` merge (see the module docstring).
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
model_class: The model class to resolve the key for.
|
|
83
|
+
key: The [TinydanticConfig][tinydantic.TinydanticConfig]
|
|
84
|
+
key to look up.
|
|
85
|
+
default: Value returned when no class in the MRO provides the
|
|
86
|
+
key.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
The resolved value, or ``default``.
|
|
90
|
+
"""
|
|
91
|
+
for klass in model_class.__mro__:
|
|
92
|
+
config = klass.__dict__.get(CONFIG_ATTR)
|
|
93
|
+
if config is not None and key in config:
|
|
94
|
+
return config[key]
|
|
95
|
+
return default
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def check_config_ambiguity(model_class: type) -> None:
|
|
99
|
+
"""Raise if unrelated bases supply conflicting config values.
|
|
100
|
+
|
|
101
|
+
For each config key not set on ``model_class`` itself, this checks
|
|
102
|
+
whether two classes in the MRO that are *not* part of one
|
|
103
|
+
inheritance chain (neither is a subclass of the other) provide
|
|
104
|
+
different values. Ordinary single-chain overrides — a subclass
|
|
105
|
+
overriding its parent — are never flagged.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
model_class: The freshly created model class to validate.
|
|
109
|
+
|
|
110
|
+
Raises:
|
|
111
|
+
AmbiguousConfigError: If a genuine multi-base conflict exists.
|
|
112
|
+
"""
|
|
113
|
+
own = model_class.__dict__.get(CONFIG_ATTR) or {}
|
|
114
|
+
for key in _CONFIG_KEYS:
|
|
115
|
+
if key in own:
|
|
116
|
+
continue
|
|
117
|
+
providers = [
|
|
118
|
+
klass
|
|
119
|
+
for klass in model_class.__mro__[1:]
|
|
120
|
+
if key in (klass.__dict__.get(CONFIG_ATTR) or {})
|
|
121
|
+
]
|
|
122
|
+
if not providers[1:]:
|
|
123
|
+
continue
|
|
124
|
+
first = providers[0]
|
|
125
|
+
first_value = first.__dict__[CONFIG_ATTR][key]
|
|
126
|
+
for other in providers[1:]:
|
|
127
|
+
if issubclass(first, other):
|
|
128
|
+
# Normal override along one inheritance chain: the
|
|
129
|
+
# earlier (more derived) class legitimately wins.
|
|
130
|
+
continue
|
|
131
|
+
if first_value != other.__dict__[CONFIG_ATTR][key]:
|
|
132
|
+
raise AmbiguousConfigError(
|
|
133
|
+
model_name=model_class.__name__,
|
|
134
|
+
key=key,
|
|
135
|
+
first=first.__name__,
|
|
136
|
+
second=other.__name__,
|
|
137
|
+
)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Chris Wilson <christopher.david.wilson@gmail.com>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
4
|
+
|
|
5
|
+
"""Errors specific to tinydantic."""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TinydanticError(Exception):
|
|
11
|
+
"""Base class for `tinydantic` errors."""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TinydanticUserError(TinydanticError):
|
|
15
|
+
"""Base class for errors caused by incorrect use of tinydantic."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class DatabaseNotBoundError(TinydanticUserError):
|
|
19
|
+
"""No database is bound to the model class.
|
|
20
|
+
|
|
21
|
+
Raised when a table operation is attempted on a model that has no
|
|
22
|
+
``database`` configured anywhere in its class hierarchy.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, model_name: str) -> None:
|
|
26
|
+
"""Initialize with the name of the unbound model class."""
|
|
27
|
+
super().__init__(
|
|
28
|
+
f"No database is bound to {model_name!r}. Pass "
|
|
29
|
+
"database=<TinyDB instance> as a class keyword when "
|
|
30
|
+
f"defining the model, or call {model_name}.bind("
|
|
31
|
+
"database=...) before using it.",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class AmbiguousConfigError(TinydanticUserError):
|
|
36
|
+
"""Conflicting tinydantic config inherited from unrelated bases.
|
|
37
|
+
|
|
38
|
+
Raised at class-definition time when two base classes that are not
|
|
39
|
+
part of one inheritance chain supply different values for the same
|
|
40
|
+
tinydantic config key and the new class does not set that key
|
|
41
|
+
itself. tinydantic refuses to guess which base should win — see
|
|
42
|
+
the ``tinydantic._config`` module docstring for the design
|
|
43
|
+
rationale.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
*,
|
|
49
|
+
model_name: str,
|
|
50
|
+
key: str,
|
|
51
|
+
first: str,
|
|
52
|
+
second: str,
|
|
53
|
+
) -> None:
|
|
54
|
+
"""Initialize with the conflicting classes and config key."""
|
|
55
|
+
super().__init__(
|
|
56
|
+
f"{model_name!r} inherits conflicting values for tinydantic "
|
|
57
|
+
f"config key {key!r} from unrelated base classes {first!r} "
|
|
58
|
+
f"and {second!r}. Set {key}= explicitly on {model_name!r} "
|
|
59
|
+
"to resolve the ambiguity.",
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class DocumentNotFoundError(TinydanticError):
|
|
64
|
+
"""Requested document is not found."""
|
|
65
|
+
|
|
66
|
+
def __init__(self) -> None:
|
|
67
|
+
"""Initializes an instance of this class."""
|
|
68
|
+
super().__init__("Document not found")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class DocumentIDRequiredError(TinydanticError):
|
|
72
|
+
"""Required document ID is missing."""
|
|
73
|
+
|
|
74
|
+
def __init__(self) -> None:
|
|
75
|
+
"""Initializes an instance of this class."""
|
|
76
|
+
super().__init__("Document ID is required")
|