fastapi-toolsets 4.1.1__tar.gz → 4.1.3__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.
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/PKG-INFO +1 -1
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/pyproject.toml +1 -1
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/__init__.py +1 -1
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/pytest/utils.py +67 -18
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/LICENSE +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/README.md +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/_imports.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/cli/__init__.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/cli/app.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/cli/commands/__init__.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/cli/commands/fixtures.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/cli/config.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/cli/pyproject.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/cli/utils.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/crud/__init__.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/crud/factory.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/crud/search.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/db.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/dependencies.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/exceptions/__init__.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/exceptions/exceptions.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/exceptions/handler.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/fixtures/__init__.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/fixtures/enum.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/fixtures/registry.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/fixtures/utils.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/logger.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/metrics/__init__.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/metrics/handler.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/metrics/registry.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/models/__init__.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/models/columns.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/models/watched.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/py.typed +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/pytest/__init__.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/pytest/plugin.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/schemas.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/security/__init__.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/security/abc.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/security/oauth.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/security/sources/__init__.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/security/sources/bearer.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/security/sources/cookie.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/security/sources/header.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/security/sources/multi.py +0 -0
- {fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/types.py +0 -0
|
@@ -7,7 +7,7 @@ from typing import Any
|
|
|
7
7
|
|
|
8
8
|
from httpx import ASGITransport, AsyncClient
|
|
9
9
|
from sqlalchemy import text
|
|
10
|
-
from sqlalchemy.engine import make_url
|
|
10
|
+
from sqlalchemy.engine import URL, make_url
|
|
11
11
|
from sqlalchemy.ext.asyncio import (
|
|
12
12
|
AsyncSession,
|
|
13
13
|
async_sessionmaker,
|
|
@@ -34,12 +34,18 @@ def _get_xdist_worker(default_test_db: str) -> str:
|
|
|
34
34
|
return os.environ.get("PYTEST_XDIST_WORKER", default_test_db)
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
def worker_database_url(
|
|
37
|
+
def worker_database_url(
|
|
38
|
+
database_url: str,
|
|
39
|
+
default_test_db: str,
|
|
40
|
+
*,
|
|
41
|
+
prefix: str | None = None,
|
|
42
|
+
) -> str:
|
|
38
43
|
"""Derive a per-worker database URL for pytest-xdist parallel runs.
|
|
39
44
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
Sets the database name to the worker name so each xdist worker operates
|
|
46
|
+
on its own database. When not running under xdist, *default_test_db* is
|
|
47
|
+
used instead. When *prefix* is provided, the name becomes
|
|
48
|
+
``{prefix}_{worker}``.
|
|
43
49
|
|
|
44
50
|
The worker name is read from the ``PYTEST_XDIST_WORKER`` environment
|
|
45
51
|
variable (set automatically by xdist in each worker process).
|
|
@@ -48,6 +54,9 @@ def worker_database_url(database_url: str, default_test_db: str) -> str:
|
|
|
48
54
|
database_url: Original database connection URL.
|
|
49
55
|
default_test_db: Suffix appended to the database name when
|
|
50
56
|
``PYTEST_XDIST_WORKER`` is not set.
|
|
57
|
+
prefix: Optional prefix prepended to the worker name
|
|
58
|
+
(e.g. ``"test"`` → ``"test_gw0"``). Without it, the database
|
|
59
|
+
name is just the worker name (e.g. ``"gw0"``).
|
|
51
60
|
|
|
52
61
|
Returns:
|
|
53
62
|
A database URL with a worker- or default-specific database name.
|
|
@@ -55,7 +64,8 @@ def worker_database_url(database_url: str, default_test_db: str) -> str:
|
|
|
55
64
|
worker = _get_xdist_worker(default_test_db=default_test_db)
|
|
56
65
|
|
|
57
66
|
url = make_url(database_url)
|
|
58
|
-
|
|
67
|
+
db_name = f"{prefix}_{worker}" if prefix else worker
|
|
68
|
+
url = url.set(database=db_name)
|
|
59
69
|
return url.render_as_string(hide_password=False)
|
|
60
70
|
|
|
61
71
|
|
|
@@ -63,6 +73,9 @@ def worker_database_url(database_url: str, default_test_db: str) -> str:
|
|
|
63
73
|
async def create_worker_database(
|
|
64
74
|
database_url: str,
|
|
65
75
|
default_test_db: str = "test_db",
|
|
76
|
+
*,
|
|
77
|
+
prefix: str | None = None,
|
|
78
|
+
server_url: str | None = None,
|
|
66
79
|
) -> AsyncGenerator[str, None]:
|
|
67
80
|
"""Create and drop a per-worker database for pytest-xdist isolation.
|
|
68
81
|
|
|
@@ -74,10 +87,16 @@ async def create_worker_database(
|
|
|
74
87
|
name (e.g. ``_gw0``). Otherwise it is suffixed with *default_test_db*.
|
|
75
88
|
|
|
76
89
|
Args:
|
|
77
|
-
database_url: Original database connection URL (used as the
|
|
78
|
-
|
|
90
|
+
database_url: Original database connection URL (used as the base for
|
|
91
|
+
the worker database name).
|
|
79
92
|
default_test_db: Suffix appended to the database name when
|
|
80
93
|
``PYTEST_XDIST_WORKER`` is not set. Defaults to ``"test_db"``.
|
|
94
|
+
prefix: Optional prefix prepended to the worker name
|
|
95
|
+
(e.g. ``prefix="test"`` → ``"test_gw0"``). Without it, the
|
|
96
|
+
database name is just the worker name (e.g. ``"gw0"``).
|
|
97
|
+
server_url: URL used for server-level DDL (must point to an existing
|
|
98
|
+
database on the same server). Defaults to *database_url* with the
|
|
99
|
+
database omitted, letting asyncpg fall back to the username.
|
|
81
100
|
|
|
82
101
|
Yields:
|
|
83
102
|
The worker-specific database URL.
|
|
@@ -86,7 +105,7 @@ async def create_worker_database(
|
|
|
86
105
|
```python
|
|
87
106
|
from fastapi_toolsets.pytest import create_worker_database, create_db_session
|
|
88
107
|
|
|
89
|
-
DATABASE_URL = "postgresql+asyncpg://postgres:postgres@localhost/
|
|
108
|
+
DATABASE_URL = "postgresql+asyncpg://postgres:postgres@localhost/myapp"
|
|
90
109
|
|
|
91
110
|
@pytest.fixture(scope="session")
|
|
92
111
|
async def worker_db_url():
|
|
@@ -102,21 +121,35 @@ async def create_worker_database(
|
|
|
102
121
|
```
|
|
103
122
|
"""
|
|
104
123
|
worker_url = worker_database_url(
|
|
105
|
-
database_url=database_url, default_test_db=default_test_db
|
|
124
|
+
database_url=database_url, default_test_db=default_test_db, prefix=prefix
|
|
106
125
|
)
|
|
107
126
|
worker_db_name = make_url(worker_url).database
|
|
108
127
|
assert worker_db_name is not None
|
|
109
128
|
|
|
110
|
-
|
|
129
|
+
_parsed = make_url(database_url)
|
|
130
|
+
_server_url = server_url or URL.create(
|
|
131
|
+
drivername=_parsed.drivername,
|
|
132
|
+
username=_parsed.username,
|
|
133
|
+
password=_parsed.password,
|
|
134
|
+
host=_parsed.host,
|
|
135
|
+
port=_parsed.port,
|
|
136
|
+
query=_parsed.query,
|
|
137
|
+
).render_as_string(hide_password=False)
|
|
138
|
+
|
|
139
|
+
engine = create_async_engine(_server_url, isolation_level="AUTOCOMMIT")
|
|
111
140
|
try:
|
|
112
141
|
async with engine.connect() as conn:
|
|
113
|
-
await conn.execute(
|
|
114
|
-
|
|
142
|
+
await conn.execute(
|
|
143
|
+
text(f"DROP DATABASE IF EXISTS {worker_db_name} WITH (FORCE)")
|
|
144
|
+
)
|
|
145
|
+
await create_database(db_name=worker_db_name, server_url=_server_url)
|
|
115
146
|
|
|
116
147
|
yield worker_url
|
|
117
148
|
|
|
118
149
|
async with engine.connect() as conn:
|
|
119
|
-
await conn.execute(
|
|
150
|
+
await conn.execute(
|
|
151
|
+
text(f"DROP DATABASE IF EXISTS {worker_db_name} WITH (FORCE)")
|
|
152
|
+
)
|
|
120
153
|
finally:
|
|
121
154
|
await engine.dispose()
|
|
122
155
|
|
|
@@ -126,6 +159,7 @@ async def create_async_client(
|
|
|
126
159
|
app: Any,
|
|
127
160
|
base_url: str = "http://test",
|
|
128
161
|
dependency_overrides: dict[Callable[..., Any], Callable[..., Any]] | None = None,
|
|
162
|
+
**kwargs: Any,
|
|
129
163
|
) -> AsyncGenerator[AsyncClient, None]:
|
|
130
164
|
"""Create an async httpx client for testing FastAPI applications.
|
|
131
165
|
|
|
@@ -135,6 +169,9 @@ async def create_async_client(
|
|
|
135
169
|
dependency_overrides: Optional mapping of original dependencies to
|
|
136
170
|
their test replacements. Applied via ``app.dependency_overrides``
|
|
137
171
|
before yielding and cleaned up after.
|
|
172
|
+
**kwargs: Additional keyword arguments forwarded to
|
|
173
|
+
:class:`httpx.AsyncClient` (e.g. ``headers``, ``cookies``,
|
|
174
|
+
``auth``, ``timeout``).
|
|
138
175
|
|
|
139
176
|
Yields:
|
|
140
177
|
An AsyncClient configured for the app.
|
|
@@ -182,7 +219,9 @@ async def create_async_client(
|
|
|
182
219
|
|
|
183
220
|
transport = ASGITransport(app=app)
|
|
184
221
|
try:
|
|
185
|
-
async with AsyncClient(
|
|
222
|
+
async with AsyncClient(
|
|
223
|
+
transport=transport, base_url=base_url, **kwargs
|
|
224
|
+
) as client:
|
|
186
225
|
yield client
|
|
187
226
|
finally:
|
|
188
227
|
if dependency_overrides:
|
|
@@ -199,6 +238,8 @@ async def create_db_session(
|
|
|
199
238
|
expire_on_commit: bool = False,
|
|
200
239
|
drop_tables: bool = True,
|
|
201
240
|
cleanup: bool = False,
|
|
241
|
+
engine_kwargs: dict[str, Any] | None = None,
|
|
242
|
+
session_kwargs: dict[str, Any] | None = None,
|
|
202
243
|
) -> AsyncGenerator[AsyncSession, None]:
|
|
203
244
|
"""Create a database session for testing.
|
|
204
245
|
|
|
@@ -213,6 +254,12 @@ async def create_db_session(
|
|
|
213
254
|
drop_tables: Drop tables after test. Defaults to True.
|
|
214
255
|
cleanup: Truncate all tables after test using
|
|
215
256
|
:func:`cleanup_tables`. Defaults to False.
|
|
257
|
+
engine_kwargs: Additional keyword arguments forwarded to
|
|
258
|
+
:func:`sqlalchemy.ext.asyncio.create_async_engine`
|
|
259
|
+
(e.g. ``pool_size``, ``connect_args``).
|
|
260
|
+
session_kwargs: Additional keyword arguments forwarded to
|
|
261
|
+
:class:`sqlalchemy.ext.asyncio.async_sessionmaker`
|
|
262
|
+
(e.g. ``autoflush``, ``class_``).
|
|
216
263
|
|
|
217
264
|
Yields:
|
|
218
265
|
An AsyncSession ready for database operations.
|
|
@@ -237,15 +284,17 @@ async def create_db_session(
|
|
|
237
284
|
await db_session.commit()
|
|
238
285
|
```
|
|
239
286
|
"""
|
|
240
|
-
engine = create_async_engine(database_url, echo=echo)
|
|
287
|
+
engine = create_async_engine(database_url, echo=echo, **(engine_kwargs or {}))
|
|
241
288
|
|
|
242
289
|
try:
|
|
243
|
-
# Create tables
|
|
244
290
|
async with engine.begin() as conn:
|
|
245
291
|
await conn.run_sync(base.metadata.create_all)
|
|
246
292
|
|
|
247
293
|
session_maker = async_sessionmaker(
|
|
248
|
-
engine,
|
|
294
|
+
engine,
|
|
295
|
+
expire_on_commit=expire_on_commit,
|
|
296
|
+
class_=EventSession,
|
|
297
|
+
**(session_kwargs or {}),
|
|
249
298
|
)
|
|
250
299
|
async with session_maker() as session:
|
|
251
300
|
yield session
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/cli/commands/__init__.py
RENAMED
|
File without changes
|
{fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/cli/commands/fixtures.py
RENAMED
|
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
|
{fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/exceptions/__init__.py
RENAMED
|
File without changes
|
{fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/exceptions/exceptions.py
RENAMED
|
File without changes
|
{fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/exceptions/handler.py
RENAMED
|
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
|
{fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/security/sources/__init__.py
RENAMED
|
File without changes
|
{fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/security/sources/bearer.py
RENAMED
|
File without changes
|
{fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/security/sources/cookie.py
RENAMED
|
File without changes
|
{fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/security/sources/header.py
RENAMED
|
File without changes
|
{fastapi_toolsets-4.1.1 → fastapi_toolsets-4.1.3}/src/fastapi_toolsets/security/sources/multi.py
RENAMED
|
File without changes
|
|
File without changes
|