TypeDAL 3.17.1__tar.gz → 3.17.3__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.

Potentially problematic release.


This version of TypeDAL might be problematic. Click here for more details.

Files changed (58) hide show
  1. {typedal-3.17.1 → typedal-3.17.3}/CHANGELOG.md +12 -0
  2. {typedal-3.17.1 → typedal-3.17.3}/PKG-INFO +1 -1
  3. {typedal-3.17.1 → typedal-3.17.3}/src/typedal/__about__.py +1 -1
  4. {typedal-3.17.1 → typedal-3.17.3}/src/typedal/core.py +24 -1
  5. {typedal-3.17.1 → typedal-3.17.3}/src/typedal/types.py +3 -1
  6. {typedal-3.17.1 → typedal-3.17.3}/tests/test_helpers.py +1 -1
  7. {typedal-3.17.1 → typedal-3.17.3}/tests/test_query_builder.py +17 -4
  8. {typedal-3.17.1 → typedal-3.17.3}/.github/workflows/su6.yml +0 -0
  9. {typedal-3.17.1 → typedal-3.17.3}/.gitignore +0 -0
  10. {typedal-3.17.1 → typedal-3.17.3}/.readthedocs.yml +0 -0
  11. {typedal-3.17.1 → typedal-3.17.3}/README.md +0 -0
  12. {typedal-3.17.1 → typedal-3.17.3}/coverage.svg +0 -0
  13. {typedal-3.17.1 → typedal-3.17.3}/docs/1_getting_started.md +0 -0
  14. {typedal-3.17.1 → typedal-3.17.3}/docs/2_defining_tables.md +0 -0
  15. {typedal-3.17.1 → typedal-3.17.3}/docs/3_building_queries.md +0 -0
  16. {typedal-3.17.1 → typedal-3.17.3}/docs/4_relationships.md +0 -0
  17. {typedal-3.17.1 → typedal-3.17.3}/docs/5_py4web.md +0 -0
  18. {typedal-3.17.1 → typedal-3.17.3}/docs/6_migrations.md +0 -0
  19. {typedal-3.17.1 → typedal-3.17.3}/docs/7_mixins.md +0 -0
  20. {typedal-3.17.1 → typedal-3.17.3}/docs/css/code_blocks.css +0 -0
  21. {typedal-3.17.1 → typedal-3.17.3}/docs/index.md +0 -0
  22. {typedal-3.17.1 → typedal-3.17.3}/docs/requirements.txt +0 -0
  23. {typedal-3.17.1 → typedal-3.17.3}/example_new.py +0 -0
  24. {typedal-3.17.1 → typedal-3.17.3}/example_old.py +0 -0
  25. {typedal-3.17.1 → typedal-3.17.3}/mkdocs.yml +0 -0
  26. {typedal-3.17.1 → typedal-3.17.3}/pyproject.toml +0 -0
  27. {typedal-3.17.1 → typedal-3.17.3}/src/typedal/__init__.py +0 -0
  28. {typedal-3.17.1 → typedal-3.17.3}/src/typedal/caching.py +0 -0
  29. {typedal-3.17.1 → typedal-3.17.3}/src/typedal/cli.py +0 -0
  30. {typedal-3.17.1 → typedal-3.17.3}/src/typedal/config.py +0 -0
  31. {typedal-3.17.1 → typedal-3.17.3}/src/typedal/fields.py +0 -0
  32. {typedal-3.17.1 → typedal-3.17.3}/src/typedal/for_py4web.py +0 -0
  33. {typedal-3.17.1 → typedal-3.17.3}/src/typedal/for_web2py.py +0 -0
  34. {typedal-3.17.1 → typedal-3.17.3}/src/typedal/helpers.py +0 -0
  35. {typedal-3.17.1 → typedal-3.17.3}/src/typedal/mixins.py +0 -0
  36. {typedal-3.17.1 → typedal-3.17.3}/src/typedal/py.typed +0 -0
  37. {typedal-3.17.1 → typedal-3.17.3}/src/typedal/serializers/as_json.py +0 -0
  38. {typedal-3.17.1 → typedal-3.17.3}/src/typedal/web2py_py4web_shared.py +0 -0
  39. {typedal-3.17.1 → typedal-3.17.3}/tests/__init__.py +0 -0
  40. {typedal-3.17.1 → typedal-3.17.3}/tests/configs/simple.toml +0 -0
  41. {typedal-3.17.1 → typedal-3.17.3}/tests/configs/valid.env +0 -0
  42. {typedal-3.17.1 → typedal-3.17.3}/tests/configs/valid.toml +0 -0
  43. {typedal-3.17.1 → typedal-3.17.3}/tests/test_cli.py +0 -0
  44. {typedal-3.17.1 → typedal-3.17.3}/tests/test_config.py +0 -0
  45. {typedal-3.17.1 → typedal-3.17.3}/tests/test_docs_examples.py +0 -0
  46. {typedal-3.17.1 → typedal-3.17.3}/tests/test_json.py +0 -0
  47. {typedal-3.17.1 → typedal-3.17.3}/tests/test_main.py +0 -0
  48. {typedal-3.17.1 → typedal-3.17.3}/tests/test_mixins.py +0 -0
  49. {typedal-3.17.1 → typedal-3.17.3}/tests/test_mypy.py +0 -0
  50. {typedal-3.17.1 → typedal-3.17.3}/tests/test_orm.py +0 -0
  51. {typedal-3.17.1 → typedal-3.17.3}/tests/test_py4web.py +0 -0
  52. {typedal-3.17.1 → typedal-3.17.3}/tests/test_relationships.py +0 -0
  53. {typedal-3.17.1 → typedal-3.17.3}/tests/test_row.py +0 -0
  54. {typedal-3.17.1 → typedal-3.17.3}/tests/test_stats.py +0 -0
  55. {typedal-3.17.1 → typedal-3.17.3}/tests/test_table.py +0 -0
  56. {typedal-3.17.1 → typedal-3.17.3}/tests/test_web2py.py +0 -0
  57. {typedal-3.17.1 → typedal-3.17.3}/tests/test_xx_others.py +0 -0
  58. {typedal-3.17.1 → typedal-3.17.3}/tests/timings.py +0 -0
