TypeDAL 4.9.6__tar.gz → 4.9.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.9.6 → typedal-4.9.7}/CHANGELOG.md +6 -0
- {typedal-4.9.6 → typedal-4.9.7}/PKG-INFO +1 -1
- {typedal-4.9.6 → typedal-4.9.7}/pyproject.toml +1 -2
- {typedal-4.9.6 → typedal-4.9.7}/src/typedal/__about__.py +1 -1
- {typedal-4.9.6 → typedal-4.9.7}/src/typedal/cli.py +1 -2
- {typedal-4.9.6 → typedal-4.9.7}/src/typedal/fields.py +6 -0
- {typedal-4.9.6 → typedal-4.9.7}/tasks.py +1 -1
- {typedal-4.9.6 → typedal-4.9.7}/tests/test_main.py +20 -0
- {typedal-4.9.6 → typedal-4.9.7}/tests/test_mypy.py +4 -4
- typedal-4.9.7/tests/test_py4web.py +91 -0
- {typedal-4.9.6 → typedal-4.9.7}/tests/test_relationships.py +20 -19
- typedal-4.9.6/tests/test_py4web.py +0 -92
- {typedal-4.9.6 → typedal-4.9.7}/.crush/.gitignore +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/.crush/crush.db-shm +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/.crush/crush.db-wal +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/.crush/init +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/.crush/logs/crush.log +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/.github/workflows/su6.yml +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/.gitignore +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/.readthedocs.yml +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/README.md +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/coverage.svg +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/docs/10_advanced_apis.md +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/docs/1_getting_started.md +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/docs/2_defining_tables.md +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/docs/3_building_queries.md +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/docs/4_relationships.md +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/docs/5_py4web.md +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/docs/6_migrations.md +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/docs/7_configuration.md +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/docs/8_mixins.md +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/docs/9_memoization.md +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/docs/css/code_blocks.css +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/docs/index.md +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/docs/requirements.txt +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/example_new.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/example_old.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/mkdocs.yml +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/src/typedal/__init__.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/src/typedal/caching.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/src/typedal/config.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/src/typedal/constants.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/src/typedal/core.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/src/typedal/define.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/src/typedal/enum_helpers.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/src/typedal/for_py4web.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/src/typedal/for_web2py.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/src/typedal/helpers.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/src/typedal/mixins.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/src/typedal/py.typed +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/src/typedal/query_builder.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/src/typedal/relationships.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/src/typedal/rows.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/src/typedal/serializers/as_json.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/src/typedal/serializers/typescript.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/src/typedal/tables.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/src/typedal/types.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/src/typedal/web2py_py4web_shared.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/tests/__init__.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/tests/configs/simple.toml +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/tests/configs/valid.env +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/tests/configs/valid.toml +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/tests/conftest.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/tests/py314_tests.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/tests/test_cli.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/tests/test_config.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/tests/test_docs_examples.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/tests/test_helpers.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/tests/test_json.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/tests/test_mixins.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/tests/test_orm.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/tests/test_query_builder.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/tests/test_row.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/tests/test_stats.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/tests/test_table.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/tests/test_typescript.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/tests/test_typing_mypy.md +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/tests/test_typing_pyright.md +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/tests/test_web2py.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/tests/test_xx_others.py +0 -0
- {typedal-4.9.6 → typedal-4.9.7}/tests/timings.py +0 -0
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
<!--next-version-placeholder-->
|
|
4
4
|
|
|
5
|
+
## v4.9.7 (2026-07-14)
|
|
6
|
+
|
|
7
|
+
### Fix
|
|
8
|
+
|
|
9
|
+
* **fields:** Clear field requirements during unbind ([`7e054f9`](https://github.com/trialandsuccess/TypeDAL/commit/7e054f947358a3fbb491db0d777410433da43d44))
|
|
10
|
+
|
|
5
11
|
## v4.9.6 (2026-07-14)
|
|
6
12
|
|
|
7
13
|
### Fix
|
|
@@ -425,8 +425,7 @@ def fake_migrations(
|
|
|
425
425
|
|
|
426
426
|
previously_migrated = (
|
|
427
427
|
db(
|
|
428
|
-
db.ewh_implemented_features.name.belongs(to_fake)
|
|
429
|
-
& (db.ewh_implemented_features.installed == True), # noqa E712
|
|
428
|
+
db.ewh_implemented_features.name.belongs(to_fake) & (db.ewh_implemented_features.installed == True), # noqa E712
|
|
430
429
|
)
|
|
431
430
|
.select(db.ewh_implemented_features.name)
|
|
432
431
|
.column("name")
|
|
@@ -199,6 +199,12 @@ class TypedField[T_Value](Expression): # pragma: no cover
|
|
|
199
199
|
|
|
200
200
|
def unbind(self) -> None:
|
|
201
201
|
"""Remove references to the pydal objects created during ``bind``."""
|
|
202
|
+
if getattr(self, "requires", None):
|
|
203
|
+
if isinstance(self.requires, list):
|
|
204
|
+
self.requires.clear()
|
|
205
|
+
else:
|
|
206
|
+
self.requires = []
|
|
207
|
+
|
|
202
208
|
self._table = None
|
|
203
209
|
self._field = None
|
|
204
210
|
|
|
@@ -33,7 +33,7 @@ def generate_nav_entries() -> list[str]:
|
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
@task
|
|
36
|
-
def update_docs_nav(
|
|
36
|
+
def update_docs_nav(_: Context) -> None:
|
|
37
37
|
"""Update mkdocs.yml nav section from actual markdown files to prevent sync issues."""
|
|
38
38
|
mkdocs_file = Path(__file__).parent / "mkdocs.yml"
|
|
39
39
|
|
|
@@ -14,6 +14,7 @@ from src.typedal import TypedRows
|
|
|
14
14
|
from src.typedal.__about__ import __version__
|
|
15
15
|
from src.typedal.enum_helpers import InvalidEnumValue
|
|
16
16
|
from src.typedal.fields import *
|
|
17
|
+
from src.typedal.web2py_py4web_shared import AuthUser
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
def test_about():
|
|
@@ -47,6 +48,25 @@ def test_database_is_garbage_collected_after_close():
|
|
|
47
48
|
assert db_ref() is None
|
|
48
49
|
|
|
49
50
|
|
|
51
|
+
def test_database_with_auth_user_is_garbage_collected_after_close():
|
|
52
|
+
db = TypeDAL("sqlite:memory", enable_typedal_caching=False)
|
|
53
|
+
db.define(AuthUser, redefine=True)
|
|
54
|
+
|
|
55
|
+
AuthUser.after_insert(lambda row, db=db: print(db))
|
|
56
|
+
|
|
57
|
+
db_ref = weakref.ref(db)
|
|
58
|
+
|
|
59
|
+
db.close()
|
|
60
|
+
|
|
61
|
+
assert AuthUser._db is None
|
|
62
|
+
assert AuthUser._table is None
|
|
63
|
+
|
|
64
|
+
del db
|
|
65
|
+
gc.collect()
|
|
66
|
+
|
|
67
|
+
assert db_ref() is None
|
|
68
|
+
|
|
69
|
+
|
|
50
70
|
def test_mixed_defines(capsys):
|
|
51
71
|
### DEFINE
|
|
52
72
|
|
|
@@ -178,12 +178,12 @@ def mypy_test_mixin_typed_table_argument() -> None:
|
|
|
178
178
|
|
|
179
179
|
@pytest.mark.mypy_testing
|
|
180
180
|
def mypy_test_cachefn() -> None:
|
|
181
|
-
def cache_model(
|
|
181
|
+
def cache_model(_key: str, fn: CacheFn, _expire: int) -> Rows:
|
|
182
182
|
return fn()
|
|
183
183
|
|
|
184
|
-
|
|
184
|
+
_cache_valid: CacheTuple = (cache_model, 3000)
|
|
185
185
|
|
|
186
|
-
def invalid_cache_model(
|
|
186
|
+
def invalid_cache_model(_key: str, fn: typing.Callable[..., list[str]], _expire: int = None) -> list[str]:
|
|
187
187
|
return fn()
|
|
188
188
|
|
|
189
|
-
|
|
189
|
+
_cache_invalid: CacheTuple = (invalid_cache_model, 3000) # E: [assignment]
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import tempfile
|
|
3
|
+
|
|
4
|
+
from contextlib_chdir import chdir
|
|
5
|
+
from pydal.validators import IS_EMAIL, IS_NOT_IN_DB
|
|
6
|
+
|
|
7
|
+
from src.typedal import TypedTable
|
|
8
|
+
from src.typedal.for_py4web import DAL, AuthUser, setup_py4web_tables
|
|
9
|
+
from src.typedal.serializers import as_json
|
|
10
|
+
from typedal.config import TypeDALConfig
|
|
11
|
+
|
|
12
|
+
db = DAL("sqlite:memory")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_P4W():
|
|
16
|
+
assert hasattr(DAL, "on_request")
|
|
17
|
+
assert hasattr(db, "on_request")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_serialize():
|
|
21
|
+
@db.define()
|
|
22
|
+
class JsonTable(TypedTable):
|
|
23
|
+
field: str
|
|
24
|
+
|
|
25
|
+
row = JsonTable.insert(field="Hey")
|
|
26
|
+
|
|
27
|
+
loaded_data = json.loads(as_json.encode(JsonTable))
|
|
28
|
+
assert loaded_data
|
|
29
|
+
assert isinstance(loaded_data, dict)
|
|
30
|
+
loaded_data = json.loads(as_json.encode(row))
|
|
31
|
+
assert isinstance(loaded_data, dict)
|
|
32
|
+
assert loaded_data
|
|
33
|
+
|
|
34
|
+
loaded_data = json.loads(as_json.encode(JsonTable.paginate(limit=1)))
|
|
35
|
+
|
|
36
|
+
assert loaded_data["pagination"]
|
|
37
|
+
assert loaded_data["data"]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_auth_user():
|
|
41
|
+
setup_py4web_tables(db)
|
|
42
|
+
requirements = AuthUser.email.requires
|
|
43
|
+
|
|
44
|
+
assert requirements
|
|
45
|
+
assert isinstance(requirements, list)
|
|
46
|
+
assert len(requirements) == 2
|
|
47
|
+
|
|
48
|
+
assert isinstance(requirements[0], IS_EMAIL)
|
|
49
|
+
assert isinstance(requirements[1], IS_NOT_IN_DB)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_py4web_dal_singleton():
|
|
53
|
+
with tempfile.TemporaryDirectory() as d, chdir(d):
|
|
54
|
+
# note: typedal caching is disabled here because otherwise the cache table might send logs to {d}/log.sql
|
|
55
|
+
# which can lead to problems in other tests :(
|
|
56
|
+
db_1a = DAL("sqlite:memory", enable_typedal_caching=False)
|
|
57
|
+
db_1b = DAL("sqlite:memory", enable_typedal_caching=False)
|
|
58
|
+
|
|
59
|
+
db_2a = DAL("sqlite://test_py4web_dal_singleton", folder=d, enable_typedal_caching=False)
|
|
60
|
+
db_2b = DAL("sqlite://test_py4web_dal_singleton", folder=d, enable_typedal_caching=False)
|
|
61
|
+
|
|
62
|
+
conf = {"database": "sqlite:memory", "dialect": "sqlite", "pyproject": "", "flag_location": f"{d}/flags"}
|
|
63
|
+
db_3a = DAL(config=TypeDALConfig.load(conf))
|
|
64
|
+
db_3b = DAL(config=TypeDALConfig.load(conf))
|
|
65
|
+
|
|
66
|
+
assert db_1a is db_1b
|
|
67
|
+
assert db_1a._uri == db_1b._uri
|
|
68
|
+
assert db_1a._db_uid == db_1b._db_uid
|
|
69
|
+
|
|
70
|
+
assert db_2a is db_2b
|
|
71
|
+
assert db_2a._uri == db_2b._uri
|
|
72
|
+
assert db_2a._db_uid == db_2b._db_uid
|
|
73
|
+
|
|
74
|
+
assert db_1a is not db_2a
|
|
75
|
+
assert db_1a._uri != db_2a._uri
|
|
76
|
+
assert db_1a._db_uid != db_2a._db_uid
|
|
77
|
+
|
|
78
|
+
assert db_1b is not db_2b
|
|
79
|
+
assert db_1b._uri != db_2b._uri
|
|
80
|
+
assert db_1b._db_uid != db_2b._db_uid
|
|
81
|
+
|
|
82
|
+
assert db_3a is db_3b
|
|
83
|
+
assert db_3a is not db_1a
|
|
84
|
+
assert db_3a is not db_2a
|
|
85
|
+
|
|
86
|
+
assert db_1a._uri == db_1b._uri
|
|
87
|
+
assert db_3a._uri == db_1a._uri
|
|
88
|
+
assert db_3a._uri != db_2a._uri
|
|
89
|
+
|
|
90
|
+
# reset singletons for later use:
|
|
91
|
+
DAL._clear()
|
|
@@ -129,8 +129,8 @@ def _setup_data():
|
|
|
129
129
|
|
|
130
130
|
# no relationships:
|
|
131
131
|
new_author = User.insert(name="Untagged Author", roles=[], main_role=writer, extra_roles=[])
|
|
132
|
-
|
|
133
|
-
|
|
132
|
+
_untagged1 = Article.insert(title="Untagged Article 1", author=new_author)
|
|
133
|
+
_untagged2 = Article.insert(title="Untagged Article 2", author=new_author)
|
|
134
134
|
|
|
135
135
|
# articles
|
|
136
136
|
|
|
@@ -355,10 +355,9 @@ def test_reprs():
|
|
|
355
355
|
|
|
356
356
|
assert "AND" not in repr(relation)
|
|
357
357
|
|
|
358
|
-
relation = Article.join("author", condition_and=lambda
|
|
358
|
+
relation = Article.join("author", condition_and=lambda _article, author: author.name != "Hank").relationships[
|
|
359
359
|
"author"
|
|
360
360
|
]
|
|
361
|
-
|
|
362
361
|
assert "AND" in repr(relation) and "Hank" in repr(relation)
|
|
363
362
|
|
|
364
363
|
|
|
@@ -436,7 +435,7 @@ def test_join_with_different_condition():
|
|
|
436
435
|
role_with_users = Role.join(
|
|
437
436
|
"users",
|
|
438
437
|
method="inner",
|
|
439
|
-
condition_and=lambda
|
|
438
|
+
condition_and=lambda _role, user: ~user.name.like("Reader%"),
|
|
440
439
|
).first()
|
|
441
440
|
|
|
442
441
|
assert role_with_users.users
|
|
@@ -446,7 +445,7 @@ def test_join_with_different_condition():
|
|
|
446
445
|
role_with_users = Role.join(
|
|
447
446
|
"users",
|
|
448
447
|
method="left",
|
|
449
|
-
condition_and=lambda
|
|
448
|
+
condition_and=lambda _role, user: ~user.name.like("Reader%"),
|
|
450
449
|
).first()
|
|
451
450
|
|
|
452
451
|
assert role_with_users.users
|
|
@@ -729,11 +728,11 @@ def test_memoize_nested_dependencies():
|
|
|
729
728
|
assert len(users) > 0
|
|
730
729
|
|
|
731
730
|
# First memoize - should be fresh
|
|
732
|
-
|
|
731
|
+
_result1, status = db.memoize(process_users, users)
|
|
733
732
|
assert status == "fresh"
|
|
734
733
|
|
|
735
734
|
# Second call - should be cached
|
|
736
|
-
|
|
735
|
+
_result2, status = db.memoize(process_users, users)
|
|
737
736
|
assert status == "cached"
|
|
738
737
|
|
|
739
738
|
# Update a Role that was accessed inside process_users
|
|
@@ -742,7 +741,7 @@ def test_memoize_nested_dependencies():
|
|
|
742
741
|
|
|
743
742
|
# Third call - should be fresh (invalidated by Role change)
|
|
744
743
|
# but currently will still be "cached" because we only tracked User deps
|
|
745
|
-
|
|
744
|
+
_result3, status = db.memoize(process_users, users)
|
|
746
745
|
assert status == "fresh"
|
|
747
746
|
|
|
748
747
|
|
|
@@ -755,25 +754,25 @@ def test_memoize_nested_dependencies2():
|
|
|
755
754
|
# (which happens when using _determine_dependencies_auto)
|
|
756
755
|
return list(User.join())
|
|
757
756
|
|
|
758
|
-
|
|
757
|
+
_bogus, status = db.memoize(something_slow)
|
|
759
758
|
assert status == "fresh"
|
|
760
759
|
|
|
761
760
|
# no change
|
|
762
|
-
|
|
761
|
+
_bogus, status = db.memoize(something_slow)
|
|
763
762
|
assert status == "cached"
|
|
764
763
|
|
|
765
764
|
# user change
|
|
766
765
|
User.first().update_record(name="New Name :)")
|
|
767
|
-
|
|
766
|
+
_bogus, status = db.memoize(something_slow)
|
|
768
767
|
assert status == "fresh"
|
|
769
768
|
|
|
770
769
|
# role change
|
|
771
770
|
Role.first().update_record(name="New Role Name :)")
|
|
772
|
-
|
|
771
|
+
_bogus, status = db.memoize(something_slow)
|
|
773
772
|
assert status == "fresh"
|
|
774
773
|
|
|
775
774
|
# no change
|
|
776
|
-
|
|
775
|
+
_bogus, status = db.memoize(something_slow)
|
|
777
776
|
assert status == "cached"
|
|
778
777
|
|
|
779
778
|
|
|
@@ -931,7 +930,8 @@ def test_accessing_raw_data():
|
|
|
931
930
|
|
|
932
931
|
user = User.where(id=4).join("articles").first()
|
|
933
932
|
|
|
934
|
-
# <User({"id": 4, "name": "Untagged Author", "roles": [],
|
|
933
|
+
# <User({"id": 4, "name": "Untagged Author", "roles": [],
|
|
934
|
+
# "gid": "967da807-eb13-46dc-a0f3-d5c04751edf4", "main_role": 2, "extra_roles": []}) + ['articles']>
|
|
935
935
|
assert user._row
|
|
936
936
|
|
|
937
937
|
# one row per user+article combination like how postgres returns it:
|
|
@@ -1031,9 +1031,9 @@ def test_nested_join_with_shared_foreign_key():
|
|
|
1031
1031
|
tech_corp = Company.insert(name="Tech Corp")
|
|
1032
1032
|
|
|
1033
1033
|
# Create multiple offices in the same city for the same company
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1034
|
+
_office1 = Office.insert(address="123 Market St", city_id=san_francisco.id, company=tech_corp.id)
|
|
1035
|
+
_office2 = Office.insert(address="456 Mission St", city_id=san_francisco.id, company=tech_corp.id)
|
|
1036
|
+
_office3 = Office.insert(address="789 Howard St", city_id=san_francisco.id, company=tech_corp.id)
|
|
1037
1037
|
|
|
1038
1038
|
db.commit()
|
|
1039
1039
|
|
|
@@ -1072,7 +1072,8 @@ def test_nested_join_with_shared_foreign_key():
|
|
|
1072
1072
|
f"Office {idx} ('{office.address}'): city relationship is None (BUG!)"
|
|
1073
1073
|
)
|
|
1074
1074
|
assert isinstance(office.city_alternative, City), (
|
|
1075
|
-
f"Office {idx}: city ({office.city_alternative})
|
|
1075
|
+
f"Office {idx}: city ({office.city_alternative}) "
|
|
1076
|
+
f"is not a City instance but a {type(office.city_alternative)}"
|
|
1076
1077
|
)
|
|
1077
1078
|
assert office.city_alternative.name == "San Francisco", (
|
|
1078
1079
|
f"Office {idx}: expected 'San Francisco', got '{office.city_alternative.name}'"
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import tempfile
|
|
3
|
-
|
|
4
|
-
from contextlib_chdir import chdir
|
|
5
|
-
from pydal.validators import IS_EMAIL, IS_NOT_IN_DB
|
|
6
|
-
|
|
7
|
-
from src.typedal import TypedTable
|
|
8
|
-
from src.typedal.for_py4web import DAL, AuthUser, setup_py4web_tables
|
|
9
|
-
from src.typedal.serializers import as_json
|
|
10
|
-
from typedal.config import TypeDALConfig
|
|
11
|
-
|
|
12
|
-
db = DAL("sqlite:memory")
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def test_P4W():
|
|
16
|
-
assert hasattr(DAL, "on_request")
|
|
17
|
-
assert hasattr(db, "on_request")
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def test_serialize():
|
|
21
|
-
@db.define()
|
|
22
|
-
class JsonTable(TypedTable):
|
|
23
|
-
field: str
|
|
24
|
-
|
|
25
|
-
row = JsonTable.insert(field="Hey")
|
|
26
|
-
|
|
27
|
-
l = json.loads(as_json.encode(JsonTable))
|
|
28
|
-
assert l
|
|
29
|
-
assert isinstance(l, dict)
|
|
30
|
-
l = json.loads(as_json.encode(row))
|
|
31
|
-
assert isinstance(l, dict)
|
|
32
|
-
assert l
|
|
33
|
-
|
|
34
|
-
l = json.loads(as_json.encode(JsonTable.paginate(limit=1)))
|
|
35
|
-
|
|
36
|
-
assert l["pagination"]
|
|
37
|
-
assert l["data"]
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def test_auth_user():
|
|
41
|
-
setup_py4web_tables(db)
|
|
42
|
-
requirements = AuthUser.email.requires
|
|
43
|
-
|
|
44
|
-
assert requirements
|
|
45
|
-
assert isinstance(requirements, list)
|
|
46
|
-
assert len(requirements) == 2
|
|
47
|
-
|
|
48
|
-
assert isinstance(requirements[0], IS_EMAIL)
|
|
49
|
-
assert isinstance(requirements[1], IS_NOT_IN_DB)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def test_py4web_dal_singleton():
|
|
53
|
-
with tempfile.TemporaryDirectory() as d:
|
|
54
|
-
with chdir(d):
|
|
55
|
-
# note: typedal caching is disabled here because otherwise the cache table might send logs to {d}/log.sql
|
|
56
|
-
# which can lead to problems in other tests :(
|
|
57
|
-
db_1a = DAL("sqlite:memory", enable_typedal_caching=False)
|
|
58
|
-
db_1b = DAL("sqlite:memory", enable_typedal_caching=False)
|
|
59
|
-
|
|
60
|
-
db_2a = DAL("sqlite://test_py4web_dal_singleton", folder=d, enable_typedal_caching=False)
|
|
61
|
-
db_2b = DAL("sqlite://test_py4web_dal_singleton", folder=d, enable_typedal_caching=False)
|
|
62
|
-
|
|
63
|
-
conf = {"database": "sqlite:memory", "dialect": "sqlite", "pyproject": "", "flag_location": f"{d}/flags"}
|
|
64
|
-
db_3a = DAL(config=TypeDALConfig.load(conf))
|
|
65
|
-
db_3b = DAL(config=TypeDALConfig.load(conf))
|
|
66
|
-
|
|
67
|
-
assert db_1a is db_1b
|
|
68
|
-
assert db_1a._uri == db_1b._uri
|
|
69
|
-
assert db_1a._db_uid == db_1b._db_uid
|
|
70
|
-
|
|
71
|
-
assert db_2a is db_2b
|
|
72
|
-
assert db_2a._uri == db_2b._uri
|
|
73
|
-
assert db_2a._db_uid == db_2b._db_uid
|
|
74
|
-
|
|
75
|
-
assert db_1a is not db_2a
|
|
76
|
-
assert db_1a._uri != db_2a._uri
|
|
77
|
-
assert db_1a._db_uid != db_2a._db_uid
|
|
78
|
-
|
|
79
|
-
assert db_1b is not db_2b
|
|
80
|
-
assert db_1b._uri != db_2b._uri
|
|
81
|
-
assert db_1b._db_uid != db_2b._db_uid
|
|
82
|
-
|
|
83
|
-
assert db_3a is db_3b
|
|
84
|
-
assert db_3a is not db_1a
|
|
85
|
-
assert db_3a is not db_2a
|
|
86
|
-
|
|
87
|
-
assert db_1a._uri == db_1b._uri
|
|
88
|
-
assert db_3a._uri == db_1a._uri
|
|
89
|
-
assert db_3a._uri != db_2a._uri
|
|
90
|
-
|
|
91
|
-
# reset singletons for later use:
|
|
92
|
-
DAL._clear()
|
|
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
|
|
File without changes
|
|
File without changes
|