TypeDAL 3.3.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 CHANGED
@@ -5,4 +5,4 @@ This file contains the Version info for this package.
5
5
  # SPDX-FileCopyrightText: 2023-present Robin van der Noord <robinvandernoord@gmail.com>
6
6
  #
7
7
  # SPDX-License-Identifier: MIT
8
- __version__ = "3.3.0"
8
+ __version__ = "3.3.1"
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
@@ -65,8 +65,8 @@ from .types import (
65
65
  )
66
66
 
67
67
  # use typing.cast(type, ...) to make mypy happy with unions
68
- T_annotation = typing.Type[Any] | types.UnionType
69
- T_Query = typing.Union["Table", Query, bool, None, "TypedTable", typing.Type["TypedTable"]]
68
+ T_annotation = Type[Any] | types.UnionType
69
+ T_Query = typing.Union["Table", Query, bool, None, "TypedTable", Type["TypedTable"]]
70
70
  T_Value = typing.TypeVar("T_Value") # actual type of the Field (via Generic)
71
71
  T_MetaInstance = typing.TypeVar("T_MetaInstance", bound="TypedTable") # bound="TypedTable"; bound="TableMeta"
72
72
  T = typing.TypeVar("T")
@@ -102,7 +102,7 @@ JOIN_OPTIONS = typing.Literal["left", "inner", None]
102
102
  DEFAULT_JOIN_OPTION: JOIN_OPTIONS = "left"
103
103
 
104
104
  # table-ish paramter:
105
- P_Table = typing.Union[typing.Type["TypedTable"], pydal.objects.Table]
105
+ P_Table = typing.Union[Type["TypedTable"], pydal.objects.Table]
106
106
 
