cuneus 0.2.1__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.
- cuneus-0.2.1/.gitignore +18 -0
- cuneus-0.2.1/.python-version +1 -0
- cuneus-0.2.1/PKG-INFO +224 -0
- cuneus-0.2.1/README.md +183 -0
- cuneus-0.2.1/pyproject.toml +69 -0
- cuneus-0.2.1/src/cuneus/__init__.py +63 -0
- cuneus-0.2.1/src/cuneus/cli/__init__.py +394 -0
- cuneus-0.2.1/src/cuneus/cli/console.py +208 -0
- cuneus-0.2.1/src/cuneus/core/__init__.py +0 -0
- cuneus-0.2.1/src/cuneus/core/application.py +230 -0
- cuneus-0.2.1/src/cuneus/core/execptions.py +214 -0
- cuneus-0.2.1/src/cuneus/ext/__init__.py +0 -0
- cuneus-0.2.1/src/cuneus/ext/health.py +128 -0
- cuneus-0.2.1/src/cuneus/middleware/__init__.py +0 -0
- cuneus-0.2.1/src/cuneus/middleware/logging.py +165 -0
- cuneus-0.2.1/src/cuneus/py.typed +0 -0
- cuneus-0.2.1/tests/test_integration.py +25 -0
- cuneus-0.2.1/uv.lock +1496 -0
cuneus-0.2.1/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.14
|
cuneus-0.2.1/PKG-INFO
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cuneus
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: ASGI application wrapper
|
|
5
|
+
Project-URL: Homepage, https://github.com/rmyers/cuneus
|
|
6
|
+
Project-URL: Documentation, https://github.com/rmyers/cuneus#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/rmyers/cuneus
|
|
8
|
+
Author-email: Robert Myers <robert@julython.org>
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Requires-Dist: click>=8.0
|
|
11
|
+
Requires-Dist: fastapi>=0.109.0
|
|
12
|
+
Requires-Dist: pydantic-settings>=2.0
|
|
13
|
+
Requires-Dist: pydantic>=2.0
|
|
14
|
+
Requires-Dist: structlog>=24.1.0
|
|
15
|
+
Requires-Dist: svcs>=24.1.0
|
|
16
|
+
Requires-Dist: uvicorn[standard]>=0.27.0
|
|
17
|
+
Provides-Extra: all
|
|
18
|
+
Requires-Dist: alembic>=1.13.0; extra == 'all'
|
|
19
|
+
Requires-Dist: asyncpg>=0.29.0; extra == 'all'
|
|
20
|
+
Requires-Dist: redis>=5.0; extra == 'all'
|
|
21
|
+
Requires-Dist: sqlalchemy[asyncio]>=2.0; extra == 'all'
|
|
22
|
+
Provides-Extra: database
|
|
23
|
+
Requires-Dist: alembic>=1.13.0; extra == 'database'
|
|
24
|
+
Requires-Dist: asyncpg>=0.29.0; extra == 'database'
|
|
25
|
+
Requires-Dist: sqlalchemy[asyncio]>=2.0; extra == 'database'
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: alembic>=1.13.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: asgi-lifespan>=2.1.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: asyncpg>=0.29.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: httpx>=0.27; extra == 'dev'
|
|
31
|
+
Requires-Dist: mypy>=1.8; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: redis>=5.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: ruff>=0.3; extra == 'dev'
|
|
37
|
+
Requires-Dist: sqlalchemy[asyncio]>=2.0; extra == 'dev'
|
|
38
|
+
Provides-Extra: redis
|
|
39
|
+
Requires-Dist: redis>=5.0; extra == 'redis'
|
|
40
|
+
Description-Content-Type: text/markdown
|
|
41
|
+
|
|
42
|
+
# cuneus
|
|
43
|
+
|
|
44
|
+
> _The wedge stone that locks the arch together_
|
|
45
|
+
|
|
46
|
+
**cuneus** is a lightweight lifespan manager for FastAPI applications. It provides a simple pattern for composing extensions that handle startup/shutdown and service registration.
|
|
47
|
+
|
|
48
|
+
The name comes from Roman architecture: a _cuneus_ is the wedge-shaped stone in a Roman arch. Each stone is simple on its own, but together they lock under pressure to create structures that have stood for millennia—no rebar required.
|
|
49
|
+
|
|
50
|
+
## Installation
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
uv add cuneus
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
or
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install cuneus
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Quick Start
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
# app.py
|
|
66
|
+
from fastapi import FastAPI
|
|
67
|
+
from cuneus import build_lifespan, Settings
|
|
68
|
+
from cuneus.middleware.logging import LoggingMiddleware
|
|
69
|
+
|
|
70
|
+
from myapp.extensions import DatabaseExtension
|
|
71
|
+
|
|
72
|
+
settings = Settings()
|
|
73
|
+
lifespan = build_lifespan(
|
|
74
|
+
settings,
|
|
75
|
+
DatabaseExtension(settings),
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
app = FastAPI(lifespan=lifespan, title="My App", version="1.0.0")
|
|
79
|
+
|
|
80
|
+
# Add middleware directly to FastAPI
|
|
81
|
+
app.add_middleware(LoggingMiddleware)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
That's it. Extensions handle their lifecycle, FastAPI handles the rest.
|
|
85
|
+
|
|
86
|
+
## Creating Extensions
|
|
87
|
+
|
|
88
|
+
Use `BaseExtension` for simple cases:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from cuneus import BaseExtension
|
|
92
|
+
from sqlalchemy.ext.asyncio import create_async_engine, AsyncEngine
|
|
93
|
+
import svcs
|
|
94
|
+
|
|
95
|
+
class DatabaseExtension(BaseExtension):
|
|
96
|
+
def __init__(self, settings):
|
|
97
|
+
self.settings = settings
|
|
98
|
+
self.engine: AsyncEngine | None = None
|
|
99
|
+
|
|
100
|
+
async def startup(self, registry: svcs.Registry, app: FastAPI) -> dict[str, Any]:
|
|
101
|
+
self.engine = create_async_engine(self.settings.database_url)
|
|
102
|
+
|
|
103
|
+
# Register with svcs for dependency injection
|
|
104
|
+
registry.register_value(AsyncEngine, self.engine)
|
|
105
|
+
|
|
106
|
+
# Add routes
|
|
107
|
+
app.include_router(health_router, prefix="/health")
|
|
108
|
+
|
|
109
|
+
# Add exception handlers
|
|
110
|
+
app.add_exception_handler(DBError, self.handle_db_error)
|
|
111
|
+
|
|
112
|
+
# Return state (accessible via request.state.db)
|
|
113
|
+
return {"db": self.engine}
|
|
114
|
+
|
|
115
|
+
async def shutdown(self, app: FastAPI) -> None:
|
|
116
|
+
if self.engine:
|
|
117
|
+
await self.engine.dispose()
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
For full control, override `register()` directly:
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
from contextlib import asynccontextmanager
|
|
124
|
+
|
|
125
|
+
class RedisExtension(BaseExtension):
|
|
126
|
+
def __init__(self, settings):
|
|
127
|
+
self.settings = settings
|
|
128
|
+
|
|
129
|
+
@asynccontextmanager
|
|
130
|
+
async def register(self, registry: svcs.Registry, app: FastAPI):
|
|
131
|
+
redis = await aioredis.from_url(self.settings.redis_url)
|
|
132
|
+
registry.register_value(Redis, redis)
|
|
133
|
+
|
|
134
|
+
try:
|
|
135
|
+
yield {"redis": redis}
|
|
136
|
+
finally:
|
|
137
|
+
await redis.close()
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Testing
|
|
141
|
+
|
|
142
|
+
The lifespan exposes a `.registry` attribute for test overrides:
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
# test_app.py
|
|
146
|
+
from unittest.mock import Mock
|
|
147
|
+
from starlette.testclient import TestClient
|
|
148
|
+
from myapp import app, lifespan, Database
|
|
149
|
+
|
|
150
|
+
def test_db_error_handling():
|
|
151
|
+
with TestClient(app) as client:
|
|
152
|
+
# Override after app startup
|
|
153
|
+
mock_db = Mock(spec=Database)
|
|
154
|
+
mock_db.get_user.side_effect = Exception("boom")
|
|
155
|
+
lifespan.registry.register_value(Database, mock_db)
|
|
156
|
+
|
|
157
|
+
resp = client.get("/users/42")
|
|
158
|
+
assert resp.status_code == 500
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Settings
|
|
162
|
+
|
|
163
|
+
cuneus includes a base `Settings` class that loads from multiple sources:
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
from cuneus import Settings
|
|
167
|
+
|
|
168
|
+
class AppSettings(Settings):
|
|
169
|
+
database_url: str = "sqlite+aiosqlite:///./app.db"
|
|
170
|
+
redis_url: str = "redis://localhost"
|
|
171
|
+
|
|
172
|
+
model_config = SettingsConfigDict(env_prefix="APP_")
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Load priority (highest wins):
|
|
176
|
+
|
|
177
|
+
1. Environment variables
|
|
178
|
+
2. `.env` file
|
|
179
|
+
3. `pyproject.toml` under `[tool.cuneus]`
|
|
180
|
+
|
|
181
|
+
## API Reference
|
|
182
|
+
|
|
183
|
+
### `build_lifespan(settings, *extensions)`
|
|
184
|
+
|
|
185
|
+
Creates a lifespan context manager for FastAPI.
|
|
186
|
+
|
|
187
|
+
- `settings`: Your settings instance (subclass of `Settings`)
|
|
188
|
+
- `*extensions`: Extension instances to register
|
|
189
|
+
|
|
190
|
+
Returns a lifespan with a `.registry` attribute for testing.
|
|
191
|
+
|
|
192
|
+
### `BaseExtension`
|
|
193
|
+
|
|
194
|
+
Base class with `startup()` and `shutdown()` hooks:
|
|
195
|
+
|
|
196
|
+
- `startup(registry, app) -> dict[str, Any]`: Setup resources, return state
|
|
197
|
+
- `shutdown(app) -> None`: Cleanup resources
|
|
198
|
+
|
|
199
|
+
### `Extension` Protocol
|
|
200
|
+
|
|
201
|
+
For full control, implement the protocol directly:
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
def register(self, registry: svcs.Registry, app: FastAPI) -> AsyncContextManager[dict[str, Any]]
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Accessors
|
|
208
|
+
|
|
209
|
+
- `aget(request, *types)` - Async get services from svcs
|
|
210
|
+
- `get(request, *types)` - Sync get services from svcs
|
|
211
|
+
- `get_settings(request)` - Get settings from request state
|
|
212
|
+
- `get_request_id(request)` - Get request ID from request state
|
|
213
|
+
|
|
214
|
+
## Why cuneus?
|
|
215
|
+
|
|
216
|
+
- **Simple** — one function, `build_lifespan()`, does what you need
|
|
217
|
+
- **No magic** — middleware added directly to FastAPI, not hidden
|
|
218
|
+
- **Testable** — registry exposed via `lifespan.registry`
|
|
219
|
+
- **Composable** — extensions are just async context managers
|
|
220
|
+
- **Built on svcs** — proper dependency injection, not global state
|
|
221
|
+
|
|
222
|
+
## License
|
|
223
|
+
|
|
224
|
+
MIT
|
cuneus-0.2.1/README.md
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# cuneus
|
|
2
|
+
|
|
3
|
+
> _The wedge stone that locks the arch together_
|
|
4
|
+
|
|
5
|
+
**cuneus** is a lightweight lifespan manager for FastAPI applications. It provides a simple pattern for composing extensions that handle startup/shutdown and service registration.
|
|
6
|
+
|
|
7
|
+
The name comes from Roman architecture: a _cuneus_ is the wedge-shaped stone in a Roman arch. Each stone is simple on its own, but together they lock under pressure to create structures that have stood for millennia—no rebar required.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
uv add cuneus
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
or
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install cuneus
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
# app.py
|
|
25
|
+
from fastapi import FastAPI
|
|
26
|
+
from cuneus import build_lifespan, Settings
|
|
27
|
+
from cuneus.middleware.logging import LoggingMiddleware
|
|
28
|
+
|
|
29
|
+
from myapp.extensions import DatabaseExtension
|
|
30
|
+
|
|
31
|
+
settings = Settings()
|
|
32
|
+
lifespan = build_lifespan(
|
|
33
|
+
settings,
|
|
34
|
+
DatabaseExtension(settings),
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
app = FastAPI(lifespan=lifespan, title="My App", version="1.0.0")
|
|
38
|
+
|
|
39
|
+
# Add middleware directly to FastAPI
|
|
40
|
+
app.add_middleware(LoggingMiddleware)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
That's it. Extensions handle their lifecycle, FastAPI handles the rest.
|
|
44
|
+
|
|
45
|
+
## Creating Extensions
|
|
46
|
+
|
|
47
|
+
Use `BaseExtension` for simple cases:
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from cuneus import BaseExtension
|
|
51
|
+
from sqlalchemy.ext.asyncio import create_async_engine, AsyncEngine
|
|
52
|
+
import svcs
|
|
53
|
+
|
|
54
|
+
class DatabaseExtension(BaseExtension):
|
|
55
|
+
def __init__(self, settings):
|
|
56
|
+
self.settings = settings
|
|
57
|
+
self.engine: AsyncEngine | None = None
|
|
58
|
+
|
|
59
|
+
async def startup(self, registry: svcs.Registry, app: FastAPI) -> dict[str, Any]:
|
|
60
|
+
self.engine = create_async_engine(self.settings.database_url)
|
|
61
|
+
|
|
62
|
+
# Register with svcs for dependency injection
|
|
63
|
+
registry.register_value(AsyncEngine, self.engine)
|
|
64
|
+
|
|
65
|
+
# Add routes
|
|
66
|
+
app.include_router(health_router, prefix="/health")
|
|
67
|
+
|
|
68
|
+
# Add exception handlers
|
|
69
|
+
app.add_exception_handler(DBError, self.handle_db_error)
|
|
70
|
+
|
|
71
|
+
# Return state (accessible via request.state.db)
|
|
72
|
+
return {"db": self.engine}
|
|
73
|
+
|
|
74
|
+
async def shutdown(self, app: FastAPI) -> None:
|
|
75
|
+
if self.engine:
|
|
76
|
+
await self.engine.dispose()
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
For full control, override `register()` directly:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from contextlib import asynccontextmanager
|
|
83
|
+
|
|
84
|
+
class RedisExtension(BaseExtension):
|
|
85
|
+
def __init__(self, settings):
|
|
86
|
+
self.settings = settings
|
|
87
|
+
|
|
88
|
+
@asynccontextmanager
|
|
89
|
+
async def register(self, registry: svcs.Registry, app: FastAPI):
|
|
90
|
+
redis = await aioredis.from_url(self.settings.redis_url)
|
|
91
|
+
registry.register_value(Redis, redis)
|
|
92
|
+
|
|
93
|
+
try:
|
|
94
|
+
yield {"redis": redis}
|
|
95
|
+
finally:
|
|
96
|
+
await redis.close()
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Testing
|
|
100
|
+
|
|
101
|
+
The lifespan exposes a `.registry` attribute for test overrides:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
# test_app.py
|
|
105
|
+
from unittest.mock import Mock
|
|
106
|
+
from starlette.testclient import TestClient
|
|
107
|
+
from myapp import app, lifespan, Database
|
|
108
|
+
|
|
109
|
+
def test_db_error_handling():
|
|
110
|
+
with TestClient(app) as client:
|
|
111
|
+
# Override after app startup
|
|
112
|
+
mock_db = Mock(spec=Database)
|
|
113
|
+
mock_db.get_user.side_effect = Exception("boom")
|
|
114
|
+
lifespan.registry.register_value(Database, mock_db)
|
|
115
|
+
|
|
116
|
+
resp = client.get("/users/42")
|
|
117
|
+
assert resp.status_code == 500
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Settings
|
|
121
|
+
|
|
122
|
+
cuneus includes a base `Settings` class that loads from multiple sources:
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
from cuneus import Settings
|
|
126
|
+
|
|
127
|
+
class AppSettings(Settings):
|
|
128
|
+
database_url: str = "sqlite+aiosqlite:///./app.db"
|
|
129
|
+
redis_url: str = "redis://localhost"
|
|
130
|
+
|
|
131
|
+
model_config = SettingsConfigDict(env_prefix="APP_")
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Load priority (highest wins):
|
|
135
|
+
|
|
136
|
+
1. Environment variables
|
|
137
|
+
2. `.env` file
|
|
138
|
+
3. `pyproject.toml` under `[tool.cuneus]`
|
|
139
|
+
|
|
140
|
+
## API Reference
|
|
141
|
+
|
|
142
|
+
### `build_lifespan(settings, *extensions)`
|
|
143
|
+
|
|
144
|
+
Creates a lifespan context manager for FastAPI.
|
|
145
|
+
|
|
146
|
+
- `settings`: Your settings instance (subclass of `Settings`)
|
|
147
|
+
- `*extensions`: Extension instances to register
|
|
148
|
+
|
|
149
|
+
Returns a lifespan with a `.registry` attribute for testing.
|
|
150
|
+
|
|
151
|
+
### `BaseExtension`
|
|
152
|
+
|
|
153
|
+
Base class with `startup()` and `shutdown()` hooks:
|
|
154
|
+
|
|
155
|
+
- `startup(registry, app) -> dict[str, Any]`: Setup resources, return state
|
|
156
|
+
- `shutdown(app) -> None`: Cleanup resources
|
|
157
|
+
|
|
158
|
+
### `Extension` Protocol
|
|
159
|
+
|
|
160
|
+
For full control, implement the protocol directly:
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
def register(self, registry: svcs.Registry, app: FastAPI) -> AsyncContextManager[dict[str, Any]]
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Accessors
|
|
167
|
+
|
|
168
|
+
- `aget(request, *types)` - Async get services from svcs
|
|
169
|
+
- `get(request, *types)` - Sync get services from svcs
|
|
170
|
+
- `get_settings(request)` - Get settings from request state
|
|
171
|
+
- `get_request_id(request)` - Get request ID from request state
|
|
172
|
+
|
|
173
|
+
## Why cuneus?
|
|
174
|
+
|
|
175
|
+
- **Simple** — one function, `build_lifespan()`, does what you need
|
|
176
|
+
- **No magic** — middleware added directly to FastAPI, not hidden
|
|
177
|
+
- **Testable** — registry exposed via `lifespan.registry`
|
|
178
|
+
- **Composable** — extensions are just async context managers
|
|
179
|
+
- **Built on svcs** — proper dependency injection, not global state
|
|
180
|
+
|
|
181
|
+
## License
|
|
182
|
+
|
|
183
|
+
MIT
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "cuneus"
|
|
3
|
+
version = "0.2.1"
|
|
4
|
+
description = "ASGI application wrapper"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [{ name = "Robert Myers", email = "robert@julython.org" }]
|
|
7
|
+
requires-python = ">=3.10"
|
|
8
|
+
|
|
9
|
+
dependencies = [
|
|
10
|
+
"fastapi>=0.109.0",
|
|
11
|
+
"pydantic>=2.0",
|
|
12
|
+
"pydantic-settings>=2.0",
|
|
13
|
+
"click>=8.0",
|
|
14
|
+
"uvicorn[standard]>=0.27.0",
|
|
15
|
+
# Core dependencies we build on
|
|
16
|
+
"svcs>=24.1.0",
|
|
17
|
+
"structlog>=24.1.0",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.optional-dependencies]
|
|
21
|
+
database = ["sqlalchemy[asyncio]>=2.0", "asyncpg>=0.29.0", "alembic>=1.13.0"]
|
|
22
|
+
redis = ["redis>=5.0"]
|
|
23
|
+
all = ["cuneus[database,redis]"]
|
|
24
|
+
dev = [
|
|
25
|
+
"cuneus[all]",
|
|
26
|
+
"pytest>=8.0",
|
|
27
|
+
"pytest-asyncio>=0.23",
|
|
28
|
+
"pytest-cov>=4.0",
|
|
29
|
+
"httpx>=0.27",
|
|
30
|
+
"asgi-lifespan>=2.1.0",
|
|
31
|
+
"ruff>=0.3",
|
|
32
|
+
"mypy>=1.8",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[project.scripts]
|
|
36
|
+
cuneus = "cuneus.cli:main"
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Homepage = "https://github.com/rmyers/cuneus"
|
|
40
|
+
Documentation = "https://github.com/rmyers/cuneus#readme"
|
|
41
|
+
Repository = "https://github.com/rmyers/cuneus"
|
|
42
|
+
|
|
43
|
+
[build-system]
|
|
44
|
+
requires = ["hatchling"]
|
|
45
|
+
build-backend = "hatchling.build"
|
|
46
|
+
|
|
47
|
+
[tool.hatch.build.targets.wheel]
|
|
48
|
+
packages = ["src/cuneus"]
|
|
49
|
+
|
|
50
|
+
[tool.ruff]
|
|
51
|
+
target-version = "py311"
|
|
52
|
+
line-length = 100
|
|
53
|
+
|
|
54
|
+
[tool.ruff.lint]
|
|
55
|
+
select = ["E", "F", "I", "UP", "B", "SIM", "ASYNC"]
|
|
56
|
+
|
|
57
|
+
[tool.mypy]
|
|
58
|
+
python_version = "3.11"
|
|
59
|
+
strict = true
|
|
60
|
+
warn_return_any = true
|
|
61
|
+
warn_unused_ignores = true
|
|
62
|
+
|
|
63
|
+
[tool.pytest.ini_options]
|
|
64
|
+
asyncio_mode = "auto"
|
|
65
|
+
testpaths = ["tests"]
|
|
66
|
+
|
|
67
|
+
[tool.coverage.run]
|
|
68
|
+
source = ["src/cuneus"]
|
|
69
|
+
branch = true
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""
|
|
2
|
+
qtip - A wrapper for FastAPI applications, like the artist.
|
|
3
|
+
|
|
4
|
+
Example:
|
|
5
|
+
from qtip import Application, Settings
|
|
6
|
+
from qtip.ext.database import DatabaseExtension
|
|
7
|
+
|
|
8
|
+
class AppSettings(Settings):
|
|
9
|
+
database_url: str
|
|
10
|
+
|
|
11
|
+
settings = AppSettings()
|
|
12
|
+
app = Application(settings)
|
|
13
|
+
app.add_extension(DatabaseExtension(settings))
|
|
14
|
+
|
|
15
|
+
fastapi_app = app.build()
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from cuneus.core.application import (
|
|
19
|
+
BaseExtension,
|
|
20
|
+
Extension,
|
|
21
|
+
Settings,
|
|
22
|
+
build_lifespan,
|
|
23
|
+
get_settings,
|
|
24
|
+
load_pyproject_config,
|
|
25
|
+
)
|
|
26
|
+
from cuneus.core.execptions import (
|
|
27
|
+
AppException,
|
|
28
|
+
BadRequest,
|
|
29
|
+
Unauthorized,
|
|
30
|
+
Forbidden,
|
|
31
|
+
NotFound,
|
|
32
|
+
Conflict,
|
|
33
|
+
RateLimited,
|
|
34
|
+
ServiceUnavailable,
|
|
35
|
+
DatabaseError,
|
|
36
|
+
RedisError,
|
|
37
|
+
ExternalServiceError,
|
|
38
|
+
ExceptionExtension,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
__version__ = "0.2.1"
|
|
42
|
+
__all__ = [
|
|
43
|
+
# Core
|
|
44
|
+
"BaseExtension",
|
|
45
|
+
"Extension",
|
|
46
|
+
"Settings",
|
|
47
|
+
"build_lifespan",
|
|
48
|
+
"get_settings",
|
|
49
|
+
"load_pyproject_config",
|
|
50
|
+
# Exceptions
|
|
51
|
+
"AppException",
|
|
52
|
+
"BadRequest",
|
|
53
|
+
"Unauthorized",
|
|
54
|
+
"Forbidden",
|
|
55
|
+
"NotFound",
|
|
56
|
+
"Conflict",
|
|
57
|
+
"RateLimited",
|
|
58
|
+
"ServiceUnavailable",
|
|
59
|
+
"DatabaseError",
|
|
60
|
+
"RedisError",
|
|
61
|
+
"ExternalServiceError",
|
|
62
|
+
"ExceptionExtension",
|
|
63
|
+
]
|