TypeDAL 3.3.1__py3-none-any.whl → 3.5.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.3.1"
8
+ __version__ = "3.5.0"
typedal/cli.py CHANGED
@@ -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 == int:
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 == bool:
200
+ if annotation is bool:
201
201
  answer = bool(answer)
202
- elif annotation == int:
202
+ elif annotation is int:
203
203
  answer = int(answer)
204
204
 
205
205
  config.update(**{prop: answer})
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 == list:
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) == list else "inner"
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
- # @typing.dataclass_transform()
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[BeforeInsertCallable]
1338
- _after_insert: list[AfterInsertCallable]
1339
- _before_update: list[BeforeUpdateCallable]
1340
- _after_update: list[AfterUpdateCallable]
1341
- _before_delete: list[BeforeDeleteCallable]
1342
- _after_delete: list[AfterDeleteCallable]
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:
@@ -1841,15 +1896,6 @@ class TypedRows(typing.Collection[T_MetaInstance], Rows):
1841
1896
  result = super().group_by_value(*fields, **kwargs)
1842
1897
  return typing.cast(dict[T, list[T_MetaInstance]], result)
1843
1898
 
1844
- def column(self, column: str = None) -> list[Any]:
1845
- """
1846
- Get a list of all values in a specific column.
1847
-
1848
- Example:
1849
- rows.column('name') -> ['Name 1', 'Name 2', ...]
1850
- """
1851
- return typing.cast(list[Any], super().column(column))
1852
-
1853
1899
  def as_csv(self) -> str:
1854
1900
  """
1855
1901
  Dump the data to csv.
@@ -2426,6 +2472,26 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
2426
2472
  # only saves if requested in metadata:
2427
2473
  return save_to_cache(typed_rows, rows)
2428
2474
 
2475
+ @typing.overload
2476
+ def column(self, field: TypedField[T]) -> list[T]:
2477
+ """
2478
+ If a typedfield is passed, the output type can be safely determined.
2479
+ """
2480
+
2481
+ @typing.overload
2482
+ def column(self, field: T) -> list[T]:
2483
+ """
2484
+ Otherwise, the output type is loosely determined (assumes `field: type` or Any).
2485
+ """
2486
+
2487
+ def column(self, field: TypedField[T] | T) -> list[T]:
2488
+ """
2489
+ Get all values in a specific column.
2490
+
2491
+ Shortcut for `.select(field).execute().column(field)`.
2492
+ """
2493
+ return self.select(field).execute().column(field)
2494
+
2429
2495
  def _handle_relationships_pre_select(
2430
2496
  self,
2431
2497
  query: Query,
typedal/types.py CHANGED
@@ -48,12 +48,35 @@ class Set(_Set): # type: ignore
48
48
  """
49
49
 
50
50
 
51
- class OpRow(_OpRow): # type: ignore
52
- """
53
- Pydal OpRow object.
51
+ if typing.TYPE_CHECKING:
54
52
 
55
- Make mypy happy.
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
@@ -79,6 +102,15 @@ class Rows(_Rows): # type: ignore
79
102
  Make mypy happy.
80
103
  """
81
104
 
105
+ def column(self, column: typing.Any = None) -> list[typing.Any]:
106
+ """
107
+ Get a list of all values in a specific column.
108
+
109
+ Example:
110
+ rows.column('name') -> ['Name 1', 'Name 2', ...]
111
+ """
112
+ return [r[str(column) if column else self.colnames[0]] for r in self]
113
+
82
114
 
83
115
  class Validator(_Validator): # type: ignore
84
116
  """
@@ -96,14 +128,6 @@ class _Types:
96
128
  NONETYPE = type(None)
97
129
 
98
130
 
99
- BeforeInsertCallable: typing.TypeAlias = typing.Callable[[OpRow], Any]
100
- AfterInsertCallable: typing.TypeAlias = typing.Callable[[OpRow, Reference], Any]
101
- BeforeUpdateCallable: typing.TypeAlias = typing.Callable[[Set, OpRow], Any]
102
- AfterUpdateCallable: typing.TypeAlias = typing.Callable[[Set, OpRow], Any]
103
- BeforeDeleteCallable: typing.TypeAlias = typing.Callable[[Set], Any]
104
- AfterDeleteCallable: typing.TypeAlias = typing.Callable[[Set], Any]
105
-
106
-
107
131
  class Pagination(TypedDict):
108
132
  """
109
133
  Pagination key of a paginate dict has these items.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: TypeDAL
3
- Version: 3.3.1
3
+ Version: 3.5.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=Wu5Lv4Jglqs8h2_AjyrHv-m67Gz9cmuGT7s5MPNnAFs,206
1
+ typedal/__about__.py,sha256=AjgocyPCjgkgwSg0cozJLLzDEJz3aZN-Ze0xcPDV5pc,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=BXXu1w9WRRK9GA82xh_JLvvfpNDFWICXKCAWNg4a3-Y,18180
4
+ typedal/cli.py,sha256=n2TKQGOryck91ByFEcLqwPgh6_kMtoBr3UBIDwU_8n8,18180
5
5
  typedal/config.py,sha256=0qy1zrTUdtmXPM9jHzFnSR1DJsqGJqcdG6pvhzKQHe0,11625
6
- typedal/core.py,sha256=ScUZkDzHGULNTcrW9Jq4Anf4axUfha-CXWmm7SWrn90,95991
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=AsUHrAXk058zmdqDbZlpf6sE85ID1Q2vofsGgCXni1s,4851
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.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,,
16
+ typedal-3.5.0.dist-info/METADATA,sha256=pffl-arVDPP0CBg3EcVhlze5mxypyCH-Ds_iZ6sZY6s,9462
17
+ typedal-3.5.0.dist-info/WHEEL,sha256=KGYbc1zXlYddvwxnNty23BeaKzh7YuoSIvIMO4jEhvw,87
18
+ typedal-3.5.0.dist-info/entry_points.txt,sha256=m1wqcc_10rHWPdlQ71zEkmJDADUAnZtn7Jac_6mbyUc,44
19
+ typedal-3.5.0.dist-info/RECORD,,