TypeDAL 4.8.5__tar.gz → 4.8.7__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-4.8.5 → typedal-4.8.7}/CHANGELOG.md +12 -0
- {typedal-4.8.5 → typedal-4.8.7}/PKG-INFO +1 -1
- typedal-4.8.7/coverage.svg +1 -0
- {typedal-4.8.5 → typedal-4.8.7}/src/typedal/__about__.py +1 -1
- {typedal-4.8.5 → typedal-4.8.7}/src/typedal/fields.py +12 -1
- {typedal-4.8.5 → typedal-4.8.7}/src/typedal/tables.py +6 -5
- {typedal-4.8.5 → typedal-4.8.7}/tests/test_config.py +9 -2
- typedal-4.8.5/coverage.svg +0 -1
- typedal-4.8.5/typing_again.py +0 -40
- {typedal-4.8.5 → typedal-4.8.7}/.github/workflows/su6.yml +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/.gitignore +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/.readthedocs.yml +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/README.md +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/docs/10_advanced_apis.md +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/docs/1_getting_started.md +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/docs/2_defining_tables.md +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/docs/3_building_queries.md +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/docs/4_relationships.md +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/docs/5_py4web.md +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/docs/6_migrations.md +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/docs/7_configuration.md +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/docs/8_mixins.md +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/docs/9_memoization.md +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/docs/css/code_blocks.css +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/docs/index.md +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/docs/requirements.txt +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/example_new.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/example_old.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/mkdocs.yml +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/pyproject.toml +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/src/typedal/__init__.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/src/typedal/caching.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/src/typedal/cli.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/src/typedal/config.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/src/typedal/constants.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/src/typedal/core.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/src/typedal/define.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/src/typedal/enum_helpers.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/src/typedal/for_py4web.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/src/typedal/for_web2py.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/src/typedal/helpers.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/src/typedal/mixins.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/src/typedal/py.typed +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/src/typedal/query_builder.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/src/typedal/relationships.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/src/typedal/rows.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/src/typedal/serializers/as_json.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/src/typedal/serializers/typescript.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/src/typedal/types.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/src/typedal/web2py_py4web_shared.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/tasks.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/tests/__init__.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/tests/configs/simple.toml +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/tests/configs/valid.env +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/tests/configs/valid.toml +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/tests/py314_tests.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/tests/test_cli.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/tests/test_docs_examples.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/tests/test_helpers.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/tests/test_json.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/tests/test_main.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/tests/test_mixins.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/tests/test_mypy.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/tests/test_orm.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/tests/test_py4web.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/tests/test_query_builder.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/tests/test_relationships.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/tests/test_row.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/tests/test_stats.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/tests/test_table.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/tests/test_typescript.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/tests/test_typing_mypy.md +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/tests/test_typing_pyright.md +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/tests/test_web2py.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/tests/test_xx_others.py +0 -0
- {typedal-4.8.5 → typedal-4.8.7}/tests/timings.py +0 -0
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
<!--next-version-placeholder-->
|
|
4
4
|
|
|
5
|
+
## v4.8.7 (2026-06-11)
|
|
6
|
+
|
|
7
|
+
### Fix
|
|
8
|
+
|
|
9
|
+
* Make type checker happy about as_dict and similar methods; reformat ([`f6be55f`](https://github.com/trialandsuccess/TypeDAL/commit/f6be55f253e87d409e0ac017f2bdb89233620391))
|
|
10
|
+
|
|
11
|
+
## v4.8.6 (2026-06-02)
|
|
12
|
+
|
|
13
|
+
### Fix
|
|
14
|
+
|
|
15
|
+
* Support `None` for timestamp fields ([`e49be64`](https://github.com/trialandsuccess/TypeDAL/commit/e49be6425158c0c299bdccb9d5c18187f4f1c10e))
|
|
16
|
+
|
|
5
17
|
## v4.8.5 (2026-05-28)
|
|
6
18
|
|
|
7
19
|
### Fix
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="122" height="20" role="img" aria-label="coverage: 100.00%"><title>coverage: 100.00%</title><filter id="blur"><feGaussianBlur stdDeviation="16"/></filter><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="122" height="20" rx="3"/></clipPath><g clip-path="url(#r)"><rect width="61" height="20" fill="#555"/><rect x="61" width="61" height="20" fill="#4b0"/><rect width="122" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="315" y="150" fill-opacity=".8" filter="url(#blur)" textLength="510">coverage</text><text x="315" y="150" fill-opacity=".3" textLength="510">coverage</text></g><text x="315" y="140" textLength="510">coverage</text></g><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="905" y="150" fill-opacity=".8" filter="url(#blur)" textLength="510">100.00%</text><text x="905" y="150" fill-opacity=".3" textLength="510">100.00%</text></g><text x="905" y="140" textLength="510">100.00%</text></g></g></svg>
|
|
@@ -490,10 +490,21 @@ Bigint = BigintField
|
|
|
490
490
|
|
|
491
491
|
## Custom:
|
|
492
492
|
|
|
493
|
+
|
|
494
|
+
def safe_encode_native_timestamp(value: dt.datetime | None) -> str | None:
|
|
495
|
+
"""
|
|
496
|
+
Encode timestamp values for SQL without turning None into the string 'None'.
|
|
497
|
+
"""
|
|
498
|
+
if value is None:
|
|
499
|
+
return None
|
|
500
|
+
|
|
501
|
+
return f"'{value}'"
|
|
502
|
+
|
|
503
|
+
|
|
493
504
|
NativeTimestampField = SQLCustomType(
|
|
494
505
|
type="datetime",
|
|
495
506
|
native="timestamp",
|
|
496
|
-
encoder=
|
|
507
|
+
encoder=safe_encode_native_timestamp,
|
|
497
508
|
# decoder=lambda x: x, # already parsed into datetime
|
|
498
509
|
)
|
|
499
510
|
|
|
@@ -880,12 +880,13 @@ class TypedTable(_TypedTable, metaclass=TableMeta):
|
|
|
880
880
|
}
|
|
881
881
|
|
|
882
882
|
def _setup_instance_methods(self) -> None:
|
|
883
|
-
self.as_dict = self._as_dict # type: ignore
|
|
884
|
-
self.__json__ = self.as_json = self._as_json # type: ignore
|
|
885
|
-
# self.as_yaml = self._as_yaml # type: ignore
|
|
886
|
-
self.as_xml = self._as_xml # type: ignore
|
|
887
|
-
|
|
888
883
|
# use setattr instead of self.x = y to make the typecheckers happier
|
|
884
|
+
setattr(self, "as_dict", self._as_dict)
|
|
885
|
+
setattr(self, "__json__", self._as_json)
|
|
886
|
+
setattr(self, "as_json", self._as_json)
|
|
887
|
+
setattr(self, "as_xml", self._as_xml)
|
|
888
|
+
# setattr(self, "as_yaml", self._as_yaml)
|
|
889
|
+
|
|
889
890
|
setattr(self, "update", self._update)
|
|
890
891
|
setattr(self, "delete_record", self._delete_record)
|
|
891
892
|
setattr(self, "update_record", self._update_record)
|
|
@@ -4,6 +4,7 @@ import shutil
|
|
|
4
4
|
import sqlite3
|
|
5
5
|
import tempfile
|
|
6
6
|
import uuid
|
|
7
|
+
from datetime import datetime
|
|
7
8
|
from pathlib import Path
|
|
8
9
|
|
|
9
10
|
import pytest
|
|
@@ -179,9 +180,11 @@ def test_timestamp_fields_sqlite(at_temp_dir):
|
|
|
179
180
|
ts = TimestampField(default=dt.datetime.now)
|
|
180
181
|
dt = TypedField(dt.datetime, default=dt.datetime.now)
|
|
181
182
|
|
|
183
|
+
ts_optional: TypedField[datetime | None] = TimestampField(default=None, notnull=False)
|
|
184
|
+
|
|
182
185
|
db.define(Timestamp)
|
|
183
186
|
|
|
184
|
-
row = Timestamp.insert()
|
|
187
|
+
row = Timestamp.insert(ts_optional=None)
|
|
185
188
|
|
|
186
189
|
# old:
|
|
187
190
|
assert isinstance(row.dt, dt.datetime), "not a datetime"
|
|
@@ -190,6 +193,7 @@ def test_timestamp_fields_sqlite(at_temp_dir):
|
|
|
190
193
|
# new:
|
|
191
194
|
assert isinstance(row.ts, dt.datetime), "not a datetime"
|
|
192
195
|
assert "." in str(row.ts) # ms precision
|
|
196
|
+
assert row.ts_optional is None
|
|
193
197
|
|
|
194
198
|
assert '"ts" timestamp NOT NULL' in Timestamp._sql()
|
|
195
199
|
|
|
@@ -206,9 +210,11 @@ def test_timestamp_fields_psql(at_temp_dir):
|
|
|
206
210
|
ts = TimestampField(default=dt.datetime.now)
|
|
207
211
|
dt = TypedField(dt.datetime, default=dt.datetime.now)
|
|
208
212
|
|
|
213
|
+
ts_optional: TypedField[datetime | None] = TimestampField(default=None, notnull=False)
|
|
214
|
+
|
|
209
215
|
db.define(Timestamp)
|
|
210
216
|
|
|
211
|
-
row = Timestamp.insert()
|
|
217
|
+
row = Timestamp.insert(ts_optional=None)
|
|
212
218
|
|
|
213
219
|
# old:
|
|
214
220
|
assert isinstance(row.dt, dt.datetime), "not a datetime"
|
|
@@ -217,6 +223,7 @@ def test_timestamp_fields_psql(at_temp_dir):
|
|
|
217
223
|
# new:
|
|
218
224
|
assert isinstance(row.ts, dt.datetime), "not a datetime"
|
|
219
225
|
assert "." in str(row.ts) # ms precision
|
|
226
|
+
assert row.ts_optional is None
|
|
220
227
|
|
|
221
228
|
assert '"ts" timestamp NOT NULL' in Timestamp._sql()
|
|
222
229
|
|
typedal-4.8.5/coverage.svg
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="122" height="20" role="img" aria-label="coverage: 100.00%"><title>coverage: 100.00%</title><filter id="blur"><feGaussianBlur in="SourceGraphic" stdDeviation="16"/></filter><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="122" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="61" height="20" fill="#555"/><rect x="61" width="61" height="20" fill="#4b0"/><rect width="122" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="315" y="150" fill="#010101" fill-opacity=".80" filter="url(#blur)" transform="scale(.1)" textLength="510">coverage</text><text aria-hidden="true" x="315" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="510">coverage</text><text x="315" y="140" transform="scale(.1)" fill="#fff" textLength="510">coverage</text><text aria-hidden="true" x="905" y="150" fill="#010101" fill-opacity=".80" filter="url(#blur)" transform="scale(.1)" textLength="510">100.00%</text><text aria-hidden="true" x="905" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="510">100.00%</text><text x="905" y="140" transform="scale(.1)" fill="#fff" textLength="510">100.00%</text></g></svg>
|
typedal-4.8.5/typing_again.py
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
from typedal import TypeDAL, TypedTable, TypedField, relationship, Relationship
|
|
2
|
-
|
|
3
|
-
from typing import reveal_type
|
|
4
|
-
|
|
5
|
-
db = TypeDAL()
|
|
6
|
-
|
|
7
|
-
reveal_type(db)
|
|
8
|
-
|
|
9
|
-
@db.define
|
|
10
|
-
class OtherTable(TypedTable):
|
|
11
|
-
...
|
|
12
|
-
|
|
13
|
-
@db.define
|
|
14
|
-
class MyTable(TypedTable):
|
|
15
|
-
field = TypedField(str)
|
|
16
|
-
|
|
17
|
-
rel = relationship(OtherTable)
|
|
18
|
-
multiple = relationship(list[OtherTable])
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
row = MyTable.first_or_fail()
|
|
22
|
-
|
|
23
|
-
reveal_type(row) # expected: MyTable; pycharm: MyTable
|
|
24
|
-
reveal_type(MyTable.field) # expected: TypedField[str]; pycharm: TypedField[str]
|
|
25
|
-
reveal_type(row.field) # expected: str; pycharm: TypedField[str]
|
|
26
|
-
|
|
27
|
-
q = MyTable.field.belongs([""])
|
|
28
|
-
|
|
29
|
-
reveal_type(q) # expected: Query; pycharm: Query
|
|
30
|
-
|
|
31
|
-
reveal_type(MyTable.rel) # expected: Relationship[OtherTable]; pycharm: Relationship[OtherTable | None]
|
|
32
|
-
reveal_type(row.rel) # expected: OtherTable | None; pycharm: Relationship[OtherTable | None]
|
|
33
|
-
|
|
34
|
-
reveal_type(MyTable.multiple) # expected: Relationship[list[OtherTable]]; pycharm: Relationship[list[OtherTable]]
|
|
35
|
-
reveal_type(row.multiple) # expected: list[OtherTable]; pycharm: Relationship[list[OtherTable]]
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
MyTable.field.label = "Test Label"
|
|
40
|
-
MyTable.field.represent = lambda value, row: "Test Repr"
|
|
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
|
|
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
|