TypeDAL 3.17.3__tar.gz → 4.0.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 TypeDAL might be problematic. Click here for more details.
- {typedal-3.17.3 → typedal-4.0.1}/.github/workflows/su6.yml +3 -2
- {typedal-3.17.3 → typedal-4.0.1}/.readthedocs.yml +1 -1
- {typedal-3.17.3 → typedal-4.0.1}/CHANGELOG.md +31 -0
- {typedal-3.17.3 → typedal-4.0.1}/PKG-INFO +8 -7
- {typedal-3.17.3 → typedal-4.0.1}/pyproject.toml +14 -9
- {typedal-3.17.3 → typedal-4.0.1}/src/typedal/__about__.py +1 -1
- {typedal-3.17.3 → typedal-4.0.1}/src/typedal/__init__.py +10 -9
- {typedal-3.17.3 → typedal-4.0.1}/src/typedal/caching.py +36 -33
- {typedal-3.17.3 → typedal-4.0.1}/src/typedal/config.py +15 -16
- typedal-4.0.1/src/typedal/constants.py +25 -0
- typedal-4.0.1/src/typedal/core.py +460 -0
- typedal-4.0.1/src/typedal/define.py +188 -0
- typedal-4.0.1/src/typedal/fields.py +577 -0
- {typedal-3.17.3 → typedal-4.0.1}/src/typedal/for_web2py.py +1 -1
- typedal-4.0.1/src/typedal/helpers.py +622 -0
- {typedal-3.17.3 → typedal-4.0.1}/src/typedal/mixins.py +21 -25
- typedal-4.0.1/src/typedal/query_builder.py +1059 -0
- typedal-4.0.1/src/typedal/relationships.py +264 -0
- typedal-4.0.1/src/typedal/rows.py +524 -0
- {typedal-3.17.3 → typedal-4.0.1}/src/typedal/serializers/as_json.py +9 -10
- typedal-4.0.1/src/typedal/tables.py +1122 -0
- typedal-4.0.1/src/typedal/types.py +323 -0
- {typedal-3.17.3 → typedal-4.0.1}/src/typedal/web2py_py4web_shared.py +1 -1
- typedal-4.0.1/tests/py314_tests.py +146 -0
- {typedal-3.17.3 → typedal-4.0.1}/tests/test_helpers.py +32 -4
- {typedal-3.17.3 → typedal-4.0.1}/tests/test_main.py +43 -3
- {typedal-3.17.3 → typedal-4.0.1}/tests/test_mixins.py +0 -3
- {typedal-3.17.3 → typedal-4.0.1}/tests/test_mypy.py +13 -12
- {typedal-3.17.3 → typedal-4.0.1}/tests/test_orm.py +0 -26
- {typedal-3.17.3 → typedal-4.0.1}/tests/test_query_builder.py +1 -2
- {typedal-3.17.3 → typedal-4.0.1}/tests/test_relationships.py +60 -19
- typedal-3.17.3/src/typedal/core.py +0 -3452
- typedal-3.17.3/src/typedal/fields.py +0 -352
- typedal-3.17.3/src/typedal/helpers.py +0 -415
- typedal-3.17.3/src/typedal/types.py +0 -317
- {typedal-3.17.3 → typedal-4.0.1}/.gitignore +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/README.md +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/coverage.svg +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/docs/1_getting_started.md +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/docs/2_defining_tables.md +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/docs/3_building_queries.md +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/docs/4_relationships.md +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/docs/5_py4web.md +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/docs/6_migrations.md +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/docs/7_mixins.md +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/docs/css/code_blocks.css +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/docs/index.md +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/docs/requirements.txt +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/example_new.py +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/example_old.py +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/mkdocs.yml +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/src/typedal/cli.py +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/src/typedal/for_py4web.py +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/src/typedal/py.typed +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/tests/__init__.py +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/tests/configs/simple.toml +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/tests/configs/valid.env +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/tests/configs/valid.toml +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/tests/test_cli.py +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/tests/test_config.py +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/tests/test_docs_examples.py +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/tests/test_json.py +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/tests/test_py4web.py +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/tests/test_row.py +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/tests/test_stats.py +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/tests/test_table.py +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/tests/test_web2py.py +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/tests/test_xx_others.py +0 -0
- {typedal-3.17.3 → typedal-4.0.1}/tests/timings.py +0 -0
|
@@ -11,7 +11,7 @@ jobs:
|
|
|
11
11
|
- uses: actions/checkout@v3
|
|
12
12
|
- uses: actions/setup-python@v4
|
|
13
13
|
with:
|
|
14
|
-
python-version: '3.
|
|
14
|
+
python-version: '3.11'
|
|
15
15
|
- uses: yezz123/setup-uv@v4
|
|
16
16
|
with:
|
|
17
17
|
uv-venv: ".venv"
|
|
@@ -25,7 +25,8 @@ jobs:
|
|
|
25
25
|
- uses: actions/checkout@v3
|
|
26
26
|
- uses: actions/setup-python@v4
|
|
27
27
|
with:
|
|
28
|
-
python-version: '3.
|
|
28
|
+
python-version: '3.14'
|
|
29
|
+
allow-prereleases: true
|
|
29
30
|
- uses: yezz123/setup-uv@v4
|
|
30
31
|
with:
|
|
31
32
|
uv-venv: ".venv"
|
|
@@ -2,6 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
<!--next-version-placeholder-->
|
|
4
4
|
|
|
5
|
+
## v4.0.1 (2025-10-13)
|
|
6
|
+
|
|
7
|
+
### Fix
|
|
8
|
+
|
|
9
|
+
* Export 'PaginatedRows' ([`977a83b`](https://github.com/trialandsuccess/TypeDAL/commit/977a83b30e21412893fe24434b86d7f40ef77e87))
|
|
10
|
+
|
|
11
|
+
## v4.0.0 (2025-10-12)
|
|
12
|
+
|
|
13
|
+
### Feature
|
|
14
|
+
|
|
15
|
+
* Continued work for nested joins like .join('relationship.with_relationship'), still some todo's for test coverage ([`2a57301`](https://github.com/trialandsuccess/TypeDAL/commit/2a57301bdb7f6e3075bd156a3e79f7f8c922382f))
|
|
16
|
+
* Support nested joins like .join('relationship.with_relationship') ([`6ac30d0`](https://github.com/trialandsuccess/TypeDAL/commit/6ac30d0ad8f0c4e2cb67de92852c917b3c8bb94e))
|
|
17
|
+
* T-string support for `sql_expression` and `executesql` in Python 3.14 ([`7d21200`](https://github.com/trialandsuccess/TypeDAL/commit/7d2120062021579b1dee4a46fde6e6da61b62671))
|
|
18
|
+
* Extend Expression functionality with Python 3.14 template strings (t-string) ([`b902005`](https://github.com/trialandsuccess/TypeDAL/commit/b902005f378f2fe7cfe89feb3a76bb6e079edc7b))
|
|
19
|
+
|
|
20
|
+
### Fix
|
|
21
|
+
|
|
22
|
+
* Bump pydal to version that supports 3.14 ([`b4c836c`](https://github.com/trialandsuccess/TypeDAL/commit/b4c836c3a6a0363ac3e9f3c2018147b15b639016))
|
|
23
|
+
* Support .orderby as alias for .select(orderby= ([`e6d33e6`](https://github.com/trialandsuccess/TypeDAL/commit/e6d33e608c37d0e594881594b869063557b18758))
|
|
24
|
+
* Fix 3.14-related issues in tests and bump typer to specific version to prevent breaking changes (clirunner in test) ([`1c23954`](https://github.com/trialandsuccess/TypeDAL/commit/1c2395486d1a6d7990ccabaaf016e378e6aff5e0))
|
|
25
|
+
* Don't force slug if already manually set ([`f14962c`](https://github.com/trialandsuccess/TypeDAL/commit/f14962c1b1bb7b6f659071f97ec2bf3d8287e609))
|
|
26
|
+
* Support explicit `ForwardRef()` ([`74edf4c`](https://github.com/trialandsuccess/TypeDAL/commit/74edf4c32d93289098fb8b34bcf2e58b47a33bd9))
|
|
27
|
+
* Use custom patched pydal for python 3.14 ([`07040ad`](https://github.com/trialandsuccess/TypeDAL/commit/07040ade9ebf86d4c05a0519b4ceccfacd62195e))
|
|
28
|
+
* Better error for missing ForwardRef ([`b7df789`](https://github.com/trialandsuccess/TypeDAL/commit/b7df7895f7995b524c0571485068be98fcf7c39c))
|
|
29
|
+
* Upgrade to at least configuraptor 1.27.1 for python 3.14 ([`19cebd2`](https://github.com/trialandsuccess/TypeDAL/commit/19cebd26473fe7343d8a777274b4f5fa0b2d78e6))
|
|
30
|
+
* Prepare for python 3.14 which introduces annotationlib to parse (forward reference) annotations ([`75bb725`](https://github.com/trialandsuccess/TypeDAL/commit/75bb7256bf790a518803437096fec24739fc35c0))
|
|
31
|
+
|
|
32
|
+
### Documentation
|
|
33
|
+
|
|
34
|
+
* Remove old annotation parsing comments that are irrelevant with annotationlib ([`8ff6d73`](https://github.com/trialandsuccess/TypeDAL/commit/8ff6d7382e2bec5a50b40308d76645d098cad973))
|
|
35
|
+
|
|
5
36
|
## v3.17.3 (2025-09-30)
|
|
6
37
|
|
|
7
38
|
### Fix
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: TypeDAL
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0.1
|
|
4
4
|
Summary: Typing support for PyDAL
|
|
5
5
|
Project-URL: Documentation, https://typedal.readthedocs.io/
|
|
6
6
|
Project-URL: Issues, https://github.com/trialandsuccess/TypeDAL/issues
|
|
@@ -8,18 +8,19 @@ Project-URL: Source, https://github.com/trialandsuccess/TypeDAL
|
|
|
8
8
|
Author-email: Robin van der Noord <contact@trialandsuccess.nl>
|
|
9
9
|
Classifier: Development Status :: 4 - Beta
|
|
10
10
|
Classifier: Programming Language :: Python
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
12
11
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
15
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
16
16
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
17
|
-
Requires-Python: >=3.
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
18
|
Requires-Dist: configurable-json<2
|
|
19
|
-
Requires-Dist: configuraptor<2,>=1.
|
|
19
|
+
Requires-Dist: configuraptor<2,>=1.27.1
|
|
20
20
|
Requires-Dist: dill<1
|
|
21
21
|
Requires-Dist: legacy-cgi; python_version >= '3.13'
|
|
22
|
-
Requires-Dist: pydal
|
|
22
|
+
Requires-Dist: pydal>=20251012.3
|
|
23
|
+
Requires-Dist: python-dateutil<3
|
|
23
24
|
Requires-Dist: python-slugify<9
|
|
24
25
|
Provides-Extra: all
|
|
25
26
|
Requires-Dist: edwh-migrate[full]>=0.8.0; extra == 'all'
|
|
@@ -28,7 +29,7 @@ Requires-Dist: pydal2sql[all]>=1.2.0; extra == 'all'
|
|
|
28
29
|
Requires-Dist: questionary; extra == 'all'
|
|
29
30
|
Requires-Dist: tabulate; extra == 'all'
|
|
30
31
|
Requires-Dist: tomlkit; extra == 'all'
|
|
31
|
-
Requires-Dist: typer; extra == 'all'
|
|
32
|
+
Requires-Dist: typer<0.19,>=0.18; extra == 'all'
|
|
32
33
|
Provides-Extra: dev
|
|
33
34
|
Requires-Dist: contextlib-chdir; extra == 'dev'
|
|
34
35
|
Requires-Dist: hatch; extra == 'dev'
|
|
@@ -48,7 +49,7 @@ Requires-Dist: pydal2sql>=1.2.0; extra == 'migrations'
|
|
|
48
49
|
Requires-Dist: questionary; extra == 'migrations'
|
|
49
50
|
Requires-Dist: tabulate; extra == 'migrations'
|
|
50
51
|
Requires-Dist: tomlkit; extra == 'migrations'
|
|
51
|
-
Requires-Dist: typer; extra == 'migrations'
|
|
52
|
+
Requires-Dist: typer<0.19,>=0.18; extra == 'migrations'
|
|
52
53
|
Provides-Extra: py4web
|
|
53
54
|
Requires-Dist: py4web; extra == 'py4web'
|
|
54
55
|
Description-Content-Type: text/markdown
|
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
requires = ["hatchling"]
|
|
3
3
|
build-backend = "hatchling.build"
|
|
4
4
|
|
|
5
|
+
[tool.hatch.metadata]
|
|
6
|
+
allow-direct-references = true
|
|
7
|
+
|
|
5
8
|
[project]
|
|
6
9
|
name = "TypeDAL"
|
|
7
10
|
dynamic = ["version"]
|
|
8
11
|
description = 'Typing support for PyDAL'
|
|
9
12
|
readme = "README.md"
|
|
10
|
-
requires-python = ">=3.
|
|
13
|
+
requires-python = ">=3.11"
|
|
11
14
|
license-expression = "MIT"
|
|
12
15
|
keywords = []
|
|
13
16
|
authors = [
|
|
@@ -16,20 +19,21 @@ authors = [
|
|
|
16
19
|
classifiers = [
|
|
17
20
|
"Development Status :: 4 - Beta",
|
|
18
21
|
"Programming Language :: Python",
|
|
19
|
-
"Programming Language :: Python :: 3.10",
|
|
20
22
|
"Programming Language :: Python :: 3.11",
|
|
21
23
|
"Programming Language :: Python :: 3.12",
|
|
22
24
|
"Programming Language :: Python :: 3.13",
|
|
25
|
+
"Programming Language :: Python :: 3.14",
|
|
23
26
|
"Programming Language :: Python :: Implementation :: CPython",
|
|
24
27
|
"Programming Language :: Python :: Implementation :: PyPy",
|
|
25
28
|
]
|
|
26
29
|
dependencies = [
|
|
27
|
-
"pydal
|
|
30
|
+
"pydal >= 20251012.3", # core
|
|
28
31
|
"dill < 1", # caching
|
|
29
|
-
"configuraptor >= 1.
|
|
32
|
+
"configuraptor >= 1.27.1, < 2", # config
|
|
30
33
|
"Configurable-JSON < 2", # json dumping
|
|
31
34
|
"python-slugify < 9",
|
|
32
|
-
"legacy-cgi; python_version >= '3.13'"
|
|
35
|
+
"legacy-cgi; python_version >= '3.13'",
|
|
36
|
+
"python-dateutil < 3",
|
|
33
37
|
]
|
|
34
38
|
|
|
35
39
|
[project.optional-dependencies]
|
|
@@ -38,7 +42,7 @@ py4web = [
|
|
|
38
42
|
]
|
|
39
43
|
|
|
40
44
|
migrations = [
|
|
41
|
-
"typer",
|
|
45
|
+
"typer >=0.18, <0.19",
|
|
42
46
|
"tabulate",
|
|
43
47
|
"pydal2sql>=1.2.0",
|
|
44
48
|
"edwh-migrate>=0.8.0",
|
|
@@ -48,7 +52,7 @@ migrations = [
|
|
|
48
52
|
|
|
49
53
|
all = [
|
|
50
54
|
"py4web",
|
|
51
|
-
"typer",
|
|
55
|
+
"typer >=0.18, <0.19",
|
|
52
56
|
"tabulate",
|
|
53
57
|
"pydal2sql[all]>=1.2.0",
|
|
54
58
|
"edwh-migrate[full]>=0.8.0",
|
|
@@ -114,7 +118,7 @@ badge = true
|
|
|
114
118
|
mypy = "--disable-error-code misc"
|
|
115
119
|
|
|
116
120
|
[tool.black]
|
|
117
|
-
target-version = ["
|
|
121
|
+
target-version = ["py313"]
|
|
118
122
|
line-length = 120
|
|
119
123
|
# 'extend-exclude' excludes files or directories in addition to the defaults
|
|
120
124
|
extend-exclude = '''
|
|
@@ -130,6 +134,7 @@ extend-exclude = '''
|
|
|
130
134
|
[tool.coverage.report]
|
|
131
135
|
exclude_also = [
|
|
132
136
|
"if TYPE_CHECKING:",
|
|
137
|
+
"if t.TYPE_CHECKING:",
|
|
133
138
|
"if typing.TYPE_CHECKING:",
|
|
134
139
|
"except ImportError as e:",
|
|
135
140
|
"except ImportError:",
|
|
@@ -156,7 +161,7 @@ strict = true
|
|
|
156
161
|
exclude = ["venv", ".bak"]
|
|
157
162
|
|
|
158
163
|
[tool.ruff]
|
|
159
|
-
target-version = "
|
|
164
|
+
target-version = "py313"
|
|
160
165
|
line-length = 120
|
|
161
166
|
|
|
162
167
|
extend-exclude = ["*.bak/", "venv*/"]
|
|
@@ -2,16 +2,15 @@
|
|
|
2
2
|
TypeDAL Library.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from . import
|
|
6
|
-
from .
|
|
7
|
-
Relationship,
|
|
8
|
-
TypeDAL,
|
|
9
|
-
TypedField,
|
|
10
|
-
TypedRows,
|
|
11
|
-
TypedTable,
|
|
12
|
-
relationship,
|
|
13
|
-
)
|
|
5
|
+
from .core import TypeDAL
|
|
6
|
+
from .fields import TypedField
|
|
14
7
|
from .helpers import sql_expression
|
|
8
|
+
from .query_builder import QueryBuilder
|
|
9
|
+
from .relationships import Relationship, relationship
|
|
10
|
+
from .rows import TypedRows, PaginatedRows
|
|
11
|
+
from .tables import TypedTable
|
|
12
|
+
|
|
13
|
+
from . import fields # isort: skip
|
|
15
14
|
|
|
16
15
|
try:
|
|
17
16
|
from .for_py4web import DAL as P4W_DAL
|
|
@@ -19,6 +18,8 @@ except ImportError: # pragma: no cover
|
|
|
19
18
|
P4W_DAL = None # type: ignore
|
|
20
19
|
|
|
21
20
|
__all__ = [
|
|
21
|
+
"PaginatedRows",
|
|
22
|
+
"QueryBuilder",
|
|
22
23
|
"Relationship",
|
|
23
24
|
"TypeDAL",
|
|
24
25
|
"TypedField",
|
|
@@ -3,27 +3,28 @@ Helpers to facilitate db-based caching.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import contextlib
|
|
6
|
+
import datetime as dt
|
|
6
7
|
import hashlib
|
|
7
8
|
import json
|
|
8
|
-
import typing
|
|
9
|
-
from datetime import datetime, timedelta, timezone
|
|
10
|
-
from typing import Any, Iterable, Mapping, Optional, TypeVar
|
|
9
|
+
import typing as t
|
|
11
10
|
|
|
12
11
|
import dill # nosec
|
|
13
12
|
from pydal.objects import Field, Rows, Set
|
|
14
13
|
|
|
15
|
-
from .
|
|
14
|
+
from .fields import TypedField
|
|
15
|
+
from .rows import TypedRows
|
|
16
|
+
from .tables import TypedTable
|
|
16
17
|
from .types import Query
|
|
17
18
|
|
|
18
|
-
if
|
|
19
|
+
if t.TYPE_CHECKING:
|
|
19
20
|
from .core import TypeDAL
|
|
20
21
|
|
|
21
22
|
|
|
22
|
-
def get_now(tz: timezone = timezone.utc) -> datetime:
|
|
23
|
+
def get_now(tz: dt.timezone = dt.timezone.utc) -> dt.datetime:
|
|
23
24
|
"""
|
|
24
25
|
Get the default datetime, optionally in a specific timezone.
|
|
25
26
|
"""
|
|
26
|
-
return datetime.now(tz)
|
|
27
|
+
return dt.datetime.now(tz)
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
class _TypedalCache(TypedTable):
|
|
@@ -33,8 +34,8 @@ class _TypedalCache(TypedTable):
|
|
|
33
34
|
|
|
34
35
|
key: TypedField[str]
|
|
35
36
|
data: TypedField[bytes]
|
|
36
|
-
cached_at = TypedField(datetime, default=get_now)
|
|
37
|
-
expires_at: TypedField[datetime | None]
|
|
37
|
+
cached_at = TypedField(dt.datetime, default=get_now)
|
|
38
|
+
expires_at: TypedField[dt.datetime | None]
|
|
38
39
|
|
|
39
40
|
|
|
40
41
|
class _TypedalCacheDependency(TypedTable):
|
|
@@ -47,7 +48,7 @@ class _TypedalCacheDependency(TypedTable):
|
|
|
47
48
|
idx: TypedField[int]
|
|
48
49
|
|
|
49
50
|
|
|
50
|
-
def prepare(field: Any) -> str:
|
|
51
|
+
def prepare(field: t.Any) -> str:
|
|
51
52
|
"""
|
|
52
53
|
Prepare data to be used in a cache key.
|
|
53
54
|
|
|
@@ -56,10 +57,10 @@ def prepare(field: Any) -> str:
|
|
|
56
57
|
"""
|
|
57
58
|
if isinstance(field, str):
|
|
58
59
|
return field
|
|
59
|
-
elif isinstance(field, (dict, Mapping)):
|
|
60
|
+
elif isinstance(field, (dict, t.Mapping)):
|
|
60
61
|
data = {str(k): prepare(v) for k, v in field.items()}
|
|
61
62
|
return json.dumps(data, sort_keys=True)
|
|
62
|
-
elif isinstance(field, Iterable):
|
|
63
|
+
elif isinstance(field, t.Iterable):
|
|
63
64
|
return ",".join(sorted([prepare(_) for _ in field]))
|
|
64
65
|
elif isinstance(field, bool):
|
|
65
66
|
return str(int(field))
|
|
@@ -67,7 +68,7 @@ def prepare(field: Any) -> str:
|
|
|
67
68
|
return str(field)
|
|
68
69
|
|
|
69
70
|
|
|
70
|
-
def create_cache_key(*fields: Any) -> str:
|
|
71
|
+
def create_cache_key(*fields: t.Any) -> str:
|
|
71
72
|
"""
|
|
72
73
|
Turn any fields of data into a string.
|
|
73
74
|
"""
|
|
@@ -83,7 +84,7 @@ def hash_cache_key(cache_key: str | bytes) -> str:
|
|
|
83
84
|
return h.hexdigest()
|
|
84
85
|
|
|
85
86
|
|
|
86
|
-
def create_and_hash_cache_key(*fields: Any) -> tuple[str, str]:
|
|
87
|
+
def create_and_hash_cache_key(*fields: t.Any) -> tuple[str, str]:
|
|
87
88
|
"""
|
|
88
89
|
Combine the input fields into one key and hash it with SHA 256.
|
|
89
90
|
"""
|
|
@@ -112,7 +113,7 @@ def _get_dependency_ids(rows: Rows, dependency_keys: list[tuple[Field, str]]) ->
|
|
|
112
113
|
return dependencies
|
|
113
114
|
|
|
114
115
|
|
|
115
|
-
def _determine_dependencies_auto(_: TypedRows[Any], rows: Rows) -> DependencyTupleSet:
|
|
116
|
+
def _determine_dependencies_auto(_: TypedRows[t.Any], rows: Rows) -> DependencyTupleSet:
|
|
116
117
|
dependency_keys = []
|
|
117
118
|
for field in rows.fields:
|
|
118
119
|
if str(field).endswith(".id"):
|
|
@@ -123,7 +124,7 @@ def _determine_dependencies_auto(_: TypedRows[Any], rows: Rows) -> DependencyTup
|
|
|
123
124
|
return _get_dependency_ids(rows, dependency_keys)
|
|
124
125
|
|
|
125
126
|
|
|
126
|
-
def _determine_dependencies(instance: TypedRows[Any], rows: Rows, depends_on: list[Any]) -> DependencyTupleSet:
|
|
127
|
+
def _determine_dependencies(instance: TypedRows[t.Any], rows: Rows, depends_on: list[t.Any]) -> DependencyTupleSet:
|
|
127
128
|
if not depends_on:
|
|
128
129
|
return _determine_dependencies_auto(instance, rows)
|
|
129
130
|
|
|
@@ -144,11 +145,11 @@ def _determine_dependencies(instance: TypedRows[Any], rows: Rows, depends_on: li
|
|
|
144
145
|
return _get_dependency_ids(rows, dependency_keys)
|
|
145
146
|
|
|
146
147
|
|
|
147
|
-
def remove_cache(idx: int | Iterable[int], table: str) -> None:
|
|
148
|
+
def remove_cache(idx: int | t.Iterable[int], table: str) -> None:
|
|
148
149
|
"""
|
|
149
150
|
Remove any cache entries that are dependant on one or multiple indices of a table.
|
|
150
151
|
"""
|
|
151
|
-
if not isinstance(idx, Iterable):
|
|
152
|
+
if not isinstance(idx, t.Iterable):
|
|
152
153
|
idx = [idx]
|
|
153
154
|
|
|
154
155
|
related = (
|
|
@@ -184,12 +185,14 @@ def _remove_cache(s: Set, tablename: str) -> None:
|
|
|
184
185
|
remove_cache(indeces, tablename)
|
|
185
186
|
|
|
186
187
|
|
|
187
|
-
T_TypedTable = TypeVar("T_TypedTable", bound=TypedTable)
|
|
188
|
+
T_TypedTable = t.TypeVar("T_TypedTable", bound=TypedTable)
|
|
188
189
|
|
|
189
190
|
|
|
190
191
|
def get_expire(
|
|
191
|
-
expires_at: Optional[datetime] = None,
|
|
192
|
-
|
|
192
|
+
expires_at: t.Optional[dt.datetime] = None,
|
|
193
|
+
ttl: t.Optional[int | dt.timedelta] = None,
|
|
194
|
+
now: t.Optional[dt.datetime] = None,
|
|
195
|
+
) -> dt.datetime | None:
|
|
193
196
|
"""
|
|
194
197
|
Based on an expires_at date or a ttl (in seconds or a time delta), determine the expire date.
|
|
195
198
|
"""
|
|
@@ -197,10 +200,10 @@ def get_expire(
|
|
|
197
200
|
|
|
198
201
|
if expires_at and ttl:
|
|
199
202
|
raise ValueError("Please only supply an `expired at` date or a `ttl` in seconds!")
|
|
200
|
-
elif isinstance(ttl, timedelta):
|
|
203
|
+
elif isinstance(ttl, dt.timedelta):
|
|
201
204
|
return now + ttl
|
|
202
205
|
elif ttl:
|
|
203
|
-
return now + timedelta(seconds=ttl)
|
|
206
|
+
return now + dt.timedelta(seconds=ttl)
|
|
204
207
|
elif expires_at:
|
|
205
208
|
return expires_at
|
|
206
209
|
|
|
@@ -210,8 +213,8 @@ def get_expire(
|
|
|
210
213
|
def save_to_cache(
|
|
211
214
|
instance: TypedRows[T_TypedTable],
|
|
212
215
|
rows: Rows,
|
|
213
|
-
expires_at: Optional[datetime] = None,
|
|
214
|
-
ttl: Optional[int | timedelta] = None,
|
|
216
|
+
expires_at: t.Optional[dt.datetime] = None,
|
|
217
|
+
ttl: t.Optional[int | dt.timedelta] = None,
|
|
215
218
|
) -> TypedRows[T_TypedTable]:
|
|
216
219
|
"""
|
|
217
220
|
Save a typedrows result to the database, and save dependencies from rows.
|
|
@@ -237,13 +240,13 @@ def save_to_cache(
|
|
|
237
240
|
return instance
|
|
238
241
|
|
|
239
242
|
|
|
240
|
-
def _load_from_cache(key: str, db: "TypeDAL") -> Any | None:
|
|
243
|
+
def _load_from_cache(key: str, db: "TypeDAL") -> t.Any | None:
|
|
241
244
|
if not (row := _TypedalCache.where(key=key).first()):
|
|
242
245
|
return None
|
|
243
246
|
|
|
244
247
|
now = get_now()
|
|
245
248
|
|
|
246
|
-
expires = row.expires_at.replace(tzinfo=timezone.utc) if row.expires_at else None
|
|
249
|
+
expires = row.expires_at.replace(tzinfo=dt.timezone.utc) if row.expires_at else None
|
|
247
250
|
|
|
248
251
|
if expires and now >= expires:
|
|
249
252
|
row.delete_record()
|
|
@@ -261,7 +264,7 @@ def _load_from_cache(key: str, db: "TypeDAL") -> Any | None:
|
|
|
261
264
|
return inst
|
|
262
265
|
|
|
263
266
|
|
|
264
|
-
def load_from_cache(key: str, db: "TypeDAL") -> Any | None:
|
|
267
|
+
def load_from_cache(key: str, db: "TypeDAL") -> t.Any | None:
|
|
265
268
|
"""
|
|
266
269
|
If 'key' matches a non-expired row in the database, try to load the dill.
|
|
267
270
|
|
|
@@ -302,10 +305,10 @@ def _expired_and_valid_query() -> tuple[str, str]:
|
|
|
302
305
|
return expired_items, valid_items
|
|
303
306
|
|
|
304
307
|
|
|
305
|
-
T =
|
|
306
|
-
Stats =
|
|
308
|
+
T = t.TypeVar("T")
|
|
309
|
+
Stats = t.TypedDict("Stats", {"total": T, "valid": T, "expired": T})
|
|
307
310
|
|
|
308
|
-
RowStats =
|
|
311
|
+
RowStats = t.TypedDict(
|
|
309
312
|
"RowStats",
|
|
310
313
|
{
|
|
311
314
|
"Dependent Cache Entries": int,
|
|
@@ -338,7 +341,7 @@ def row_stats(db: "TypeDAL", table: str, row_id: str) -> Stats[RowStats]:
|
|
|
338
341
|
}
|
|
339
342
|
|
|
340
343
|
|
|
341
|
-
TableStats =
|
|
344
|
+
TableStats = t.TypedDict(
|
|
342
345
|
"TableStats",
|
|
343
346
|
{
|
|
344
347
|
"Dependent Cache Entries": int,
|
|
@@ -371,7 +374,7 @@ def table_stats(db: "TypeDAL", table: str) -> Stats[TableStats]:
|
|
|
371
374
|
}
|
|
372
375
|
|
|
373
376
|
|
|
374
|
-
GenericStats =
|
|
377
|
+
GenericStats = t.TypedDict(
|
|
375
378
|
"GenericStats",
|
|
376
379
|
{
|
|
377
380
|
"entries": int,
|
|
@@ -4,11 +4,10 @@ TypeDAL can be configured by a combination of pyproject.toml (static), env (dyna
|
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
6
|
import re
|
|
7
|
-
import typing
|
|
7
|
+
import typing as t
|
|
8
8
|
import warnings
|
|
9
9
|
from collections import defaultdict
|
|
10
10
|
from pathlib import Path
|
|
11
|
-
from typing import Any, Optional
|
|
12
11
|
|
|
13
12
|
import tomli
|
|
14
13
|
from configuraptor import TypedConfig, alias
|
|
@@ -17,7 +16,7 @@ from dotenv import dotenv_values, find_dotenv
|
|
|
17
16
|
|
|
18
17
|
from .types import AnyDict
|
|
19
18
|
|
|
20
|
-
if
|
|
19
|
+
if t.TYPE_CHECKING:
|
|
21
20
|
from edwh_migrate import Config as MigrateConfig
|
|
22
21
|
from pydal2sql.typer_support import Config as P2SConfig
|
|
23
22
|
|
|
@@ -41,15 +40,15 @@ class TypeDALConfig(TypedConfig):
|
|
|
41
40
|
output: str = ""
|
|
42
41
|
noop: bool = False
|
|
43
42
|
magic: bool = True
|
|
44
|
-
tables: Optional[list[str]] = None
|
|
43
|
+
tables: t.Optional[list[str]] = None
|
|
45
44
|
function: str = "define_tables"
|
|
46
45
|
|
|
47
46
|
# edwh-migrate:
|
|
48
47
|
# migrate uri = database
|
|
49
|
-
database_to_restore: Optional[str]
|
|
50
|
-
migrate_cat_command: Optional[str]
|
|
51
|
-
schema_version: Optional[str]
|
|
52
|
-
redis_host: Optional[str]
|
|
48
|
+
database_to_restore: t.Optional[str]
|
|
49
|
+
migrate_cat_command: t.Optional[str]
|
|
50
|
+
schema_version: t.Optional[str]
|
|
51
|
+
redis_host: t.Optional[str]
|
|
53
52
|
migrate_table: str = "typedal_implemented_features"
|
|
54
53
|
flag_location: str
|
|
55
54
|
create_flag_location: bool = True
|
|
@@ -148,7 +147,7 @@ def _load_toml(path: str | bool | Path | None = True) -> tuple[str, AnyDict]:
|
|
|
148
147
|
with open(toml_path, "rb") as f:
|
|
149
148
|
data = tomli.load(f)
|
|
150
149
|
|
|
151
|
-
return str(toml_path) or "",
|
|
150
|
+
return str(toml_path) or "", t.cast(AnyDict, data["tool"]["typedal"])
|
|
152
151
|
except Exception as e:
|
|
153
152
|
warnings.warn(f"Could not load typedal config toml: {e}", source=e)
|
|
154
153
|
return str(toml_path) or "", {}
|
|
@@ -194,7 +193,7 @@ def get_db_for_alias(db_name: str) -> str:
|
|
|
194
193
|
return DB_ALIASES.get(db_name, db_name)
|
|
195
194
|
|
|
196
195
|
|
|
197
|
-
DEFAULTS: dict[str, Any |
|
|
196
|
+
DEFAULTS: dict[str, t.Any | t.Callable[[AnyDict], t.Any]] = {
|
|
198
197
|
"database": lambda data: data.get("db_uri") or "sqlite:memory",
|
|
199
198
|
"dialect": lambda data: (
|
|
200
199
|
get_db_for_alias(data["database"].split(":")[0]) if ":" in data["database"] else data.get("db_type")
|
|
@@ -208,7 +207,7 @@ DEFAULTS: dict[str, Any | typing.Callable[[AnyDict], Any]] = {
|
|
|
208
207
|
}
|
|
209
208
|
|
|
210
209
|
|
|
211
|
-
def _fill_defaults(data: AnyDict, prop: str, fallback: Any = None) -> None:
|
|
210
|
+
def _fill_defaults(data: AnyDict, prop: str, fallback: t.Any = None) -> None:
|
|
212
211
|
default = DEFAULTS.get(prop, fallback)
|
|
213
212
|
if callable(default):
|
|
214
213
|
default = default(data)
|
|
@@ -223,7 +222,7 @@ def fill_defaults(data: AnyDict, prop: str) -> None:
|
|
|
223
222
|
_fill_defaults(data, prop)
|
|
224
223
|
|
|
225
224
|
|
|
226
|
-
TRANSFORMS: dict[str,
|
|
225
|
+
TRANSFORMS: dict[str, t.Callable[[AnyDict], t.Any]] = {
|
|
227
226
|
"database": lambda data: (
|
|
228
227
|
data["database"]
|
|
229
228
|
if (":" in data["database"] or not data.get("dialect"))
|
|
@@ -264,7 +263,7 @@ def expand_posix_vars(posix_expr: str, context: dict[str, str]) -> str:
|
|
|
264
263
|
# Regular expression to match "${VAR:default}" pattern
|
|
265
264
|
pattern = r"\$\{([^}]+)\}"
|
|
266
265
|
|
|
267
|
-
def replace_var(match: re.Match[Any]) -> str:
|
|
266
|
+
def replace_var(match: re.Match[t.Any]) -> str:
|
|
268
267
|
var_with_default = match.group(1)
|
|
269
268
|
var_name, default_value = var_with_default.split(":") if ":" in var_with_default else (var_with_default, "")
|
|
270
269
|
return env.get(var_name.lower(), default_value)
|
|
@@ -325,10 +324,10 @@ def expand_env_vars_into_toml_values(toml: AnyDict, env: AnyDict) -> None:
|
|
|
325
324
|
|
|
326
325
|
|
|
327
326
|
def load_config(
|
|
328
|
-
connection_name: Optional[str] = None,
|
|
327
|
+
connection_name: t.Optional[str] = None,
|
|
329
328
|
_use_pyproject: bool | str | None = True,
|
|
330
329
|
_use_env: bool | str | None = True,
|
|
331
|
-
**fallback: Any,
|
|
330
|
+
**fallback: t.Any,
|
|
332
331
|
) -> TypeDALConfig:
|
|
333
332
|
"""
|
|
334
333
|
Combines multiple sources of config into one config instance.
|
|
@@ -338,7 +337,7 @@ def load_config(
|
|
|
338
337
|
# combine and fill with fallback values
|
|
339
338
|
# load typedal config or fail
|
|
340
339
|
toml_path, toml = _load_toml(_use_pyproject)
|
|
341
|
-
|
|
340
|
+
_dotenv_path, dotenv = _load_dotenv(_use_env)
|
|
342
341
|
|
|
343
342
|
expand_env_vars_into_toml_values(toml, dotenv)
|
|
344
343
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Constants values.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import datetime as dt
|
|
6
|
+
import typing as t
|
|
7
|
+
from decimal import Decimal
|
|
8
|
+
|
|
9
|
+
from .types import T_annotation
|
|
10
|
+
|
|
11
|
+
JOIN_OPTIONS = t.Literal["left", "inner", None]
|
|
12
|
+
DEFAULT_JOIN_OPTION: JOIN_OPTIONS = "left"
|
|
13
|
+
|
|
14
|
+
BASIC_MAPPINGS: dict[T_annotation, str] = {
|
|
15
|
+
str: "string",
|
|
16
|
+
int: "integer",
|
|
17
|
+
bool: "boolean",
|
|
18
|
+
bytes: "blob",
|
|
19
|
+
float: "double",
|
|
20
|
+
object: "json",
|
|
21
|
+
Decimal: "decimal(10,2)",
|
|
22
|
+
dt.date: "date",
|
|
23
|
+
dt.time: "time",
|
|
24
|
+
dt.datetime: "datetime",
|
|
25
|
+
}
|