107
107
  Condition: typing.TypeAlias = typing.Optional[
108
108
  typing.Callable[
@@ -120,7 +120,7 @@ OnQuery: typing.TypeAlias = typing.Optional[
120
120
  ]
121
121
  ]
122
122
 
123
- To_Type = typing.TypeVar("To_Type", type[Any], typing.Type[Any], str)
123
+ To_Type = typing.TypeVar("To_Type", type[Any], Type[Any], str)
124
124
 
125
125
 
126
126
  class Relationship(typing.Generic[To_Type]):
@@ -129,7 +129,7 @@ class Relationship(typing.Generic[To_Type]):
129
129
  """
130
130
 
131
131
  _type: To_Type
132
- table: typing.Type["TypedTable"] | type | str
132
+ table: Type["TypedTable"] | type | str
133
133
  condition: Condition
134
134
  on: OnQuery
135
135
  multiple: bool
@@ -188,7 +188,7 @@ class Relationship(typing.Generic[To_Type]):
188
188
  join = f":{self.join}" if self.join else ""
189
189
  return f"<Relationship{join} {src_code}>"
190
190
 
191
- def get_table(self, db: "TypeDAL") -> typing.Type["TypedTable"]:
191
+ def get_table(self, db: "TypeDAL") -> Type["TypedTable"]:
192
192
  """
193
193
  Get the table this relationship is bound to.
194
194
  """
@@ -199,7 +199,7 @@ class Relationship(typing.Generic[To_Type]):
199
199
  return mapped
200
200
 
201
201
  # boo, fall back to untyped table but pretend it is typed:
202
- return typing.cast(typing.Type["TypedTable"], db[table]) # eh close enough!
202
+ return typing.cast(Type["TypedTable"], db[table]) # eh close enough!
203
203
 
204
204
  return table
205
205
 
@@ -241,9 +241,7 @@ class Relationship(typing.Generic[To_Type]):
241
241
  return None
242
242
 
243
243
 
244
- def relationship(
245
- _type: To_Type, condition: Condition = None, join: JOIN_OPTIONS = None, on: OnQuery = None
246
- ) -> Relationship[To_Type]:
244
+ def relationship(_type: To_Type, condition: Condition = None, join: JOIN_OPTIONS = None, on: OnQuery = None) -> To_Type:
247
245
  """
248
246
  Define a relationship to another table, when its id is not stored in the current table.
249
247
 
@@ -273,11 +271,17 @@ def relationship(
273
271
 
274
272
  If you'd try to capture this in a single 'condition', pydal would create a cross join which is much less efficient.
275
273
  """
276
- return Relationship(_type, condition, join, on)
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
+ )
277
281
 
278
282
 
279
283
  def _generate_relationship_condition(
280
- _: typing.Type["TypedTable"], key: str, field: typing.Union["TypedField[Any]", "Table", typing.Type["TypedTable"]]
284
+ _: Type["TypedTable"], key: str, field: typing.Union["TypedField[Any]", "Table", Type["TypedTable"]]
281
285
  ) -> Condition:
282
286
  origin = typing.get_origin(field)
283
287
  # else: generic
@@ -294,9 +298,9 @@ def _generate_relationship_condition(
294
298
 
295
299
 
296
300
  def to_relationship(
297
- cls: typing.Type["TypedTable"] | type[Any],
301
+ cls: Type["TypedTable"] | type[Any],
298
302
  key: str,
299
- field: typing.Union["TypedField[Any]", "Table", typing.Type["TypedTable"]],
303
+ field: typing.Union["TypedField[Any]", "Table", Type["TypedTable"]],
300
304
  ) -> typing.Optional[Relationship[Any]]:
301
305
  """
302
306
  Used to automatically create relationship instance for reference fields.
@@ -427,7 +431,7 @@ class TypeDAL(pydal.DAL): # type: ignore
427
431
  self.try_define(_TypedalCache)
428
432
  self.try_define(_TypedalCacheDependency)
429
433
 
430
- def try_define(self, model: typing.Type[T], verbose: bool = False) -> typing.Type[T]:
434
+ def try_define(self, model: Type[T], verbose: bool = False) -> Type[T]:
431
435
  """
432
436
  Try to define a model with migrate or fall back to fake migrate.
433
437
  """
@@ -451,9 +455,9 @@ class TypeDAL(pydal.DAL): # type: ignore
451
455
  }
452
456
 
453
457
  # maps table name to typedal class, for resolving future references
454
- _class_map: typing.ClassVar[dict[str, typing.Type["TypedTable"]]] = {}
458
+ _class_map: typing.ClassVar[dict[str, Type["TypedTable"]]] = {}
455
459
 
456
- def _define(self, cls: typing.Type[T], **kwargs: Any) -> typing.Type[T]:
460
+ def _define(self, cls: Type[T], **kwargs: Any) -> Type[T]:
457
461
  # todo: new relationship item added should also invalidate (previously unrelated) cache result
458
462
 
459
463
  # todo: option to enable/disable cache dependency behavior:
@@ -555,7 +559,7 @@ class TypeDAL(pydal.DAL): # type: ignore
555
559
  return cls
556
560
 
557
561
  @typing.overload
558
- def define(self, maybe_cls: None = None, **kwargs: Any) -> typing.Callable[[typing.Type[T]], typing.Type[T]]:
562
+ def define(self, maybe_cls: None = None, **kwargs: Any) -> typing.Callable[[Type[T]], Type[T]]:
559
563
  """
560
564
  Typing Overload for define without a class.
561
565
 
@@ -564,7 +568,7 @@ class TypeDAL(pydal.DAL): # type: ignore
564
568
  """
565
569
 
566
570
  @typing.overload
567
- def define(self, maybe_cls: typing.Type[T], **kwargs: Any) -> typing.Type[T]:
571
+ def define(self, maybe_cls: Type[T], **kwargs: Any) -> Type[T]:
568
572
  """
569
573
  Typing Overload for define with a class.
570
574
 
@@ -572,9 +576,7 @@ class TypeDAL(pydal.DAL): # type: ignore
572
576
  class MyTable(TypedTable): ...
573
577
  """
574
578
 
575
- def define(
576
- self, maybe_cls: typing.Type[T] | None = None, **kwargs: Any
577
- ) -> 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]]:
578
580
  """
579
581
  Can be used as a decorator on a class that inherits `TypedTable`, \
580
582
  or as a regular method if you need to define your classes before you have access to a 'db' instance.
@@ -597,7 +599,7 @@ class TypeDAL(pydal.DAL): # type: ignore
597
599
  the result of pydal.define_table
598
600
  """
