FastSQLA 0.7.0__tar.gz → 0.8.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: FastSQLA
3
- Version: 0.7.0
3
+ Version: 0.8.0
4
4
  Summary: SQLAlchemy extension for FastAPI that supports asynchronous sessions and includes built-in pagination.
5
5
  Author-email: Hadrien David <h@driendavid.com>
6
6
  Project-URL: Homepage, https://github.com/hadrien/fastsqla
@@ -8,7 +8,7 @@ Project-URL: Documentation, https://github.com/hadrien/fastsqla
8
8
  Project-URL: Repository, https://github.com/hadrien/fastsqla
9
9
  Project-URL: Issues, https://github.com/hadrien/fastsqla/issues
10
10
  Project-URL: Changelog, https://github.com/hadrien/fastsqla/releases
11
- Keywords: FastAPI,SQLAlchemy,AsyncIO
11
+ Keywords: fastapi,sqlalchemy,async,session,pagination,sqlmodel
12
12
  Classifier: Development Status :: 4 - Beta
13
13
  Classifier: Environment :: Web Environment
14
14
  Classifier: Framework :: AsyncIO
@@ -37,11 +37,6 @@ Description-Content-Type: text/markdown
37
37
  Requires-Dist: fastapi>=0.115.6
38
38
  Requires-Dist: sqlalchemy[asyncio]>=2.0.37
39
39
  Requires-Dist: structlog>=24.4.0
40
- Provides-Extra: docs
41
- Requires-Dist: mkdocs-glightbox>=0.5.2; extra == "docs"
42
- Requires-Dist: mkdocs-llmstxt>=0.5.0; extra == "docs"
43
- Requires-Dist: mkdocs-material>=9.7.1; extra == "docs"
44
- Requires-Dist: mkdocstrings[python]>=1.0.0; extra == "docs"
45
40
  Provides-Extra: sqlmodel
46
41
  Requires-Dist: sqlmodel>=0.0.22; extra == "sqlmodel"
47
42
 
@@ -71,7 +66,6 @@ providing boilerplate and intuitive helpers. Additionally, it offers built-in
71
66
  customizable pagination and automatically manages the `SQLAlchemy` session lifecycle
