TypeDAL 3.2.0__py3-none-any.whl → 3.3.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 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.2.0"
8
+ __version__ = "3.3.0"
typedal/caching.py CHANGED
@@ -15,7 +15,7 @@ from pydal.objects import Field, Rows, Set
15
15
  from .core import TypedField, TypedRows, TypedTable
16
16
  from .types import Query
17
17
 
18
- if typing.TYPE_CHECKING: # pragma: no cover
18
+ if typing.TYPE_CHECKING:
19
19
  from .core import TypeDAL
20
20
 
21
21
 
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: # pragma: no cover
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: # pragma: no cover
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
@@ -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,6 +58,8 @@ from .types import (
58
58
  Pagination,
59
59
  Query,
60
60
  Rows,
61
+ SelectKwargs,
62
+ Table,
61
63
  Validator,
62
64
  _Types,
63
65
  )
@@ -753,25 +755,6 @@ class TypeDAL(pydal.DAL): # type: ignore
753
755
  return to_snake(camel)
754
756
 
755
757
 
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
758
  class TableMeta(type):
776
759
  """
777
760
  This metaclass contains functionality on table classes, that doesn't exist on its instances.
@@ -953,10 +936,7 @@ class TableMeta(type):
953
936
  """
954
937
  table = self._ensure_table_defined()
955
938
 
956
- try:
957
- result = table.validate_and_update(query, **fields)
958
- except Exception as e:
959
- result = {"errors": {"exception": str(e)}}
939
+ result = table.validate_and_update(query, **fields)
960
940
 
961
941
  if errors := result.get("errors"):
962
942
  return None, errors
@@ -1029,6 +1009,12 @@ class TableMeta(type):
1029
1009
  """
1030
1010
  return QueryBuilder(self).first()
1031
1011
 
1012
+ def first_or_fail(self: typing.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
1019
  self: typing.Type[T_MetaInstance],
1034
1020
  *fields: str | typing.Type["TypedTable"],
@@ -1156,7 +1142,7 @@ class TableMeta(type):
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
  """
@@ -1179,7 +1165,7 @@ class TypedField(typing.Generic[T_Value]): # pragma: no cover
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
1171
  def __get__(self, instance: T_MetaInstance, owner: typing.Type[T_MetaInstance]) -> T_Value: # pragma: no cover
@@ -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
- ) -> "TypedTable":
1377
+ ) -> typing.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:
@@ -2052,7 +2050,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
2052
2050
  model: typing.Type[T_MetaInstance]
2053
2051
  query: Query
2054
2052
  select_args: list[Any]
2055
- select_kwargs: AnyDict
2053
+ select_kwargs: SelectKwargs
2056
2054
  relationships: dict[str, Relationship[Any]]
2057
2055
  metadata: Metadata
2058
2056
 
@@ -2061,7 +2059,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
2061
2059
  model: typing.Type[T_MetaInstance],
2062
2060
  add_query: Optional[Query] = None,
2063
2061
  select_args: Optional[list[Any]] = None,
2064
- select_kwargs: Optional[AnyDict] = None,
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[AnyDict] = None,
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: Any) -> "QueryBuilder[T_MetaInstance]":
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
 
@@ -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], AnyDict]:
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
@@ -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: AnyDict,
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", None):
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
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[AnyDict]
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.3
1
+ Metadata-Version: 2.1
2
2
  Name: TypeDAL
3
- Version: 3.2.0
3
+ Version: 3.3.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
@@ -1,19 +1,19 @@
1
- typedal/__about__.py,sha256=JM7UvclHJLbyevmmJGGt8HQwzgu3SMpy7q2WeNEm9Io,206
1
+ typedal/__about__.py,sha256=3-Xok9ZOvvrfDNbH5xUjdw3tCd6M_cLiUWxppT-gDuk,206
2
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
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=Wa6Vu7ppD278tIPAl4eW8rHZRXYETM1rI3-raJ4_8GE,96148
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=mtRYPFlS0dx2wK8kYSJ4vm1wsTXRkdPumFRlOAjF_xU,7177
11
11
  typedal/mixins.py,sha256=OHhVYLBGTzPSJKgp1uovBs1Y6NJa0d-DxHTOk9LyOXA,5414
12
12
  typedal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- typedal/types.py,sha256=1kGkNX6vfGg6ln84AG558C4Zx5ACRz-emrUTnuy-rRY,3410
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.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,,
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,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.22.4
2
+ Generator: hatchling 1.17.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any