599
601
 
600
- def wrapper(cls: typing.Type[T]) -> typing.Type[T]:
602
+ def wrapper(cls: Type[T]) -> Type[T]:
601
603
  return self._define(cls, **kwargs)
602
604
 
603
605
  if maybe_cls:
@@ -647,7 +649,7 @@ class TypeDAL(pydal.DAL): # type: ignore
647
649
 
648
650
  if isinstance(cls, type) and issubclass(type(cls), type) and issubclass(cls, TypedTable):
649
651
  # table defined without @db.define decorator!
650
- _cls: typing.Type[TypedTable] = cls
652
+ _cls: Type[TypedTable] = cls
651
653
  args[0] = _cls.id != None
652
654
 
653
655
  _set = super().__call__(*args, **kwargs)
@@ -671,11 +673,11 @@ class TypeDAL(pydal.DAL): # type: ignore
671
673
  cls, _ftype: T_annotation, mut_kw: typing.MutableMapping[str, Any]
672
674
  ) -> Optional[str]:
673
675
  # ftype can be a union or type. typing.cast is sometimes used to tell mypy when it's not a union.
674
- ftype = typing.cast(type, _ftype) # cast from typing.Type to type to make mypy happy)
676
+ ftype = typing.cast(type, _ftype) # cast from Type to type to make mypy happy)
675
677
 
676
678
  if isinstance(ftype, str):
677
679
  # extract type from string
