TypeDAL 3.4.0__py3-none-any.whl → 3.6.0__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/cli.py +36 -4
- typedal/core.py +70 -15
- typedal/types.py +28 -13
- {typedal-3.4.0.dist-info → typedal-3.6.0.dist-info}/METADATA +4 -4
- {typedal-3.4.0.dist-info → typedal-3.6.0.dist-info}/RECORD +8 -8
- {typedal-3.4.0.dist-info → typedal-3.6.0.dist-info}/WHEEL +1 -1
- {typedal-3.4.0.dist-info → typedal-3.6.0.dist-info}/entry_points.txt +0 -0
typedal/__about__.py
CHANGED
typedal/cli.py
CHANGED
|
@@ -40,7 +40,7 @@ from pydal2sql.types import (
|
|
|
40
40
|
OutputFormat_Option,
|
|
41
41
|
Tables_Option,
|
|
42
42
|
)
|
|
43
|
-
from pydal2sql_core import core_alter, core_create
|
|
43
|
+
from pydal2sql_core import core_alter, core_create, core_stub
|
|
44
44
|
from typing_extensions import Never
|
|
45
45
|
|
|
46
46
|
from . import caching
|
|
@@ -122,7 +122,7 @@ def get_question(prop: str, annotation: typing.Type[T], default: T | None) -> Op
|
|
|
122
122
|
question["message"] = question.get("message", f"{prop}? ")
|
|
123
123
|
default = typing.cast(T, default or question.get("default") or "")
|
|
124
124
|
|
|
125
|
-
if annotation
|
|
125
|
+
if annotation is int:
|
|
126
126
|
default = typing.cast(T, str(default))
|
|
127
127
|
|
|
128
128
|
response = questionary.unsafe_prompt([question], default=default)[prop]
|
|
@@ -197,9 +197,9 @@ def setup(
|
|
|
197
197
|
if isinstance(answer, str):
|
|
198
198
|
answer = answer.strip()
|
|
199
199
|
|
|
200
|
-
if annotation
|
|
200
|
+
if annotation is bool:
|
|
201
201
|
answer = bool(answer)
|
|
202
|
-
elif annotation
|
|
202
|
+
elif annotation is int:
|
|
203
203
|
answer = int(answer)
|
|
204
204
|
|
|
205
205
|
config.update(**{prop: answer})
|
|
@@ -417,6 +417,38 @@ def fake_migrations(
|
|
|
417
417
|
return 0
|
|
418
418
|
|
|
419
419
|
|
|
420
|
+
@app.command(name="migrations.stub")
|
|
421
|
+
@with_exit_code(hide_tb=IS_DEBUG)
|
|
422
|
+
def migrations_stub(
|
|
423
|
+
migration_name: typing.Annotated[str, typer.Argument()] = "stub_migration",
|
|
424
|
+
connection: typing.Annotated[str, typer.Option("--connection", "-c")] = None,
|
|
425
|
+
output_format: OutputFormat_Option = None,
|
|
426
|
+
output_file: Optional[str] = None,
|
|
427
|
+
dry_run: typing.Annotated[bool, typer.Option("--dry", "--dry-run")] = False,
|
|
428
|
+
is_pydal: typing.Annotated[bool, typer.Option("--pydal", "-p")] = False,
|
|
429
|
+
# defaults to is_typedal of course
|
|
430
|
+
) -> int:
|
|
431
|
+
"""
|
|
432
|
+
Create an empty migration via pydal2sql.
|
|
433
|
+
"""
|
|
434
|
+
generic_config = load_config(connection)
|
|
435
|
+
pydal2sql_config = generic_config.to_pydal2sql()
|
|
436
|
+
pydal2sql_config.update(
|
|
437
|
+
format=output_format,
|
|
438
|
+
output=output_file,
|
|
439
|
+
_skip_none=True,
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
core_stub(
|
|
443
|
+
migration_name, # raw, without date or number
|
|
444
|
+
output_format=pydal2sql_config.format,
|
|
445
|
+
output_file=pydal2sql_config.output or None,
|
|
446
|
+
dry_run=dry_run,
|
|
447
|
+
is_typedal=not is_pydal,
|
|
448
|
+
)
|
|
449
|
+
return 0
|
|
450
|
+
|
|
451
|
+
|
|
420
452
|
AnyNestedDict: typing.TypeAlias = dict[str, AnyDict]
|
|
421
453
|
|
|
422
454
|
|
typedal/core.py
CHANGED
|
@@ -43,22 +43,19 @@ from .helpers import (
|
|
|
43
43
|
)
|
|
44
44
|
from .serializers import as_json
|
|
45
45
|
from .types import (
|
|
46
|
-
AfterDeleteCallable,
|
|
47
|
-
AfterInsertCallable,
|
|
48
|
-
AfterUpdateCallable,
|
|
49
46
|
AnyDict,
|
|
50
|
-
BeforeDeleteCallable,
|
|
51
|
-
BeforeInsertCallable,
|
|
52
|
-
BeforeUpdateCallable,
|
|
53
47
|
CacheMetadata,
|
|
54
48
|
Expression,
|
|
55
49
|
Field,
|
|
56
50
|
Metadata,
|
|
51
|
+
OpRow,
|
|
57
52
|
PaginateDict,
|
|
58
53
|
Pagination,
|
|
59
54
|
Query,
|
|
55
|
+
Reference,
|
|
60
56
|
Rows,
|
|
61
57
|
SelectKwargs,
|
|
58
|
+
Set,
|
|
62
59
|
Table,
|
|
63
60
|
Validator,
|
|
64
61
|
_Types,
|
|
@@ -286,7 +283,7 @@ def _generate_relationship_condition(
|
|
|
286
283
|
origin = typing.get_origin(field)
|
|
287
284
|
# else: generic
|
|
288
285
|
|
|
289
|
-
if origin
|
|
286
|
+
if origin is list:
|
|
290
287
|
# field = typing.get_args(field)[0] # actual field
|
|
291
288
|
# return lambda _self, _other: cls[key].contains(field)
|
|
292
289
|
|
|
@@ -337,7 +334,7 @@ def to_relationship(
|
|
|
337
334
|
warnings.warn(f"Invalid relationship for {cls.__name__}.{key}: {field}")
|
|
338
335
|
return None
|
|
339
336
|
|
|
340
|
-
join = "left" if optional or typing.get_origin(field)
|
|
337
|
+
join = "left" if optional or typing.get_origin(field) is list else "inner"
|
|
341
338
|
|
|
342
339
|
return Relationship(typing.cast(type[TypedTable], field), condition, typing.cast(JOIN_OPTIONS, join))
|
|
343
340
|
|
|
@@ -1139,7 +1136,63 @@ class TableMeta(type):
|
|
|
1139
1136
|
table = self._ensure_table_defined()
|
|
1140
1137
|
return typing.cast(Type[T_MetaInstance], table.with_alias(alias))
|
|
1141
1138
|
|
|
1142
|
-
#
|
|
1139
|
+
# hooks:
|
|
1140
|
+
def before_insert(
|
|
1141
|
+
cls: Type[T_MetaInstance],
|
|
1142
|
+
fn: typing.Callable[[T_MetaInstance], Optional[bool]] | typing.Callable[[OpRow], Optional[bool]],
|
|
1143
|
+
) -> Type[T_MetaInstance]:
|
|
1144
|
+
"""
|
|
1145
|
+
Add a before insert hook.
|
|
1146
|
+
"""
|
|
1147
|
+
cls._before_insert.append(fn) # type: ignore
|
|
1148
|
+
return cls
|
|
1149
|
+
|
|
1150
|
+
def after_insert(
|
|
1151
|
+
cls: Type[T_MetaInstance],
|
|
1152
|
+
fn: (
|
|
1153
|
+
typing.Callable[[T_MetaInstance, Reference], Optional[bool]]
|
|
1154
|
+
| typing.Callable[[OpRow, Reference], Optional[bool]]
|
|
1155
|
+
),
|
|
1156
|
+
) -> Type[T_MetaInstance]:
|
|
1157
|
+
"""
|
|
1158
|
+
Add an after insert hook.
|
|
1159
|
+
"""
|
|
1160
|
+
cls._after_insert.append(fn) # type: ignore
|
|
1161
|
+
return cls
|
|
1162
|
+
|
|
1163
|
+
def before_update(
|
|
1164
|
+
cls: Type[T_MetaInstance],
|
|
1165
|
+
fn: typing.Callable[[Set, T_MetaInstance], Optional[bool]] | typing.Callable[[Set, OpRow], Optional[bool]],
|
|
1166
|
+
) -> Type[T_MetaInstance]:
|
|
1167
|
+
"""
|
|
1168
|
+
Add a before update hook.
|
|
1169
|
+
"""
|
|
1170
|
+
cls._before_update.append(fn) # type: ignore
|
|
1171
|
+
return cls
|
|
1172
|
+
|
|
1173
|
+
def after_update(
|
|
1174
|
+
cls: Type[T_MetaInstance],
|
|
1175
|
+
fn: typing.Callable[[Set, T_MetaInstance], Optional[bool]] | typing.Callable[[Set, OpRow], Optional[bool]],
|
|
1176
|
+
) -> Type[T_MetaInstance]:
|
|
1177
|
+
"""
|
|
1178
|
+
Add an after update hook.
|
|
1179
|
+
"""
|
|
1180
|
+
cls._after_update.append(fn) # type: ignore
|
|
1181
|
+
return cls
|
|
1182
|
+
|
|
1183
|
+
def before_delete(cls: Type[T_MetaInstance], fn: typing.Callable[[Set], Optional[bool]]) -> Type[T_MetaInstance]:
|
|
1184
|
+
"""
|
|
1185
|
+
Add a before delete hook.
|
|
1186
|
+
"""
|
|
1187
|
+
cls._before_delete.append(fn)
|
|
1188
|
+
return cls
|
|
1189
|
+
|
|
1190
|
+
def after_delete(cls: Type[T_MetaInstance], fn: typing.Callable[[Set], Optional[bool]]) -> Type[T_MetaInstance]:
|
|
1191
|
+
"""
|
|
1192
|
+
Add an after delete hook.
|
|
1193
|
+
"""
|
|
1194
|
+
cls._after_delete.append(fn)
|
|
1195
|
+
return cls
|
|
1143
1196
|
|
|
1144
1197
|
|
|
1145
1198
|
class TypedField(Expression, typing.Generic[T_Value]): # pragma: no cover
|
|
@@ -1334,12 +1387,14 @@ class _TypedTable:
|
|
|
1334
1387
|
|
|
1335
1388
|
id: "TypedField[int]"
|
|
1336
1389
|
|
|
1337
|
-
_before_insert: list[
|
|
1338
|
-
_after_insert: list[
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1390
|
+
_before_insert: list[typing.Callable[[Self], Optional[bool]] | typing.Callable[[OpRow], Optional[bool]]]
|
|
1391
|
+
_after_insert: list[
|
|
1392
|
+
typing.Callable[[Self, Reference], Optional[bool]] | typing.Callable[[OpRow, Reference], Optional[bool]]
|
|
1393
|
+
]
|
|
1394
|
+
_before_update: list[typing.Callable[[Set, Self], Optional[bool]] | typing.Callable[[Set, OpRow], Optional[bool]]]
|
|
1395
|
+
_after_update: list[typing.Callable[[Set, Self], Optional[bool]] | typing.Callable[[Set, OpRow], Optional[bool]]]
|
|
1396
|
+
_before_delete: list[typing.Callable[[Set], Optional[bool]]]
|
|
1397
|
+
_after_delete: list[typing.Callable[[Set], Optional[bool]]]
|
|
1343
1398
|
|
|
1344
1399
|
@classmethod
|
|
1345
1400
|
def __on_define__(cls, db: TypeDAL) -> None:
|
typedal/types.py
CHANGED
|
@@ -48,12 +48,35 @@ class Set(_Set): # type: ignore
|
|
|
48
48
|
"""
|
|
49
49
|
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
"""
|
|
53
|
-
Pydal OpRow object.
|
|
51
|
+
if typing.TYPE_CHECKING:
|
|
54
52
|
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
class OpRow:
|
|
54
|
+
"""
|
|
55
|
+
Pydal OpRow object for typing (otherwise mypy thinks it's Any).
|
|
56
|
+
|
|
57
|
+
Make mypy happy.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
def __getitem__(self, item: str) -> typing.Any:
|
|
61
|
+
"""
|
|
62
|
+
Dict [] get notation.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
def __setitem__(self, key: str, value: typing.Any) -> None:
|
|
66
|
+
"""
|
|
67
|
+
Dict [] set notation.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
# ... and more methods
|
|
71
|
+
|
|
72
|
+
else:
|
|
73
|
+
|
|
74
|
+
class OpRow(_OpRow): # type: ignore
|
|
75
|
+
"""
|
|
76
|
+
Pydal OpRow object at runtime just uses pydal's version.
|
|
77
|
+
|
|
78
|
+
Make mypy happy.
|
|
79
|
+
"""
|
|
57
80
|
|
|
58
81
|
|
|
59
82
|
class Reference(_Reference): # type: ignore
|
|
@@ -105,14 +128,6 @@ class _Types:
|
|
|
105
128
|
NONETYPE = type(None)
|
|
106
129
|
|
|
107
130
|
|
|
108
|
-
BeforeInsertCallable: typing.TypeAlias = typing.Callable[[OpRow], Any]
|
|
109
|
-
AfterInsertCallable: typing.TypeAlias = typing.Callable[[OpRow, Reference], Any]
|
|
110
|
-
BeforeUpdateCallable: typing.TypeAlias = typing.Callable[[Set, OpRow], Any]
|
|
111
|
-
AfterUpdateCallable: typing.TypeAlias = typing.Callable[[Set, OpRow], Any]
|
|
112
|
-
BeforeDeleteCallable: typing.TypeAlias = typing.Callable[[Set], Any]
|
|
113
|
-
AfterDeleteCallable: typing.TypeAlias = typing.Callable[[Set], Any]
|
|
114
|
-
|
|
115
|
-
|
|
116
131
|
class Pagination(TypedDict):
|
|
117
132
|
"""
|
|
118
133
|
Pagination key of a paginate dict has these items.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: TypeDAL
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.6.0
|
|
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
|
|
@@ -23,7 +23,7 @@ Requires-Dist: python-slugify
|
|
|
23
23
|
Provides-Extra: all
|
|
24
24
|
Requires-Dist: edwh-migrate>=0.8.0; extra == 'all'
|
|
25
25
|
Requires-Dist: py4web; extra == 'all'
|
|
26
|
-
Requires-Dist: pydal2sql[all]>=1.
|
|
26
|
+
Requires-Dist: pydal2sql[all]>=1.2.0; extra == 'all'
|
|
27
27
|
Requires-Dist: questionary; extra == 'all'
|
|
28
28
|
Requires-Dist: tabulate; extra == 'all'
|
|
29
29
|
Requires-Dist: tomlkit; extra == 'all'
|
|
@@ -43,7 +43,7 @@ Requires-Dist: types-requests; extra == 'dev'
|
|
|
43
43
|
Requires-Dist: types-tabulate; extra == 'dev'
|
|
44
44
|
Provides-Extra: migrations
|
|
45
45
|
Requires-Dist: edwh-migrate>=0.8.0; extra == 'migrations'
|
|
46
|
-
Requires-Dist: pydal2sql>=1.
|
|
46
|
+
Requires-Dist: pydal2sql>=1.2.0; extra == 'migrations'
|
|
47
47
|
Requires-Dist: questionary; extra == 'migrations'
|
|
48
48
|
Requires-Dist: tabulate; extra == 'migrations'
|
|
49
49
|
Requires-Dist: tomlkit; extra == 'migrations'
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
typedal/__about__.py,sha256=
|
|
1
|
+
typedal/__about__.py,sha256=6BHlKIfFsDoanUXT6U-wyOOCusiaMl22UUFAVmE4Clg,206
|
|
2
2
|
typedal/__init__.py,sha256=QQpLiVl9w9hm2LBxey49Y_tCF_VB2bScVaS_mCjYy54,366
|
|
3
3
|
typedal/caching.py,sha256=SMcJsahLlZ79yykWCveERFx1ZJUNEKhA9SPmCTIuLp8,11798
|
|
4
|
-
typedal/cli.py,sha256=
|
|
4
|
+
typedal/cli.py,sha256=wzyId6YwRyqfuJ2byxvl6YecDNaKpkLmo-R5HvRuTok,19265
|
|
5
5
|
typedal/config.py,sha256=0qy1zrTUdtmXPM9jHzFnSR1DJsqGJqcdG6pvhzKQHe0,11625
|
|
6
|
-
typedal/core.py,sha256=
|
|
6
|
+
typedal/core.py,sha256=mbNFMv3NJ2QRDuohs03LCGhVVnffsxjsoe-m887U6c8,98367
|
|
7
7
|
typedal/fields.py,sha256=z2PD9vLWqBR_zXtiY0DthqTG4AeF3yxKoeuVfGXnSdg,5197
|
|
8
8
|
typedal/for_py4web.py,sha256=d07b8hL_PvNDUS26Z5fDH2OxWb-IETBuAFPSzrRwm04,1285
|
|
9
9
|
typedal/for_web2py.py,sha256=4RHgzGXgKIO_BYB-7adC5e35u52rX-p1t4tPEz-NK24,1867
|
|
10
10
|
typedal/helpers.py,sha256=KbgP4ZRW7KCroyHwTwdErPqylOX4dsqVnKJeZ5TtTFY,7363
|
|
11
11
|
typedal/mixins.py,sha256=OHhVYLBGTzPSJKgp1uovBs1Y6NJa0d-DxHTOk9LyOXA,5414
|
|
12
12
|
typedal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
-
typedal/types.py,sha256=
|
|
13
|
+
typedal/types.py,sha256=W2zsmJSPYbIvhjb5Df_ZANH8riCIJhOOHjI8UCQINls,5239
|
|
14
14
|
typedal/web2py_py4web_shared.py,sha256=VK9T8P5UwVLvfNBsY4q79ANcABv-jX76YKADt1Zz_co,1539
|
|
15
15
|
typedal/serializers/as_json.py,sha256=ffo152W-sARYXym4BzwX709rrO2-QwKk2KunWY8RNl4,2229
|
|
16
|
-
typedal-3.
|
|
17
|
-
typedal-3.
|
|
18
|
-
typedal-3.
|
|
19
|
-
typedal-3.
|
|
16
|
+
typedal-3.6.0.dist-info/METADATA,sha256=yZpPjMmR24Kzh0QRmMrTihEHGNCJFgX4NtyfOIqMf-A,9462
|
|
17
|
+
typedal-3.6.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
|
18
|
+
typedal-3.6.0.dist-info/entry_points.txt,sha256=m1wqcc_10rHWPdlQ71zEkmJDADUAnZtn7Jac_6mbyUc,44
|
|
19
|
+
typedal-3.6.0.dist-info/RECORD,,
|
|
File without changes
|