TypeDAL 3.2.0__py3-none-any.whl → 3.3.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/caching.py +1 -1
- typedal/cli.py +1 -1
- typedal/config.py +1 -1
- typedal/core.py +105 -104
- typedal/helpers.py +9 -0
- typedal/types.py +62 -1
- {typedal-3.2.0.dist-info → typedal-3.3.1.dist-info}/METADATA +6 -3
- typedal-3.3.1.dist-info/RECORD +19 -0
- {typedal-3.2.0.dist-info → typedal-3.3.1.dist-info}/WHEEL +1 -1
- typedal-3.2.0.dist-info/RECORD +0 -19
- {typedal-3.2.0.dist-info → typedal-3.3.1.dist-info}/entry_points.txt +0 -0
typedal/__about__.py
CHANGED
typedal/caching.py
CHANGED
typedal/cli.py
CHANGED
|
@@ -24,7 +24,7 @@ try:
|
|
|
24
24
|
import tomlkit
|
|
25
25
|
import typer
|
|
26
26
|
from tabulate import tabulate
|
|
27
|
-
except ImportError as e:
|
|
27
|
+
except ImportError as e:
|
|
28
28
|
# ImportWarning is hidden by default
|
|
29
29
|
warnings.warn(
|
|
30
30
|
"`migrations` extra not installed. Please run `pip install typedal[migrations]` to fix this.",
|
typedal/config.py
CHANGED
|
@@ -17,7 +17,7 @@ from dotenv import dotenv_values, find_dotenv
|
|
|
17
17
|
|
|
18
18
|
from .types import AnyDict
|
|
19
19
|
|
|
20
|
-
if typing.TYPE_CHECKING:
|
|
20
|
+
if typing.TYPE_CHECKING:
|
|
21
21
|
from edwh_migrate import Config as MigrateConfig
|
|
22
22
|
from pydal2sql.typer_support import Config as P2SConfig
|
|
23
23
|
|
typedal/core.py
CHANGED
|
@@ -15,7 +15,7 @@ from collections import defaultdict
|
|
|
15
15
|
from copy import copy
|
|
16
16
|
from decimal import Decimal
|
|
17
17
|
from pathlib import Path
|
|
18
|
-
from typing import Any, Optional
|
|
18
|
+
from typing import Any, Optional, Type
|
|
19
19
|
|
|
20
20
|
import pydal
|
|
21
21
|
from pydal._globals import DEFAULT
|
|
@@ -23,7 +23,7 @@ from pydal.objects import Field as _Field
|
|
|
23
23
|
from pydal.objects import Query as _Query
|
|
24
24
|
from pydal.objects import Row
|
|
25
25
|
from pydal.objects import Table as _Table
|
|
26
|
-
from typing_extensions import Self
|
|
26
|
+
from typing_extensions import Self, Unpack
|
|
27
27
|
|
|
28
28
|
from .config import TypeDALConfig, load_config
|
|
29
29
|
from .helpers import (
|
|
@@ -58,13 +58,15 @@ from .types import (
|
|
|
58
58
|
Pagination,
|
|
59
59
|
Query,
|
|
60
60
|
Rows,
|
|
61
|
+
SelectKwargs,
|
|
62
|
+
Table,
|
|
61
63
|
Validator,
|
|
62
64
|
_Types,
|
|
63
65
|
)
|
|
64
66
|
|
|
65
67
|
# use typing.cast(type, ...) to make mypy happy with unions
|
|
66
|
-
T_annotation =
|
|
67
|
-
T_Query = typing.Union["Table", Query, bool, None, "TypedTable",
|
|
68
|
+
T_annotation = Type[Any] | types.UnionType
|
|
69
|
+
T_Query = typing.Union["Table", Query, bool, None, "TypedTable", Type["TypedTable"]]
|
|
68
70
|
T_Value = typing.TypeVar("T_Value") # actual type of the Field (via Generic)
|
|
69
71
|
T_MetaInstance = typing.TypeVar("T_MetaInstance", bound="TypedTable") # bound="TypedTable"; bound="TableMeta"
|
|
70
72
|
T = typing.TypeVar("T")
|
|
@@ -100,7 +102,7 @@ JOIN_OPTIONS = typing.Literal["left", "inner", None]
|
|
|
100
102
|
DEFAULT_JOIN_OPTION: JOIN_OPTIONS = "left"
|
|
101
103
|
|
|
102
104
|
# table-ish paramter:
|
|
103
|
-
P_Table = typing.Union[
|
|
105
|
+
P_Table = typing.Union[Type["TypedTable"], pydal.objects.Table]
|
|
104
106
|
|
|
105
107
|
Condition: typing.TypeAlias = typing.Optional[
|
|
106
108
|
typing.Callable[
|
|
@@ -118,7 +120,7 @@ OnQuery: typing.TypeAlias = typing.Optional[
|
|
|
118
120
|
]
|
|
119
121
|
]
|
|
120
122
|
|
|
121
|
-
To_Type = typing.TypeVar("To_Type", type[Any],
|
|
123
|
+
To_Type = typing.TypeVar("To_Type", type[Any], Type[Any], str)
|
|
122
124
|
|
|
123
125
|
|
|
124
126
|
class Relationship(typing.Generic[To_Type]):
|
|
@@ -127,7 +129,7 @@ class Relationship(typing.Generic[To_Type]):
|
|
|
127
129
|
"""
|
|
128
130
|
|
|
129
131
|
_type: To_Type
|
|
130
|
-
table:
|
|
132
|
+
table: Type["TypedTable"] | type | str
|
|
131
133
|
condition: Condition
|
|
132
134
|
on: OnQuery
|
|
133
135
|
multiple: bool
|
|
@@ -186,7 +188,7 @@ class Relationship(typing.Generic[To_Type]):
|
|
|
186
188
|
join = f":{self.join}" if self.join else ""
|
|
187
189
|
return f"<Relationship{join} {src_code}>"
|
|
188
190
|
|
|
189
|
-
def get_table(self, db: "TypeDAL") ->
|
|
191
|
+
def get_table(self, db: "TypeDAL") -> Type["TypedTable"]:
|
|
190
192
|
"""
|
|
191
193
|
Get the table this relationship is bound to.
|
|
192
194
|
"""
|
|
@@ -197,7 +199,7 @@ class Relationship(typing.Generic[To_Type]):
|
|
|
197
199
|
return mapped
|
|
198
200
|
|
|
199
201
|
# boo, fall back to untyped table but pretend it is typed:
|
|
200
|
-
return typing.cast(
|
|
202
|
+
return typing.cast(Type["TypedTable"], db[table]) # eh close enough!
|
|
201
203
|
|
|
202
204
|
return table
|
|
203
205
|
|
|
@@ -239,9 +241,7 @@ class Relationship(typing.Generic[To_Type]):
|
|
|
239
241
|
return None
|
|
240
242
|
|
|
241
243
|
|
|
242
|
-
def relationship(
|
|
243
|
-
_type: To_Type, condition: Condition = None, join: JOIN_OPTIONS = None, on: OnQuery = None
|
|
244
|
-
) -> Relationship[To_Type]:
|
|
244
|
+
def relationship(_type: To_Type, condition: Condition = None, join: JOIN_OPTIONS = None, on: OnQuery = None) -> To_Type:
|
|
245
245
|
"""
|
|
246
246
|
Define a relationship to another table, when its id is not stored in the current table.
|
|
247
247
|
|
|
@@ -271,11 +271,17 @@ def relationship(
|
|
|
271
271
|
|
|
272
272
|
If you'd try to capture this in a single 'condition', pydal would create a cross join which is much less efficient.
|
|
273
273
|
"""
|
|
274
|
-
return
|
|
274
|
+
return typing.cast(
|
|
275
|
+
# note: The descriptor `Relationship[To_Type]` is more correct, but pycharm doesn't really get that.
|
|
276
|
+
# so for ease of use, just cast to the refered type for now!
|
|
277
|
+
# e.g. x = relationship(Author) -> x: Author
|
|
278
|
+
To_Type,
|
|
279
|
+
Relationship(_type, condition, join, on),
|
|
280
|
+
)
|
|
275
281
|
|
|
276
282
|
|
|
277
283
|
def _generate_relationship_condition(
|
|
278
|
-
_:
|
|
284
|
+
_: Type["TypedTable"], key: str, field: typing.Union["TypedField[Any]", "Table", Type["TypedTable"]]
|
|
279
285
|
) -> Condition:
|
|
280
286
|
origin = typing.get_origin(field)
|
|
281
287
|
# else: generic
|
|
@@ -292,9 +298,9 @@ def _generate_relationship_condition(
|
|
|
292
298
|
|
|
293
299
|
|
|
294
300
|
def to_relationship(
|
|
295
|
-
cls:
|
|
301
|
+
cls: Type["TypedTable"] | type[Any],
|
|
296
302
|
key: str,
|
|
297
|
-
field: typing.Union["TypedField[Any]", "Table",
|
|
303
|
+
field: typing.Union["TypedField[Any]", "Table", Type["TypedTable"]],
|
|
298
304
|
) -> typing.Optional[Relationship[Any]]:
|
|
299
305
|
"""
|
|
300
306
|
Used to automatically create relationship instance for reference fields.
|
|
@@ -425,7 +431,7 @@ class TypeDAL(pydal.DAL): # type: ignore
|
|
|
425
431
|
self.try_define(_TypedalCache)
|
|
426
432
|
self.try_define(_TypedalCacheDependency)
|
|
427
433
|
|
|
428
|
-
def try_define(self, model:
|
|
434
|
+
def try_define(self, model: Type[T], verbose: bool = False) -> Type[T]:
|
|
429
435
|
"""
|
|
430
436
|
Try to define a model with migrate or fall back to fake migrate.
|
|
431
437
|
"""
|
|
@@ -449,9 +455,9 @@ class TypeDAL(pydal.DAL): # type: ignore
|
|
|
449
455
|
}
|
|
450
456
|
|
|
451
457
|
# maps table name to typedal class, for resolving future references
|
|
452
|
-
_class_map: typing.ClassVar[dict[str,
|
|
458
|
+
_class_map: typing.ClassVar[dict[str, Type["TypedTable"]]] = {}
|
|
453
459
|
|
|
454
|
-
def _define(self, cls:
|
|
460
|
+
def _define(self, cls: Type[T], **kwargs: Any) -> Type[T]:
|
|
455
461
|
# todo: new relationship item added should also invalidate (previously unrelated) cache result
|
|
456
462
|
|
|
457
463
|
# todo: option to enable/disable cache dependency behavior:
|
|
@@ -553,7 +559,7 @@ class TypeDAL(pydal.DAL): # type: ignore
|
|
|
553
559
|
return cls
|
|
554
560
|
|
|
555
561
|
@typing.overload
|
|
556
|
-
def define(self, maybe_cls: None = None, **kwargs: Any) -> typing.Callable[[
|
|
562
|
+
def define(self, maybe_cls: None = None, **kwargs: Any) -> typing.Callable[[Type[T]], Type[T]]:
|
|
557
563
|
"""
|
|
558
564
|
Typing Overload for define without a class.
|
|
559
565
|
|
|
@@ -562,7 +568,7 @@ class TypeDAL(pydal.DAL): # type: ignore
|
|
|
562
568
|
"""
|
|
563
569
|
|
|
564
570
|
@typing.overload
|
|
565
|
-
def define(self, maybe_cls:
|
|
571
|
+
def define(self, maybe_cls: Type[T], **kwargs: Any) -> Type[T]:
|
|
566
572
|
"""
|
|
567
573
|
Typing Overload for define with a class.
|
|
568
574
|
|
|
@@ -570,9 +576,7 @@ class TypeDAL(pydal.DAL): # type: ignore
|
|
|
570
576
|
class MyTable(TypedTable): ...
|
|
571
577
|
"""
|
|
572
578
|
|
|
573
|
-
def define(
|
|
574
|
-
self, maybe_cls: typing.Type[T] | None = None, **kwargs: Any
|
|
575
|
-
) -> typing.Type[T] | typing.Callable[[typing.Type[T]], typing.Type[T]]:
|
|
579
|
+
def define(self, maybe_cls: Type[T] | None = None, **kwargs: Any) -> Type[T] | typing.Callable[[Type[T]], Type[T]]:
|
|
576
580
|
"""
|
|
577
581
|
Can be used as a decorator on a class that inherits `TypedTable`, \
|
|
578
582
|
or as a regular method if you need to define your classes before you have access to a 'db' instance.
|
|
@@ -595,7 +599,7 @@ class TypeDAL(pydal.DAL): # type: ignore
|
|
|
595
599
|
the result of pydal.define_table
|
|
596
600
|
"""
|
|
597
601
|
|
|
598
|
-
def wrapper(cls:
|
|
602
|
+
def wrapper(cls: Type[T]) -> Type[T]:
|
|
599
603
|
return self._define(cls, **kwargs)
|
|
600
604
|
|
|
601
605
|
if maybe_cls:
|
|
@@ -645,7 +649,7 @@ class TypeDAL(pydal.DAL): # type: ignore
|
|
|
645
649
|
|
|
646
650
|
if isinstance(cls, type) and issubclass(type(cls), type) and issubclass(cls, TypedTable):
|
|
647
651
|
# table defined without @db.define decorator!
|
|
648
|
-
_cls:
|
|
652
|
+
_cls: Type[TypedTable] = cls
|
|
649
653
|
args[0] = _cls.id != None
|
|
650
654
|
|
|
651
655
|
_set = super().__call__(*args, **kwargs)
|
|
@@ -669,11 +673,11 @@ class TypeDAL(pydal.DAL): # type: ignore
|
|
|
669
673
|
cls, _ftype: T_annotation, mut_kw: typing.MutableMapping[str, Any]
|
|
670
674
|
) -> Optional[str]:
|
|
671
675
|
# ftype can be a union or type. typing.cast is sometimes used to tell mypy when it's not a union.
|
|
672
|
-
ftype = typing.cast(type, _ftype) # cast from
|
|
676
|
+
ftype = typing.cast(type, _ftype) # cast from Type to type to make mypy happy)
|
|
673
677
|
|
|
674
678
|
if isinstance(ftype, str):
|
|
675
679
|
# extract type from string
|
|
676
|
-
ftype = typing.get_args(
|
|
680
|
+
ftype = typing.get_args(Type[ftype])[0]._evaluate(
|
|
677
681
|
localns=locals(), globalns=globals(), recursive_guard=frozenset()
|
|
678
682
|
)
|
|
679
683
|
|
|
@@ -753,25 +757,6 @@ class TypeDAL(pydal.DAL): # type: ignore
|
|
|
753
757
|
return to_snake(camel)
|
|
754
758
|
|
|
755
759
|
|
|
756
|
-
class TableProtocol(typing.Protocol): # pragma: no cover
|
|
757
|
-
"""
|
|
758
|
-
Make mypy happy.
|
|
759
|
-
"""
|
|
760
|
-
|
|
761
|
-
id: "TypedField[int]"
|
|
762
|
-
|
|
763
|
-
def __getitem__(self, item: str) -> Field:
|
|
764
|
-
"""
|
|
765
|
-
Tell mypy a Table supports dictionary notation for columns.
|
|
766
|
-
"""
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
class Table(_Table, TableProtocol): # type: ignore
|
|
770
|
-
"""
|
|
771
|
-
Make mypy happy.
|
|
772
|
-
"""
|
|
773
|
-
|
|
774
|
-
|
|
775
760
|
class TableMeta(type):
|
|
776
761
|
"""
|
|
777
762
|
This metaclass contains functionality on table classes, that doesn't exist on its instances.
|
|
@@ -852,13 +837,13 @@ class TableMeta(type):
|
|
|
852
837
|
else:
|
|
853
838
|
return f"<unbound table {self.__name__}>"
|
|
854
839
|
|
|
855
|
-
def from_row(self:
|
|
840
|
+
def from_row(self: Type[T_MetaInstance], row: pydal.objects.Row) -> T_MetaInstance:
|
|
856
841
|
"""
|
|
857
842
|
Create a model instance from a pydal row.
|
|
858
843
|
"""
|
|
859
844
|
return self(row)
|
|
860
845
|
|
|
861
|
-
def all(self:
|
|
846
|
+
def all(self: Type[T_MetaInstance]) -> "TypedRows[T_MetaInstance]":
|
|
862
847
|
"""
|
|
863
848
|
Return all rows for this model.
|
|
864
849
|
"""
|
|
@@ -874,7 +859,7 @@ class TableMeta(type):
|
|
|
874
859
|
# TypeDAL Modified Logic #
|
|
875
860
|
##########################
|
|
876
861
|
|
|
877
|
-
def insert(self:
|
|
862
|
+
def insert(self: Type[T_MetaInstance], **fields: Any) -> T_MetaInstance:
|
|
878
863
|
"""
|
|
879
864
|
This is only called when db.define is not used as a decorator.
|
|
880
865
|
|
|
@@ -897,7 +882,7 @@ class TableMeta(type):
|
|
|
897
882
|
|
|
898
883
|
return str(table._insert(**fields))
|
|
899
884
|
|
|
900
|
-
def bulk_insert(self:
|
|
885
|
+
def bulk_insert(self: Type[T_MetaInstance], items: list[AnyDict]) -> "TypedRows[T_MetaInstance]":
|
|
901
886
|
"""
|
|
902
887
|
Insert multiple rows, returns a TypedRows set of new instances.
|
|
903
888
|
"""
|
|
@@ -906,7 +891,7 @@ class TableMeta(type):
|
|
|
906
891
|
return self.where(lambda row: row.id.belongs(result)).collect()
|
|
907
892
|
|
|
908
893
|
def update_or_insert(
|
|
909
|
-
self:
|
|
894
|
+
self: Type[T_MetaInstance], query: T_Query | AnyDict = DEFAULT, **values: Any
|
|
910
895
|
) -> T_MetaInstance:
|
|
911
896
|
"""
|
|
912
897
|
Update a row if query matches, else insert a new one.
|
|
@@ -929,7 +914,7 @@ class TableMeta(type):
|
|
|
929
914
|
return self(record)
|
|
930
915
|
|
|
931
916
|
def validate_and_insert(
|
|
932
|
-
self:
|
|
917
|
+
self: Type[T_MetaInstance], **fields: Any
|
|
933
918
|
) -> tuple[Optional[T_MetaInstance], Optional[dict[str, str]]]:
|
|
934
919
|
"""
|
|
935
920
|
Validate input data and then insert a row.
|
|
@@ -944,7 +929,7 @@ class TableMeta(type):
|
|
|
944
929
|
return None, result.get("errors")
|
|
945
930
|
|
|
946
931
|
def validate_and_update(
|
|
947
|
-
self:
|
|
932
|
+
self: Type[T_MetaInstance], query: Query, **fields: Any
|
|
948
933
|
) -> tuple[Optional[T_MetaInstance], Optional[dict[str, str]]]:
|
|
949
934
|
"""
|
|
950
935
|
Validate input data and then update max 1 row.
|
|
@@ -953,10 +938,7 @@ class TableMeta(type):
|
|
|
953
938
|
"""
|
|
954
939
|
table = self._ensure_table_defined()
|
|
955
940
|
|
|
956
|
-
|
|
957
|
-
result = table.validate_and_update(query, **fields)
|
|
958
|
-
except Exception as e:
|
|
959
|
-
result = {"errors": {"exception": str(e)}}
|
|
941
|
+
result = table.validate_and_update(query, **fields)
|
|
960
942
|
|
|
961
943
|
if errors := result.get("errors"):
|
|
962
944
|
return None, errors
|
|
@@ -967,7 +949,7 @@ class TableMeta(type):
|
|
|
967
949
|
return None, None
|
|
968
950
|
|
|
969
951
|
def validate_and_update_or_insert(
|
|
970
|
-
self:
|
|
952
|
+
self: Type[T_MetaInstance], query: Query, **fields: Any
|
|
971
953
|
) -> tuple[Optional[T_MetaInstance], Optional[dict[str, str]]]:
|
|
972
954
|
"""
|
|
973
955
|
Validate input data and then update_and_insert (on max 1 row).
|
|
@@ -985,53 +967,57 @@ class TableMeta(type):
|
|
|
985
967
|
# update on query without result (shouldnt happen)
|
|
986
968
|
return None, None
|
|
987
969
|
|
|
988
|
-
def select(self:
|
|
970
|
+
def select(self: Type[T_MetaInstance], *a: Any, **kw: Any) -> "QueryBuilder[T_MetaInstance]":
|
|
989
971
|
"""
|
|
990
972
|
See QueryBuilder.select!
|
|
991
973
|
"""
|
|
992
974
|
return QueryBuilder(self).select(*a, **kw)
|
|
993
975
|
|
|
994
|
-
def paginate(self:
|
|
976
|
+
def paginate(self: Type[T_MetaInstance], limit: int, page: int = 1) -> "PaginatedRows[T_MetaInstance]":
|
|
995
977
|
"""
|
|
996
978
|
See QueryBuilder.paginate!
|
|
997
979
|
"""
|
|
998
980
|
return QueryBuilder(self).paginate(limit=limit, page=page)
|
|
999
981
|
|
|
1000
|
-
def chunk(
|
|
1001
|
-
self: typing.Type[T_MetaInstance], chunk_size: int
|
|
1002
|
-
) -> typing.Generator["TypedRows[T_MetaInstance]", Any, None]:
|
|
982
|
+
def chunk(self: Type[T_MetaInstance], chunk_size: int) -> typing.Generator["TypedRows[T_MetaInstance]", Any, None]:
|
|
1003
983
|
"""
|
|
1004
984
|
See QueryBuilder.chunk!
|
|
1005
985
|
"""
|
|
1006
986
|
return QueryBuilder(self).chunk(chunk_size)
|
|
1007
987
|
|
|
1008
|
-
def where(self:
|
|
988
|
+
def where(self: Type[T_MetaInstance], *a: Any, **kw: Any) -> "QueryBuilder[T_MetaInstance]":
|
|
1009
989
|
"""
|
|
1010
990
|
See QueryBuilder.where!
|
|
1011
991
|
"""
|
|
1012
992
|
return QueryBuilder(self).where(*a, **kw)
|
|
1013
993
|
|
|
1014
|
-
def cache(self:
|
|
994
|
+
def cache(self: Type[T_MetaInstance], *deps: Any, **kwargs: Any) -> "QueryBuilder[T_MetaInstance]":
|
|
1015
995
|
"""
|
|
1016
996
|
See QueryBuilder.cache!
|
|
1017
997
|
"""
|
|
1018
998
|
return QueryBuilder(self).cache(*deps, **kwargs)
|
|
1019
999
|
|
|
1020
|
-
def count(self:
|
|
1000
|
+
def count(self: Type[T_MetaInstance]) -> int:
|
|
1021
1001
|
"""
|
|
1022
1002
|
See QueryBuilder.count!
|
|
1023
1003
|
"""
|
|
1024
1004
|
return QueryBuilder(self).count()
|
|
1025
1005
|
|
|
1026
|
-
def first(self:
|
|
1006
|
+
def first(self: Type[T_MetaInstance]) -> T_MetaInstance | None:
|
|
1027
1007
|
"""
|
|
1028
1008
|
See QueryBuilder.first!
|
|
1029
1009
|
"""
|
|
1030
1010
|
return QueryBuilder(self).first()
|
|
1031
1011
|
|
|
1012
|
+
def first_or_fail(self: Type[T_MetaInstance]) -> T_MetaInstance:
|
|
1013
|
+
"""
|
|
1014
|
+
See QueryBuilder.first_or_fail!
|
|
1015
|
+
"""
|
|
1016
|
+
return QueryBuilder(self).first_or_fail()
|
|
1017
|
+
|
|
1032
1018
|
def join(
|
|
1033
|
-
self:
|
|
1034
|
-
*fields: str |
|
|
1019
|
+
self: Type[T_MetaInstance],
|
|
1020
|
+
*fields: str | Type["TypedTable"],
|
|
1035
1021
|
method: JOIN_OPTIONS = None,
|
|
1036
1022
|
on: OnQuery | list[Expression] | Expression = None,
|
|
1037
1023
|
condition: Condition = None,
|
|
@@ -1041,7 +1027,7 @@ class TableMeta(type):
|
|
|
1041
1027
|
"""
|
|
1042
1028
|
return QueryBuilder(self).join(*fields, on=on, condition=condition, method=method)
|
|
1043
1029
|
|
|
1044
|
-
def collect(self:
|
|
1030
|
+
def collect(self: Type[T_MetaInstance], verbose: bool = False) -> "TypedRows[T_MetaInstance]":
|
|
1045
1031
|
"""
|
|
1046
1032
|
See QueryBuilder.collect!
|
|
1047
1033
|
"""
|
|
@@ -1141,7 +1127,7 @@ class TableMeta(type):
|
|
|
1141
1127
|
table = self._ensure_table_defined()
|
|
1142
1128
|
return typing.cast(Expression, table.on(query))
|
|
1143
1129
|
|
|
1144
|
-
def with_alias(self, alias: str) ->
|
|
1130
|
+
def with_alias(self: Type[T_MetaInstance], alias: str) -> Type[T_MetaInstance]:
|
|
1145
1131
|
"""
|
|
1146
1132
|
Shadow Table.with_alias.
|
|
1147
1133
|
|
|
@@ -1151,12 +1137,12 @@ class TableMeta(type):
|
|
|
1151
1137
|
http://web2py.com/books/default/chapter/29/06/the-database-abstraction-layer?search=export_to_csv_file#One-to-many-relation
|
|
1152
1138
|
"""
|
|
1153
1139
|
table = self._ensure_table_defined()
|
|
1154
|
-
return table.with_alias(alias)
|
|
1140
|
+
return typing.cast(Type[T_MetaInstance], table.with_alias(alias))
|
|
1155
1141
|
|
|
1156
1142
|
# @typing.dataclass_transform()
|
|
1157
1143
|
|
|
1158
1144
|
|
|
1159
|
-
class TypedField(typing.Generic[T_Value]): # pragma: no cover
|
|
1145
|
+
class TypedField(Expression, typing.Generic[T_Value]): # pragma: no cover
|
|
1160
1146
|
"""
|
|
1161
1147
|
Typed version of pydal.Field, which will be converted to a normal Field in the background.
|
|
1162
1148
|
"""
|
|
@@ -1173,28 +1159,28 @@ class TypedField(typing.Generic[T_Value]): # pragma: no cover
|
|
|
1173
1159
|
|
|
1174
1160
|
requires: Validator | typing.Iterable[Validator]
|
|
1175
1161
|
|
|
1176
|
-
def __init__(self, _type:
|
|
1162
|
+
def __init__(self, _type: Type[T_Value] | types.UnionType = str, /, **settings: Any) -> None: # type: ignore
|
|
1177
1163
|
"""
|
|
1178
1164
|
A TypedFieldType should not be inited manually, but TypedField (from `fields.py`) should be used!
|
|
1179
1165
|
"""
|
|
1180
1166
|
self._type = _type
|
|
1181
1167
|
self.kwargs = settings
|
|
1182
|
-
super().__init__()
|
|
1168
|
+
# super().__init__()
|
|
1183
1169
|
|
|
1184
1170
|
@typing.overload
|
|
1185
|
-
def __get__(self, instance: T_MetaInstance, owner:
|
|
1171
|
+
def __get__(self, instance: T_MetaInstance, owner: Type[T_MetaInstance]) -> T_Value: # pragma: no cover
|
|
1186
1172
|
"""
|
|
1187
1173
|
row.field -> (actual data).
|
|
1188
1174
|
"""
|
|
1189
1175
|
|
|
1190
1176
|
@typing.overload
|
|
1191
|
-
def __get__(self, instance: None, owner: "
|
|
1177
|
+
def __get__(self, instance: None, owner: "Type[TypedTable]") -> "TypedField[T_Value]": # pragma: no cover
|
|
1192
1178
|
"""
|
|
1193
1179
|
Table.field -> Field.
|
|
1194
1180
|
"""
|
|
1195
1181
|
|
|
1196
1182
|
def __get__(
|
|
1197
|
-
self, instance: T_MetaInstance | None, owner:
|
|
1183
|
+
self, instance: T_MetaInstance | None, owner: Type[T_MetaInstance]
|
|
1198
1184
|
) -> typing.Union[T_Value, "TypedField[T_Value]"]:
|
|
1199
1185
|
"""
|
|
1200
1186
|
Since this class is a Descriptor field, \
|
|
@@ -1323,6 +1309,17 @@ class TypedField(typing.Generic[T_Value]): # pragma: no cover
|
|
|
1323
1309
|
|
|
1324
1310
|
return typing.cast(Expression, ~self._field)
|
|
1325
1311
|
|
|
1312
|
+
def lower(self) -> Expression:
|
|
1313
|
+
"""
|
|
1314
|
+
For string-fields: compare lowercased values.
|
|
1315
|
+
"""
|
|
1316
|
+
if not self._field: # pragma: no cover
|
|
1317
|
+
raise ValueError("Unbound Field can not be lowered!")
|
|
1318
|
+
|
|
1319
|
+
return typing.cast(Expression, self._field.lower())
|
|
1320
|
+
|
|
1321
|
+
# ... etc
|
|
1322
|
+
|
|
1326
1323
|
|
|
1327
1324
|
class _TypedTable:
|
|
1328
1325
|
"""
|
|
@@ -1377,7 +1374,7 @@ class TypedTable(_TypedTable, metaclass=TableMeta):
|
|
|
1377
1374
|
|
|
1378
1375
|
def __new__(
|
|
1379
1376
|
cls, row_or_id: typing.Union[Row, Query, pydal.objects.Set, int, str, None, "TypedTable"] = None, **filters: Any
|
|
1380
|
-
) ->
|
|
1377
|
+
) -> Self:
|
|
1381
1378
|
"""
|
|
1382
1379
|
Create a Typed Rows model instance from an existing row, ID or query.
|
|
1383
1380
|
|
|
@@ -1391,7 +1388,8 @@ class TypedTable(_TypedTable, metaclass=TableMeta):
|
|
|
1391
1388
|
|
|
1392
1389
|
if isinstance(row_or_id, TypedTable):
|
|
1393
1390
|
# existing typed table instance!
|
|
1394
|
-
return row_or_id
|
|
1391
|
+
return typing.cast(Self, row_or_id)
|
|
1392
|
+
|
|
1395
1393
|
elif isinstance(row_or_id, pydal.objects.Row):
|
|
1396
1394
|
row = row_or_id
|
|
1397
1395
|
elif row_or_id is not None:
|
|
@@ -1598,7 +1596,7 @@ class TypedTable(_TypedTable, metaclass=TableMeta):
|
|
|
1598
1596
|
super().__setattr__(key, value)
|
|
1599
1597
|
|
|
1600
1598
|
@classmethod
|
|
1601
|
-
def update(cls:
|
|
1599
|
+
def update(cls: Type[T_MetaInstance], query: Query, **fields: Any) -> T_MetaInstance | None:
|
|
1602
1600
|
"""
|
|
1603
1601
|
Update one record.
|
|
1604
1602
|
|
|
@@ -1697,7 +1695,7 @@ class TypedRows(typing.Collection[T_MetaInstance], Rows):
|
|
|
1697
1695
|
|
|
1698
1696
|
records: dict[int, T_MetaInstance]
|
|
1699
1697
|
# _rows: Rows
|
|
1700
|
-
model:
|
|
1698
|
+
model: Type[T_MetaInstance]
|
|
1701
1699
|
metadata: Metadata
|
|
1702
1700
|
|
|
1703
1701
|
# pseudo-properties: actually stored in _rows
|
|
@@ -1710,7 +1708,7 @@ class TypedRows(typing.Collection[T_MetaInstance], Rows):
|
|
|
1710
1708
|
def __init__(
|
|
1711
1709
|
self,
|
|
1712
1710
|
rows: Rows,
|
|
1713
|
-
model:
|
|
1711
|
+
model: Type[T_MetaInstance],
|
|
1714
1712
|
records: dict[int, T_MetaInstance] = None,
|
|
1715
1713
|
metadata: Metadata = None,
|
|
1716
1714
|
) -> None:
|
|
@@ -1940,7 +1938,7 @@ class TypedRows(typing.Collection[T_MetaInstance], Rows):
|
|
|
1940
1938
|
Update the current rows in the database with new_values.
|
|
1941
1939
|
"""
|
|
1942
1940
|
# cast to make mypy understand .id is a TypedField and not an int!
|
|
1943
|
-
table = typing.cast(
|
|
1941
|
+
table = typing.cast(Type[TypedTable], self.model._ensure_table_defined())
|
|
1944
1942
|
|
|
1945
1943
|
ids = set(self.column("id"))
|
|
1946
1944
|
query = table.id.belongs(ids)
|
|
@@ -1951,7 +1949,7 @@ class TypedRows(typing.Collection[T_MetaInstance], Rows):
|
|
|
1951
1949
|
Delete the currently selected rows from the database.
|
|
1952
1950
|
"""
|
|
1953
1951
|
# cast to make mypy understand .id is a TypedField and not an int!
|
|
1954
|
-
table = typing.cast(
|
|
1952
|
+
table = typing.cast(Type[TypedTable], self.model._ensure_table_defined())
|
|
1955
1953
|
|
|
1956
1954
|
ids = set(self.column("id"))
|
|
1957
1955
|
query = table.id.belongs(ids)
|
|
@@ -2006,7 +2004,7 @@ class TypedRows(typing.Collection[T_MetaInstance], Rows):
|
|
|
2006
2004
|
|
|
2007
2005
|
@classmethod
|
|
2008
2006
|
def from_rows(
|
|
2009
|
-
cls, rows: Rows, model:
|
|
2007
|
+
cls, rows: Rows, model: Type[T_MetaInstance], metadata: Metadata = None
|
|
2010
2008
|
) -> "TypedRows[T_MetaInstance]":
|
|
2011
2009
|
"""
|
|
2012
2010
|
Internal method to convert a Rows object to a TypedRows.
|
|
@@ -2049,19 +2047,19 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
|
|
|
2049
2047
|
Abstration on top of pydal's query system.
|
|
2050
2048
|
"""
|
|
2051
2049
|
|
|
2052
|
-
model:
|
|
2050
|
+
model: Type[T_MetaInstance]
|
|
2053
2051
|
query: Query
|
|
2054
2052
|
select_args: list[Any]
|
|
2055
|
-
select_kwargs:
|
|
2053
|
+
select_kwargs: SelectKwargs
|
|
2056
2054
|
relationships: dict[str, Relationship[Any]]
|
|
2057
2055
|
metadata: Metadata
|
|
2058
2056
|
|
|
2059
2057
|
def __init__(
|
|
2060
2058
|
self,
|
|
2061
|
-
model:
|
|
2059
|
+
model: Type[T_MetaInstance],
|
|
2062
2060
|
add_query: Optional[Query] = None,
|
|
2063
2061
|
select_args: Optional[list[Any]] = None,
|
|
2064
|
-
select_kwargs: Optional[
|
|
2062
|
+
select_kwargs: Optional[SelectKwargs] = None,
|
|
2065
2063
|
relationships: dict[str, Relationship[Any]] = None,
|
|
2066
2064
|
metadata: Metadata = None,
|
|
2067
2065
|
):
|
|
@@ -2111,7 +2109,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
|
|
|
2111
2109
|
add_query: Optional[Query] = None,
|
|
2112
2110
|
overwrite_query: Optional[Query] = None,
|
|
2113
2111
|
select_args: Optional[list[Any]] = None,
|
|
2114
|
-
select_kwargs: Optional[
|
|
2112
|
+
select_kwargs: Optional[SelectKwargs] = None,
|
|
2115
2113
|
relationships: dict[str, Relationship[Any]] = None,
|
|
2116
2114
|
metadata: Metadata = None,
|
|
2117
2115
|
) -> "QueryBuilder[T_MetaInstance]":
|
|
@@ -2124,7 +2122,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
|
|
|
2124
2122
|
(self.metadata | (metadata or {})) if metadata else self.metadata,
|
|
2125
2123
|
)
|
|
2126
2124
|
|
|
2127
|
-
def select(self, *fields: Any, **options:
|
|
2125
|
+
def select(self, *fields: Any, **options: Unpack[SelectKwargs]) -> "QueryBuilder[T_MetaInstance]":
|
|
2128
2126
|
"""
|
|
2129
2127
|
Fields: database columns by name ('id'), by field reference (table.id) or other (e.g. table.ALL).
|
|
2130
2128
|
|
|
@@ -2153,7 +2151,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
|
|
|
2153
2151
|
|
|
2154
2152
|
def where(
|
|
2155
2153
|
self,
|
|
2156
|
-
*queries_or_lambdas: Query | typing.Callable[[
|
|
2154
|
+
*queries_or_lambdas: Query | typing.Callable[[Type[T_MetaInstance]], Query],
|
|
2157
2155
|
**filters: Any,
|
|
2158
2156
|
) -> "QueryBuilder[T_MetaInstance]":
|
|
2159
2157
|
"""
|
|
@@ -2194,7 +2192,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
|
|
|
2194
2192
|
|
|
2195
2193
|
def join(
|
|
2196
2194
|
self,
|
|
2197
|
-
*fields: str |
|
|
2195
|
+
*fields: str | Type[TypedTable],
|
|
2198
2196
|
method: JOIN_OPTIONS = None,
|
|
2199
2197
|
on: OnQuery | list[Expression] | Expression = None,
|
|
2200
2198
|
condition: Condition = None,
|
|
@@ -2221,7 +2219,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
|
|
|
2221
2219
|
if isinstance(condition, pydal.objects.Query):
|
|
2222
2220
|
condition = as_lambda(condition)
|
|
2223
2221
|
|
|
2224
|
-
relationships = {str(fields[0]):
|
|
2222
|
+
relationships = {str(fields[0]): Relationship(fields[0], condition=condition, join=method)}
|
|
2225
2223
|
elif on:
|
|
2226
2224
|
if len(fields) != 1:
|
|
2227
2225
|
raise ValueError("join(field, on=...) can only be used with exactly one field!")
|
|
@@ -2231,7 +2229,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
|
|
|
2231
2229
|
|
|
2232
2230
|
if isinstance(on, list):
|
|
2233
2231
|
on = as_lambda(on)
|
|
2234
|
-
relationships = {str(fields[0]):
|
|
2232
|
+
relationships = {str(fields[0]): Relationship(fields[0], on=on, join=method)}
|
|
2235
2233
|
|
|
2236
2234
|
else:
|
|
2237
2235
|
if fields:
|
|
@@ -2313,7 +2311,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
|
|
|
2313
2311
|
db = self._get_db()
|
|
2314
2312
|
return str(db(self.query)._update(**fields))
|
|
2315
2313
|
|
|
2316
|
-
def _before_query(self, mut_metadata: Metadata, add_id: bool = True) -> tuple[Query, list[Any],
|
|
2314
|
+
def _before_query(self, mut_metadata: Metadata, add_id: bool = True) -> tuple[Query, list[Any], SelectKwargs]:
|
|
2317
2315
|
select_args = [self._select_arg_convert(_) for _ in self.select_args] or [self.model.ALL]
|
|
2318
2316
|
select_kwargs = self.select_kwargs.copy()
|
|
2319
2317
|
query = self.query
|
|
@@ -2385,7 +2383,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
|
|
|
2385
2383
|
return db(query).select(*select_args, **select_kwargs)
|
|
2386
2384
|
|
|
2387
2385
|
def collect(
|
|
2388
|
-
self, verbose: bool = False, _to:
|
|
2386
|
+
self, verbose: bool = False, _to: Type["TypedRows[Any]"] = None, add_id: bool = True
|
|
2389
2387
|
) -> "TypedRows[T_MetaInstance]":
|
|
2390
2388
|
"""
|
|
2391
2389
|
Execute the built query and turn it into model instances, while handling relationships.
|
|
@@ -2432,7 +2430,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
|
|
|
2432
2430
|
self,
|
|
2433
2431
|
query: Query,
|
|
2434
2432
|
select_args: list[Any],
|
|
2435
|
-
select_kwargs:
|
|
2433
|
+
select_kwargs: SelectKwargs,
|
|
2436
2434
|
metadata: Metadata,
|
|
2437
2435
|
) -> tuple[Query, list[Any]]:
|
|
2438
2436
|
db = self._get_db()
|
|
@@ -2450,13 +2448,16 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
|
|
|
2450
2448
|
other = other.with_alias(f"{key}_{hash(relation)}")
|
|
2451
2449
|
join.append(other.on(relation.condition(model, other)))
|
|
2452
2450
|
|
|
2453
|
-
if limitby := select_kwargs.pop("limitby",
|
|
2451
|
+
if limitby := select_kwargs.pop("limitby", ()):
|
|
2452
|
+
|
|
2454
2453
|
# if limitby + relationships:
|
|
2455
2454
|
# 1. get IDs of main table entries that match 'query'
|
|
2456
2455
|
# 2. change query to .belongs(id)
|
|
2457
2456
|
# 3. add joins etc
|
|
2458
2457
|
|
|
2459
|
-
kwargs = {"limitby": limitby}
|
|
2458
|
+
kwargs: SelectKwargs = select_kwargs | {"limitby": limitby}
|
|
2459
|
+
# if orderby := select_kwargs.get("orderby"):
|
|
2460
|
+
# kwargs["orderby"] = orderby
|
|
2460
2461
|
|
|
2461
2462
|
if join:
|
|
2462
2463
|
kwargs["join"] = join
|
|
@@ -2520,7 +2521,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
|
|
|
2520
2521
|
return query, select_args
|
|
2521
2522
|
|
|
2522
2523
|
def _collect_with_relationships(
|
|
2523
|
-
self, rows: Rows, metadata: Metadata, _to:
|
|
2524
|
+
self, rows: Rows, metadata: Metadata, _to: Type["TypedRows[Any]"]
|
|
2524
2525
|
) -> "TypedRows[T_MetaInstance]":
|
|
2525
2526
|
"""
|
|
2526
2527
|
Transform the raw rows into Typed Table model instances.
|
|
@@ -2801,7 +2802,7 @@ class TypedSet(pydal.objects.Set): # type: ignore # pragma: no cover
|
|
|
2801
2802
|
result: TypedRows[MyTable] = db(MyTable.id > 0).select()
|
|
2802
2803
|
|
|
2803
2804
|
for row in result:
|
|
2804
|
-
|
|
2805
|
+
reveal_type(row) # MyTable
|
|
2805
2806
|
"""
|
|
2806
2807
|
rows = super().select(*fields, **attributes)
|
|
2807
2808
|
return typing.cast(TypedRows[T_MetaInstance], rows)
|
typedal/helpers.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Helpers that work independently of core.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
import datetime as dt
|
|
5
6
|
import fnmatch
|
|
6
7
|
import io
|
|
7
8
|
import types
|
|
@@ -265,3 +266,11 @@ def match_strings(patterns: list[str] | str, string_list: list[str]) -> list[str
|
|
|
265
266
|
matches.extend([s for s in string_list if fnmatch.fnmatch(s, pattern)])
|
|
266
267
|
|
|
267
268
|
return matches
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def utcnow() -> dt.datetime:
|
|
272
|
+
"""
|
|
273
|
+
Replacement of datetime.utcnow.
|
|
274
|
+
"""
|
|
275
|
+
# return dt.datetime.now(dt.UTC)
|
|
276
|
+
return dt.datetime.now(dt.timezone.utc)
|
typedal/types.py
CHANGED
|
@@ -6,6 +6,7 @@ import typing
|
|
|
6
6
|
from datetime import datetime
|
|
7
7
|
from typing import Any, Optional, TypedDict
|
|
8
8
|
|
|
9
|
+
from pydal.adapters.base import BaseAdapter
|
|
9
10
|
from pydal.helpers.classes import OpRow as _OpRow
|
|
10
11
|
from pydal.helpers.classes import Reference as _Reference
|
|
11
12
|
from pydal.objects import Expression as _Expression
|
|
@@ -13,9 +14,13 @@ from pydal.objects import Field as _Field
|
|
|
13
14
|
from pydal.objects import Query as _Query
|
|
14
15
|
from pydal.objects import Rows as _Rows
|
|
15
16
|
from pydal.objects import Set as _Set
|
|
17
|
+
from pydal.objects import Table as _Table
|
|
16
18
|
from pydal.validators import Validator as _Validator
|
|
17
19
|
from typing_extensions import NotRequired
|
|
18
20
|
|
|
21
|
+
if typing.TYPE_CHECKING:
|
|
22
|
+
from .core import TypedField
|
|
23
|
+
|
|
19
24
|
AnyDict: typing.TypeAlias = dict[str, Any]
|
|
20
25
|
|
|
21
26
|
|
|
@@ -148,6 +153,62 @@ class PaginationMetadata(TypedDict):
|
|
|
148
153
|
min_max: tuple[int, int]
|
|
149
154
|
|
|
150
155
|
|
|
156
|
+
class TableProtocol(typing.Protocol): # pragma: no cover
|
|
157
|
+
"""
|
|
158
|
+
Make mypy happy.
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
id: "TypedField[int]"
|
|
162
|
+
|
|
163
|
+
def __getitem__(self, item: str) -> Field:
|
|
164
|
+
"""
|
|
165
|
+
Tell mypy a Table supports dictionary notation for columns.
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class Table(_Table, TableProtocol): # type: ignore
|
|
170
|
+
"""
|
|
171
|
+
Make mypy happy.
|
|
172
|
+
"""
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class CacheFn(typing.Protocol):
|
|
176
|
+
"""
|
|
177
|
+
The cache model (e.g. cache.ram) accepts these parameters (all filled by dfeault).
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
def __call__(
|
|
181
|
+
self: BaseAdapter,
|
|
182
|
+
sql: str = "",
|
|
183
|
+
fields: typing.Iterable[str] = (),
|
|
184
|
+
attributes: typing.Iterable[str] = (),
|
|
185
|
+
colnames: typing.Iterable[str] = (),
|
|
186
|
+
) -> Rows:
|
|
187
|
+
"""
|
|
188
|
+
Only used for type-hinting.
|
|
189
|
+
"""
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
# CacheFn = typing.Callable[[], Rows]
|
|
193
|
+
CacheModel = typing.Callable[[str, CacheFn, int], Rows]
|
|
194
|
+
CacheTuple = tuple[CacheModel, int]
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class SelectKwargs(typing.TypedDict, total=False):
|
|
198
|
+
"""
|
|
199
|
+
Possible keyword arguments for .select().
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
join: Optional[list[Expression]]
|
|
203
|
+
left: Optional[list[Expression]]
|
|
204
|
+
orderby: Optional[Expression | str | Table]
|
|
205
|
+
limitby: Optional[tuple[int, int]]
|
|
206
|
+
distinct: bool | Field | Expression
|
|
207
|
+
orderby_on_limitby: bool
|
|
208
|
+
cacheable: bool
|
|
209
|
+
cache: CacheTuple
|
|
210
|
+
|
|
211
|
+
|
|
151
212
|
class Metadata(TypedDict):
|
|
152
213
|
"""
|
|
153
214
|
Loosely structured metadata used by Query Builder.
|
|
@@ -161,7 +222,7 @@ class Metadata(TypedDict):
|
|
|
161
222
|
|
|
162
223
|
final_query: NotRequired[Query | str | None]
|
|
163
224
|
final_args: NotRequired[list[Any]]
|
|
164
|
-
final_kwargs: NotRequired[
|
|
225
|
+
final_kwargs: NotRequired[SelectKwargs]
|
|
165
226
|
relationships: NotRequired[set[str]]
|
|
166
227
|
|
|
167
228
|
sql: NotRequired[str]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: TypeDAL
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.3.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
|
|
@@ -29,14 +29,17 @@ Requires-Dist: tabulate; extra == 'all'
|
|
|
29
29
|
Requires-Dist: tomlkit; extra == 'all'
|
|
30
30
|
Requires-Dist: typer; extra == 'all'
|
|
31
31
|
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: contextlib-chdir; extra == 'dev'
|
|
32
33
|
Requires-Dist: hatch; extra == 'dev'
|
|
33
34
|
Requires-Dist: mkdocs; extra == 'dev'
|
|
34
35
|
Requires-Dist: mkdocs-dracula-theme; extra == 'dev'
|
|
35
36
|
Requires-Dist: pytest-mypy-testing; extra == 'dev'
|
|
36
37
|
Requires-Dist: python-semantic-release<8; extra == 'dev'
|
|
37
|
-
Requires-Dist:
|
|
38
|
+
Requires-Dist: requests<2.32; extra == 'dev'
|
|
39
|
+
Requires-Dist: su6[all]>=1.9.0; extra == 'dev'
|
|
38
40
|
Requires-Dist: testcontainers; extra == 'dev'
|
|
39
41
|
Requires-Dist: types-pyyaml; extra == 'dev'
|
|
42
|
+
Requires-Dist: types-requests; extra == 'dev'
|
|
40
43
|
Requires-Dist: types-tabulate; extra == 'dev'
|
|
41
44
|
Provides-Extra: migrations
|
|
42
45
|
Requires-Dist: edwh-migrate>=0.8.0; extra == 'migrations'
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
typedal/__about__.py,sha256=Wu5Lv4Jglqs8h2_AjyrHv-m67Gz9cmuGT7s5MPNnAFs,206
|
|
2
|
+
typedal/__init__.py,sha256=QQpLiVl9w9hm2LBxey49Y_tCF_VB2bScVaS_mCjYy54,366
|
|
3
|
+
typedal/caching.py,sha256=SMcJsahLlZ79yykWCveERFx1ZJUNEKhA9SPmCTIuLp8,11798
|
|
4
|
+
typedal/cli.py,sha256=BXXu1w9WRRK9GA82xh_JLvvfpNDFWICXKCAWNg4a3-Y,18180
|
|
5
|
+
typedal/config.py,sha256=0qy1zrTUdtmXPM9jHzFnSR1DJsqGJqcdG6pvhzKQHe0,11625
|
|
6
|
+
typedal/core.py,sha256=ScUZkDzHGULNTcrW9Jq4Anf4axUfha-CXWmm7SWrn90,95991
|
|
7
|
+
typedal/fields.py,sha256=z2PD9vLWqBR_zXtiY0DthqTG4AeF3yxKoeuVfGXnSdg,5197
|
|
8
|
+
typedal/for_py4web.py,sha256=d07b8hL_PvNDUS26Z5fDH2OxWb-IETBuAFPSzrRwm04,1285
|
|
9
|
+
typedal/for_web2py.py,sha256=4RHgzGXgKIO_BYB-7adC5e35u52rX-p1t4tPEz-NK24,1867
|
|
10
|
+
typedal/helpers.py,sha256=KbgP4ZRW7KCroyHwTwdErPqylOX4dsqVnKJeZ5TtTFY,7363
|
|
11
|
+
typedal/mixins.py,sha256=OHhVYLBGTzPSJKgp1uovBs1Y6NJa0d-DxHTOk9LyOXA,5414
|
|
12
|
+
typedal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
+
typedal/types.py,sha256=AsUHrAXk058zmdqDbZlpf6sE85ID1Q2vofsGgCXni1s,4851
|
|
14
|
+
typedal/web2py_py4web_shared.py,sha256=VK9T8P5UwVLvfNBsY4q79ANcABv-jX76YKADt1Zz_co,1539
|
|
15
|
+
typedal/serializers/as_json.py,sha256=ffo152W-sARYXym4BzwX709rrO2-QwKk2KunWY8RNl4,2229
|
|
16
|
+
typedal-3.3.1.dist-info/METADATA,sha256=8sBC0FTFEy7qE17mgnL5sj06hkl3LQc_tRP8_j2-kbU,9462
|
|
17
|
+
typedal-3.3.1.dist-info/WHEEL,sha256=KGYbc1zXlYddvwxnNty23BeaKzh7YuoSIvIMO4jEhvw,87
|
|
18
|
+
typedal-3.3.1.dist-info/entry_points.txt,sha256=m1wqcc_10rHWPdlQ71zEkmJDADUAnZtn7Jac_6mbyUc,44
|
|
19
|
+
typedal-3.3.1.dist-info/RECORD,,
|
typedal-3.2.0.dist-info/RECORD
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
typedal/__about__.py,sha256=JM7UvclHJLbyevmmJGGt8HQwzgu3SMpy7q2WeNEm9Io,206
|
|
2
|
-
typedal/__init__.py,sha256=QQpLiVl9w9hm2LBxey49Y_tCF_VB2bScVaS_mCjYy54,366
|
|
3
|
-
typedal/caching.py,sha256=8UABVAhOlBpL96ykmqhxLaFYOe-XeAh7JoGh57OkxP8,11818
|
|
4
|
-
typedal/cli.py,sha256=3tge8B-YjgjMC6425-RMczmWvpOTfWV5QYPXRY23IWA,18200
|
|
5
|
-
typedal/config.py,sha256=jS1K0_1F5rwJtvwTZ-qR29ZCX7WlyORGEIFvfSnusko,11645
|
|
6
|
-
typedal/core.py,sha256=tocfC9KDnuOoPgUTpBhFo2WGTupS5Y0esyFSTcANwLc,95866
|
|
7
|
-
typedal/fields.py,sha256=z2PD9vLWqBR_zXtiY0DthqTG4AeF3yxKoeuVfGXnSdg,5197
|
|
8
|
-
typedal/for_py4web.py,sha256=d07b8hL_PvNDUS26Z5fDH2OxWb-IETBuAFPSzrRwm04,1285
|
|
9
|
-
typedal/for_web2py.py,sha256=4RHgzGXgKIO_BYB-7adC5e35u52rX-p1t4tPEz-NK24,1867
|
|
10
|
-
typedal/helpers.py,sha256=mtRYPFlS0dx2wK8kYSJ4vm1wsTXRkdPumFRlOAjF_xU,7177
|
|
11
|
-
typedal/mixins.py,sha256=OHhVYLBGTzPSJKgp1uovBs1Y6NJa0d-DxHTOk9LyOXA,5414
|
|
12
|
-
typedal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
-
typedal/types.py,sha256=1kGkNX6vfGg6ln84AG558C4Zx5ACRz-emrUTnuy-rRY,3410
|
|
14
|
-
typedal/web2py_py4web_shared.py,sha256=VK9T8P5UwVLvfNBsY4q79ANcABv-jX76YKADt1Zz_co,1539
|
|
15
|
-
typedal/serializers/as_json.py,sha256=ffo152W-sARYXym4BzwX709rrO2-QwKk2KunWY8RNl4,2229
|
|
16
|
-
typedal-3.2.0.dist-info/METADATA,sha256=foiNGED0K4DE5WJOKqWZt693_ms7zMp8JO8GXbblkok,9316
|
|
17
|
-
typedal-3.2.0.dist-info/WHEEL,sha256=uNdcs2TADwSd5pVaP0Z_kcjcvvTUklh2S7bxZMF8Uj0,87
|
|
18
|
-
typedal-3.2.0.dist-info/entry_points.txt,sha256=m1wqcc_10rHWPdlQ71zEkmJDADUAnZtn7Jac_6mbyUc,44
|
|
19
|
-
typedal-3.2.0.dist-info/RECORD,,
|
|
File without changes
|