sqlakit 0.10.8__tar.gz → 0.11.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.
- {sqlakit-0.10.8 → sqlakit-0.11.0}/PKG-INFO +1 -1
- {sqlakit-0.10.8 → sqlakit-0.11.0}/pyproject.toml +1 -1
- {sqlakit-0.10.8 → sqlakit-0.11.0}/pyproject.toml.orig +1 -1
- {sqlakit-0.10.8 → sqlakit-0.11.0}/sqlakit/_base.py +69 -2
- {sqlakit-0.10.8 → sqlakit-0.11.0}/sqlakit/_model.py +1 -1
- {sqlakit-0.10.8 → sqlakit-0.11.0}/sqlakit/exceptions.py +3 -3
- {sqlakit-0.10.8 → sqlakit-0.11.0}/sqlakit/pytest_plugin.py +14 -12
- {sqlakit-0.10.8 → sqlakit-0.11.0}/LICENSE +0 -0
- {sqlakit-0.10.8 → sqlakit-0.11.0}/README.md +0 -0
- {sqlakit-0.10.8 → sqlakit-0.11.0}/sqlakit/__init__.py +0 -0
- {sqlakit-0.10.8 → sqlakit-0.11.0}/sqlakit/_cli.py +0 -0
- {sqlakit-0.10.8 → sqlakit-0.11.0}/sqlakit/_db.py +0 -0
- {sqlakit-0.10.8 → sqlakit-0.11.0}/sqlakit/_debugserver.py +0 -0
- {sqlakit-0.10.8 → sqlakit-0.11.0}/sqlakit/_discovery.py +0 -0
- {sqlakit-0.10.8 → sqlakit-0.11.0}/sqlakit/_query.py +0 -0
- {sqlakit-0.10.8 → sqlakit-0.11.0}/sqlakit/_recording.py +0 -0
- {sqlakit-0.10.8 → sqlakit-0.11.0}/sqlakit/_registry.py +0 -0
- {sqlakit-0.10.8 → sqlakit-0.11.0}/sqlakit/_routing.py +0 -0
- {sqlakit-0.10.8 → sqlakit-0.11.0}/sqlakit/_sql.py +0 -0
- {sqlakit-0.10.8 → sqlakit-0.11.0}/sqlakit/asyncio/__init__.py +0 -0
- {sqlakit-0.10.8 → sqlakit-0.11.0}/sqlakit/asyncio/_db.py +0 -0
- {sqlakit-0.10.8 → sqlakit-0.11.0}/sqlakit/asyncio/_registry.py +0 -0
- {sqlakit-0.10.8 → sqlakit-0.11.0}/sqlakit/asyncio/orm.py +0 -0
- {sqlakit-0.10.8 → sqlakit-0.11.0}/sqlakit/asyncio/sql.py +0 -0
- {sqlakit-0.10.8 → sqlakit-0.11.0}/sqlakit/debugserver.html +0 -0
- {sqlakit-0.10.8 → sqlakit-0.11.0}/sqlakit/orm.py +0 -0
- {sqlakit-0.10.8 → sqlakit-0.11.0}/sqlakit/py.typed +0 -0
- {sqlakit-0.10.8 → sqlakit-0.11.0}/sqlakit/sql.py +0 -0
- {sqlakit-0.10.8 → sqlakit-0.11.0}/sqlakit/testing.py +0 -0
- {sqlakit-0.10.8 → sqlakit-0.11.0}/sqlakit/types.py +0 -0
|
@@ -470,6 +470,29 @@ class BaseDatabase(Generic[ConnectionT, SessionT]):
|
|
|
470
470
|
"""
|
|
471
471
|
return self._outer.get(None) is not None
|
|
472
472
|
|
|
473
|
+
@property
|
|
474
|
+
def aliases(self) -> tuple[str, ...]:
|
|
475
|
+
"""The names this database goes by, which for one database is its own.
|
|
476
|
+
|
|
477
|
+
A registry has one for each database it holds. Both carry this, so code
|
|
478
|
+
that takes either does not have to ask which it was given.
|
|
479
|
+
"""
|
|
480
|
+
return (self._name,)
|
|
481
|
+
|
|
482
|
+
def __getitem__(self, alias: str) -> Self:
|
|
483
|
+
"""Return this database, under the name it carries.
|
|
484
|
+
|
|
485
|
+
Raises:
|
|
486
|
+
UnknownDatabaseError: if the alias is another database's.
|
|
487
|
+
|
|
488
|
+
"""
|
|
489
|
+
if alias == self._name:
|
|
490
|
+
return self
|
|
491
|
+
raise UnknownDatabaseError(alias, self.aliases)
|
|
492
|
+
|
|
493
|
+
def __contains__(self, alias: str) -> bool:
|
|
494
|
+
return alias == self._name
|
|
495
|
+
|
|
473
496
|
def in_session(self) -> bool:
|
|
474
497
|
"""Whether a session is open in the current context.
|
|
475
498
|
|
|
@@ -910,11 +933,15 @@ class _DatabaseRegistryMixin(BaseDatabase[Any, Any], Generic[DatabaseT]):
|
|
|
910
933
|
opened with `using()` stands in for the default database.
|
|
911
934
|
"""
|
|
912
935
|
placement = self._routed(model) or model.__db__
|
|
936
|
+
override = self._using.get()
|
|
913
937
|
if isinstance(placement, str):
|
|
914
|
-
override = self._using.get()
|
|
915
938
|
if override is not None and placement == DEFAULT_ALIAS:
|
|
916
939
|
placement = override
|
|
917
940
|
return self[placement]
|
|
941
|
+
# A model pinned to the database itself follows `using()` as one on the
|
|
942
|
+
# default alias does, when that database is the one being stood in for.
|
|
943
|
+
if override is not None and self[DEFAULT_ALIAS] is placement:
|
|
944
|
+
return self[override]
|
|
918
945
|
return placement
|
|
919
946
|
|
|
920
947
|
def _routed(self, model: type[Any]) -> str | None:
|
|
@@ -940,6 +967,41 @@ class _DatabaseRegistryMixin(BaseDatabase[Any, Any], Generic[DatabaseT]):
|
|
|
940
967
|
"""Whether the default database is this registry, `configure` having built it."""
|
|
941
968
|
return "url" in self.__dict__
|
|
942
969
|
|
|
970
|
+
if not TYPE_CHECKING:
|
|
971
|
+
# Hidden from type checkers, which keep reading these off `Database`
|
|
972
|
+
# and its asyncio twin, signatures and all.
|
|
973
|
+
def _proxy(name: str, *, attribute: bool = False) -> Any: # noqa: ANN401, N805
|
|
974
|
+
"""Proxy to the database this registry holds, or call its own.
|
|
975
|
+
|
|
976
|
+
A registry handed a default proxies to that database. One that
|
|
977
|
+
`configure` built calls what it inherits, which `super()` reaches.
|
|
978
|
+
"""
|
|
979
|
+
|
|
980
|
+
def reach(self: Any, *args: Any, **kwargs: Any) -> Any: # noqa: ANN401
|
|
981
|
+
held = self._default
|
|
982
|
+
found = (
|
|
983
|
+
getattr(held, name) if held is not None else getattr(super(), name)
|
|
984
|
+
)
|
|
985
|
+
return found if attribute else found(*args, **kwargs)
|
|
986
|
+
|
|
987
|
+
return property(reach) if attribute else reach
|
|
988
|
+
|
|
989
|
+
connection = _proxy("connection", attribute=True)
|
|
990
|
+
engine = _proxy("engine", attribute=True)
|
|
991
|
+
session = _proxy("session", attribute=True)
|
|
992
|
+
sql = _proxy("sql", attribute=True)
|
|
993
|
+
assert_queries = _proxy("assert_queries")
|
|
994
|
+
autocommit = _proxy("autocommit")
|
|
995
|
+
connect = _proxy("connect")
|
|
996
|
+
in_session = _proxy("in_session")
|
|
997
|
+
in_transaction = _proxy("in_transaction")
|
|
998
|
+
ping = _proxy("ping")
|
|
999
|
+
provisioned_tables = _proxy("provisioned_tables")
|
|
1000
|
+
query = _proxy("query")
|
|
1001
|
+
session_factory = _proxy("session_factory")
|
|
1002
|
+
transaction = _proxy("transaction")
|
|
1003
|
+
del _proxy
|
|
1004
|
+
|
|
943
1005
|
@overload
|
|
944
1006
|
def configure(
|
|
945
1007
|
self,
|
|
@@ -1061,10 +1123,15 @@ class _DatabaseRegistryMixin(BaseDatabase[Any, Any], Generic[DatabaseT]):
|
|
|
1061
1123
|
# fails, as it does on a registry with no database of its own, from
|
|
1062
1124
|
# the outside and from its own methods.
|
|
1063
1125
|
def __getattr__(self, name: str) -> object:
|
|
1126
|
+
# A dunder is the machinery asking, and a container that patched
|
|
1127
|
+
# `__getattribute__` bounces back here until it is answered.
|
|
1128
|
+
if name.startswith("__") and name.endswith("__"):
|
|
1129
|
+
raise AttributeError(name)
|
|
1064
1130
|
# Only the database half is worth explaining. Anything else is a
|
|
1065
1131
|
# name that does not exist, and saying so lets `hasattr`,
|
|
1066
1132
|
# `copy` and every library that introspects work.
|
|
1067
|
-
|
|
1133
|
+
# Through `object`, so a patched `__getattribute__` cannot loop.
|
|
1134
|
+
state = object.__getattribute__(self, "__dict__")
|
|
1068
1135
|
if "url" in state:
|
|
1069
1136
|
raise AttributeError(name)
|
|
1070
1137
|
asked_as_a_database = name in DATABASE_STATE or (
|
|
@@ -145,7 +145,7 @@ class BaseModel(Generic[DatabaseT]):
|
|
|
145
145
|
Base.register_db(Database(DB_URL))
|
|
146
146
|
```
|
|
147
147
|
|
|
148
|
-
The registry
|
|
148
|
+
The registry holds it either way, so `dbs["default"]`,
|
|
149
149
|
`dbs.transactions()` and `using()` all reach what was registered.
|
|
150
150
|
[`set_db`][sqlakit.orm.ModelMixin.set_db] is the other way to name a
|
|
151
151
|
database, and it leaves the registry out of it.
|
|
@@ -86,10 +86,10 @@ class RetryNotSupportedError(SQLAKitError, TypeError):
|
|
|
86
86
|
|
|
87
87
|
|
|
88
88
|
REGISTERED_DEFAULT = (
|
|
89
|
-
"This registry did not build the default database, it was registered
|
|
90
|
-
"
|
|
89
|
+
"This registry did not build the default database, it was registered, so "
|
|
90
|
+
"the settings it was built from live on it. Read them from `db['default']`."
|
|
91
91
|
)
|
|
92
|
-
"""What a registry says when
|
|
92
|
+
"""What a registry says when asked for settings the database it holds carries."""
|
|
93
93
|
|
|
94
94
|
|
|
95
95
|
class DatabaseNotConfiguredError(SQLAKitError, RuntimeError):
|
|
@@ -1,25 +1,23 @@
|
|
|
1
1
|
"""The `db` marker, and the fixtures behind it.
|
|
2
2
|
|
|
3
|
-
Installed with the library, so a project
|
|
4
|
-
|
|
3
|
+
Installed with the library, so a project says which models it has and nothing
|
|
4
|
+
else:
|
|
5
5
|
|
|
6
6
|
```python title="conftest.py"
|
|
7
7
|
import pytest
|
|
8
8
|
|
|
9
|
-
from app.db import db
|
|
10
9
|
from app.models import Model
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
@pytest.fixture(scope="session")
|
|
14
|
-
def
|
|
15
|
-
return
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
@pytest.fixture(scope="session")
|
|
19
|
-
def sqlakit_metadata() -> sa.MetaData:
|
|
20
|
-
return Model.metadata
|
|
13
|
+
def sqlakit_base() -> type[Model]:
|
|
14
|
+
return Model
|
|
21
15
|
```
|
|
22
16
|
|
|
17
|
+
The database comes from the base: the registry its models look their aliases
|
|
18
|
+
up in, or the one they were given in person. A project with no model layer
|
|
19
|
+
defines `sqlakit_db` and `sqlakit_metadata` instead.
|
|
20
|
+
|
|
23
21
|
A test marked `db` runs in a transaction that rolls back, on every database.
|
|
24
22
|
`using` narrows that to the ones a test works on:
|
|
25
23
|
|
|
@@ -361,7 +359,9 @@ def _schema_blocks(
|
|
|
361
359
|
database to put it on.
|
|
362
360
|
"""
|
|
363
361
|
if base is not None:
|
|
364
|
-
|
|
362
|
+
# A database of its own is where the models are pinned, and the alias
|
|
363
|
+
# a registry knows it by is not how the base reaches it.
|
|
364
|
+
aliases = db.aliases if hasattr(db, "transactions") else (None,)
|
|
365
365
|
return [base.provisioned_tables(alias) for alias in aliases]
|
|
366
366
|
if metadata is not None:
|
|
367
367
|
return [db.provisioned_tables(metadata)]
|
|
@@ -393,9 +393,11 @@ def _rolled_back(db: Any, using: tuple[Any, ...]) -> list[Any]: # noqa: ANN401
|
|
|
393
393
|
a connection to each in the tests that read one.
|
|
394
394
|
"""
|
|
395
395
|
if not using:
|
|
396
|
+
# A registry opens every database it holds, and one database opens
|
|
397
|
+
# itself. `transactions` is the one a registry has.
|
|
396
398
|
return [
|
|
397
399
|
db.transactions(rollback=True)
|
|
398
|
-
if
|
|
400
|
+
if hasattr(db, "transactions")
|
|
399
401
|
else db.transaction(rollback=True)
|
|
400
402
|
]
|
|
401
403
|
return [
|
|
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
|