TypeDAL 5.0.6__tar.gz → 5.1.1__tar.gz
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.
- {typedal-5.0.6 → typedal-5.1.1}/PKG-INFO +1 -1
- {typedal-5.0.6 → typedal-5.1.1}/pyproject.toml +3 -3
- {typedal-5.0.6 → typedal-5.1.1}/pyproject.toml.orig +3 -3
- {typedal-5.0.6 → typedal-5.1.1}/src/typedal/asynchronous.py +1 -0
- {typedal-5.0.6 → typedal-5.1.1}/src/typedal/cli.py +2 -1
- {typedal-5.0.6 → typedal-5.1.1}/src/typedal/config.py +1 -1
- {typedal-5.0.6 → typedal-5.1.1}/src/typedal/core.py +8 -3
- {typedal-5.0.6 → typedal-5.1.1}/src/typedal/define.py +4 -1
- {typedal-5.0.6 → typedal-5.1.1}/src/typedal/mixins.py +1 -0
- {typedal-5.0.6 → typedal-5.1.1}/src/typedal/query_builder.py +82 -5
- {typedal-5.0.6 → typedal-5.1.1}/src/typedal/relationships.py +6 -3
- {typedal-5.0.6 → typedal-5.1.1}/src/typedal/serializers/typescript.py +2 -1
- {typedal-5.0.6 → typedal-5.1.1}/src/typedal/tables.py +23 -0
- {typedal-5.0.6 → typedal-5.1.1}/src/typedal/types.py +2 -0
- typedal-5.1.1/src/typedal/warnings.py +9 -0
- {typedal-5.0.6 → typedal-5.1.1}/README.md +0 -0
- {typedal-5.0.6 → typedal-5.1.1}/src/typedal/__about__.py +0 -0
- {typedal-5.0.6 → typedal-5.1.1}/src/typedal/__init__.py +0 -0
- {typedal-5.0.6 → typedal-5.1.1}/src/typedal/caching.py +0 -0
- {typedal-5.0.6 → typedal-5.1.1}/src/typedal/constants.py +0 -0
- {typedal-5.0.6 → typedal-5.1.1}/src/typedal/enum_helpers.py +0 -0
- {typedal-5.0.6 → typedal-5.1.1}/src/typedal/fields.py +0 -0
- {typedal-5.0.6 → typedal-5.1.1}/src/typedal/for_py4web.py +0 -0
- {typedal-5.0.6 → typedal-5.1.1}/src/typedal/for_web2py.py +0 -0
- {typedal-5.0.6 → typedal-5.1.1}/src/typedal/helpers.py +0 -0
- {typedal-5.0.6 → typedal-5.1.1}/src/typedal/py.typed +0 -0
- {typedal-5.0.6 → typedal-5.1.1}/src/typedal/rows.py +0 -0
- {typedal-5.0.6 → typedal-5.1.1}/src/typedal/serializers/as_json.py +0 -0
- {typedal-5.0.6 → typedal-5.1.1}/src/typedal/web2py_py4web_shared.py +0 -0
|
@@ -6,7 +6,7 @@ build-backend = "uv_build"
|
|
|
6
6
|
name = "TypeDAL"
|
|
7
7
|
description = "Typing support for PyDAL"
|
|
8
8
|
readme = "README.md"
|
|
9
|
-
version = "5.
|
|
9
|
+
version = "5.1.1"
|
|
10
10
|
requires-python = ">=3.12"
|
|
11
11
|
license-expression = "MIT"
|
|
12
12
|
keywords = []
|
|
@@ -164,7 +164,7 @@ badge = true
|
|
|
164
164
|
mypy = "--disable-error-code misc"
|
|
165
165
|
|
|
166
166
|
[tool.black]
|
|
167
|
-
target-version = ["
|
|
167
|
+
target-version = ["py312"]
|
|
168
168
|
line-length = 120
|
|
169
169
|
extend-exclude = '''
|
|
170
170
|
# A regex preceded with ^/ will apply only to files and directories
|
|
@@ -199,7 +199,7 @@ exclude = [
|
|
|
199
199
|
]
|
|
200
200
|
|
|
201
201
|
[tool.ruff]
|
|
202
|
-
target-version = "
|
|
202
|
+
target-version = "py312"
|
|
203
203
|
line-length = 120
|
|
204
204
|
extend-exclude = [
|
|
205
205
|
"*.bak/",
|
|
@@ -6,7 +6,7 @@ build-backend = "uv_build"
|
|
|
6
6
|
name = "TypeDAL"
|
|
7
7
|
description = 'Typing support for PyDAL'
|
|
8
8
|
readme = "README.md"
|
|
9
|
-
version = "5.
|
|
9
|
+
version = "5.1.1"
|
|
10
10
|
requires-python = ">=3.12"
|
|
11
11
|
license-expression = "MIT"
|
|
12
12
|
keywords = []
|
|
@@ -181,7 +181,7 @@ badge = true
|
|
|
181
181
|
mypy = "--disable-error-code misc"
|
|
182
182
|
|
|
183
183
|
[tool.black]
|
|
184
|
-
target-version = ["
|
|
184
|
+
target-version = ["py312"]
|
|
185
185
|
line-length = 120
|
|
186
186
|
# 'extend-exclude' excludes files or directories in addition to the defaults
|
|
187
187
|
extend-exclude = '''
|
|
@@ -229,7 +229,7 @@ strict = true
|
|
|
229
229
|
exclude = ["venv", ".bak"]
|
|
230
230
|
|
|
231
231
|
[tool.ruff]
|
|
232
|
-
target-version = "
|
|
232
|
+
target-version = "py312"
|
|
233
233
|
line-length = 120
|
|
234
234
|
|
|
235
235
|
extend-exclude = ["*.bak/", "venv*/", "tests/", "example_*.py"]
|
|
@@ -30,6 +30,7 @@ except ImportError as e:
|
|
|
30
30
|
"to fix this.",
|
|
31
31
|
source=e,
|
|
32
32
|
category=RuntimeWarning,
|
|
33
|
+
stacklevel=2,
|
|
33
34
|
)
|
|
34
35
|
exit(127) # command not found
|
|
35
36
|
|
|
@@ -226,7 +227,7 @@ def setup(
|
|
|
226
227
|
data.pop("connection", None)
|
|
227
228
|
|
|
228
229
|
# ignore any None:
|
|
229
|
-
old_contents["tool"]["typedal"] = {k: v for k, v in data.items() if v is not None}
|
|
230
|
+
old_contents["tool"]["typedal"] = {k: v for k, v in data.items() if v is not None} # type: ignore
|
|
230
231
|
|
|
231
232
|
with toml_path.open("w") as f:
|
|
232
233
|
tomlkit.dump(old_contents, f)
|
|
@@ -159,7 +159,7 @@ def _load_toml(path: str | bool | Path | None = True) -> tuple[str, AnyDict]:
|
|
|
159
159
|
|
|
160
160
|
return str(toml_path) or "", t.cast(AnyDict, data["tool"]["typedal"])
|
|
161
161
|
except Exception as e:
|
|
162
|
-
warnings.warn(f"Could not load typedal config toml: {e}", source=e)
|
|
162
|
+
warnings.warn(f"Could not load typedal config toml: {e}", source=e, stacklevel=2)
|
|
163
163
|
return str(toml_path) or "", {}
|
|
164
164
|
|
|
165
165
|
|
|
@@ -53,8 +53,8 @@ def _expression_subclasses() -> t.Iterator[type]:
|
|
|
53
53
|
"""
|
|
54
54
|
Yield Expression and every (nested) subclass currently loaded, e.g. Field and TypedField.
|
|
55
55
|
"""
|
|
56
|
-
seen = {Expression}
|
|
57
|
-
stack = [Expression]
|
|
56
|
+
seen: set[type[Expression]] = {Expression}
|
|
57
|
+
stack: list[type[Expression]] = [Expression]
|
|
58
58
|
while stack:
|
|
59
59
|
for subclass in stack.pop().__subclasses__():
|
|
60
60
|
if subclass not in seen:
|
|
@@ -476,7 +476,12 @@ class TypeDAL(_TypeDALBase):
|
|
|
476
476
|
delattr(self, tablename)
|
|
477
477
|
|
|
478
478
|
if verbose:
|
|
479
|
-
warnings.warn(
|
|
479
|
+
warnings.warn(
|
|
480
|
+
f"{model} could not be migrated, try faking",
|
|
481
|
+
source=e,
|
|
482
|
+
category=RuntimeWarning,
|
|
483
|
+
stacklevel=2,
|
|
484
|
+
)
|
|
480
485
|
|
|
481
486
|
# try again:
|
|
482
487
|
return self.define(model, migrate=self._migrate, fake_migrate=self._migrate, redefine=True)
|
|
@@ -123,7 +123,10 @@ class TableDefinitionBuilder:
|
|
|
123
123
|
self.class_map[table._rname] = cls # table_model - sql name
|
|
124
124
|
cls.__on_define__(self.db)
|
|
125
125
|
else:
|
|
126
|
-
warnings.warn(
|
|
126
|
+
warnings.warn(
|
|
127
|
+
"db.define used without inheriting TypedTable. This could lead to strange problems!",
|
|
128
|
+
stacklevel=2,
|
|
129
|
+
)
|
|
127
130
|
|
|
128
131
|
if not tablename.startswith("typedal_") and cache_dependency:
|
|
129
132
|
from .caching import _remove_cache, remove_cache_for_table
|
|
@@ -8,6 +8,7 @@ import datetime as dt
|
|
|
8
8
|
import math
|
|
9
9
|
import time
|
|
10
10
|
import typing as t
|
|
11
|
+
import warnings
|
|
11
12
|
from collections import defaultdict
|
|
12
13
|
|
|
13
14
|
from pydal.helpers.classes import SQLALL
|
|
@@ -45,6 +46,19 @@ from .types import (
|
|
|
45
46
|
merge_permissions,
|
|
46
47
|
require_permission,
|
|
47
48
|
)
|
|
49
|
+
from .warnings import NoopQueryWarning, UnusedWindowWarning
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def warn_noop(method: str) -> None:
|
|
53
|
+
"""
|
|
54
|
+
Warn that `method` was called without arguments, which has no effect.
|
|
55
|
+
"""
|
|
56
|
+
# stacklevel: 1 = here, 2 = the query builder method, 3 = the caller.
|
|
57
|
+
warnings.warn(
|
|
58
|
+
f"`.{method}()` without arguments does nothing. You can remove this call.",
|
|
59
|
+
stacklevel=3,
|
|
60
|
+
category=NoopQueryWarning,
|
|
61
|
+
)
|
|
48
62
|
|
|
49
63
|
|
|
50
64
|
class QueryBuilder[T_MetaInstance: _TypedTable](Select):
|
|
@@ -173,6 +187,10 @@ class QueryBuilder[T_MetaInstance: _TypedTable](Select):
|
|
|
173
187
|
"""
|
|
174
188
|
Return a clone of this builder with permission overrides merged in.
|
|
175
189
|
"""
|
|
190
|
+
if not permissions and self:
|
|
191
|
+
warn_noop("permissions")
|
|
192
|
+
return self
|
|
193
|
+
|
|
176
194
|
return self._extend(permissions=permissions)
|
|
177
195
|
|
|
178
196
|
def _normalize_select_option(
|
|
@@ -214,8 +232,15 @@ class QueryBuilder[T_MetaInstance: _TypedTable](Select):
|
|
|
214
232
|
join: othertable.on(query) - do an INNER JOIN. Using TypeDAL relationships with .join() is recommended!
|
|
215
233
|
left: othertable.on(query) - do a LEFT JOIN. Using TypeDAL relationships with .join() is recommended!
|
|
216
234
|
cache: cache the query result to speed up repeated queries; e.g. (cache=(cache.ram, 3600), cacheable=True)
|
|
235
|
+
|
|
236
|
+
Calling this without any fields or options on a builder that already has settings
|
|
237
|
+
does nothing and emits a NoopQueryWarning.
|
|
217
238
|
"""
|
|
218
239
|
|
|
240
|
+
if not fields and not options and self:
|
|
241
|
+
warn_noop("select")
|
|
242
|
+
return self
|
|
243
|
+
|
|
219
244
|
for key in ("distinct",):
|
|
220
245
|
if options.get(key):
|
|
221
246
|
options[key] = self._normalize_select_option(options[key])
|
|
@@ -280,7 +305,15 @@ class QueryBuilder[T_MetaInstance: _TypedTable](Select):
|
|
|
280
305
|
.where(lambda table: table.id == 5).where(lambda table: table.id == 6) == (table.id == 5) & (table.id=6)
|
|
281
306
|
When passing multiple queries to a single .where, they will be ORed:
|
|
282
307
|
.where(lambda table: table.id == 5, lambda table: table.id == 6) == (table.id == 5) | (table.id=6)
|
|
308
|
+
|
|
309
|
+
Calling this without any arguments on a builder that already has settings
|
|
310
|
+
does nothing and emits a NoopQueryWarning.
|
|
311
|
+
Starting an empty builder (e.g. `Model.where()`) is allowed and stays silent.
|
|
283
312
|
"""
|
|
313
|
+
if not queries_or_lambdas and not filters and self:
|
|
314
|
+
warn_noop("where")
|
|
315
|
+
return self
|
|
316
|
+
|
|
284
317
|
new_query = self.query
|
|
285
318
|
table = self._ensure_table_defined()
|
|
286
319
|
|
|
@@ -296,7 +329,7 @@ class QueryBuilder[T_MetaInstance: _TypedTable](Select):
|
|
|
296
329
|
elif isinstance(query_part, (Query, Expression)):
|
|
297
330
|
subquery |= t.cast(Query, query_part)
|
|
298
331
|
elif callable(query_part):
|
|
299
|
-
if result := query_part(self.model):
|
|
332
|
+
if result := query_part(self.model):
|
|
300
333
|
subquery |= result
|
|
301
334
|
elif isinstance(query_part, dict):
|
|
302
335
|
subsubquery = DummyQuery()
|
|
@@ -662,6 +695,17 @@ class QueryBuilder[T_MetaInstance: _TypedTable](Select):
|
|
|
662
695
|
|
|
663
696
|
query, select_args, select_kwargs = self._before_query(metadata, add_id=add_id)
|
|
664
697
|
|
|
698
|
+
window_size = metadata.get("window_size", None)
|
|
699
|
+
is_iterating = metadata.get("iterating", False)
|
|
700
|
+
|
|
701
|
+
if window_size is not None and is_iterating is not True:
|
|
702
|
+
warnings.warn(
|
|
703
|
+
"A window size was configured, but collect() was called directly. "
|
|
704
|
+
"Use iteration over the query builder to fetch rows in windows.",
|
|
705
|
+
stacklevel=2,
|
|
706
|
+
category=UnusedWindowWarning,
|
|
707
|
+
)
|
|
708
|
+
|
|
665
709
|
metadata["sql"] = db(query)._select(*select_args, **select_kwargs)
|
|
666
710
|
|
|
667
711
|
if verbose: # pragma: no cover
|
|
@@ -1222,13 +1266,40 @@ class QueryBuilder[T_MetaInstance: _TypedTable](Select):
|
|
|
1222
1266
|
"""
|
|
1223
1267
|
You can start iterating a Query Builder object before calling collect, for ease of use.
|
|
1224
1268
|
"""
|
|
1225
|
-
|
|
1269
|
+
builder = self._extend(metadata={"iterating": True})
|
|
1270
|
+
window_size = self.metadata.get("window_size", None)
|
|
1271
|
+
|
|
1272
|
+
if not window_size:
|
|
1273
|
+
yield from builder.collect()
|
|
1274
|
+
return
|
|
1275
|
+
|
|
1276
|
+
for chunk in builder.chunk(window_size):
|
|
1277
|
+
yield from chunk
|
|
1278
|
+
|
|
1279
|
+
def __await__(self):
|
|
1280
|
+
return self.collect_async().__await__()
|
|
1281
|
+
|
|
1282
|
+
async def __aiter__(self):
|
|
1283
|
+
builder = self._extend(metadata={"iterating": True})
|
|
1284
|
+
|
|
1285
|
+
window_size = self.metadata.get("window_size", None)
|
|
1286
|
+
|
|
1287
|
+
if window_size:
|
|
1288
|
+
async for chunk in builder.chunk_async(window_size):
|
|
1289
|
+
for row in chunk:
|
|
1290
|
+
yield row
|
|
1291
|
+
else:
|
|
1292
|
+
rows = await builder.collect_async()
|
|
1293
|
+
for row in rows:
|
|
1294
|
+
yield row
|
|
1295
|
+
|
|
1296
|
+
def window(self, window_size: int) -> QueryBuilder[T_MetaInstance]:
|
|
1297
|
+
return self._extend(metadata={"window_size": window_size})
|
|
1226
1298
|
|
|
1227
1299
|
def __count(
|
|
1228
1300
|
self,
|
|
1229
1301
|
db: TypeDAL,
|
|
1230
1302
|
distinct: t.Optional[bool] = None,
|
|
1231
|
-
*,
|
|
1232
1303
|
include_left_for_distinct: bool = True,
|
|
1233
1304
|
) -> Query:
|
|
1234
1305
|
# internal, shared logic between .count and ._count
|
|
@@ -1351,7 +1422,10 @@ class QueryBuilder[T_MetaInstance: _TypedTable](Select):
|
|
|
1351
1422
|
pass
|
|
1352
1423
|
```
|
|
1353
1424
|
"""
|
|
1354
|
-
require_permission(
|
|
1425
|
+
# require_permission checked in .collect()
|
|
1426
|
+
if "limitby" in self.select_kwargs:
|
|
1427
|
+
raise ValueError("chunk() cannot be combined with an existing limitby")
|
|
1428
|
+
|
|
1355
1429
|
page = 1
|
|
1356
1430
|
|
|
1357
1431
|
while rows := self.__paginate(chunk_size, page).collect():
|
|
@@ -1506,7 +1580,10 @@ class QueryBuilder[T_MetaInstance: _TypedTable](Select):
|
|
|
1506
1580
|
writer can be visible halfway through the iteration; wrap it in `db.session()` if you
|
|
1507
1581
|
need every page to come from the same snapshot.
|
|
1508
1582
|
"""
|
|
1509
|
-
require_permission(
|
|
1583
|
+
# require_permission checked in .collect()
|
|
1584
|
+
if "limitby" in self.select_kwargs:
|
|
1585
|
+
raise ValueError("chunk_async() cannot be combined with an existing limitby")
|
|
1586
|
+
|
|
1510
1587
|
db = self._get_db()
|
|
1511
1588
|
page = 1
|
|
1512
1589
|
|
|
@@ -110,7 +110,7 @@ class Relationship[To_Type]:
|
|
|
110
110
|
|
|
111
111
|
@staticmethod
|
|
112
112
|
def _error_duplicate_condition(condition: Condition, on: OnQuery) -> t.Never:
|
|
113
|
-
warnings.warn(f"Relation | Both specified! {condition=} {on=}")
|
|
113
|
+
warnings.warn(f"Relation | Both specified! {condition=} {on=}", stacklevel=2)
|
|
114
114
|
raise ValueError("Please specify either a condition or an 'on' statement for this relationship!")
|
|
115
115
|
|
|
116
116
|
def __repr__(self) -> str:
|
|
@@ -278,6 +278,7 @@ class Relationship[To_Type]:
|
|
|
278
278
|
f"Trying to access relationship '{self.name}' without joining. "
|
|
279
279
|
f"Did you forget to use .join('{self.name}')? Returning empty value.",
|
|
280
280
|
category=RuntimeWarning,
|
|
281
|
+
stacklevel=2,
|
|
281
282
|
)
|
|
282
283
|
return fallback_value
|
|
283
284
|
|
|
@@ -296,6 +297,7 @@ class Relationship[To_Type]:
|
|
|
296
297
|
"This performs an extra database query. "
|
|
297
298
|
f"Consider using .join('{self.name}') for better performance.",
|
|
298
299
|
category=RuntimeWarning,
|
|
300
|
+
stacklevel=2,
|
|
299
301
|
)
|
|
300
302
|
|
|
301
303
|
return builder.first()[self.name] # type: ignore
|
|
@@ -304,6 +306,7 @@ class Relationship[To_Type]:
|
|
|
304
306
|
f"Failed to lazy load relationship '{self.name}': {e}",
|
|
305
307
|
category=RuntimeWarning,
|
|
306
308
|
source=e,
|
|
309
|
+
stacklevel=2,
|
|
307
310
|
)
|
|
308
311
|
|
|
309
312
|
return fallback_value
|
|
@@ -501,12 +504,12 @@ def to_relationship(
|
|
|
501
504
|
try:
|
|
502
505
|
condition = _generate_relationship_condition(cls, key, field)
|
|
503
506
|
except Exception as e: # pragma: no cover
|
|
504
|
-
warnings.warn("Could not generate Relationship condition", source=e)
|
|
507
|
+
warnings.warn("Could not generate Relationship condition", source=e, stacklevel=2)
|
|
505
508
|
condition = None
|
|
506
509
|
|
|
507
510
|
if not condition: # pragma: no cover
|
|
508
511
|
# something went wrong, not a valid relationship
|
|
509
|
-
warnings.warn(f"Invalid relationship for {cls.__name__}.{key}: {field}")
|
|
512
|
+
warnings.warn(f"Invalid relationship for {cls.__name__}.{key}: {field}", stacklevel=2)
|
|
510
513
|
return None
|
|
511
514
|
|
|
512
515
|
join = "left" if optional or t.get_origin(field) is list else "inner"
|
|
@@ -31,7 +31,7 @@ class TypedDictRegistry(Singleton):
|
|
|
31
31
|
def __init__(self) -> None:
|
|
32
32
|
"""Initialize the singleton registry and optional shared typtyp world."""
|
|
33
33
|
self._types: dict[type, type[dict[str, t.Any]]] = {}
|
|
34
|
-
self._world = typtyp.World() if typtyp else None
|
|
34
|
+
self._world = typtyp.World() if typtyp else None # type: ignore
|
|
35
35
|
self._names: set[str] = set()
|
|
36
36
|
|
|
37
37
|
@property
|
|
@@ -78,6 +78,7 @@ class TypedDictRegistry(Singleton):
|
|
|
78
78
|
if world is None: # pragma: ignore
|
|
79
79
|
warnings.warn(
|
|
80
80
|
f"`{caller_name}` can not be used without the typescript extra. Please install `typedal[typescript]`",
|
|
81
|
+
stacklevel=2,
|
|
81
82
|
)
|
|
82
83
|
return ""
|
|
83
84
|
|
|
@@ -138,6 +138,23 @@ class TableMeta(type):
|
|
|
138
138
|
self._relationships = None
|
|
139
139
|
self._permissions = None
|
|
140
140
|
|
|
141
|
+
def __getattribute__(self, col: str) -> t.Any:
|
|
142
|
+
"""
|
|
143
|
+
Resolve bound table fields before metaclass convenience methods.
|
|
144
|
+
|
|
145
|
+
Annotation-only fields are not stored in the model class dictionary, so
|
|
146
|
+
normal class lookup would otherwise return a same-named method defined
|
|
147
|
+
on this metaclass. For example, a model may have a 'window' column
|
|
148
|
+
while this metaclass also provides 'window()'. The column takes
|
|
149
|
+
precedence on the model class; use 'QueryBuilder(Model).window()' to
|
|
150
|
+
call the query helper in that case.
|
|
151
|
+
"""
|
|
152
|
+
table = type.__getattribute__(self, "_table")
|
|
153
|
+
if table is not None and col in table._fields:
|
|
154
|
+
return table[col]
|
|
155
|
+
|
|
156
|
+
return type.__getattribute__(self, col)
|
|
157
|
+
|
|
141
158
|
def __getattr__(self, col: str) -> t.Optional[Field]:
|
|
142
159
|
"""
|
|
143
160
|
Magic method used by TypedTableMeta to get a database field with dot notation on a class.
|
|
@@ -465,6 +482,12 @@ class TableMeta(type):
|
|
|
465
482
|
"""
|
|
466
483
|
return QueryBuilder(self).join(*fields, on=on, condition=condition, method=method, condition_and=condition_and)
|
|
467
484
|
|
|
485
|
+
def window(self: t.Type[T_MetaInstance], window_size: int) -> "QueryBuilder[T_MetaInstance]":
|
|
486
|
+
"""
|
|
487
|
+
See QueryBuilder.window!
|
|
488
|
+
"""
|
|
489
|
+
return QueryBuilder(self).window(window_size)
|
|
490
|
+
|
|
468
491
|
def collect(self: t.Type[T_MetaInstance], verbose: bool = False) -> "TypedRows[T_MetaInstance]":
|
|
469
492
|
"""
|
|
470
493
|
See QueryBuilder.collect!
|
|
@@ -313,6 +313,8 @@ class Metadata(t.TypedDict):
|
|
|
313
313
|
select_duration: t.NotRequired[float]
|
|
314
314
|
relationships: t.NotRequired[set[str]]
|
|
315
315
|
sql: t.NotRequired[str]
|
|
316
|
+
window_size: t.NotRequired[int]
|
|
317
|
+
iterating: t.NotRequired[bool]
|
|
316
318
|
|
|
317
319
|
|
|
318
320
|
class FieldSettings(t.TypedDict, total=False):
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Warning types emitted by TypeDAL."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class UnusedWindowWarning(RuntimeWarning):
|
|
5
|
+
"""Warn when a window size is configured but the query is collected directly."""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class NoopQueryWarning(RuntimeWarning):
|
|
9
|
+
"""Warn when a query builder method is called without arguments, so it does nothing."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|