neva-fastapi 1.0.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.
- neva_fastapi-1.0.0/.envrc +2 -0
- neva_fastapi-1.0.0/.gitignore +21 -0
- neva_fastapi-1.0.0/.pre-commit-config.yaml +31 -0
- neva_fastapi-1.0.0/.python-version +1 -0
- neva_fastapi-1.0.0/CHANGELOG.md +24 -0
- neva_fastapi-1.0.0/PKG-INFO +6 -0
- neva_fastapi-1.0.0/README.md +0 -0
- neva_fastapi-1.0.0/neva/fastapi/__init__.py +7 -0
- neva_fastapi-1.0.0/neva/fastapi/app.py +102 -0
- neva_fastapi-1.0.0/neva/fastapi/fixtures.py +31 -0
- neva_fastapi-1.0.0/neva/fastapi/py.typed +0 -0
- neva_fastapi-1.0.0/neva/fastapi/router.py +234 -0
- neva_fastapi-1.0.0/pyproject.toml +76 -0
- neva_fastapi-1.0.0/ruff.toml +42 -0
- neva_fastapi-1.0.0/scripts/retag-with-changelog.sh +16 -0
- neva_fastapi-1.0.0/tests/__init__.py +0 -0
- neva_fastapi-1.0.0/tests/config/__init__.py +0 -0
- neva_fastapi-1.0.0/tests/config/app.py +6 -0
- neva_fastapi-1.0.0/tests/conftest.py +1 -0
- neva_fastapi-1.0.0/tests/test_fixture.py +15 -0
- neva_fastapi-1.0.0/uv.lock +2372 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Python-generated files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[ocd]
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
wheels/
|
|
7
|
+
*.egg-info
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv
|
|
11
|
+
|
|
12
|
+
# Tests / Coverage / Benchmarks
|
|
13
|
+
htmlcov/
|
|
14
|
+
.coverage
|
|
15
|
+
.pytest_cache/
|
|
16
|
+
.benchmarks/
|
|
17
|
+
profiles/
|
|
18
|
+
|
|
19
|
+
# LLM Contexts
|
|
20
|
+
llm-context.md
|
|
21
|
+
CLAUDE.md
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/gitleaks/gitleaks
|
|
3
|
+
rev: v8.30.1
|
|
4
|
+
hooks:
|
|
5
|
+
- id: gitleaks
|
|
6
|
+
|
|
7
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
8
|
+
rev: v6.0.0
|
|
9
|
+
hooks:
|
|
10
|
+
- id: trailing-whitespace
|
|
11
|
+
- id: end-of-file-fixer
|
|
12
|
+
- id: check-yaml
|
|
13
|
+
- id: check-added-large-files
|
|
14
|
+
- id: check-case-conflict
|
|
15
|
+
- id: check-merge-conflict
|
|
16
|
+
- id: debug-statements
|
|
17
|
+
|
|
18
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
19
|
+
rev: v0.15.6
|
|
20
|
+
hooks:
|
|
21
|
+
- id: ruff
|
|
22
|
+
args: [--fix, --exit-non-zero-on-fix]
|
|
23
|
+
- id: ruff-format
|
|
24
|
+
|
|
25
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
26
|
+
rev: v1.19.1
|
|
27
|
+
hooks:
|
|
28
|
+
- id: mypy
|
|
29
|
+
args: [--enable-incomplete-feature=TypeForm]
|
|
30
|
+
additional_dependencies:
|
|
31
|
+
[dishka>=1.10.0, "fastapi[all]>=0.129.0", pytest>=9.0.2]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
## 1.0.0 (2026-05-11)
|
|
2
|
+
|
|
3
|
+
### 📌➕⬇️➖⬆️ Dependencies
|
|
4
|
+
|
|
5
|
+
- update neva dep
|
|
6
|
+
|
|
7
|
+
## 0.2.1 (2026-05-11)
|
|
8
|
+
|
|
9
|
+
### 🐛🚑️ Fixes
|
|
10
|
+
|
|
11
|
+
- remove obsolete parameter in rouetr
|
|
12
|
+
|
|
13
|
+
### 🏷️ Types
|
|
14
|
+
|
|
15
|
+
- stub + re-exports
|
|
16
|
+
|
|
17
|
+
## 0.2.0 (2026-05-06)
|
|
18
|
+
|
|
19
|
+
### 🔧🔨📦️ Configuration, Scripts, Packages
|
|
20
|
+
|
|
21
|
+
- update perms on build script
|
|
22
|
+
- config commitizen
|
|
23
|
+
|
|
24
|
+
## 0.1.0 (2026-05-05)
|
|
File without changes
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Main application class.
|
|
2
|
+
|
|
3
|
+
This module provides the core App class that extends FastAPI with dependency
|
|
4
|
+
injection. DI is handled by the dependency injection container provided by
|
|
5
|
+
the Application class.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from collections.abc import AsyncIterator, Mapping, Sequence
|
|
9
|
+
from contextlib import asynccontextmanager
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from dishka import FromDishka
|
|
14
|
+
from dishka.integrations.fastapi import FastapiProvider, setup_dishka
|
|
15
|
+
from neva.arch.application import Application
|
|
16
|
+
from neva.arch.service_provider import ServiceProvider
|
|
17
|
+
from neva.support import Result
|
|
18
|
+
from starlette.middleware import Middleware
|
|
19
|
+
from starlette.routing import BaseRoute
|
|
20
|
+
from starlette.types import Lifespan, StatefulLifespan
|
|
21
|
+
|
|
22
|
+
import fastapi
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class App(fastapi.FastAPI):
|
|
26
|
+
"""Main application class extending FastAPI."""
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
*,
|
|
31
|
+
routes: list[BaseRoute] | None = None,
|
|
32
|
+
middlewares: Sequence[Middleware] | None = None,
|
|
33
|
+
lifespan: Lifespan["App"] | None = None,
|
|
34
|
+
config_path: str | Path | None = None,
|
|
35
|
+
) -> None:
|
|
36
|
+
"""Initialize the application.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
routes: List of routes to register with the application.
|
|
40
|
+
middlewares: Sequence of middleware to apply to the application.
|
|
41
|
+
lifespan: Custom lifespan context manager for application lifecycle.
|
|
42
|
+
config_path: Path to the configuration directory. Defaults to "./config"
|
|
43
|
+
relative to the current working directory.
|
|
44
|
+
|
|
45
|
+
"""
|
|
46
|
+
self.application: Application = Application(config_path=config_path)
|
|
47
|
+
self.application.build_container(FastapiProvider())
|
|
48
|
+
|
|
49
|
+
config = self.application.config
|
|
50
|
+
|
|
51
|
+
super().__init__(
|
|
52
|
+
debug=config.get("app.debug", type_=bool).unwrap_or(False), # noqa: FBT003
|
|
53
|
+
routes=routes,
|
|
54
|
+
title=config.get("app.title", type_=str).unwrap_or("Neva Application"),
|
|
55
|
+
version=config.get("app.version", type_=str).unwrap_or("0.1.0"),
|
|
56
|
+
openapi_url=config.get("app.openapi_url", type_=str).unwrap_or(
|
|
57
|
+
"/openapi.json"
|
|
58
|
+
),
|
|
59
|
+
docs_url=config.get("app.docs_url", type_=str).unwrap_or("/docs"),
|
|
60
|
+
redoc_url=config.get("app.redoc_url", type_=str).unwrap_or("/redoc"),
|
|
61
|
+
lifespan=self.build_lifespan(custom_lifespan=lifespan),
|
|
62
|
+
middleware=middlewares,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
setup_dishka(self.application.container, app=self)
|
|
66
|
+
|
|
67
|
+
def register(
|
|
68
|
+
self,
|
|
69
|
+
provider: type[ServiceProvider],
|
|
70
|
+
) -> Result[ServiceProvider, str]:
|
|
71
|
+
"""Registers a service provider with the application.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
Result containing the registered provider instance or an error message.
|
|
75
|
+
"""
|
|
76
|
+
return self.application.register(provider=provider)
|
|
77
|
+
|
|
78
|
+
def build_lifespan(
|
|
79
|
+
self,
|
|
80
|
+
custom_lifespan: Lifespan["App"] | None = None,
|
|
81
|
+
) -> StatefulLifespan["App"]:
|
|
82
|
+
"""Builds a lifespan context manager for the application.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
the lifespan context manager.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
@asynccontextmanager
|
|
89
|
+
async def lifespan(
|
|
90
|
+
app: "App",
|
|
91
|
+
) -> AsyncIterator[Mapping[str, Any]]:
|
|
92
|
+
async with self.application.lifespan():
|
|
93
|
+
if custom_lifespan is None:
|
|
94
|
+
yield {}
|
|
95
|
+
else:
|
|
96
|
+
async with custom_lifespan(app) as state:
|
|
97
|
+
yield state if state is not None else {}
|
|
98
|
+
|
|
99
|
+
return lifespan
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
Inject = FromDishka
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from collections.abc import AsyncIterator
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
from httpx import ASGITransport, AsyncClient
|
|
6
|
+
|
|
7
|
+
from neva.fastapi.app import App
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.fixture
|
|
11
|
+
def webapp(test_config: Path) -> App:
|
|
12
|
+
"""Pytest fixture for the HTTP Neva app.
|
|
13
|
+
|
|
14
|
+
Returns:
|
|
15
|
+
App: The Neva application instance.
|
|
16
|
+
"""
|
|
17
|
+
return App(config_path=test_config)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@pytest.fixture
|
|
21
|
+
async def http_client(webapp: App) -> AsyncIterator[AsyncClient]:
|
|
22
|
+
"""An async httpx client to test the application.
|
|
23
|
+
|
|
24
|
+
Yields:
|
|
25
|
+
An async httpx client.
|
|
26
|
+
"""
|
|
27
|
+
async with AsyncClient(
|
|
28
|
+
transport=ASGITransport(webapp),
|
|
29
|
+
base_url="http://localhost:8000",
|
|
30
|
+
) as client:
|
|
31
|
+
yield client
|
|
File without changes
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# ruff: noqa: B008
|
|
2
|
+
from collections.abc import Sequence
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import Annotated, Any, Callable, override
|
|
5
|
+
|
|
6
|
+
from annotated_doc import Doc
|
|
7
|
+
from dishka.integrations.fastapi import DishkaRoute
|
|
8
|
+
from starlette.routing import BaseRoute
|
|
9
|
+
from starlette.types import ASGIApp, Lifespan
|
|
10
|
+
from typing_extensions import deprecated
|
|
11
|
+
|
|
12
|
+
import fastapi
|
|
13
|
+
from fastapi import Response, params
|
|
14
|
+
from fastapi.datastructures import Default
|
|
15
|
+
from fastapi.responses import JSONResponse
|
|
16
|
+
from fastapi.routing import APIRoute
|
|
17
|
+
from fastapi.utils import generate_unique_id
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class APIRouter(fastapi.APIRouter):
|
|
21
|
+
"""A custom router that integrates dependency injection utilities."""
|
|
22
|
+
|
|
23
|
+
@override
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
*,
|
|
27
|
+
prefix: Annotated[str, Doc("An optional path prefix for the router.")] = "",
|
|
28
|
+
tags: Annotated[
|
|
29
|
+
list[str | Enum] | None,
|
|
30
|
+
Doc("""
|
|
31
|
+
A list of tags to be applied to all the *path operations* in this
|
|
32
|
+
router.
|
|
33
|
+
|
|
34
|
+
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
|
|
35
|
+
|
|
36
|
+
Read more about it in the
|
|
37
|
+
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
|
|
38
|
+
"""),
|
|
39
|
+
] = None,
|
|
40
|
+
dependencies: Annotated[
|
|
41
|
+
Sequence[params.Depends] | None,
|
|
42
|
+
Doc("""
|
|
43
|
+
A list of dependencies (using `Depends()`) to be applied to all the
|
|
44
|
+
*path operations* in this router.
|
|
45
|
+
|
|
46
|
+
Read more about it in the
|
|
47
|
+
[FastAPI docs for Bigger Applications - Multiple Files](https://fastapi.tiangolo.com/tutorial/bigger-applications/#include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies).
|
|
48
|
+
"""),
|
|
49
|
+
] = None,
|
|
50
|
+
default_response_class: Annotated[
|
|
51
|
+
type[Response],
|
|
52
|
+
Doc("""
|
|
53
|
+
The default response class to be used.
|
|
54
|
+
|
|
55
|
+
Read more in the
|
|
56
|
+
[FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#default-response-class).
|
|
57
|
+
"""),
|
|
58
|
+
] = Default(JSONResponse),
|
|
59
|
+
responses: Annotated[
|
|
60
|
+
dict[int | str, dict[str, Any]] | None,
|
|
61
|
+
Doc("""
|
|
62
|
+
Additional responses to be shown in OpenAPI.
|
|
63
|
+
|
|
64
|
+
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
|
|
65
|
+
|
|
66
|
+
Read more about it in the
|
|
67
|
+
[FastAPI docs for Additional Responses in OpenAPI](https://fastapi.tiangolo.com/advanced/additional-responses/).
|
|
68
|
+
|
|
69
|
+
And in the
|
|
70
|
+
[FastAPI docs for Bigger Applications](https://fastapi.tiangolo.com/tutorial/bigger-applications/#include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies).
|
|
71
|
+
"""),
|
|
72
|
+
] = None,
|
|
73
|
+
callbacks: Annotated[
|
|
74
|
+
list[BaseRoute] | None,
|
|
75
|
+
Doc("""
|
|
76
|
+
OpenAPI callbacks that should apply to all *path operations* in this
|
|
77
|
+
router.
|
|
78
|
+
|
|
79
|
+
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
|
|
80
|
+
|
|
81
|
+
Read more about it in the
|
|
82
|
+
[FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/).
|
|
83
|
+
"""),
|
|
84
|
+
] = None,
|
|
85
|
+
routes: Annotated[
|
|
86
|
+
list[BaseRoute] | None,
|
|
87
|
+
Doc("""
|
|
88
|
+
**Note**: you probably shouldn't use this parameter, it is inherited
|
|
89
|
+
from Starlette and supported for compatibility.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
A list of routes to serve incoming HTTP and WebSocket requests.
|
|
94
|
+
"""),
|
|
95
|
+
deprecated("""
|
|
96
|
+
You normally wouldn't use this parameter with FastAPI, it is
|
|
97
|
+
inherited from Starlette and supported for compatibility.
|
|
98
|
+
|
|
99
|
+
In FastAPI, you normally would use the *path operation methods*,
|
|
100
|
+
like `router.get()`, `router.post()`, etc.
|
|
101
|
+
"""),
|
|
102
|
+
] = None,
|
|
103
|
+
redirect_slashes: Annotated[
|
|
104
|
+
bool,
|
|
105
|
+
Doc("""
|
|
106
|
+
Whether to detect and redirect slashes in URLs when the
|
|
107
|
+
client inherited doesn't use the same format.
|
|
108
|
+
"""),
|
|
109
|
+
] = True,
|
|
110
|
+
default: Annotated[
|
|
111
|
+
ASGIApp | None,
|
|
112
|
+
Doc("""
|
|
113
|
+
Default function handler for this router. Used to handle
|
|
114
|
+
404 Not Found errors.
|
|
115
|
+
"""),
|
|
116
|
+
] = None,
|
|
117
|
+
dependency_overrides_provider: Annotated[
|
|
118
|
+
Any | None,
|
|
119
|
+
Doc("""
|
|
120
|
+
Only used internally by FastAPI to handle dependency overrides.
|
|
121
|
+
|
|
122
|
+
You shouldn't need to use it. It normally points to the `FastAPI`
|
|
123
|
+
app object.
|
|
124
|
+
"""),
|
|
125
|
+
] = None,
|
|
126
|
+
on_startup: Annotated[
|
|
127
|
+
Sequence[Callable[[], Any]] | None,
|
|
128
|
+
Doc("""
|
|
129
|
+
A list of startup event handler functions.
|
|
130
|
+
|
|
131
|
+
You should instead use the `lifespan` handlers.
|
|
132
|
+
|
|
133
|
+
Read more in the [FastAPI docs for `lifespan`](https://fastapi.tiangolo.com/advanced/events/).
|
|
134
|
+
"""),
|
|
135
|
+
] = None,
|
|
136
|
+
on_shutdown: Annotated[
|
|
137
|
+
Sequence[Callable[[], Any]] | None,
|
|
138
|
+
Doc("""
|
|
139
|
+
A list of shutdown event handler functions.
|
|
140
|
+
|
|
141
|
+
You should instead use the `lifespan` handlers.
|
|
142
|
+
|
|
143
|
+
Read more in the
|
|
144
|
+
[FastAPI docs for `lifespan`](https://fastapi.tiangolo.com/advanced/events/).
|
|
145
|
+
"""),
|
|
146
|
+
] = None,
|
|
147
|
+
lifespan: Annotated[
|
|
148
|
+
Lifespan[Any] | None,
|
|
149
|
+
Doc("""
|
|
150
|
+
A `Lifespan` context manager handler. This replaces `startup` and
|
|
151
|
+
`shutdown` functions with a single context manager.
|
|
152
|
+
|
|
153
|
+
Read more in the
|
|
154
|
+
[FastAPI docs for `lifespan`](https://fastapi.tiangolo.com/advanced/events/).
|
|
155
|
+
"""),
|
|
156
|
+
] = None,
|
|
157
|
+
deprecated: Annotated[
|
|
158
|
+
bool | None,
|
|
159
|
+
Doc("""
|
|
160
|
+
Mark all *path operations* in this router as deprecated.
|
|
161
|
+
|
|
162
|
+
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
|
|
163
|
+
|
|
164
|
+
Read more about it in the
|
|
165
|
+
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
|
|
166
|
+
"""),
|
|
167
|
+
] = None,
|
|
168
|
+
include_in_schema: Annotated[
|
|
169
|
+
bool,
|
|
170
|
+
Doc("""
|
|
171
|
+
To include (or not) all the *path operations* in this router in the
|
|
172
|
+
generated OpenAPI.
|
|
173
|
+
|
|
174
|
+
This affects the generated OpenAPI (e.g. visible at `/docs`).
|
|
175
|
+
|
|
176
|
+
Read more about it in the
|
|
177
|
+
[FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi).
|
|
178
|
+
"""),
|
|
179
|
+
] = True,
|
|
180
|
+
generate_unique_id_function: Annotated[
|
|
181
|
+
Callable[[APIRoute], str],
|
|
182
|
+
Doc("""
|
|
183
|
+
Customize the function used to generate unique IDs for the *path
|
|
184
|
+
operations* shown in the generated OpenAPI.
|
|
185
|
+
|
|
186
|
+
This is particularly useful when automatically generating clients or
|
|
187
|
+
SDKs for your API.
|
|
188
|
+
|
|
189
|
+
Read more about it in the
|
|
190
|
+
[FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
|
|
191
|
+
"""),
|
|
192
|
+
] = Default(generate_unique_id),
|
|
193
|
+
strict_content_type: Annotated[
|
|
194
|
+
bool,
|
|
195
|
+
Doc("""
|
|
196
|
+
Enable strict checking for request Content-Type headers.
|
|
197
|
+
|
|
198
|
+
When `True` (the default), requests with a body that do not include
|
|
199
|
+
a `Content-Type` header will **not** be parsed as JSON.
|
|
200
|
+
|
|
201
|
+
This prevents potential cross-site request forgery (CSRF) attacks
|
|
202
|
+
that exploit the browser's ability to send requests without a
|
|
203
|
+
Content-Type header, bypassing CORS preflight checks. In particular
|
|
204
|
+
applicable for apps that need to be run locally (in localhost).
|
|
205
|
+
|
|
206
|
+
When `False`, requests without a `Content-Type` header will have
|
|
207
|
+
their body parsed as JSON, which maintains compatibility with
|
|
208
|
+
certain clients that don't send `Content-Type` headers.
|
|
209
|
+
|
|
210
|
+
Read more about it in the
|
|
211
|
+
[FastAPI docs for Strict Content-Type](https://fastapi.tiangolo.com/advanced/strict-content-type/).
|
|
212
|
+
"""),
|
|
213
|
+
] = Default(True), # noqa: FBT003 This is inherited from FastAPI
|
|
214
|
+
) -> None:
|
|
215
|
+
super().__init__(
|
|
216
|
+
prefix=prefix,
|
|
217
|
+
tags=tags,
|
|
218
|
+
dependencies=dependencies,
|
|
219
|
+
default_response_class=default_response_class,
|
|
220
|
+
responses=responses,
|
|
221
|
+
callbacks=callbacks,
|
|
222
|
+
routes=routes,
|
|
223
|
+
redirect_slashes=redirect_slashes,
|
|
224
|
+
default=default,
|
|
225
|
+
dependency_overrides_provider=dependency_overrides_provider,
|
|
226
|
+
route_class=DishkaRoute,
|
|
227
|
+
on_startup=on_startup,
|
|
228
|
+
on_shutdown=on_shutdown,
|
|
229
|
+
lifespan=lifespan,
|
|
230
|
+
deprecated=deprecated,
|
|
231
|
+
include_in_schema=include_in_schema,
|
|
232
|
+
generate_unique_id_function=generate_unique_id_function,
|
|
233
|
+
# strict_content_type=strict_content_type,
|
|
234
|
+
)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "neva-fastapi"
|
|
3
|
+
dynamic = ["version"]
|
|
4
|
+
description = "Add your description here"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
dependencies = ["python-neva>=3.1.0"]
|
|
8
|
+
|
|
9
|
+
[dependency-groups]
|
|
10
|
+
dev = [
|
|
11
|
+
"bandit>=1.9.2",
|
|
12
|
+
"commitizen>=4.15.0",
|
|
13
|
+
"cz-conventional-gitmoji>=0.7.0",
|
|
14
|
+
"mypy>=1.19.1",
|
|
15
|
+
"poethepoet>=0.38.0",
|
|
16
|
+
"polyfactory>=3.1.0",
|
|
17
|
+
"pre-commit>=4.5.0",
|
|
18
|
+
"pytest>=9.0.2",
|
|
19
|
+
"pytest-asyncio>=0.25.3",
|
|
20
|
+
"pytest-benchmark>=5.2.3",
|
|
21
|
+
"pytest-cov>=7.0.0",
|
|
22
|
+
"ruff>=0.15.6",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[build-system]
|
|
26
|
+
requires = ["hatchling", "versioningit"]
|
|
27
|
+
build-backend = "hatchling.build"
|
|
28
|
+
|
|
29
|
+
[tool.hatch.build.targets.wheel]
|
|
30
|
+
packages = ["neva"]
|
|
31
|
+
|
|
32
|
+
[tool.hatch.version]
|
|
33
|
+
source = "versioningit"
|
|
34
|
+
|
|
35
|
+
[tool.hatch.version.format]
|
|
36
|
+
distance = "{base_version}.dev{distance}+{vcs}{rev}"
|
|
37
|
+
dirty = "{version}+dirty"
|
|
38
|
+
distance-dirty = "{next_version}.dev{distance}+{vcs}{rev}.dirty"
|
|
39
|
+
|
|
40
|
+
[tool.commitizen]
|
|
41
|
+
name = "cz_gitmoji"
|
|
42
|
+
version_provider = "scm"
|
|
43
|
+
tag_format = "$version"
|
|
44
|
+
version_scheme = "pep440"
|
|
45
|
+
update_changelog_on_bump = true
|
|
46
|
+
annotated_tag = true
|
|
47
|
+
post_bump_hooks = ["scripts/retag-with-changelog.sh"]
|
|
48
|
+
|
|
49
|
+
[tool.uv.sources]
|
|
50
|
+
python-neva = { path = "../neva", editable = true }
|
|
51
|
+
|
|
52
|
+
[tool.basedpyright]
|
|
53
|
+
enableExperimentalFeatures = true
|
|
54
|
+
|
|
55
|
+
[tool.mypy]
|
|
56
|
+
enable_incomplete_feature = ["TypeForm"]
|
|
57
|
+
plugins = ["pydantic.mypy"]
|
|
58
|
+
|
|
59
|
+
[tool.pytest.ini_options]
|
|
60
|
+
asyncio_mode = "auto"
|
|
61
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
62
|
+
testpaths = ["tests"]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
[tool.poe.tasks]
|
|
66
|
+
# Code quality
|
|
67
|
+
ruff = "uv run ruff"
|
|
68
|
+
mypy = "uv run mypy"
|
|
69
|
+
lint = "poe ruff check"
|
|
70
|
+
fmt = "poe ruff format"
|
|
71
|
+
tc = "poe mypy ."
|
|
72
|
+
|
|
73
|
+
# Testing
|
|
74
|
+
test = "pytest"
|
|
75
|
+
test-cov = "poe test --cov=neva --cov-report=term-missing"
|
|
76
|
+
test-full = "poe test-cov tests/"
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
target-version = "py312"
|
|
2
|
+
line-length = 88
|
|
3
|
+
|
|
4
|
+
[lint]
|
|
5
|
+
select = [
|
|
6
|
+
"FAST",
|
|
7
|
+
"ANN",
|
|
8
|
+
"ASYNC",
|
|
9
|
+
"S",
|
|
10
|
+
"FBT",
|
|
11
|
+
"B",
|
|
12
|
+
"A",
|
|
13
|
+
"C4",
|
|
14
|
+
"DTZ",
|
|
15
|
+
"ISC",
|
|
16
|
+
"ICN",
|
|
17
|
+
"SIM",
|
|
18
|
+
"SLOT",
|
|
19
|
+
"I",
|
|
20
|
+
"E",
|
|
21
|
+
"W",
|
|
22
|
+
"DOC",
|
|
23
|
+
"D",
|
|
24
|
+
"F",
|
|
25
|
+
"RUF",
|
|
26
|
+
]
|
|
27
|
+
preview = true
|
|
28
|
+
ignore = [
|
|
29
|
+
"D100", # Allow missing docstrings in public modules
|
|
30
|
+
"D107", # Allow missing docstrings in __init__
|
|
31
|
+
"RUF029",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[lint.per-file-ignores]
|
|
35
|
+
"tests/*" = ["D", "S101"]
|
|
36
|
+
|
|
37
|
+
[lint.isort]
|
|
38
|
+
force-single-line = false
|
|
39
|
+
lines-after-imports = 2
|
|
40
|
+
|
|
41
|
+
[lint.pydocstyle]
|
|
42
|
+
convention = "google"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
TAG="${CZ_POST_CURRENT_VERSION}"
|
|
5
|
+
|
|
6
|
+
# Generate the changelog section for this version only
|
|
7
|
+
CHANGELOG=$(cz changelog "${CZ_POST_CURRENT_VERSION}" --dry-run |
|
|
8
|
+
sed 's/^### *//' |
|
|
9
|
+
sed 's/^## *//' |
|
|
10
|
+
sed 's/\*\*//g' |
|
|
11
|
+
sed 's/`//g' |
|
|
12
|
+
sed 's/\[.\+\](\(.\+\))/\1/g')
|
|
13
|
+
|
|
14
|
+
# Replace the tag cz just created with an annotated one carrying the changelog
|
|
15
|
+
git tag -d "$TAG"
|
|
16
|
+
git tag -a "$TAG" -m "$CHANGELOG"
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pytest_plugins = ["neva.fastapi.fixtures"]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from neva.testing import TestCase
|
|
4
|
+
from pytest import fixture
|
|
5
|
+
|
|
6
|
+
from neva.fastapi.app import App
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestWebApp(TestCase):
|
|
10
|
+
@fixture
|
|
11
|
+
def test_config(self, tmp_path: Path) -> Path:
|
|
12
|
+
return Path(__file__).parent / "config"
|
|
13
|
+
|
|
14
|
+
def test_webapp(self, webapp: App) -> None:
|
|
15
|
+
assert isinstance(webapp, App)
|