TypeDAL 3.13.0__tar.gz → 3.14.0__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.
- {typedal-3.13.0 → typedal-3.14.0}/CHANGELOG.md +12 -0
- {typedal-3.13.0 → typedal-3.14.0}/PKG-INFO +2 -2
- {typedal-3.13.0 → typedal-3.14.0}/src/typedal/__about__.py +1 -1
- {typedal-3.13.0 → typedal-3.14.0}/src/typedal/__init__.py +1 -1
- {typedal-3.13.0 → typedal-3.14.0}/src/typedal/cli.py +3 -2
- {typedal-3.13.0 → typedal-3.14.0}/src/typedal/core.py +39 -14
- {typedal-3.13.0 → typedal-3.14.0}/src/typedal/for_py4web.py +1 -2
- {typedal-3.13.0 → typedal-3.14.0}/src/typedal/helpers.py +5 -1
- {typedal-3.13.0 → typedal-3.14.0}/src/typedal/mixins.py +5 -1
- {typedal-3.13.0 → typedal-3.14.0}/src/typedal/serializers/as_json.py +1 -1
- {typedal-3.13.0 → typedal-3.14.0}/tests/test_main.py +5 -0
- {typedal-3.13.0 → typedal-3.14.0}/tests/test_query_builder.py +2 -0
- {typedal-3.13.0 → typedal-3.14.0}/.github/workflows/su6.yml +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/.gitignore +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/.readthedocs.yml +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/README.md +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/coverage.svg +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/docs/1_getting_started.md +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/docs/2_defining_tables.md +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/docs/3_building_queries.md +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/docs/4_relationships.md +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/docs/5_py4web.md +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/docs/6_migrations.md +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/docs/7_mixins.md +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/docs/css/code_blocks.css +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/docs/index.md +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/docs/requirements.txt +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/example_new.py +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/example_old.py +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/mkdocs.yml +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/pyproject.toml +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/src/typedal/caching.py +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/src/typedal/config.py +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/src/typedal/fields.py +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/src/typedal/for_web2py.py +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/src/typedal/py.typed +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/src/typedal/types.py +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/src/typedal/web2py_py4web_shared.py +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/tests/__init__.py +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/tests/configs/simple.toml +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/tests/configs/valid.env +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/tests/configs/valid.toml +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/tests/test_cli.py +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/tests/test_config.py +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/tests/test_docs_examples.py +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/tests/test_helpers.py +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/tests/test_json.py +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/tests/test_mixins.py +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/tests/test_mypy.py +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/tests/test_orm.py +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/tests/test_py4web.py +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/tests/test_relationships.py +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/tests/test_row.py +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/tests/test_stats.py +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/tests/test_table.py +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/tests/test_web2py.py +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/tests/test_xx_others.py +0 -0
- {typedal-3.13.0 → typedal-3.14.0}/tests/timings.py +0 -0
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
<!--next-version-placeholder-->
|
|
4
4
|
|
|
5
|
+
## v3.14.0 (2025-05-15)
|
|
6
|
+
|
|
7
|
+
### Feature
|
|
8
|
+
|
|
9
|
+
* `db.find_model` to get the registered TypedTable class for a specific 'table_name' ([`c645303`](https://github.com/trialandsuccess/TypeDAL/commit/c645303a85c96735f48eafaaeb20f867b977686f))
|
|
10
|
+
|
|
11
|
+
## v3.13.1 (2025-04-28)
|
|
12
|
+
|
|
13
|
+
### Fix
|
|
14
|
+
|
|
15
|
+
* Pass select kwargs via `.column()` - so you can do e.g. `distinct=True` ([`e5bc168`](https://github.com/trialandsuccess/TypeDAL/commit/e5bc168b90d6a5d214f049de0ef31e544214cc23))
|
|
16
|
+
|
|
5
17
|
## v3.13.0 (2025-04-28)
|
|
6
18
|
|
|
7
19
|
### Feature
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: TypeDAL
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.14.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
|
|
@@ -10,4 +10,4 @@ try:
|
|
|
10
10
|
except ImportError: # pragma: no cover
|
|
11
11
|
P4W_DAL = None # type: ignore
|
|
12
12
|
|
|
13
|
-
__all__ = ["
|
|
13
|
+
__all__ = ["Relationship", "TypeDAL", "TypedField", "TypedRows", "TypedTable", "fields", "relationship"]
|
|
@@ -185,7 +185,7 @@ def setup(
|
|
|
185
185
|
data.pop(prop, None)
|
|
186
186
|
continue
|
|
187
187
|
|
|
188
|
-
if minimal and getattr(config, prop, None) not in (None, "") or is_optional(annotation):
|
|
188
|
+
if (minimal and getattr(config, prop, None) not in (None, "")) or is_optional(annotation):
|
|
189
189
|
# property already present or not required, SKIP!
|
|
190
190
|
data[prop] = getattr(config, prop, None)
|
|
191
191
|
continue
|
|
@@ -392,7 +392,8 @@ def fake_migrations(
|
|
|
392
392
|
|
|
393
393
|
previously_migrated = (
|
|
394
394
|
db(
|
|
395
|
-
db.ewh_implemented_features.name.belongs(to_fake)
|
|
395
|
+
db.ewh_implemented_features.name.belongs(to_fake)
|
|
396
|
+
& (db.ewh_implemented_features.installed == True) # noqa E712
|
|
396
397
|
)
|
|
397
398
|
.select(db.ewh_implemented_features.name)
|
|
398
399
|
.column("name")
|
|
@@ -93,10 +93,8 @@ def is_typed_field(cls: Any) -> typing.TypeGuard["TypedField[Any]"]:
|
|
|
93
93
|
|
|
94
94
|
Deprecated
|
|
95
95
|
"""
|
|
96
|
-
return (
|
|
97
|
-
isinstance(cls, TypedField)
|
|
98
|
-
or isinstance(typing.get_origin(cls), type)
|
|
99
|
-
and issubclass(typing.get_origin(cls), TypedField)
|
|
96
|
+
return isinstance(cls, TypedField) or (
|
|
97
|
+
isinstance(typing.get_origin(cls), type) and issubclass(typing.get_origin(cls), TypedField)
|
|
100
98
|
)
|
|
101
99
|
|
|
102
100
|
|
|
@@ -698,11 +696,30 @@ class TypeDAL(pydal.DAL): # type: ignore
|
|
|
698
696
|
"""
|
|
699
697
|
Allows dynamically accessing a table by its name as a string.
|
|
700
698
|
|
|
699
|
+
If you need the TypedTable class instead of the pydal table, use find_model instead.
|
|
700
|
+
|
|
701
701
|
Example:
|
|
702
702
|
db['users'] -> user
|
|
703
703
|
"""
|
|
704
704
|
return typing.cast(Table, super().__getitem__(str(key)))
|
|
705
705
|
|
|
706
|
+
def find_model(self, table_name: str) -> Type["TypedTable"] | None:
|
|
707
|
+
"""
|
|
708
|
+
Retrieves a mapped table class by its name.
|
|
709
|
+
|
|
710
|
+
This method searches for a table class matching the given table name
|
|
711
|
+
in the defined class map dictionary. If a match is found, the corresponding
|
|
712
|
+
table class is returned; otherwise, None is returned, indicating that no
|
|
713
|
+
table class matches the input name.
|
|
714
|
+
|
|
715
|
+
Args:
|
|
716
|
+
table_name: The name of the table to retrieve the mapped class for.
|
|
717
|
+
|
|
718
|
+
Returns:
|
|
719
|
+
The mapped table class if it exists, otherwise None.
|
|
720
|
+
"""
|
|
721
|
+
return self._class_map.get(table_name, None)
|
|
722
|
+
|
|
706
723
|
@classmethod
|
|
707
724
|
def _build_field(cls, name: str, _type: str, **kw: Any) -> Field:
|
|
708
725
|
# return Field(name, _type, **{**cls.default_kwargs, **kw})
|
|
@@ -1018,6 +1035,14 @@ class TableMeta(type):
|
|
|
1018
1035
|
"""
|
|
1019
1036
|
return QueryBuilder(self).select(*a, **kw)
|
|
1020
1037
|
|
|
1038
|
+
def column(self: Type[T_MetaInstance], field: "TypedField[T] | T", **options: Unpack[SelectKwargs]) -> list[T]:
|
|
1039
|
+
"""
|
|
1040
|
+
Get all values in a specific column.
|
|
1041
|
+
|
|
1042
|
+
Shortcut for `.select(field).execute().column(field)`.
|
|
1043
|
+
"""
|
|
1044
|
+
return QueryBuilder(self).select(field, **options).execute().column(field)
|
|
1045
|
+
|
|
1021
1046
|
def paginate(self: Type[T_MetaInstance], limit: int, page: int = 1) -> "PaginatedRows[T_MetaInstance]":
|
|
1022
1047
|
"""
|
|
1023
1048
|
See QueryBuilder.paginate!
|
|
@@ -2695,24 +2720,24 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
|
|
|
2695
2720
|
return save_to_cache(typed_rows, rows)
|
|
2696
2721
|
|
|
2697
2722
|
@typing.overload
|
|
2698
|
-
def column(self, field: TypedField[T]) -> list[T]:
|
|
2723
|
+
def column(self, field: TypedField[T], **options: Unpack[SelectKwargs]) -> list[T]:
|
|
2699
2724
|
"""
|
|
2700
2725
|
If a typedfield is passed, the output type can be safely determined.
|
|
2701
2726
|
"""
|
|
2702
2727
|
|
|
2703
2728
|
@typing.overload
|
|
2704
|
-
def column(self, field: T) -> list[T]:
|
|
2729
|
+
def column(self, field: T, **options: Unpack[SelectKwargs]) -> list[T]:
|
|
2705
2730
|
"""
|
|
2706
2731
|
Otherwise, the output type is loosely determined (assumes `field: type` or Any).
|
|
2707
2732
|
"""
|
|
2708
2733
|
|
|
2709
|
-
def column(self, field: TypedField[T] | T) -> list[T]:
|
|
2734
|
+
def column(self, field: TypedField[T] | T, **options: Unpack[SelectKwargs]) -> list[T]:
|
|
2710
2735
|
"""
|
|
2711
2736
|
Get all values in a specific column.
|
|
2712
2737
|
|
|
2713
2738
|
Shortcut for `.select(field).execute().column(field)`.
|
|
2714
2739
|
"""
|
|
2715
|
-
return self.select(field).execute().column(field)
|
|
2740
|
+
return self.select(field, **options).execute().column(field)
|
|
2716
2741
|
|
|
2717
2742
|
def _handle_relationships_pre_select(
|
|
2718
2743
|
self,
|
|
@@ -2878,7 +2903,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
|
|
|
2878
2903
|
|
|
2879
2904
|
return _to(rows, self.model, records, metadata=metadata)
|
|
2880
2905
|
|
|
2881
|
-
def collect_or_fail(self, exception: Exception = None) -> "TypedRows[T_MetaInstance]":
|
|
2906
|
+
def collect_or_fail(self, exception: typing.Optional[Exception] = None) -> "TypedRows[T_MetaInstance]":
|
|
2882
2907
|
"""
|
|
2883
2908
|
Call .collect() and raise an error if nothing found.
|
|
2884
2909
|
|
|
@@ -2898,7 +2923,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
|
|
|
2898
2923
|
"""
|
|
2899
2924
|
yield from self.collect()
|
|
2900
2925
|
|
|
2901
|
-
def __count(self, db: TypeDAL, distinct: bool = None) -> Query:
|
|
2926
|
+
def __count(self, db: TypeDAL, distinct: typing.Optional[bool] = None) -> Query:
|
|
2902
2927
|
# internal, shared logic between .count and ._count
|
|
2903
2928
|
model = self.model
|
|
2904
2929
|
query = self.query
|
|
@@ -2914,7 +2939,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
|
|
|
2914
2939
|
|
|
2915
2940
|
return query
|
|
2916
2941
|
|
|
2917
|
-
def count(self, distinct: bool = None) -> int:
|
|
2942
|
+
def count(self, distinct: typing.Optional[bool] = None) -> int:
|
|
2918
2943
|
"""
|
|
2919
2944
|
Return the amount of rows matching the current query.
|
|
2920
2945
|
"""
|
|
@@ -2923,7 +2948,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
|
|
|
2923
2948
|
|
|
2924
2949
|
return db(query).count(distinct)
|
|
2925
2950
|
|
|
2926
|
-
def _count(self, distinct: bool = None) -> str:
|
|
2951
|
+
def _count(self, distinct: typing.Optional[bool] = None) -> str:
|
|
2927
2952
|
"""
|
|
2928
2953
|
Return the SQL for .count().
|
|
2929
2954
|
"""
|
|
@@ -3022,7 +3047,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
|
|
|
3022
3047
|
def _first(self) -> str:
|
|
3023
3048
|
return self._paginate(page=1, limit=1)
|
|
3024
3049
|
|
|
3025
|
-
def first_or_fail(self, exception: Exception = None, verbose: bool = False) -> T_MetaInstance:
|
|
3050
|
+
def first_or_fail(self, exception: typing.Optional[Exception] = None, verbose: bool = False) -> T_MetaInstance:
|
|
3026
3051
|
"""
|
|
3027
3052
|
Call .first() and raise an error if nothing found.
|
|
3028
3053
|
|
|
@@ -3110,7 +3135,7 @@ class TypedSet(pydal.objects.Set): # type: ignore # pragma: no cover
|
|
|
3110
3135
|
This class is not actually used, only 'cast' by TypeDAL.__call__
|
|
3111
3136
|
"""
|
|
3112
3137
|
|
|
3113
|
-
def count(self, distinct: bool = None, cache: AnyDict = None) -> int:
|
|
3138
|
+
def count(self, distinct: typing.Optional[bool] = None, cache: AnyDict = None) -> int:
|
|
3114
3139
|
"""
|
|
3115
3140
|
Count returns an int.
|
|
3116
3141
|
"""
|
|
@@ -5,7 +5,6 @@ ONLY USE IN COMBINATION WITH PY4WEB!
|
|
|
5
5
|
import typing
|
|
6
6
|
|
|
7
7
|
import threadsafevariable
|
|
8
|
-
from configuraptor.abs import AnyType
|
|
9
8
|
from py4web.core import ICECUBE
|
|
10
9
|
from py4web.core import Fixture as _Fixture
|
|
11
10
|
from pydal.base import MetaDAL, hashlib_md5
|
|
@@ -68,8 +67,8 @@ def setup_py4web_tables(db: TypeDAL) -> None:
|
|
|
68
67
|
|
|
69
68
|
|
|
70
69
|
__all__ = [
|
|
70
|
+
"DAL",
|
|
71
71
|
"AuthUser",
|
|
72
72
|
"Fixture",
|
|
73
|
-
"DAL",
|
|
74
73
|
"setup_py4web_tables",
|
|
75
74
|
]
|
|
@@ -55,7 +55,7 @@ def all_dict(cls: type) -> AnyDict:
|
|
|
55
55
|
return dict(ChainMap(*(c.__dict__ for c in reversed_mro(cls)))) # type: ignore
|
|
56
56
|
|
|
57
57
|
|
|
58
|
-
def all_annotations(cls: type, _except: typing.Iterable[str] = None) -> dict[str, type]:
|
|
58
|
+
def all_annotations(cls: type, _except: typing.Optional[typing.Iterable[str]] = None) -> dict[str, type]:
|
|
59
59
|
"""
|
|
60
60
|
Wrapper around `_all_annotations` that filters away any keys in _except.
|
|
61
61
|
|
|
@@ -306,6 +306,10 @@ def get_field(field: "TypedField[typing.Any] | Field") -> "Field":
|
|
|
306
306
|
|
|
307
307
|
|
|
308
308
|
class classproperty:
|
|
309
|
+
"""
|
|
310
|
+
Combination of @classmethod and @property.
|
|
311
|
+
"""
|
|
312
|
+
|
|
309
313
|
def __init__(self, fget: typing.Callable[..., typing.Any]) -> None:
|
|
310
314
|
"""
|
|
311
315
|
Initialize the classproperty.
|
|
@@ -163,7 +163,11 @@ class SlugMixin(Mixin):
|
|
|
163
163
|
) # set via init subclass
|
|
164
164
|
|
|
165
165
|
def __init_subclass__(
|
|
166
|
-
cls,
|
|
166
|
+
cls,
|
|
167
|
+
slug_field: typing.Optional[str] = None,
|
|
168
|
+
slug_suffix_length: int = 0,
|
|
169
|
+
slug_suffix: Optional[int] = None,
|
|
170
|
+
**kw: Any,
|
|
167
171
|
) -> None:
|
|
168
172
|
"""
|
|
169
173
|
Bind 'slug field' option to be used later (on_define).
|
|
@@ -68,7 +68,7 @@ class SerializedJson(ConfigurableJsonEncoder):
|
|
|
68
68
|
return _rules.get(_type, JSONRule(transform=self._default) if with_default else None)
|
|
69
69
|
|
|
70
70
|
|
|
71
|
-
def encode(something: Any, indent: int = None, **kw: Any) -> str:
|
|
71
|
+
def encode(something: Any, indent: typing.Optional[int] = None, **kw: Any) -> str:
|
|
72
72
|
"""
|
|
73
73
|
Encode anything to JSON with some improved defaults.
|
|
74
74
|
"""
|
|
@@ -166,6 +166,11 @@ def test_mixed_defines(capsys):
|
|
|
166
166
|
|
|
167
167
|
assert SecondNewSyntax(1).location == "Rotterdam"
|
|
168
168
|
|
|
169
|
+
# test find_model:
|
|
170
|
+
assert db.find_model("old_syntax") is None
|
|
171
|
+
assert db.find_model("first_new_syntax") is FirstNewSyntax
|
|
172
|
+
assert db.find_model("second_new_syntax") is SecondNewSyntax
|
|
173
|
+
|
|
169
174
|
|
|
170
175
|
def test_dont_allow_bool_in_query():
|
|
171
176
|
with pytest.raises(ValueError):
|
|
@@ -483,6 +483,8 @@ def test_column():
|
|
|
483
483
|
assert len(rows) == 4
|
|
484
484
|
assert set(rows) == {33}
|
|
485
485
|
|
|
486
|
+
assert TestRelationship.column(TestRelationship.value, distinct=True, orderby=~TestRelationship.value) == [33, 3]
|
|
487
|
+
|
|
486
488
|
|
|
487
489
|
def test_collect_with_extra_fields():
|
|
488
490
|
_setup_data()
|
|
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
|
|
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
|