@@ -2,6 +2,18 @@
2
2
 
3
3
  <!--next-version-placeholder-->
4
4
 
5
+ ## v3.17.3 (2025-09-30)
6
+
7
+ ### Fix
8
+
9
+ * Make `first_or_fail` accept any exception (including BaseException) to support e.g. py4web's HTTP ([`7b42724`](https://github.com/trialandsuccess/TypeDAL/commit/7b42724dbebc8151545c5456cd49fe93ae63157f))
10
+
11
+ ## v3.17.2 (2025-09-24)
12
+
13
+ ### Fix
14
+
15
+ * Support .orderby as alias for .select(orderby= ([`be561c5`](https://github.com/trialandsuccess/TypeDAL/commit/be561c500c573a3a45b22f250fe1d2b473b56cc3))
16
+
5
17
  ## v3.17.1 (2025-09-20)
6
18
 
7
19
  ### Fix
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: TypeDAL
3
- Version: 3.17.1
3
+ Version: 3.17.3
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
@@ -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.17.1"
8
+ __version__ = "3.17.3"
@@ -60,6 +60,7 @@ from .types import (
60
60
  FieldSettings,
61
61
  Metadata,
62
62
  OpRow,
63
+ OrderBy,
63
64
  PaginateDict,
64
65
  Pagination,
65
66
  Query,
@@ -1146,6 +1147,12 @@ class TableMeta(type):
1146
1147
  """
1147
1148
  return QueryBuilder(self).where(*a, **kw)
1148
1149
 
1150
+ def orderby(self: Type[T_MetaInstance], *fields: OrderBy) -> "QueryBuilder[T_MetaInstance]":
1151
+ """
1152
+ See QueryBuilder.orderby!
1153
+ """
1154
+ return QueryBuilder(self).orderby(*fields)
1155
+
1149
1156
  def cache(self: Type[T_MetaInstance], *deps: Any, **kwargs: Any) -> "QueryBuilder[T_MetaInstance]":
1150
1157
  """
1151
1158
  See QueryBuilder.cache!
@@ -2678,6 +2685,22 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
2678
2685
  """
2679
2686
  return self._extend(select_args=list(fields), select_kwargs=options)
2680
2687
 
2688
+ def orderby(self, *fields: OrderBy) -> "QueryBuilder[T_MetaInstance]":
2689
+ """
2690
+ Order the query results by specified fields.
2691
+
2692
+ Args:
2693
+ fields: field(s) to order by. Supported:
2694
+ table.name - sort by name, ascending
2695
+ ~table.name - sort by name, descending
2696
+ <random> - sort randomly
2697
+ table.name|table.id - sort by two fields (first name, then id)
2698
+
2699
+ Returns:
2700
+ QueryBuilder: A new QueryBuilder instance with the ordering applied.
2701
+ """
2702
+ return self.select(orderby=fields)
2703
+
2681
2704
  def where(
2682
2705
  self,
2683
2706
  *queries_or_lambdas: Query | typing.Callable[[Type[T_MetaInstance]], Query] | dict[str, Any],
@@ -3320,7 +3343,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
3320
3343
  def _first(self) -> str:
3321
3344
  return self._paginate(page=1, limit=1)
3322
3345
 
3323
- def first_or_fail(self, exception: typing.Optional[Exception] = None, verbose: bool = False) -> T_MetaInstance:
3346
+ def first_or_fail(self, exception: typing.Optional[BaseException] = None, verbose: bool = False) -> T_MetaInstance:
3324
3347
  """
3325
3348
  Call .first() and raise an error if nothing found.
3326
3349
 
@@ -218,6 +218,8 @@ class CacheFn(typing.Protocol):
218
218
  CacheModel = typing.Callable[[str, CacheFn, int], Rows]
219
219
  CacheTuple = tuple[CacheModel, int]
220
220
 
221
+ OrderBy: typing.TypeAlias = Expression | str
222
+
221
223
 
222
224
  class SelectKwargs(TypedDict, total=False):
223
225
  """
@@ -226,7 +228,7 @@ class SelectKwargs(TypedDict, total=False):
226
228
 
227
229
  join: Optional[list[Expression]]
228
230
  left: Optional[list[Expression]]
229
- orderby: Optional[Expression | str | Table]
231
+ orderby: OrderBy | typing.Iterable[OrderBy] | None
230
232
  limitby: Optional[tuple[int, int]]
231
233
  distinct: bool | Field | Expression
232
234
  orderby_on_limitby: bool
@@ -243,4 +243,4 @@ def test_sql_expression():
243
243
 
244
244
  # test quoting fields and tables:
245
245
  assert str(database.sql_expression("LOWER(%s)", TestSqlExpression.value)) == 'LOWER("test_sql_expression"."value")'
246
- assert str(database.sql_expression("LOWER(%s.value)", TestSqlExpression)) == 'LOWER("test_sql_expression".value)'
246
+ assert str(database.sql_expression("LOWER(%s.value)", TestSqlExpression)) == 'LOWER("test_sql_expression".value)'
@@ -447,7 +447,7 @@ def test_orderby():
447
447
 
448
448
  assert base_qt.count() == 5
449
449
 
450
- rows1 = base_qt.select(orderby=TestQueryTable.id).paginate(limit=3, page=1)
450
+ rows1 = base_qt.orderby(TestQueryTable.id).paginate(limit=3, page=1)
451
451
  rows2 = base_qt.select(orderby=TestQueryTable.id, limitby=(0, 3)).collect()
452
452
  assert [_.id for _ in rows1] == [_.id for _ in rows2] == [1, 2, 3]
453
453
 
@@ -467,10 +467,12 @@ def test_orderby():
467
467
  rows2 = joined_qt.select(orderby=TestQueryTable.id, limitby=(0, 3)).collect()
468
468
  assert [_.id for _ in rows1] == [_.id for _ in rows2] == [1, 2, 3]
469
469
 
470
- rows1 = joined_qt.select(orderby=TestQueryTable.number).paginate(limit=3, page=1)
471
- rows2 = joined_qt.select(orderby=TestQueryTable.number, limitby=(0, 3)).collect()
470
+ rows1 = joined_qt.orderby(TestQueryTable.number, TestQueryTable.other).paginate(limit=3, page=1)
471
+ rows2 = joined_qt.select(orderby=TestQueryTable.number | TestQueryTable.other, limitby=(0, 3)).collect()
472
472
  assert [_.number for _ in rows1] == [_.number for _ in rows2] == [0, 1, 2]
473
473
 
474
+ assert rows1.metadata["sql"] == rows2.metadata["sql"]
475
+
474
476
  rows1 = joined_qt.select(orderby=~TestQueryTable.number).paginate(limit=3, page=1)
475
477
  rows2 = joined_qt.select(orderby=~TestQueryTable.number, limitby=(0, 3)).collect()
476
478
  assert [_.number for _ in rows1] == [_.number for _ in rows2] == [4, 3, 2]
@@ -479,6 +481,11 @@ def test_orderby():
479
481
  rows2 = joined_qt.select(orderby=~TestQueryTable.number, limitby=(0, 100)).collect()
480
482
  assert [_.number for _ in rows1] == [_.number for _ in rows2] == [4, 3, 2, 1, 0]
481
483
 
484
+ assert (
485
+ TestQueryTable.orderby(TestQueryTable.yet_another, TestQueryTable.number).to_sql()
486
+ == TestQueryTable.select(orderby=TestQueryTable.yet_another | TestQueryTable.number).to_sql()
487
+ )
488
+
482
489
 
483
490
  def test_execute():
484
491
  _setup_data()
@@ -514,9 +521,15 @@ def test_collect_with_extra_fields():
514
521
 
515
522
  assert builder.execute()
516
523
 
517
- row = builder.first_or_fail()
524
+ class HTTP(BaseException):
525
+ ...
526
+
527
+ row = builder.first_or_fail(HTTP(404))
518
528
 
519
529
  assert row.id
520
530
  assert row.name
521
531
  assert row._extra
522
532
  assert row[TestRelationship.querytable.count()]
533
+
534
+ with pytest.raises(HTTP):
535
+ TestRelationship.where(TestRelationship.id == 3245892384).first_or_fail(HTTP(404))
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
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
File without changes
File without changes
File without changes
File without changes