72
67
  following [`SQLAlchemy`'s best practices](https://docs.sqlalchemy.org/en/20/orm/session_basics.html#when-do-i-construct-a-session-when-do-i-commit-it-and-when-do-i-close-it).
73
68
 
74
-
75
69
  ## Features
76
70
 
77
71
  * Easy setup at app startup using
@@ -154,17 +148,23 @@ following [`SQLAlchemy`'s best practices](https://docs.sqlalchemy.org/en/20/orm/
154
148
  ```
155
149
 
156
150
  * Pagination customization:
151
+
157
152
  ```python
158
- ...
159
- from fastapi import Page, new_pagination
160
- ...
153
+ from typing import Annotated
161
154
 
162
- Paginate = new_pagination(min_page_size=5, max_page_size=500)
155
+ from fastapi import Depends
156
+ from fastsqla import Page, PaginateType, new_pagination
163
157
 
164
- @app.get("/heros", response_model=Page[HeroModel])
165
- async def get_heros(paginate:Paginate):
166
- return paginate(select(Hero))
158
+ CustomPaginate = Annotated[
159
+ PaginateType[HeroModel],
160
+ Depends(new_pagination(default_page_size=5, max_page_size=500)),
161
+ ]
162
+
163
+ @app.get("/heroes", response_model=Page[HeroModel])
164
+ async def get_heroes(paginate: CustomPaginate):
165
+ return await paginate(select(Hero))
167
166
  ```
167
+
168
168
  * Session lifecycle management: session is commited on request success or rollback on
169
169
  failure.
170
170
 
@@ -355,3 +355,33 @@ You can also check the generated openapi doc by opening your browser to
355
355
  ## License
356
356
 
357
357
  This project is licensed under the terms of the [MIT license](https://github.com/hadrien/FastSQLA/blob/main/LICENSE).
358
+
359
+ ## For coding agents and LLMs
360
+
361
+ FastSQLA publishes agent-readable documentation alongside the website:
362
+
363
+ - [`llms.txt`](https://hadrien.github.io/FastSQLA/llms.txt) is the concise documentation
364
+ index.
365
+ - [`llms-full.txt`](https://hadrien.github.io/FastSQLA/llms-full.txt) contains the
366
+ complete documentation in one file.
367
+ - Every indexed page has a Markdown twin, such as
368
+ [`setup/index.md`](https://hadrien.github.io/FastSQLA/setup/index.md).
369
+ - [Context7](https://context7.com/hadrien/fastsqla) serves the current documentation and
370
+ FastSQLA-specific usage rules.
371
+
372
+ The repository also bundles Agent Skills for setup, session management, and pagination.
373
+ Install all three as one plugin:
374
+
375
+ ### Claude Code
376
+
377
+ ```bash
378
+ claude plugin marketplace add hadrien/FastSQLA
379
+ claude plugin install fastsqla@fastsqla
380
+ ```
381
+
382
+ ### Codex
383
+
384
+ ```bash
385
+ codex plugin marketplace add hadrien/FastSQLA
386
+ codex plugin add fastsqla@fastsqla
387
+ ```
@@ -24,7 +24,6 @@ providing boilerplate and intuitive helpers. Additionally, it offers built-in
24
24
  customizable pagination and automatically manages the `SQLAlchemy` session lifecycle
25
25
  following [`SQLAlchemy`'s best practices](https://docs.sqlalchemy.org/en/20/orm/session_basics.html#when-do-i-construct-a-session-when-do-i-commit-it-and-when-do-i-close-it).
26
26
 
27
-
28
27
  ## Features
29
28
 
30
29
  * Easy setup at app startup using
@@ -107,17 +106,23 @@ following [`SQLAlchemy`'s best practices](https://docs.sqlalchemy.org/en/20/orm/
107
106
  ```
108
107
 
109
108
  * Pagination customization:
109
+
110
110
  ```python
111
- ...
112
- from fastapi import Page, new_pagination
113
- ...
111
+ from typing import Annotated
114
112
 
115
- Paginate = new_pagination(min_page_size=5, max_page_size=500)
113
+ from fastapi import Depends
114
+ from fastsqla import Page, PaginateType, new_pagination
116
115
 
117
- @app.get("/heros", response_model=Page[HeroModel])
118
- async def get_heros(paginate:Paginate):
119
- return paginate(select(Hero))
116
+ CustomPaginate = Annotated[
117
+ PaginateType[HeroModel],
118
+ Depends(new_pagination(default_page_size=5, max_page_size=500)),
119
+ ]
120
+
121
+ @app.get("/heroes", response_model=Page[HeroModel])
122
+ async def get_heroes(paginate: CustomPaginate):
123
+ return await paginate(select(Hero))
120
124
  ```
125
+
121
126
  * Session lifecycle management: session is commited on request success or rollback on
122
127
  failure.
123
128
 
@@ -308,3 +313,33 @@ You can also check the generated openapi doc by opening your browser to
308
313
  ## License
309
314
 
310
315
  This project is licensed under the terms of the [MIT license](https://github.com/hadrien/FastSQLA/blob/main/LICENSE).
316
+
317
+ ## For coding agents and LLMs
318
+
319
+ FastSQLA publishes agent-readable documentation alongside the website:
320
+
321
+ - [`llms.txt`](https://hadrien.github.io/FastSQLA/llms.txt) is the concise documentation
322
+ index.
323
+ - [`llms-full.txt`](https://hadrien.github.io/FastSQLA/llms-full.txt) contains the
324
+ complete documentation in one file.
325
+ - Every indexed page has a Markdown twin, such as
326
+ [`setup/index.md`](https://hadrien.github.io/FastSQLA/setup/index.md).
327
+ - [Context7](https://context7.com/hadrien/fastsqla) serves the current documentation and
328
+ FastSQLA-specific usage rules.
329
+
330
+ The repository also bundles Agent Skills for setup, session management, and pagination.
331
+ Install all three as one plugin:
332
+
333
+ ### Claude Code
334
+
335
+ ```bash
336
+ claude plugin marketplace add hadrien/FastSQLA
337
+ claude plugin install fastsqla@fastsqla
338
+ ```
339
+
340
+ ### Codex
341
+
342
+ ```bash
343
+ codex plugin marketplace add hadrien/FastSQLA
344
+ codex plugin add fastsqla@fastsqla
345
+ ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "FastSQLA"
3
- version = "0.7.0"
3
+ version = "0.8.0"
4
4
  description = "SQLAlchemy extension for FastAPI that supports asynchronous sessions and includes built-in pagination."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -30,7 +30,7 @@ classifiers = [
30
30
  "Topic :: Software Development",
31
31
  "Typing :: Typed",
32
32
  ]
33
- keywords = ["FastAPI", "SQLAlchemy", "AsyncIO"]
33
+ keywords = ["fastapi", "sqlalchemy", "async", "session", "pagination", "sqlmodel"]
34
34
  dependencies = ["fastapi>=0.115.6", "sqlalchemy[asyncio]>=2.0.37", "structlog>=24.4.0"]
35
35
 
36
36
  [project.urls]
@@ -44,18 +44,19 @@ Changelog = "https://github.com/hadrien/fastsqla/releases"
44
44
  license-files = []
45
45
 
46
46
  [project.optional-dependencies]
47
- docs = [
48
- "mkdocs-glightbox>=0.5.2",
49
- "mkdocs-llmstxt>=0.5.0",
50
- "mkdocs-material>=9.7.1",
51
- "mkdocstrings[python]>=1.0.0",
52
- ]
53
47
  sqlmodel = ["sqlmodel>=0.0.22"]
54
48
 
55
49
  [tool.uv]
56
50
  package = true
57
51
 
58
52
  [dependency-groups]
53
+ docs = [
54
+ "mkdocs-glightbox>=0.5.2",
55
+ "mkdocs-llmstxt>=0.5.0",
56
+ "mkdocs-material>=9.7.1",
57
+ "mkdocstrings[python]>=1.0.0",
58
+ "pygments @ git+https://github.com/pygments/pygments.git@b6f6dab78e13652741d5e5811b4256b4dcb5fcd8",
59
+ ]
59
60
  dev = [
60
61
  "asgi-lifespan>=2.1.0",
61
62
  "coverage>=7.6.1",
@@ -93,6 +94,24 @@ env = "GH_TOKEN"
93
94
  [tool.semantic_release]
94
95
  version_toml = ["pyproject.toml:project.version"]
95
96
  allow_zero_version = true
97
+ build_command_env = ["TWINE_USERNAME", "TWINE_PASSWORD"]
98
+ build_command = """
99
+ uv lock &&
100
+ git add uv.lock &&
101
+ uv build &&
102
+ uv run twine check dist/* &&
103
+ uv run twine upload --non-interactive --skip-existing --verbose dist/*
104
+ """
105
+ commit_message = """\
106
+ chore(release): v{version}
107
+
108
+ Automatically generated by python-semantic-release
109
+ """
110
+
111
+ [tool.semantic_release.commit_parser_options]
112
+ minor_tags = ["feat"]
113
+ patch_tags = ["build", "ci", "docs", "fix", "perf", "refactor", "style", "test"]
114
+ other_allowed_tags = ["chore"]
96
115
 
97
116
  [tool.semantic_release.changelog]
98
117
  mode = "init"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: FastSQLA
3
- Version: 0.7.0
3
+ Version: 0.8.0
4
4
  Summary: SQLAlchemy extension for FastAPI that supports asynchronous sessions and includes built-in pagination.
5
5
  Author-email: Hadrien David <h@driendavid.com>
6
6
  Project-URL: Homepage, https://github.com/hadrien/fastsqla
@@ -8,7 +8,7 @@ Project-URL: Documentation, https://github.com/hadrien/fastsqla
8
8
  Project-URL: Repository, https://github.com/hadrien/fastsqla
9
9
  Project-URL: Issues, https://github.com/hadrien/fastsqla/issues
10
10
  Project-URL: Changelog, https://github.com/hadrien/fastsqla/releases
11
- Keywords: FastAPI,SQLAlchemy,AsyncIO
11
+ Keywords: fastapi,sqlalchemy,async,session,pagination,sqlmodel
12
12
  Classifier: Development Status :: 4 - Beta
13
13
  Classifier: Environment :: Web Environment
14
14
  Classifier: Framework :: AsyncIO
@@ -37,11 +37,6 @@ Description-Content-Type: text/markdown
37
37
  Requires-Dist: fastapi>=0.115.6
38
38
  Requires-Dist: sqlalchemy[asyncio]>=2.0.37
39
39
  Requires-Dist: structlog>=24.4.0
40
- Provides-Extra: docs
41
- Requires-Dist: mkdocs-glightbox>=0.5.2; extra == "docs"
42
- Requires-Dist: mkdocs-llmstxt>=0.5.0; extra == "docs"
43
- Requires-Dist: mkdocs-material>=9.7.1; extra == "docs"
44
- Requires-Dist: mkdocstrings[python]>=1.0.0; extra == "docs"
45
40
  Provides-Extra: sqlmodel
46
41
  Requires-Dist: sqlmodel>=0.0.22; extra == "sqlmodel"
47
42
 
@@ -71,7 +66,6 @@ providing boilerplate and intuitive helpers. Additionally, it offers built-in
71
66
  customizable pagination and automatically manages the `SQLAlchemy` session lifecycle
72
67
  following [`SQLAlchemy`'s best practices](https://docs.sqlalchemy.org/en/20/orm/session_basics.html#when-do-i-construct-a-session-when-do-i-commit-it-and-when-do-i-close-it).
73
68
 
74
-
75
69
  ## Features
76
70
 
77
71
  * Easy setup at app startup using
@@ -154,17 +148,23 @@ following [`SQLAlchemy`'s best practices](https://docs.sqlalchemy.org/en/20/orm/
154
148
  ```
155
149
 
156
150
  * Pagination customization:
151
+
157
152
  ```python
158
- ...
159
- from fastapi import Page, new_pagination
160
- ...
153
+ from typing import Annotated
161
154
 
162
- Paginate = new_pagination(min_page_size=5, max_page_size=500)
155
+ from fastapi import Depends
156
+ from fastsqla import Page, PaginateType, new_pagination
163
157
 
164
- @app.get("/heros", response_model=Page[HeroModel])
165
- async def get_heros(paginate:Paginate):
166
- return paginate(select(Hero))
158
+ CustomPaginate = Annotated[
159
+ PaginateType[HeroModel],
160
+ Depends(new_pagination(default_page_size=5, max_page_size=500)),
161
+ ]
162
+
163
+ @app.get("/heroes", response_model=Page[HeroModel])
164
+ async def get_heroes(paginate: CustomPaginate):
165
+ return await paginate(select(Hero))
167
166
  ```
167
+
168
168
  * Session lifecycle management: session is commited on request success or rollback on
169
169
  failure.
170
170
 
@@ -355,3 +355,33 @@ You can also check the generated openapi doc by opening your browser to
355
355
  ## License
356
356
 
357
357
  This project is licensed under the terms of the [MIT license](https://github.com/hadrien/FastSQLA/blob/main/LICENSE).
358
+
359
+ ## For coding agents and LLMs
360
+
361
+ FastSQLA publishes agent-readable documentation alongside the website:
362
+
363
+ - [`llms.txt`](https://hadrien.github.io/FastSQLA/llms.txt) is the concise documentation
364
+ index.
365
+ - [`llms-full.txt`](https://hadrien.github.io/FastSQLA/llms-full.txt) contains the
366
+ complete documentation in one file.
367
+ - Every indexed page has a Markdown twin, such as
368
+ [`setup/index.md`](https://hadrien.github.io/FastSQLA/setup/index.md).
369
+ - [Context7](https://context7.com/hadrien/fastsqla) serves the current documentation and
370
+ FastSQLA-specific usage rules.
371
+
372
+ The repository also bundles Agent Skills for setup, session management, and pagination.
373
+ Install all three as one plugin:
374
+
375
+ ### Claude Code
376
+
377
+ ```bash
378
+ claude plugin marketplace add hadrien/FastSQLA
379
+ claude plugin install fastsqla@fastsqla
380
+ ```
381
+
382
+ ### Codex
383
+
384
+ ```bash
385
+ codex plugin marketplace add hadrien/FastSQLA
386
+ codex plugin add fastsqla@fastsqla
387
+ ```
@@ -0,0 +1,6 @@
1
+ fastapi>=0.115.6
2
+ sqlalchemy[asyncio]>=2.0.37
3
+ structlog>=24.4.0
4
+
5
+ [sqlmodel]
6
+ sqlmodel>=0.0.22
@@ -1,8 +1,10 @@
1
+ import functools
1
2
  import math
2
3
  import os
4
+ import warnings
3
5
  from collections.abc import AsyncGenerator, Awaitable, Callable, Iterable
4
6
  from contextlib import _AsyncGeneratorContextManager, asynccontextmanager
5
- from typing import Annotated, Generic, TypedDict, TypeVar
7
+ from typing import Annotated, TypedDict, TypeVar
6
8
 
7
9
  from fastapi import Depends as BaseDepends
8
10
  from fastapi import FastAPI, Query
@@ -31,6 +33,7 @@ __all__ = [
31
33
  "Base",
32
34
  "Collection",
33
35
  "Item",
36
+ "MissingConfigurationError",
34
37
  "Page",
35
38
  "Paginate",
36
39
  "PaginateType",
@@ -88,6 +91,10 @@ class State(TypedDict):
88
91
  fastsqla_engine: AsyncEngine
89
92
 
90
93
 
94
+ class MissingConfigurationError(RuntimeError):
95
+ """Raised when a required SQLAlchemy setting is missing."""
96
+
97
+
91
98
  def new_lifespan(
92
99
  url: str | None = None, **kw
93
100
  ) -> Callable[[FastAPI | None], _AsyncGeneratorContextManager[State, None]]:
@@ -112,6 +119,9 @@ def new_lifespan(
112
119
  Args:
113
120
  url (str): Database url.
114
121
  kw (dict): Configuration parameters as expected by [`sqlalchemy.ext.asyncio.create_async_engine`][sqlalchemy.ext.asyncio.create_async_engine]
122
+
123
+ Raises:
124
+ MissingConfigurationError: If a required SQLAlchemy setting is missing.
115
125
  """
116
126
 
117
127
  has_config = url is not None
@@ -120,7 +130,7 @@ def new_lifespan(
120
130
  async def lifespan(app: FastAPI | None) -> AsyncGenerator[State, None]:
121
131
  if has_config:
122
132
  prefix = ""
123
- sqla_config = {**kw, **{"url": url}}
133
+ sqla_config = {**kw, "url": url}
124
134
 
125
135
  else:
126
136
  prefix = "sqlalchemy_"
@@ -130,7 +140,9 @@ def new_lifespan(
130
140
  engine = async_engine_from_config(sqla_config, prefix=prefix)
131
141
 
132
142
  except KeyError as exc:
133
- raise Exception(f"Missing {prefix}{exc.args[0]} in environ.") from exc
143
+ raise MissingConfigurationError(
144
+ f"Missing {prefix}{exc.args[0]} in environ."
145
+ ) from exc
134
146
 
135
147
  async with engine.begin() as conn:
136
148
  await conn.run_sync(Base.prepare)
@@ -330,11 +342,11 @@ class Meta(BaseModel):
330
342
  T = TypeVar("T")
331
343
 
332
344
 
333
- class Item(BaseModel, Generic[T]):
345
+ class Item[T](BaseModel):
334
346
  data: T
335
347
 
336
348
 
337
- class Collection(BaseModel, Generic[T]):
349
+ class Collection[T](BaseModel):
338
350
  data: list[T]
339
351
 
340
352
 
@@ -387,18 +399,62 @@ async def _paginate(
387
399
  )
388
400
 
389
401
 
402
+ def _accept_deprecated_page_size_option[**P, R](
403
+ function: Callable[P, R],
404
+ ) -> Callable[P, R]:
405
+ @functools.wraps(function)
406
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
407
+ if "min_page_size" in kwargs:
408
+ if args or "default_page_size" in kwargs:
409
+ raise TypeError(
410
+ "new_pagination() cannot receive both default_page_size and "
411
+ "min_page_size"
412
+ )
413
+ warnings.warn(
414
+ "min_page_size is deprecated; use default_page_size instead",
415
+ DeprecationWarning,
416
+ stacklevel=2,
417
+ )
418
+ return function(*args, **kwargs)
419
+
420
+ return wrapper
421
+
422
+
423
+ @_accept_deprecated_page_size_option
390
424
  def new_pagination(
391
- min_page_size: int = 10,
425
+ default_page_size: int = 10,
392
426
  max_page_size: int = 100,
393
427
  query_count_dependency: Callable[..., Awaitable[int]] | None = None,
394
428
  result_processor: Callable[[Result], Iterable] = lambda result: iter(
395
429
  result.unique().scalars()
396
430
  ),
431
+ *,
432
+ min_page_size: int | None = None,
397
433
  ):
434
+ """Create a FastAPI pagination dependency.
435
+
436
+ Args:
437
+ default_page_size: Default value of the `limit` query parameter.
438
+ max_page_size: Maximum accepted value of the `limit` query parameter.
439
+ query_count_dependency: Optional dependency that returns the total item count.
440
+ result_processor: Function that transforms the SQLAlchemy result into page data.
441
+ min_page_size: Deprecated alias for `default_page_size`.
442
+
443
+ Raises:
444
+ TypeError: Both page-size parameter names are supplied.
445
+ ValueError: The page-size configuration is invalid.
446
+ """
447
+ if min_page_size is not None:
448
+ default_page_size = min_page_size
449
+ if max_page_size < 1:
450
+ raise ValueError("max_page_size must be at least 1")
451
+ if not 1 <= default_page_size <= max_page_size:
452
+ raise ValueError("default_page_size must be between 1 and max_page_size")
453
+
398
454
  def default_dependency(
399
455
  session: Session,
400
456
  offset: int = Query(0, ge=0),
401
- limit: int = Query(min_page_size, ge=1, le=max_page_size),
457
+ limit: int = Query(default_page_size, ge=1, le=max_page_size),
402
458
  ) -> PaginateType[T]:
403
459
  async def paginate(stmt: Select) -> Page:
404
460
  total_items = await _query_count(session, stmt)
@@ -411,7 +467,7 @@ def new_pagination(
411
467
  def dependency(
412
468
  session: Session,
413
469
  offset: int = Query(0, ge=0),
414
- limit: int = Query(min_page_size, ge=1, le=max_page_size),
470
+ limit: int = Query(default_page_size, ge=1, le=max_page_size),
415
471
  total_items: int = Depends(query_count_dependency),
416
472
  ) -> PaginateType[T]:
417
473
  async def paginate(stmt: Select) -> Page:
@@ -1,12 +0,0 @@
1
- fastapi>=0.115.6
2
- sqlalchemy[asyncio]>=2.0.37
3
- structlog>=24.4.0
4
-
5
- [docs]
6
- mkdocs-glightbox>=0.5.2
7
- mkdocs-llmstxt>=0.5.0
8
- mkdocs-material>=9.7.1
9
- mkdocstrings[python]>=1.0.0
10
-
11
- [sqlmodel]
12
- sqlmodel>=0.0.22
File without changes