678
- ftype = typing.get_args(typing.Type[ftype])[0]._evaluate(
680
+ ftype = typing.get_args(Type[ftype])[0]._evaluate(
679
681
  localns=locals(), globalns=globals(), recursive_guard=frozenset()
680
682
  )
681
683
 
@@ -835,13 +837,13 @@ class TableMeta(type):
835
837
  else:
836
838
  return f"<unbound table {self.__name__}>"
837
839
 
838
- def from_row(self: typing.Type[T_MetaInstance], row: pydal.objects.Row) -> T_MetaInstance:
840
+ def from_row(self: Type[T_MetaInstance], row: pydal.objects.Row) -> T_MetaInstance:
839
841
  """
840
842
  Create a model instance from a pydal row.
841
843
  """
842
844
  return self(row)
843
845
 
844
- def all(self: typing.Type[T_MetaInstance]) -> "TypedRows[T_MetaInstance]":
846
+ def all(self: Type[T_MetaInstance]) -> "TypedRows[T_MetaInstance]":
845
847
  """
846
848
  Return all rows for this model.
847
849
  """
@@ -857,7 +859,7 @@ class TableMeta(type):
857
859
  # TypeDAL Modified Logic #
858
860
  ##########################
859
861
 
860
- def insert(self: typing.Type[T_MetaInstance], **fields: Any) -> T_MetaInstance:
862
+ def insert(self: Type[T_MetaInstance], **fields: Any) -> T_MetaInstance:
861
863
  """
862
864
  This is only called when db.define is not used as a decorator.
863
865
 
@@ -880,7 +882,7 @@ class TableMeta(type):
880
882
 
881
883
  return str(table._insert(**fields))
882
884
 
883
- def bulk_insert(self: typing.Type[T_MetaInstance], items: list[AnyDict]) -> "TypedRows[T_MetaInstance]":
885
+ def bulk_insert(self: Type[T_MetaInstance], items: list[AnyDict]) -> "TypedRows[T_MetaInstance]":
884
886
  """
885
887
  Insert multiple rows, returns a TypedRows set of new instances.
886
888
  """
@@ -889,7 +891,7 @@ class TableMeta(type):
889
891
  return self.where(lambda row: row.id.belongs(result)).collect()
890
892
 
891
893
  def update_or_insert(
892
- self: typing.Type[T_MetaInstance], query: T_Query | AnyDict = DEFAULT, **values: Any
894
+ self: Type[T_MetaInstance], query: T_Query | AnyDict = DEFAULT, **values: Any
893
895
  ) -> T_MetaInstance:
894
896
  """
895
897
  Update a row if query matches, else insert a new one.
@@ -912,7 +914,7 @@ class TableMeta(type):
912
914
  return self(record)
913
915
 
914
916
  def validate_and_insert(
915
- self: typing.Type[T_MetaInstance], **fields: Any
917
+ self: Type[T_MetaInstance], **fields: Any
916
918
  ) -> tuple[Optional[T_MetaInstance], Optional[dict[str, str]]]:
917
919
  """
918
920
  Validate input data and then insert a row.
@@ -927,7 +929,7 @@ class TableMeta(type):
927
929
  return None, result.get("errors")
928
930
 
929
931
  def validate_and_update(
930
- self: typing.Type[T_MetaInstance], query: Query, **fields: Any
932
+ self: Type[T_MetaInstance], query: Query, **fields: Any
931
933
  ) -> tuple[Optional[T_MetaInstance], Optional[dict[str, str]]]:
932
934
  """
933
935
  Validate input data and then update max 1 row.
@@ -947,7 +949,7 @@ class TableMeta(type):
947
949
  return None, None
948
950
 
949
951
  def validate_and_update_or_insert(
950
- self: typing.Type[T_MetaInstance], query: Query, **fields: Any
952
+ self: Type[T_MetaInstance], query: Query, **fields: Any
951
953
  ) -> tuple[Optional[T_MetaInstance], Optional[dict[str, str]]]:
952
954
  """
953
955
  Validate input data and then update_and_insert (on max 1 row).
@@ -965,59 +967,57 @@ class TableMeta(type):
965
967
  # update on query without result (shouldnt happen)
966
968
  return None, None
967
969
 
968
- def select(self: typing.Type[T_MetaInstance], *a: Any, **kw: Any) -> "QueryBuilder[T_MetaInstance]":
970
+ def select(self: Type[T_MetaInstance], *a: Any, **kw: Any) -> "QueryBuilder[T_MetaInstance]":
969
971
  """
970
972
  See QueryBuilder.select!
971
973
  """
972
974
  return QueryBuilder(self).select(*a, **kw)
973
975
 
974
- def paginate(self: typing.Type[T_MetaInstance], limit: int, page: int = 1) -> "PaginatedRows[T_MetaInstance]":
976
+ def paginate(self: Type[T_MetaInstance], limit: int, page: int = 1) -> "PaginatedRows[T_MetaInstance]":
975
977
  """
976
978
  See QueryBuilder.paginate!
977
979
  """
978
980
  return QueryBuilder(self).paginate(limit=limit, page=page)
979
981
 
980
- def chunk(
981
- self: typing.Type[T_MetaInstance], chunk_size: int
982
- ) -> typing.Generator["TypedRows[T_MetaInstance]", Any, None]:
982
+ def chunk(self: Type[T_MetaInstance], chunk_size: int) -> typing.Generator["TypedRows[T_MetaInstance]", Any, None]:
983
983
  """
984
984
  See QueryBuilder.chunk!
985
985
  """
986
986
  return QueryBuilder(self).chunk(chunk_size)
987
987
 
988
- def where(self: typing.Type[T_MetaInstance], *a: Any, **kw: Any) -> "QueryBuilder[T_MetaInstance]":
988
+ def where(self: Type[T_MetaInstance], *a: Any, **kw: Any) -> "QueryBuilder[T_MetaInstance]":
989
989
  """
990
990
  See QueryBuilder.where!
991
991
  """
992
992
  return QueryBuilder(self).where(*a, **kw)
993
993
 
994
- def cache(self: typing.Type[T_MetaInstance], *deps: Any, **kwargs: Any) -> "QueryBuilder[T_MetaInstance]":
994
+ def cache(self: Type[T_MetaInstance], *deps: Any, **kwargs: Any) -> "QueryBuilder[T_MetaInstance]":
995
995
  """
996
996
  See QueryBuilder.cache!
997
997
  """
998
998
  return QueryBuilder(self).cache(*deps, **kwargs)
999
999
 
1000
- def count(self: typing.Type[T_MetaInstance]) -> int:
1000
+ def count(self: Type[T_MetaInstance]) -> int:
1001
1001
  """
1002
1002
  See QueryBuilder.count!
1003
1003
  """
1004
1004
  return QueryBuilder(self).count()
1005
1005
 
1006
- def first(self: typing.Type[T_MetaInstance]) -> T_MetaInstance | None:
1006
+ def first(self: Type[T_MetaInstance]) -> T_MetaInstance | None:
1007
1007
  """
1008
1008
  See QueryBuilder.first!
1009
1009
  """
1010
1010
  return QueryBuilder(self).first()
1011
1011
 
1012
- def first_or_fail(self: typing.Type[T_MetaInstance]) -> T_MetaInstance:
1012
+ def first_or_fail(self: Type[T_MetaInstance]) -> T_MetaInstance:
1013
1013
  """
1014
1014
  See QueryBuilder.first_or_fail!
1015
1015
  """
1016
1016
  return QueryBuilder(self).first_or_fail()
1017
1017
 
1018
1018
  def join(
1019
- self: typing.Type[T_MetaInstance],
1020
- *fields: str | typing.Type["TypedTable"],
1019
+ self: Type[T_MetaInstance],
1020
+ *fields: str | Type["TypedTable"],
1021
1021
  method: JOIN_OPTIONS = None,
1022
1022
  on: OnQuery | list[Expression] | Expression = None,
1023
1023
  condition: Condition = None,
@@ -1027,7 +1027,7 @@ class TableMeta(type):
1027
1027
  """
1028
1028
  return QueryBuilder(self).join(*fields, on=on, condition=condition, method=method)
1029
1029
 
1030
- def collect(self: typing.Type[T_MetaInstance], verbose: bool = False) -> "TypedRows[T_MetaInstance]":
1030
+ def collect(self: Type[T_MetaInstance], verbose: bool = False) -> "TypedRows[T_MetaInstance]":
1031
1031
  """
1032
1032
  See QueryBuilder.collect!
1033
1033
  """
@@ -1127,7 +1127,7 @@ class TableMeta(type):
1127
1127
  table = self._ensure_table_defined()
1128
1128
  return typing.cast(Expression, table.on(query))
1129
1129
 
1130
- def with_alias(self, alias: str) -> _Table:
1130
+ def with_alias(self: Type[T_MetaInstance], alias: str) -> Type[T_MetaInstance]:
1131
1131
  """
1132
1132
  Shadow Table.with_alias.
1133
1133
 
@@ -1137,7 +1137,7 @@ class TableMeta(type):
1137
1137
  http://web2py.com/books/default/chapter/29/06/the-database-abstraction-layer?search=export_to_csv_file#One-to-many-relation
1138
1138
  """
1139
1139
  table = self._ensure_table_defined()
1140
- return table.with_alias(alias)
1140
+ return typing.cast(Type[T_MetaInstance], table.with_alias(alias))
1141
1141
 
1142
1142
  # @typing.dataclass_transform()
1143
1143
 
@@ -1159,7 +1159,7 @@ class TypedField(Expression, typing.Generic[T_Value]): # pragma: no cover
1159
1159
 
1160
1160
  requires: Validator | typing.Iterable[Validator]
1161
1161
 
1162
- def __init__(self, _type: typing.Type[T_Value] | types.UnionType = str, /, **settings: Any) -> None: # type: ignore
1162
+ def __init__(self, _type: Type[T_Value] | types.UnionType = str, /, **settings: Any) -> None: # type: ignore
1163
1163
  """
1164
1164
  A TypedFieldType should not be inited manually, but TypedField (from `fields.py`) should be used!
1165
1165
  """
@@ -1168,19 +1168,19 @@ class TypedField(Expression, typing.Generic[T_Value]): # pragma: no cover
1168
1168
  # super().__init__()
1169
1169
 
1170
1170
  @typing.overload
1171
- def __get__(self, instance: T_MetaInstance, owner: typing.Type[T_MetaInstance]) -> T_Value: # pragma: no cover
1171
+ def __get__(self, instance: T_MetaInstance, owner: Type[T_MetaInstance]) -> T_Value: # pragma: no cover
1172
1172
  """
1173
1173
  row.field -> (actual data).
1174
1174
  """
1175
1175
 
1176
1176
  @typing.overload
1177
- def __get__(self, instance: None, owner: "typing.Type[TypedTable]") -> "TypedField[T_Value]": # pragma: no cover
1177
+ def __get__(self, instance: None, owner: "Type[TypedTable]") -> "TypedField[T_Value]": # pragma: no cover
1178
1178
  """
1179
1179
  Table.field -> Field.
1180
1180
  """
1181
1181
 
1182
1182
  def __get__(
1183
- self, instance: T_MetaInstance | None, owner: typing.Type[T_MetaInstance]
1183
+ self, instance: T_MetaInstance | None, owner: Type[T_MetaInstance]
1184
1184
  ) -> typing.Union[T_Value, "TypedField[T_Value]"]:
1185
1185
  """
1186
1186
  Since this class is a Descriptor field, \
@@ -1374,7 +1374,7 @@ class TypedTable(_TypedTable, metaclass=TableMeta):
1374
1374
 
1375
1375
  def __new__(
1376
1376
  cls, row_or_id: typing.Union[Row, Query, pydal.objects.Set, int, str, None, "TypedTable"] = None, **filters: Any
1377
- ) -> typing.Self:
1377
+ ) -> Self:
1378
1378
  """
1379
1379
  Create a Typed Rows model instance from an existing row, ID or query.
1380
1380
 
@@ -1596,7 +1596,7 @@ class TypedTable(_TypedTable, metaclass=TableMeta):
1596
1596
  super().__setattr__(key, value)
1597
1597
 
1598
1598
  @classmethod
1599
- def update(cls: typing.Type[T_MetaInstance], query: Query, **fields: Any) -> T_MetaInstance | None:
1599
+ def update(cls: Type[T_MetaInstance], query: Query, **fields: Any) -> T_MetaInstance | None:
1600
1600
  """
1601
1601
  Update one record.
1602
1602
 
@@ -1695,7 +1695,7 @@ class TypedRows(typing.Collection[T_MetaInstance], Rows):
1695
1695
 
1696
1696
  records: dict[int, T_MetaInstance]
1697
1697
  # _rows: Rows
1698
- model: typing.Type[T_MetaInstance]
1698
+ model: Type[T_MetaInstance]
1699
1699
  metadata: Metadata
1700
1700
 
1701
1701
  # pseudo-properties: actually stored in _rows
@@ -1708,7 +1708,7 @@ class TypedRows(typing.Collection[T_MetaInstance], Rows):
1708
1708
  def __init__(
1709
1709
  self,
1710
1710
  rows: Rows,
1711
- model: typing.Type[T_MetaInstance],
1711
+ model: Type[T_MetaInstance],
1712
1712
  records: dict[int, T_MetaInstance] = None,
1713
1713
  metadata: Metadata = None,
1714
1714
  ) -> None:
@@ -1938,7 +1938,7 @@ class TypedRows(typing.Collection[T_MetaInstance], Rows):
1938
1938
  Update the current rows in the database with new_values.
1939
1939
  """
1940
1940
  # cast to make mypy understand .id is a TypedField and not an int!
1941
- table = typing.cast(typing.Type[TypedTable], self.model._ensure_table_defined())
1941
+ table = typing.cast(Type[TypedTable], self.model._ensure_table_defined())
1942
1942
 
1943
1943
  ids = set(self.column("id"))
1944
1944
  query = table.id.belongs(ids)
@@ -1949,7 +1949,7 @@ class TypedRows(typing.Collection[T_MetaInstance], Rows):
1949
1949
  Delete the currently selected rows from the database.
1950
1950
  """
1951
1951
  # cast to make mypy understand .id is a TypedField and not an int!
1952
- table = typing.cast(typing.Type[TypedTable], self.model._ensure_table_defined())
1952
+ table = typing.cast(Type[TypedTable], self.model._ensure_table_defined())
1953
1953
 
1954
1954
  ids = set(self.column("id"))
1955
1955
  query = table.id.belongs(ids)
@@ -2004,7 +2004,7 @@ class TypedRows(typing.Collection[T_MetaInstance], Rows):
2004
2004
 
2005
2005
  @classmethod
2006
2006
  def from_rows(
2007
- cls, rows: Rows, model: typing.Type[T_MetaInstance], metadata: Metadata = None
2007
+ cls, rows: Rows, model: Type[T_MetaInstance], metadata: Metadata = None
2008
2008
  ) -> "TypedRows[T_MetaInstance]":
2009
2009
  """
2010
2010
  Internal method to convert a Rows object to a TypedRows.
@@ -2047,7 +2047,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
2047
2047
  Abstration on top of pydal's query system.
2048
2048
  """
2049
2049
 
2050
- model: typing.Type[T_MetaInstance]
2050
+ model: Type[T_MetaInstance]
2051
2051
  query: Query
2052
2052
  select_args: list[Any]
2053
2053
  select_kwargs: SelectKwargs
@@ -2056,7 +2056,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
2056
2056
 
2057
2057
  def __init__(
2058
2058
  self,
2059
- model: typing.Type[T_MetaInstance],
2059
+ model: Type[T_MetaInstance],
2060
2060
  add_query: Optional[Query] = None,
2061
2061
  select_args: Optional[list[Any]] = None,
2062
2062
  select_kwargs: Optional[SelectKwargs] = None,
@@ -2151,7 +2151,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
2151
2151
 
2152
2152
  def where(
2153
2153
  self,
2154
- *queries_or_lambdas: Query | typing.Callable[[typing.Type[T_MetaInstance]], Query],
2154
+ *queries_or_lambdas: Query | typing.Callable[[Type[T_MetaInstance]], Query],
2155
2155
  **filters: Any,
2156
2156
  ) -> "QueryBuilder[T_MetaInstance]":
2157
2157
  """
@@ -2192,7 +2192,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
2192
2192
 
2193
2193
  def join(
2194
2194
  self,
2195
- *fields: str | typing.Type[TypedTable],
2195
+ *fields: str | Type[TypedTable],
2196
2196
  method: JOIN_OPTIONS = None,
2197
2197
  on: OnQuery | list[Expression] | Expression = None,
2198
2198
  condition: Condition = None,
@@ -2219,7 +2219,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
2219
2219
  if isinstance(condition, pydal.objects.Query):
2220
2220
  condition = as_lambda(condition)
2221
2221
 
2222
- relationships = {str(fields[0]): relationship(fields[0], condition=condition, join=method)}
2222
+ relationships = {str(fields[0]): Relationship(fields[0], condition=condition, join=method)}
2223
2223
  elif on:
2224
2224
  if len(fields) != 1:
2225
2225
  raise ValueError("join(field, on=...) can only be used with exactly one field!")
@@ -2229,7 +2229,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
2229
2229
 
2230
2230
  if isinstance(on, list):
2231
2231
  on = as_lambda(on)
2232
- relationships = {str(fields[0]): relationship(fields[0], on=on, join=method)}
2232
+ relationships = {str(fields[0]): Relationship(fields[0], on=on, join=method)}
2233
2233
 
2234
2234
  else:
2235
2235
  if fields:
@@ -2383,7 +2383,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
2383
2383
  return db(query).select(*select_args, **select_kwargs)
2384
2384
 
2385
2385
  def collect(
2386
- self, verbose: bool = False, _to: typing.Type["TypedRows[Any]"] = None, add_id: bool = True
2386
+ self, verbose: bool = False, _to: Type["TypedRows[Any]"] = None, add_id: bool = True
2387
2387
  ) -> "TypedRows[T_MetaInstance]":
2388
2388
  """
2389
2389
  Execute the built query and turn it into model instances, while handling relationships.
@@ -2521,7 +2521,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
2521
2521
  return query, select_args
2522
2522
 
2523
2523
  def _collect_with_relationships(
2524
- self, rows: Rows, metadata: Metadata, _to: typing.Type["TypedRows[Any]"]
2524
+ self, rows: Rows, metadata: Metadata, _to: Type["TypedRows[Any]"]
2525
2525
  ) -> "TypedRows[T_MetaInstance]":
2526
2526
  """
2527
2527
  Transform the raw rows into Typed Table model instances.
@@ -2802,7 +2802,7 @@ class TypedSet(pydal.objects.Set): # type: ignore # pragma: no cover
2802
2802
  result: TypedRows[MyTable] = db(MyTable.id > 0).select()
2803
2803
 
2804
2804
  for row in result:
2805
- typing.reveal_type(row) # MyTable
2805
+ reveal_type(row) # MyTable
2806
2806
  """
2807
2807
  rows = super().select(*fields, **attributes)
2808
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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: TypeDAL
3
- Version: 3.3.0
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: su6[all]; extra == 'dev'
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'
@@ -1,19 +1,19 @@
1
- typedal/__about__.py,sha256=3-Xok9ZOvvrfDNbH5xUjdw3tCd6M_cLiUWxppT-gDuk,206
1
+ typedal/__about__.py,sha256=Wu5Lv4Jglqs8h2_AjyrHv-m67Gz9cmuGT7s5MPNnAFs,206
2
2
  typedal/__init__.py,sha256=QQpLiVl9w9hm2LBxey49Y_tCF_VB2bScVaS_mCjYy54,366
3
3
  typedal/caching.py,sha256=SMcJsahLlZ79yykWCveERFx1ZJUNEKhA9SPmCTIuLp8,11798
4
4
  typedal/cli.py,sha256=BXXu1w9WRRK9GA82xh_JLvvfpNDFWICXKCAWNg4a3-Y,18180
5
5
  typedal/config.py,sha256=0qy1zrTUdtmXPM9jHzFnSR1DJsqGJqcdG6pvhzKQHe0,11625
6
- typedal/core.py,sha256=Wa6Vu7ppD278tIPAl4eW8rHZRXYETM1rI3-raJ4_8GE,96148
6
+ typedal/core.py,sha256=ScUZkDzHGULNTcrW9Jq4Anf4axUfha-CXWmm7SWrn90,95991
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
- typedal/helpers.py,sha256=mtRYPFlS0dx2wK8kYSJ4vm1wsTXRkdPumFRlOAjF_xU,7177
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
13
  typedal/types.py,sha256=AsUHrAXk058zmdqDbZlpf6sE85ID1Q2vofsGgCXni1s,4851
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.3.0.dist-info/METADATA,sha256=IiO1uKvVp-xZHz_9xeAyizZVAA-WQlAuPhmOxRRVp7E,9316
17
- typedal-3.3.0.dist-info/WHEEL,sha256=KGYbc1zXlYddvwxnNty23BeaKzh7YuoSIvIMO4jEhvw,87
18
- typedal-3.3.0.dist-info/entry_points.txt,sha256=m1wqcc_10rHWPdlQ71zEkmJDADUAnZtn7Jac_6mbyUc,44
19
- typedal-3.3.0.dist-info/RECORD,,
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,,