TypeDAL 2.2.0__py3-none-any.whl → 2.2.1__py3-none-any.whl
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/__about__.py +1 -1
- typedal/config.py +3 -1
- typedal/core.py +13 -0
- typedal/for_py4web.py +22 -8
- typedal/types.py +9 -0
- {typedal-2.2.0.dist-info → typedal-2.2.1.dist-info}/METADATA +8 -15
- typedal-2.2.1.dist-info/RECORD +15 -0
- typedal-2.2.0.dist-info/RECORD +0 -15
- {typedal-2.2.0.dist-info → typedal-2.2.1.dist-info}/WHEEL +0 -0
- {typedal-2.2.0.dist-info → typedal-2.2.1.dist-info}/entry_points.txt +0 -0
typedal/__about__.py
CHANGED
typedal/config.py
CHANGED
|
@@ -3,6 +3,7 @@ TypeDAL can be configured by a combination of pyproject.toml (static), env (dyna
|
|
|
3
3
|
"""
|
|
4
4
|
import os
|
|
5
5
|
import typing
|
|
6
|
+
import warnings
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
from typing import Any, Optional
|
|
8
9
|
|
|
@@ -154,7 +155,8 @@ def _load_toml(path: str | bool | None = True) -> tuple[str, dict[str, Any]]:
|
|
|
154
155
|
data = tomli.load(f)
|
|
155
156
|
|
|
156
157
|
return toml_path or "", typing.cast(dict[str, Any], data["tool"]["typedal"])
|
|
157
|
-
except Exception:
|
|
158
|
+
except Exception as e:
|
|
159
|
+
warnings.warn(f"Could not load typedal config toml: {e}", source=e)
|
|
158
160
|
return toml_path or "", {}
|
|
159
161
|
|
|
160
162
|
|
typedal/core.py
CHANGED
|
@@ -54,6 +54,7 @@ from .types import (
|
|
|
54
54
|
Pagination,
|
|
55
55
|
Query,
|
|
56
56
|
Rows,
|
|
57
|
+
Validator,
|
|
57
58
|
_Types,
|
|
58
59
|
)
|
|
59
60
|
|
|
@@ -523,6 +524,7 @@ class TypeDAL(pydal.DAL): # type: ignore
|
|
|
523
524
|
relationships=typing.cast(dict[str, Relationship[Any]], relationships),
|
|
524
525
|
)
|
|
525
526
|
self._class_map[str(table)] = cls
|
|
527
|
+
cls.__on_define__(self)
|
|
526
528
|
else:
|
|
527
529
|
warnings.warn("db.define used without inheriting TypedTable. This could lead to strange problems!")
|
|
528
530
|
|
|
@@ -1165,6 +1167,8 @@ class TypedField(typing.Generic[T_Value]): # pragma: no cover
|
|
|
1165
1167
|
_type: T_annotation
|
|
1166
1168
|
kwargs: Any
|
|
1167
1169
|
|
|
1170
|
+
requires: Validator | typing.Iterable[Validator]
|
|
1171
|
+
|
|
1168
1172
|
def __init__(self, _type: typing.Type[T_Value] | types.UnionType = str, /, **settings: Any) -> None: # type: ignore
|
|
1169
1173
|
"""
|
|
1170
1174
|
A TypedFieldType should not be inited manually, but TypedField (from `fields.py`) should be used!
|
|
@@ -1381,6 +1385,15 @@ class TypedTable(metaclass=TableMeta):
|
|
|
1381
1385
|
inst._setup_instance_methods()
|
|
1382
1386
|
return inst
|
|
1383
1387
|
|
|
1388
|
+
@classmethod
|
|
1389
|
+
def __on_define__(cls, db: TypeDAL) -> None:
|
|
1390
|
+
"""
|
|
1391
|
+
Method that can be implemented by tables to do an action after db.define is completed.
|
|
1392
|
+
|
|
1393
|
+
This can be useful if you need to add something like requires=IS_NOT_IN_DB(db, "table.field"),
|
|
1394
|
+
where you need a reference to the current database, which may not exist yet when defining the model.
|
|
1395
|
+
"""
|
|
1396
|
+
|
|
1384
1397
|
def __iter__(self) -> typing.Generator[Any, None, None]:
|
|
1385
1398
|
"""
|
|
1386
1399
|
Allows looping through the columns.
|
typedal/for_py4web.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
ONLY USE IN COMBINATION WITH PY4WEB!
|
|
3
3
|
"""
|
|
4
|
+
import typing
|
|
4
5
|
from datetime import datetime
|
|
5
6
|
from typing import Any, Optional
|
|
6
7
|
|
|
@@ -8,8 +9,11 @@ import json_fix # noqa: F401
|
|
|
8
9
|
import threadsafevariable
|
|
9
10
|
from py4web.core import ICECUBE
|
|
10
11
|
from py4web.core import Fixture as _Fixture
|
|
12
|
+
from pydal.validators import CRYPT, IS_EMAIL, IS_NOT_EMPTY, IS_NOT_IN_DB, IS_STRONG
|
|
11
13
|
|
|
12
|
-
from .core import TypeDAL, TypedTable
|
|
14
|
+
from .core import TypeDAL, TypedField, TypedTable
|
|
15
|
+
from .fields import PasswordField
|
|
16
|
+
from .types import Validator
|
|
13
17
|
|
|
14
18
|
|
|
15
19
|
class Fixture(_Fixture): # type: ignore
|
|
@@ -53,13 +57,23 @@ class AuthUser(TypedTable):
|
|
|
53
57
|
|
|
54
58
|
# call db.define on this when ready
|
|
55
59
|
|
|
56
|
-
email: str
|
|
57
|
-
password
|
|
58
|
-
first_name: Optional[str]
|
|
59
|
-
last_name: Optional[str]
|
|
60
|
-
sso_id: Optional[str]
|
|
61
|
-
action_token: Optional[str]
|
|
62
|
-
last_password_change: Optional[datetime]
|
|
60
|
+
email: TypedField[str]
|
|
61
|
+
password = PasswordField(requires=[IS_STRONG(entropy=45), CRYPT()])
|
|
62
|
+
first_name: TypedField[Optional[str]]
|
|
63
|
+
last_name: TypedField[Optional[str]]
|
|
64
|
+
sso_id: TypedField[Optional[str]]
|
|
65
|
+
action_token: TypedField[Optional[str]]
|
|
66
|
+
last_password_change: TypedField[Optional[datetime]]
|
|
67
|
+
|
|
63
68
|
# past_passwords_hash: Optional[str]
|
|
64
69
|
# username: Optional[str]
|
|
65
70
|
# phone_number: Optional[str]
|
|
71
|
+
|
|
72
|
+
@classmethod
|
|
73
|
+
def __on_define__(cls, db: TypeDAL) -> None:
|
|
74
|
+
"""
|
|
75
|
+
Add some requires= to the auth_user fields.
|
|
76
|
+
"""
|
|
77
|
+
cls.email.requires = typing.cast(tuple[Validator, ...], (IS_EMAIL(), IS_NOT_IN_DB(db, "auth_user.email")))
|
|
78
|
+
cls.first_name.requires = IS_NOT_EMPTY()
|
|
79
|
+
cls.last_name.requires = IS_NOT_EMPTY()
|
typedal/types.py
CHANGED
|
@@ -12,6 +12,7 @@ from pydal.objects import Field as _Field
|
|
|
12
12
|
from pydal.objects import Query as _Query
|
|
13
13
|
from pydal.objects import Rows as _Rows
|
|
14
14
|
from pydal.objects import Set as _Set
|
|
15
|
+
from pydal.validators import Validator as _Validator
|
|
15
16
|
from typing_extensions import NotRequired
|
|
16
17
|
|
|
17
18
|
|
|
@@ -71,6 +72,14 @@ class Rows(_Rows): # type: ignore
|
|
|
71
72
|
"""
|
|
72
73
|
|
|
73
74
|
|
|
75
|
+
class Validator(_Validator): # type: ignore
|
|
76
|
+
"""
|
|
77
|
+
Pydal Validator object.
|
|
78
|
+
|
|
79
|
+
Make mypy happy.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
|
|
74
83
|
class _Types:
|
|
75
84
|
"""
|
|
76
85
|
Internal type storage for stuff that mypy otherwise won't understand.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: TypeDAL
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.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
|
|
@@ -62,10 +62,13 @@ the underlying `db.define_table` pydal Tables.
|
|
|
62
62
|
- `TypeDAL` is the replacement class for DAL that manages the code on top of DAL.
|
|
63
63
|
- `TypedTable` must be the parent class of any custom Tables you define (e.g. `class SomeTable(TypedTable)`)
|
|
64
64
|
- `TypedField` can be used instead of Python native types when extra settings (such as `default`) are required (
|
|
65
|
-
e.g. `name = TypedField(str, default="John Doe")`). It can also be used in an annotation (`name: TypedField[str]`) to
|
|
65
|
+
e.g. `name = TypedField(str, default="John Doe")`). It can also be used in an annotation (`name: TypedField[str]`) to
|
|
66
|
+
improve
|
|
66
67
|
editor support over only annotating with `str`.
|
|
67
|
-
- `TypedRows`: can be used as the return type annotation of pydal's `.select()` and subscribed with the actual table
|
|
68
|
-
|
|
68
|
+
- `TypedRows`: can be used as the return type annotation of pydal's `.select()` and subscribed with the actual table
|
|
69
|
+
class, so
|
|
70
|
+
e.g. `rows: TypedRows[SomeTable] = db(...).select()`. When using the QueryBuilder, a `TypedRows` instance is returned
|
|
71
|
+
by `.collect()`.
|
|
69
72
|
|
|
70
73
|
Version 2.0 also introduces more ORM-like funcionality.
|
|
71
74
|
Most notably, a Typed Query Builder that sees your table classes as models with relationships to each other.
|
|
@@ -75,7 +78,7 @@ details.
|
|
|
75
78
|
## Quick Overview
|
|
76
79
|
|
|
77
80
|
Below you'll find a quick overview of translation from pydal to TypeDAL. For more info,
|
|
78
|
-
see [the docs](https://
|
|
81
|
+
see [the docs](https://typedal.readthedocs.io/en/latest/).
|
|
79
82
|
|
|
80
83
|
### Translations from pydal to typedal
|
|
81
84
|
|
|
@@ -254,16 +257,6 @@ row: TableName = db.table_name(id=1)
|
|
|
254
257
|
|
|
255
258
|
See [2. Defining Tables](docs/2_defining_tables.md)
|
|
256
259
|
|
|
257
|
-
## Roadmap
|
|
258
|
-
|
|
259
|
-
This section contains a non-exhaustive list of planned features for future feature releases:
|
|
260
|
-
|
|
261
|
-
- 2.2
|
|
262
|
-
- Migrations: currently, you can use pydal's automatic migrations or disable those and manage them yourself, but
|
|
263
|
-
adding something like [`edwh-migrate`](https://github.com/educationwarehouse/migrate#readme)
|
|
264
|
-
with [`pydal2sql`](https://github.com/robinvandernoord/pydal2sql-core) as an option could make this project more
|
|
265
|
-
production-friendly.
|
|
266
|
-
|
|
267
260
|
## Caveats
|
|
268
261
|
|
|
269
262
|
- This package depends heavily on the current implementation of annotations (which are computed when the class is
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
typedal/__about__.py,sha256=h_6wVhT5zBpkimIzSHhAe8DZCR_Nn-sqaS8GZXghPko,206
|
|
2
|
+
typedal/__init__.py,sha256=QQpLiVl9w9hm2LBxey49Y_tCF_VB2bScVaS_mCjYy54,366
|
|
3
|
+
typedal/caching.py,sha256=53WU5J_yRLygkKoDfD0rV_dkh5oqqT--64R9Mvz050Y,7457
|
|
4
|
+
typedal/cli.py,sha256=MF2bGwL4SOaIqGzr1JRSvGD6CSujy88hKGgVIehlFNk,11789
|
|
5
|
+
typedal/config.py,sha256=9ORQqJJ_3tDmFiL2Py_9PiceXLihpMR69t6OKZ_qpe8,8387
|
|
6
|
+
typedal/core.py,sha256=LV0Fszbmh0pojAiARD1GKj5ooxakJmmj_4ebpvZZkcs,93644
|
|
7
|
+
typedal/fields.py,sha256=z2PD9vLWqBR_zXtiY0DthqTG4AeF3yxKoeuVfGXnSdg,5197
|
|
8
|
+
typedal/for_py4web.py,sha256=kw44_55-dBWlYmepB7YEev5sj7tbYcgzvp-Ecc7K9_I,2230
|
|
9
|
+
typedal/helpers.py,sha256=ZpHdwBMSANw-P9I5gs56Vf6GUbxGzFsIwbBvASKXX8s,6487
|
|
10
|
+
typedal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
typedal/types.py,sha256=5qm3PgS8DXGCu9ZTUWQiIi2XXD8gz4_4Csg_vZlu_yo,3379
|
|
12
|
+
typedal-2.2.1.dist-info/METADATA,sha256=-4kp0PN1msDhP9C07HqMh_4VCigOu0AhkN6yNrRPDkM,7623
|
|
13
|
+
typedal-2.2.1.dist-info/WHEEL,sha256=KGYbc1zXlYddvwxnNty23BeaKzh7YuoSIvIMO4jEhvw,87
|
|
14
|
+
typedal-2.2.1.dist-info/entry_points.txt,sha256=m1wqcc_10rHWPdlQ71zEkmJDADUAnZtn7Jac_6mbyUc,44
|
|
15
|
+
typedal-2.2.1.dist-info/RECORD,,
|
typedal-2.2.0.dist-info/RECORD
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
typedal/__about__.py,sha256=8gi3xn87HBaC4npgYYvbwLigw3OR5OW7Ke_VUmHuQow,206
|
|
2
|
-
typedal/__init__.py,sha256=QQpLiVl9w9hm2LBxey49Y_tCF_VB2bScVaS_mCjYy54,366
|
|
3
|
-
typedal/caching.py,sha256=53WU5J_yRLygkKoDfD0rV_dkh5oqqT--64R9Mvz050Y,7457
|
|
4
|
-
typedal/cli.py,sha256=MF2bGwL4SOaIqGzr1JRSvGD6CSujy88hKGgVIehlFNk,11789
|
|
5
|
-
typedal/config.py,sha256=zOINxqyZ9QczzlAuPqCVqeFWq94NUiBnVQ2H89FUIWY,8290
|
|
6
|
-
typedal/core.py,sha256=8g8IT3o7DQdNk_uTdjW56CTFRbFddKje6XBgpXTJMxM,93140
|
|
7
|
-
typedal/fields.py,sha256=z2PD9vLWqBR_zXtiY0DthqTG4AeF3yxKoeuVfGXnSdg,5197
|
|
8
|
-
typedal/for_py4web.py,sha256=Y7ceKEkqAThNtyO1rdP1hYZc51o4T4ZUpbkTgiCStQU,1575
|
|
9
|
-
typedal/helpers.py,sha256=ZpHdwBMSANw-P9I5gs56Vf6GUbxGzFsIwbBvASKXX8s,6487
|
|
10
|
-
typedal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
-
typedal/types.py,sha256=lIUc826lN59_eKKojmFyhQI2auJwdzJSZ2fNbLl5D0U,3213
|
|
12
|
-
typedal-2.2.0.dist-info/METADATA,sha256=tW3APEybDas5z-1Mb37DO0xkwXVXfAIVQXxVbuUISnA,8110
|
|
13
|
-
typedal-2.2.0.dist-info/WHEEL,sha256=KGYbc1zXlYddvwxnNty23BeaKzh7YuoSIvIMO4jEhvw,87
|
|
14
|
-
typedal-2.2.0.dist-info/entry_points.txt,sha256=m1wqcc_10rHWPdlQ71zEkmJDADUAnZtn7Jac_6mbyUc,44
|
|
15
|
-
typedal-2.2.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|