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.
Files changed (76) hide show
  1. {typedal-4.8.5 → typedal-4.8.7}/CHANGELOG.md +12 -0
  2. {typedal-4.8.5 → typedal-4.8.7}/PKG-INFO +1 -1
  3. typedal-4.8.7/coverage.svg +1 -0
  4. {typedal-4.8.5 → typedal-4.8.7}/src/typedal/__about__.py +1 -1
  5. {typedal-4.8.5 → typedal-4.8.7}/src/typedal/fields.py +12 -1
  6. {typedal-4.8.5 → typedal-4.8.7}/src/typedal/tables.py +6 -5
  7. {typedal-4.8.5 → typedal-4.8.7}/tests/test_config.py +9 -2
  8. typedal-4.8.5/coverage.svg +0 -1
  9. typedal-4.8.5/typing_again.py +0 -40
  10. {typedal-4.8.5 → typedal-4.8.7}/.github/workflows/su6.yml +0 -0
  11. {typedal-4.8.5 → typedal-4.8.7}/.gitignore +0 -0
  12. {typedal-4.8.5 → typedal-4.8.7}/.readthedocs.yml +0 -0
  13. {typedal-4.8.5 → typedal-4.8.7}/README.md +0 -0
  14. {typedal-4.8.5 → typedal-4.8.7}/docs/10_advanced_apis.md +0 -0
  15. {typedal-4.8.5 → typedal-4.8.7}/docs/1_getting_started.md +0 -0
  16. {typedal-4.8.5 → typedal-4.8.7}/docs/2_defining_tables.md +0 -0
  17. {typedal-4.8.5 → typedal-4.8.7}/docs/3_building_queries.md +0 -0
  18. {typedal-4.8.5 → typedal-4.8.7}/docs/4_relationships.md +0 -0
  19. {typedal-4.8.5 → typedal-4.8.7}/docs/5_py4web.md +0 -0
  20. {typedal-4.8.5 → typedal-4.8.7}/docs/6_migrations.md +0 -0
  21. {typedal-4.8.5 → typedal-4.8.7}/docs/7_configuration.md +0 -0
  22. {typedal-4.8.5 → typedal-4.8.7}/docs/8_mixins.md +0 -0
  23. {typedal-4.8.5 → typedal-4.8.7}/docs/9_memoization.md +0 -0
  24. {typedal-4.8.5 → typedal-4.8.7}/docs/css/code_blocks.css +0 -0
  25. {typedal-4.8.5 → typedal-4.8.7}/docs/index.md +0 -0
  26. {typedal-4.8.5 → typedal-4.8.7}/docs/requirements.txt +0 -0
  27. {typedal-4.8.5 → typedal-4.8.7}/example_new.py +0 -0
  28. {typedal-4.8.5 → typedal-4.8.7}/example_old.py +0 -0
  29. {typedal-4.8.5 → typedal-4.8.7}/mkdocs.yml +0 -0
  30. {typedal-4.8.5 → typedal-4.8.7}/pyproject.toml +0 -0
  31. {typedal-4.8.5 → typedal-4.8.7}/src/typedal/__init__.py +0 -0
  32. {typedal-4.8.5 → typedal-4.8.7}/src/typedal/caching.py +0 -0
  33. {typedal-4.8.5 → typedal-4.8.7}/src/typedal/cli.py +0 -0
  34. {typedal-4.8.5 → typedal-4.8.7}/src/typedal/config.py +0 -0
  35. {typedal-4.8.5 → typedal-4.8.7}/src/typedal/constants.py +0 -0
  36. {typedal-4.8.5 → typedal-4.8.7}/src/typedal/core.py +0 -0
  37. {typedal-4.8.5 → typedal-4.8.7}/src/typedal/define.py +0 -0
  38. {typedal-4.8.5 → typedal-4.8.7}/src/typedal/enum_helpers.py +0 -0
  39. {typedal-4.8.5 → typedal-4.8.7}/src/typedal/for_py4web.py +0 -0
  40. {typedal-4.8.5 → typedal-4.8.7}/src/typedal/for_web2py.py +0 -0
  41. {typedal-4.8.5 → typedal-4.8.7}/src/typedal/helpers.py +0 -0
  42. {typedal-4.8.5 → typedal-4.8.7}/src/typedal/mixins.py +0 -0
  43. {typedal-4.8.5 → typedal-4.8.7}/src/typedal/py.typed +0 -0
  44. {typedal-4.8.5 → typedal-4.8.7}/src/typedal/query_builder.py +0 -0
  45. {typedal-4.8.5 → typedal-4.8.7}/src/typedal/relationships.py +0 -0
  46. {typedal-4.8.5 → typedal-4.8.7}/src/typedal/rows.py +0 -0
  47. {typedal-4.8.5 → typedal-4.8.7}/src/typedal/serializers/as_json.py +0 -0
  48. {typedal-4.8.5 → typedal-4.8.7}/src/typedal/serializers/typescript.py +0 -0
  49. {typedal-4.8.5 → typedal-4.8.7}/src/typedal/types.py +0 -0
  50. {typedal-4.8.5 → typedal-4.8.7}/src/typedal/web2py_py4web_shared.py +0 -0
  51. {typedal-4.8.5 → typedal-4.8.7}/tasks.py +0 -0
  52. {typedal-4.8.5 → typedal-4.8.7}/tests/__init__.py +0 -0
  53. {typedal-4.8.5 → typedal-4.8.7}/tests/configs/simple.toml +0 -0
  54. {typedal-4.8.5 → typedal-4.8.7}/tests/configs/valid.env +0 -0
  55. {typedal-4.8.5 → typedal-4.8.7}/tests/configs/valid.toml +0 -0
  56. {typedal-4.8.5 → typedal-4.8.7}/tests/py314_tests.py +0 -0
  57. {typedal-4.8.5 → typedal-4.8.7}/tests/test_cli.py +0 -0
  58. {typedal-4.8.5 → typedal-4.8.7}/tests/test_docs_examples.py +0 -0
  59. {typedal-4.8.5 → typedal-4.8.7}/tests/test_helpers.py +0 -0
  60. {typedal-4.8.5 → typedal-4.8.7}/tests/test_json.py +0 -0
  61. {typedal-4.8.5 → typedal-4.8.7}/tests/test_main.py +0 -0
  62. {typedal-4.8.5 → typedal-4.8.7}/tests/test_mixins.py +0 -0
  63. {typedal-4.8.5 → typedal-4.8.7}/tests/test_mypy.py +0 -0
  64. {typedal-4.8.5 → typedal-4.8.7}/tests/test_orm.py +0 -0
  65. {typedal-4.8.5 → typedal-4.8.7}/tests/test_py4web.py +0 -0
  66. {typedal-4.8.5 → typedal-4.8.7}/tests/test_query_builder.py +0 -0
  67. {typedal-4.8.5 → typedal-4.8.7}/tests/test_relationships.py +0 -0
  68. {typedal-4.8.5 → typedal-4.8.7}/tests/test_row.py +0 -0
  69. {typedal-4.8.5 → typedal-4.8.7}/tests/test_stats.py +0 -0
  70. {typedal-4.8.5 → typedal-4.8.7}/tests/test_table.py +0 -0
  71. {typedal-4.8.5 → typedal-4.8.7}/tests/test_typescript.py +0 -0
  72. {typedal-4.8.5 → typedal-4.8.7}/tests/test_typing_mypy.md +0 -0
  73. {typedal-4.8.5 → typedal-4.8.7}/tests/test_typing_pyright.md +0 -0
  74. {typedal-4.8.5 → typedal-4.8.7}/tests/test_web2py.py +0 -0
  75. {typedal-4.8.5 → typedal-4.8.7}/tests/test_xx_others.py +0 -0
  76. {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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: TypeDAL
3
- Version: 4.8.5
3
+ Version: 4.8.7
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
@@ -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>
@@ -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__ = "4.8.5"
8
+ __version__ = "4.8.7"
@@ -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=lambda x: f"'{x}'", # extra quotes
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
 
@@ -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>
@@ -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