austial 0.1.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.
- austial-0.1.1/.claude/settings.local.json +7 -0
- austial-0.1.1/.gitignore +11 -0
- austial-0.1.1/.pre-commit-config.yaml +36 -0
- austial-0.1.1/.vscode/settings.json +3 -0
- austial-0.1.1/LICENSE +21 -0
- austial-0.1.1/PKG-INFO +241 -0
- austial-0.1.1/README.md +211 -0
- austial-0.1.1/assets/apple-icon.png +0 -0
- austial-0.1.1/assets/icon.svg +9 -0
- austial-0.1.1/assets/logo-dark.svg +137 -0
- austial-0.1.1/assets/logo.svg +137 -0
- austial-0.1.1/austial/__init__.py +136 -0
- austial-0.1.1/austial/cli/__init__.py +0 -0
- austial-0.1.1/austial/cli/generators/__init__.py +13 -0
- austial-0.1.1/austial/cli/generators/app_module_patcher.py +61 -0
- austial-0.1.1/austial/cli/generators/base.py +61 -0
- austial-0.1.1/austial/cli/generators/controller_gen.py +21 -0
- austial-0.1.1/austial/cli/generators/module_gen.py +30 -0
- austial-0.1.1/austial/cli/generators/new_project.py +67 -0
- austial-0.1.1/austial/cli/generators/resource_gen.py +39 -0
- austial-0.1.1/austial/cli/generators/service_gen.py +21 -0
- austial-0.1.1/austial/cli/main.py +133 -0
- austial-0.1.1/austial/cli/naming.py +33 -0
- austial-0.1.1/austial/cli/templates/controller/controller.py.j2 +8 -0
- austial-0.1.1/austial/cli/templates/module/module.py.j2 +6 -0
- austial-0.1.1/austial/cli/templates/project/app_controller.py.j2 +13 -0
- austial-0.1.1/austial/cli/templates/project/app_module.py.j2 +19 -0
- austial-0.1.1/austial/cli/templates/project/app_service.py.j2 +7 -0
- austial-0.1.1/austial/cli/templates/project/env_example.j2 +2 -0
- austial-0.1.1/austial/cli/templates/project/gitignore.j2 +11 -0
- austial-0.1.1/austial/cli/templates/project/main.py.j2 +37 -0
- austial-0.1.1/austial/cli/templates/project/modules/health/guards/api_key_guard.py.j2 +13 -0
- austial-0.1.1/austial/cli/templates/project/modules/health/health_controller.py.j2 +20 -0
- austial-0.1.1/austial/cli/templates/project/modules/health/health_dto.py.j2 +10 -0
- austial-0.1.1/austial/cli/templates/project/modules/health/health_module.py.j2 +10 -0
- austial-0.1.1/austial/cli/templates/project/modules/health/health_service.py.j2 +19 -0
- austial-0.1.1/austial/cli/templates/project/pre_commit_config.yaml.j2 +36 -0
- austial-0.1.1/austial/cli/templates/project/pyproject.toml.j2 +46 -0
- austial-0.1.1/austial/cli/templates/project/readme.j2 +68 -0
- austial-0.1.1/austial/cli/templates/project/tests/e2e/app_e2e_spec.py.j2 +38 -0
- austial-0.1.1/austial/cli/templates/project/tests/unit/health_service_spec.py.j2 +35 -0
- austial-0.1.1/austial/cli/templates/resource/controller.py.j2 +33 -0
- austial-0.1.1/austial/cli/templates/resource/create_dto.py.j2 +5 -0
- austial-0.1.1/austial/cli/templates/resource/entity.py.j2 +6 -0
- austial-0.1.1/austial/cli/templates/resource/module.py.j2 +9 -0
- austial-0.1.1/austial/cli/templates/resource/service.py.j2 +39 -0
- austial-0.1.1/austial/cli/templates/resource/update_dto.py.j2 +5 -0
- austial-0.1.1/austial/cli/templates/service/service.py.j2 +6 -0
- austial-0.1.1/austial/common/__init__.py +106 -0
- austial-0.1.1/austial/common/decorators/__init__.py +48 -0
- austial-0.1.1/austial/common/decorators/controllers.py +109 -0
- austial-0.1.1/austial/common/decorators/filters.py +31 -0
- austial-0.1.1/austial/common/decorators/guards.py +20 -0
- austial-0.1.1/austial/common/decorators/injectable.py +68 -0
- austial-0.1.1/austial/common/decorators/interceptors.py +20 -0
- austial-0.1.1/austial/common/decorators/modules.py +65 -0
- austial-0.1.1/austial/common/decorators/params.py +50 -0
- austial-0.1.1/austial/common/decorators/pipes.py +20 -0
- austial-0.1.1/austial/common/enums.py +15 -0
- austial-0.1.1/austial/common/exceptions/__init__.py +29 -0
- austial-0.1.1/austial/common/exceptions/http_exceptions.py +109 -0
- austial-0.1.1/austial/common/filters/__init__.py +4 -0
- austial-0.1.1/austial/common/filters/all_exceptions_filter.py +31 -0
- austial-0.1.1/austial/common/filters/base.py +28 -0
- austial-0.1.1/austial/common/guards/__init__.py +3 -0
- austial-0.1.1/austial/common/guards/base.py +39 -0
- austial-0.1.1/austial/common/interceptors/__init__.py +4 -0
- austial-0.1.1/austial/common/interceptors/base.py +30 -0
- austial-0.1.1/austial/common/interceptors/transform_interceptor.py +19 -0
- austial-0.1.1/austial/common/logger.py +56 -0
- austial-0.1.1/austial/common/middleware/__init__.py +4 -0
- austial-0.1.1/austial/common/middleware/base.py +60 -0
- austial-0.1.1/austial/common/middleware/logging_middleware.py +24 -0
- austial-0.1.1/austial/common/pipes/__init__.py +3 -0
- austial-0.1.1/austial/common/pipes/validation_pipe.py +51 -0
- austial-0.1.1/austial/config/__init__.py +4 -0
- austial-0.1.1/austial/config/config_module.py +31 -0
- austial-0.1.1/austial/config/config_service.py +22 -0
- austial-0.1.1/austial/core/__init__.py +19 -0
- austial-0.1.1/austial/core/app.py +155 -0
- austial-0.1.1/austial/core/container.py +161 -0
- austial-0.1.1/austial/core/dynamic_module.py +22 -0
- austial-0.1.1/austial/core/exception_dispatch.py +23 -0
- austial-0.1.1/austial/core/factory.py +43 -0
- austial-0.1.1/austial/core/metadata.py +65 -0
- austial-0.1.1/austial/core/module_scanner.py +62 -0
- austial-0.1.1/austial/core/reflector.py +38 -0
- austial-0.1.1/austial/core/router_builder.py +184 -0
- austial-0.1.1/austial/database/__init__.py +3 -0
- austial-0.1.1/austial/database/database_module.py +61 -0
- austial-0.1.1/austial/terminus/__init__.py +14 -0
- austial-0.1.1/austial/terminus/database_health_indicator.py +26 -0
- austial-0.1.1/austial/terminus/health_check_service.py +42 -0
- austial-0.1.1/austial/terminus/health_indicator.py +28 -0
- austial-0.1.1/austial/terminus/memory_health_indicator.py +20 -0
- austial-0.1.1/austial/testing/__init__.py +3 -0
- austial-0.1.1/austial/testing/testing_module.py +64 -0
- austial-0.1.1/pyproject.toml +68 -0
- austial-0.1.1/tests/__init__.py +0 -0
- austial-0.1.1/tests/e2e/__init__.py +0 -0
- austial-0.1.1/tests/e2e/request_lifecycle_spec.py +125 -0
- austial-0.1.1/tests/unit/__init__.py +0 -0
- austial-0.1.1/tests/unit/health_check_service_spec.py +48 -0
- austial-0.1.1/uv.lock +1534 -0
austial-0.1.1/.gitignore
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v5.0.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: trailing-whitespace
|
|
6
|
+
- id: end-of-file-fixer
|
|
7
|
+
- id: check-yaml
|
|
8
|
+
- id: check-toml
|
|
9
|
+
- id: check-merge-conflict
|
|
10
|
+
- id: check-added-large-files
|
|
11
|
+
- id: mixed-line-ending
|
|
12
|
+
- id: debug-statements
|
|
13
|
+
|
|
14
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
15
|
+
rev: v0.15.21
|
|
16
|
+
hooks:
|
|
17
|
+
- id: ruff
|
|
18
|
+
args: [--fix]
|
|
19
|
+
- id: ruff-format
|
|
20
|
+
|
|
21
|
+
- repo: local
|
|
22
|
+
hooks:
|
|
23
|
+
- id: mypy
|
|
24
|
+
name: mypy
|
|
25
|
+
entry: uv run mypy austial tests
|
|
26
|
+
language: system
|
|
27
|
+
pass_filenames: false
|
|
28
|
+
types: [python]
|
|
29
|
+
|
|
30
|
+
- id: pytest
|
|
31
|
+
name: pytest
|
|
32
|
+
entry: uv run pytest -q
|
|
33
|
+
language: system
|
|
34
|
+
pass_filenames: false
|
|
35
|
+
types: [python]
|
|
36
|
+
stages: [pre-push]
|
austial-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aayush Solanki
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
austial-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: austial
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: A NestJS-inspired, batteries-included web framework for Python, built on top of FastAPI.
|
|
5
|
+
Project-URL: Homepage, https://github.com/austial/austial-py
|
|
6
|
+
Project-URL: Repository, https://github.com/austial/austial-py
|
|
7
|
+
Project-URL: Issues, https://github.com/austial/austial-py/issues
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Python: >=3.11
|
|
11
|
+
Requires-Dist: aiosqlite>=0.20.0
|
|
12
|
+
Requires-Dist: fastapi>=0.115.0
|
|
13
|
+
Requires-Dist: httpx>=0.27.0
|
|
14
|
+
Requires-Dist: jinja2>=3.1.0
|
|
15
|
+
Requires-Dist: pydantic-settings>=2.4.0
|
|
16
|
+
Requires-Dist: pydantic>=2.7.0
|
|
17
|
+
Requires-Dist: python-multipart>=0.0.9
|
|
18
|
+
Requires-Dist: rich>=13.7.0
|
|
19
|
+
Requires-Dist: sqlalchemy>=2.0.30
|
|
20
|
+
Requires-Dist: typer>=0.12.0
|
|
21
|
+
Requires-Dist: uvicorn[standard]>=0.30.0
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: mypy>=1.11.0; extra == 'dev'
|
|
24
|
+
Requires-Dist: pre-commit>=3.7.0; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest>=8.2.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: ruff>=0.6.0; extra == 'dev'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
<p align="center">
|
|
32
|
+
<img src="assets/icon.svg" alt="Austial icon" width="56">
|
|
33
|
+
</p>
|
|
34
|
+
|
|
35
|
+
<p align="center">
|
|
36
|
+
<picture>
|
|
37
|
+
<source media="(prefers-color-scheme: dark)" srcset="assets/logo-dark.svg">
|
|
38
|
+
<source media="(prefers-color-scheme: light)" srcset="assets/logo.svg">
|
|
39
|
+
<img src="assets/logo.svg" alt="Austial" width="380">
|
|
40
|
+
</picture>
|
|
41
|
+
</p>
|
|
42
|
+
|
|
43
|
+
<p align="center">
|
|
44
|
+
<b>A NestJS-style, batteries-included web framework for Python, built on top of FastAPI.</b>
|
|
45
|
+
</p>
|
|
46
|
+
|
|
47
|
+
<p align="center">
|
|
48
|
+
<a href="https://pypi.org/project/austial/"><img src="https://img.shields.io/pypi/v/austial?label=pypi" alt="PyPI version"></a>
|
|
49
|
+
<a href="https://github.com/austial/austial-py/tags"><img src="https://img.shields.io/github/v/tag/austial/austial-py?label=tag&sort=semver" alt="Latest tag"></a>
|
|
50
|
+
<a href="https://github.com/austial/austial-py/commits/main"><img src="https://img.shields.io/github/last-commit/austial/austial-py" alt="Last commit"></a>
|
|
51
|
+
<a href="https://github.com/austial/austial-py/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="MIT License"></a>
|
|
52
|
+
<a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.11%2B-blue" alt="Python 3.11+"></a>
|
|
53
|
+
<a href="https://fastapi.tiangolo.com/"><img src="https://img.shields.io/badge/built%20with-FastAPI-009688?logo=fastapi&logoColor=white" alt="Built with FastAPI"></a>
|
|
54
|
+
<a href="https://github.com/astral-sh/uv"><img src="https://img.shields.io/badge/managed%20with-uv-DE5FE9" alt="Managed with uv"></a>
|
|
55
|
+
</p>
|
|
56
|
+
|
|
57
|
+
# Austial
|
|
58
|
+
|
|
59
|
+
Austial gives Python the exact developer experience of [NestJS](https://nestjs.com/):
|
|
60
|
+
decorator-driven modules/controllers/providers, real constructor-based
|
|
61
|
+
dependency injection, the full request lifecycle (guards, pipes,
|
|
62
|
+
interceptors, exception filters, middleware), a config layer, a database
|
|
63
|
+
layer, Terminus-style health checks, a testing module -- and a CLI so you can
|
|
64
|
+
scaffold new projects and artifacts the same way `nest new`/`nest generate` do.
|
|
65
|
+
|
|
66
|
+
This repository (`austial-py`) is the **framework itself** -- an installable
|
|
67
|
+
library, no bundled sample app. To see it in action, scaffold a new project
|
|
68
|
+
with the CLI (see [Quickstart](#quickstart) below); that generated project is
|
|
69
|
+
the "hello world" app, kept separate from the framework's own source tree.
|
|
70
|
+
|
|
71
|
+
## Why
|
|
72
|
+
|
|
73
|
+
FastAPI is a fantastic ASGI toolkit, but it doesn't prescribe an application
|
|
74
|
+
architecture. Austial is opinionated on top of FastAPI the way Nest is
|
|
75
|
+
opinionated on top of Express/Fastify: modules own controllers and providers,
|
|
76
|
+
providers get dependency-injected by type, and the request pipeline
|
|
77
|
+
(guard → pipe → interceptor → handler → filter) is a first-class concept
|
|
78
|
+
instead of something you hand-roll with dependencies everywhere.
|
|
79
|
+
|
|
80
|
+
## Architecture at a glance
|
|
81
|
+
|
|
82
|
+
| NestJS | Austial |
|
|
83
|
+
|---|---|
|
|
84
|
+
| `@Module()` | `@Module(imports=, controllers=, providers=, exports=)` |
|
|
85
|
+
| `@Controller()` / `@Get()` etc. | `@Controller(prefix)` / `@Get/@Post/@Put/@Patch/@Delete/@Options/@Head` |
|
|
86
|
+
| `@Injectable()` | `@Injectable(scope=Scope.DEFAULT \| Scope.TRANSIENT)` |
|
|
87
|
+
| Constructor DI | Container resolves `__init__` type hints recursively, singleton by default |
|
|
88
|
+
| `NestFactory.create(AppModule)` | `AustialFactory.create(AppModule)` -> `AustialApplication` |
|
|
89
|
+
| `app.listen(3000)` | `await app.listen(8000)` |
|
|
90
|
+
| `CanActivate` / `@UseGuards` | `CanActivate` ABC + `ExecutionContext`, `@UseGuards(...)` |
|
|
91
|
+
| `NestInterceptor` / `@UseInterceptors` | `NestInterceptor` ABC (`intercept(context, call_next)`) + `@UseInterceptors(...)` |
|
|
92
|
+
| `PipeTransform` / `ValidationPipe` | `PipeTransform` ABC + `ValidationPipe` |
|
|
93
|
+
| `ExceptionFilter` / `@Catch` | `ExceptionFilter` ABC + `@Catch(...)`, default `AllExceptionsFilter` |
|
|
94
|
+
| `NestMiddleware`, `MiddlewareConsumer` | `NestMiddleware` ABC, `configure(consumer)` on modules |
|
|
95
|
+
| `ConfigModule.forRoot()` | `ConfigModule.for_root(env_file=".env")` / `ConfigService` |
|
|
96
|
+
| `TypeOrmModule.forRootAsync` | `DatabaseModule.for_root_async(use_factory=, inject=)` (SQLAlchemy async) |
|
|
97
|
+
| `@nestjs/terminus` | `austial.terminus.HealthCheckService` + `MemoryHealthIndicator`, `DatabaseHealthIndicator` |
|
|
98
|
+
| `@nestjs/testing` | `austial.testing.Test.create_testing_module(...).compile()` |
|
|
99
|
+
| `nest new` / `nest generate` | `austial new <name>` / `austial generate\|g module\|controller\|service\|resource <name>` |
|
|
100
|
+
|
|
101
|
+
### A note on file names
|
|
102
|
+
|
|
103
|
+
Nest names files with dots (`health.controller.ts`) because Node resolves
|
|
104
|
+
modules by explicit relative path strings. Python's `import a.b` instead
|
|
105
|
+
resolves `a` as a package and `b` as a submodule -- a literal file
|
|
106
|
+
`health.controller.py` isn't importable via a normal `import` statement. So
|
|
107
|
+
Austial uses underscore suffixes instead: `health_controller.py`,
|
|
108
|
+
`health_service.py`, `health_module.py`, `health_dto.py`. The directory
|
|
109
|
+
layout still mirrors Nest 1:1 -- one folder per feature under
|
|
110
|
+
`src/modules/`.
|
|
111
|
+
|
|
112
|
+
## Quickstart
|
|
113
|
+
|
|
114
|
+
Austial is published on PyPI -- install the CLI and scaffold a new project:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
uv tool install austial # or: pipx install austial / pip install austial
|
|
118
|
+
austial new my-app # scaffold a new project
|
|
119
|
+
cd my-app
|
|
120
|
+
uv sync
|
|
121
|
+
cp .env.example .env
|
|
122
|
+
uv run austial serve # http://localhost:8000, auto-reload
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`austial new` scaffolds a small health-check "hello world" app --
|
|
126
|
+
`GET /`, `GET /health`, `GET /health/protected`, `/docs` (Swagger UI) -- see
|
|
127
|
+
[Using the CLI](#using-the-cli) below for the full generator reference.
|
|
128
|
+
|
|
129
|
+
## Developing Austial itself
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
git clone https://github.com/austial/austial-py
|
|
133
|
+
cd austial-py
|
|
134
|
+
uv sync --all-extras
|
|
135
|
+
uv run pytest
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Linting, type-checking & pre-commit hooks
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
uv run ruff check . # lint
|
|
142
|
+
uv run ruff format . # format
|
|
143
|
+
uv run mypy austial tests # type-check
|
|
144
|
+
|
|
145
|
+
uv run pre-commit install --hook-type pre-commit --hook-type pre-push
|
|
146
|
+
uv run pre-commit run --all-files # run every hook once, on demand
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Once installed, `git commit` runs formatting/linting/type-checks automatically,
|
|
150
|
+
and `git push` also runs the test suite -- mirroring a typical Nest project's
|
|
151
|
+
husky + lint-staged setup. Projects scaffolded with `austial new` get the same
|
|
152
|
+
`.pre-commit-config.yaml` out of the box.
|
|
153
|
+
|
|
154
|
+
## Using the CLI
|
|
155
|
+
|
|
156
|
+
`austial new` scaffolds a brand-new project exactly like `nest new`:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
uv run austial new my-app
|
|
160
|
+
cd my-app
|
|
161
|
+
uv sync
|
|
162
|
+
cp .env.example .env
|
|
163
|
+
uv run austial serve
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
> New projects depend on the published `austial` package from PyPI by
|
|
167
|
+
> default. If you're developing the framework itself, point new projects at
|
|
168
|
+
> your local checkout instead with `--link`:
|
|
169
|
+
> `austial new my-app --link /path/to/austial-py` -- this adds an editable
|
|
170
|
+
> `[tool.uv.sources]` entry so `uv sync` resolves `austial` from your local
|
|
171
|
+
> checkout rather than PyPI.
|
|
172
|
+
|
|
173
|
+
`austial generate` (alias `austial g`) adds artifacts to an existing project,
|
|
174
|
+
patching `src/app_module.py`'s imports/`imports=[...]` array automatically --
|
|
175
|
+
just like `nest g`:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
uv run austial generate module cats # src/modules/cats/cats_module.py
|
|
179
|
+
uv run austial generate controller cats # src/modules/cats/cats_controller.py
|
|
180
|
+
uv run austial generate service cats # src/modules/cats/cats_service.py
|
|
181
|
+
uv run austial generate resource cats # module + full CRUD controller/service/dto/entity
|
|
182
|
+
uv run austial g resource cats # same as above, short alias
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Repository layout
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
austial-py/
|
|
189
|
+
├── pyproject.toml # package "austial", console-script entry point
|
|
190
|
+
├── LICENSE # MIT
|
|
191
|
+
├── assets/ # logo.svg (+ logo-dark.svg for dark mode) / icon.svg / apple-icon.png
|
|
192
|
+
├── austial/ # the framework (installable library) -- this *is* the package
|
|
193
|
+
│ ├── common/ # decorators, guards, interceptors, pipes, filters,
|
|
194
|
+
│ │ # middleware, exceptions, logger
|
|
195
|
+
│ ├── core/ # metadata/reflector, DI container, router builder,
|
|
196
|
+
│ │ # AustialFactory / AustialApplication
|
|
197
|
+
│ ├── config/ # ConfigModule / ConfigService
|
|
198
|
+
│ ├── database/ # DatabaseModule (SQLAlchemy async)
|
|
199
|
+
│ ├── terminus/ # HealthCheckService + health indicators
|
|
200
|
+
│ ├── testing/ # Test.create_testing_module(...)
|
|
201
|
+
│ └── cli/ # `austial new` / `generate` / `serve` + templates
|
|
202
|
+
└── tests/ # tests for the framework itself -- no sample app needed;
|
|
203
|
+
├── unit/ # test modules/controllers are defined inline per test file
|
|
204
|
+
└── e2e/
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
There's deliberately no `src/` here -- that's what `austial new` generates for
|
|
208
|
+
*your* project, kept out of the framework's own repo. Test files use Nest's
|
|
209
|
+
`*.spec.ts` naming convention (`*_spec.py` here); `pyproject.toml`'s
|
|
210
|
+
`[tool.pytest.ini_options]` is configured to discover them.
|
|
211
|
+
|
|
212
|
+
## Request lifecycle
|
|
213
|
+
|
|
214
|
+
Every route runs through the same pipeline Nest uses:
|
|
215
|
+
|
|
216
|
+
```
|
|
217
|
+
guards -> pipes -> interceptors (wrapping) -> handler -> exception filters
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
from austial import Controller, Get, UseGuards, UseInterceptors
|
|
222
|
+
from austial.common.interceptors import TransformInterceptor
|
|
223
|
+
|
|
224
|
+
from .api_key_guard import ApiKeyGuard
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
@Controller("cats")
|
|
228
|
+
class CatsController:
|
|
229
|
+
@Get(":id")
|
|
230
|
+
@UseGuards(ApiKeyGuard)
|
|
231
|
+
@UseInterceptors(TransformInterceptor())
|
|
232
|
+
async def find_one(self, id: int):
|
|
233
|
+
...
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Note path params use Nest's `:id` syntax (translated to FastAPI's `{id}`
|
|
237
|
+
under the hood), so route strings read exactly like their Nest counterparts.
|
|
238
|
+
|
|
239
|
+
## License
|
|
240
|
+
|
|
241
|
+
[MIT](./LICENSE)
|
austial-0.1.1/README.md
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/icon.svg" alt="Austial icon" width="56">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<picture>
|
|
7
|
+
<source media="(prefers-color-scheme: dark)" srcset="assets/logo-dark.svg">
|
|
8
|
+
<source media="(prefers-color-scheme: light)" srcset="assets/logo.svg">
|
|
9
|
+
<img src="assets/logo.svg" alt="Austial" width="380">
|
|
10
|
+
</picture>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
<b>A NestJS-style, batteries-included web framework for Python, built on top of FastAPI.</b>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
<p align="center">
|
|
18
|
+
<a href="https://pypi.org/project/austial/"><img src="https://img.shields.io/pypi/v/austial?label=pypi" alt="PyPI version"></a>
|
|
19
|
+
<a href="https://github.com/austial/austial-py/tags"><img src="https://img.shields.io/github/v/tag/austial/austial-py?label=tag&sort=semver" alt="Latest tag"></a>
|
|
20
|
+
<a href="https://github.com/austial/austial-py/commits/main"><img src="https://img.shields.io/github/last-commit/austial/austial-py" alt="Last commit"></a>
|
|
21
|
+
<a href="https://github.com/austial/austial-py/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="MIT License"></a>
|
|
22
|
+
<a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.11%2B-blue" alt="Python 3.11+"></a>
|
|
23
|
+
<a href="https://fastapi.tiangolo.com/"><img src="https://img.shields.io/badge/built%20with-FastAPI-009688?logo=fastapi&logoColor=white" alt="Built with FastAPI"></a>
|
|
24
|
+
<a href="https://github.com/astral-sh/uv"><img src="https://img.shields.io/badge/managed%20with-uv-DE5FE9" alt="Managed with uv"></a>
|
|
25
|
+
</p>
|
|
26
|
+
|
|
27
|
+
# Austial
|
|
28
|
+
|
|
29
|
+
Austial gives Python the exact developer experience of [NestJS](https://nestjs.com/):
|
|
30
|
+
decorator-driven modules/controllers/providers, real constructor-based
|
|
31
|
+
dependency injection, the full request lifecycle (guards, pipes,
|
|
32
|
+
interceptors, exception filters, middleware), a config layer, a database
|
|
33
|
+
layer, Terminus-style health checks, a testing module -- and a CLI so you can
|
|
34
|
+
scaffold new projects and artifacts the same way `nest new`/`nest generate` do.
|
|
35
|
+
|
|
36
|
+
This repository (`austial-py`) is the **framework itself** -- an installable
|
|
37
|
+
library, no bundled sample app. To see it in action, scaffold a new project
|
|
38
|
+
with the CLI (see [Quickstart](#quickstart) below); that generated project is
|
|
39
|
+
the "hello world" app, kept separate from the framework's own source tree.
|
|
40
|
+
|
|
41
|
+
## Why
|
|
42
|
+
|
|
43
|
+
FastAPI is a fantastic ASGI toolkit, but it doesn't prescribe an application
|
|
44
|
+
architecture. Austial is opinionated on top of FastAPI the way Nest is
|
|
45
|
+
opinionated on top of Express/Fastify: modules own controllers and providers,
|
|
46
|
+
providers get dependency-injected by type, and the request pipeline
|
|
47
|
+
(guard → pipe → interceptor → handler → filter) is a first-class concept
|
|
48
|
+
instead of something you hand-roll with dependencies everywhere.
|
|
49
|
+
|
|
50
|
+
## Architecture at a glance
|
|
51
|
+
|
|
52
|
+
| NestJS | Austial |
|
|
53
|
+
|---|---|
|
|
54
|
+
| `@Module()` | `@Module(imports=, controllers=, providers=, exports=)` |
|
|
55
|
+
| `@Controller()` / `@Get()` etc. | `@Controller(prefix)` / `@Get/@Post/@Put/@Patch/@Delete/@Options/@Head` |
|
|
56
|
+
| `@Injectable()` | `@Injectable(scope=Scope.DEFAULT \| Scope.TRANSIENT)` |
|
|
57
|
+
| Constructor DI | Container resolves `__init__` type hints recursively, singleton by default |
|
|
58
|
+
| `NestFactory.create(AppModule)` | `AustialFactory.create(AppModule)` -> `AustialApplication` |
|
|
59
|
+
| `app.listen(3000)` | `await app.listen(8000)` |
|
|
60
|
+
| `CanActivate` / `@UseGuards` | `CanActivate` ABC + `ExecutionContext`, `@UseGuards(...)` |
|
|
61
|
+
| `NestInterceptor` / `@UseInterceptors` | `NestInterceptor` ABC (`intercept(context, call_next)`) + `@UseInterceptors(...)` |
|
|
62
|
+
| `PipeTransform` / `ValidationPipe` | `PipeTransform` ABC + `ValidationPipe` |
|
|
63
|
+
| `ExceptionFilter` / `@Catch` | `ExceptionFilter` ABC + `@Catch(...)`, default `AllExceptionsFilter` |
|
|
64
|
+
| `NestMiddleware`, `MiddlewareConsumer` | `NestMiddleware` ABC, `configure(consumer)` on modules |
|
|
65
|
+
| `ConfigModule.forRoot()` | `ConfigModule.for_root(env_file=".env")` / `ConfigService` |
|
|
66
|
+
| `TypeOrmModule.forRootAsync` | `DatabaseModule.for_root_async(use_factory=, inject=)` (SQLAlchemy async) |
|
|
67
|
+
| `@nestjs/terminus` | `austial.terminus.HealthCheckService` + `MemoryHealthIndicator`, `DatabaseHealthIndicator` |
|
|
68
|
+
| `@nestjs/testing` | `austial.testing.Test.create_testing_module(...).compile()` |
|
|
69
|
+
| `nest new` / `nest generate` | `austial new <name>` / `austial generate\|g module\|controller\|service\|resource <name>` |
|
|
70
|
+
|
|
71
|
+
### A note on file names
|
|
72
|
+
|
|
73
|
+
Nest names files with dots (`health.controller.ts`) because Node resolves
|
|
74
|
+
modules by explicit relative path strings. Python's `import a.b` instead
|
|
75
|
+
resolves `a` as a package and `b` as a submodule -- a literal file
|
|
76
|
+
`health.controller.py` isn't importable via a normal `import` statement. So
|
|
77
|
+
Austial uses underscore suffixes instead: `health_controller.py`,
|
|
78
|
+
`health_service.py`, `health_module.py`, `health_dto.py`. The directory
|
|
79
|
+
layout still mirrors Nest 1:1 -- one folder per feature under
|
|
80
|
+
`src/modules/`.
|
|
81
|
+
|
|
82
|
+
## Quickstart
|
|
83
|
+
|
|
84
|
+
Austial is published on PyPI -- install the CLI and scaffold a new project:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
uv tool install austial # or: pipx install austial / pip install austial
|
|
88
|
+
austial new my-app # scaffold a new project
|
|
89
|
+
cd my-app
|
|
90
|
+
uv sync
|
|
91
|
+
cp .env.example .env
|
|
92
|
+
uv run austial serve # http://localhost:8000, auto-reload
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`austial new` scaffolds a small health-check "hello world" app --
|
|
96
|
+
`GET /`, `GET /health`, `GET /health/protected`, `/docs` (Swagger UI) -- see
|
|
97
|
+
[Using the CLI](#using-the-cli) below for the full generator reference.
|
|
98
|
+
|
|
99
|
+
## Developing Austial itself
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
git clone https://github.com/austial/austial-py
|
|
103
|
+
cd austial-py
|
|
104
|
+
uv sync --all-extras
|
|
105
|
+
uv run pytest
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Linting, type-checking & pre-commit hooks
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
uv run ruff check . # lint
|
|
112
|
+
uv run ruff format . # format
|
|
113
|
+
uv run mypy austial tests # type-check
|
|
114
|
+
|
|
115
|
+
uv run pre-commit install --hook-type pre-commit --hook-type pre-push
|
|
116
|
+
uv run pre-commit run --all-files # run every hook once, on demand
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Once installed, `git commit` runs formatting/linting/type-checks automatically,
|
|
120
|
+
and `git push` also runs the test suite -- mirroring a typical Nest project's
|
|
121
|
+
husky + lint-staged setup. Projects scaffolded with `austial new` get the same
|
|
122
|
+
`.pre-commit-config.yaml` out of the box.
|
|
123
|
+
|
|
124
|
+
## Using the CLI
|
|
125
|
+
|
|
126
|
+
`austial new` scaffolds a brand-new project exactly like `nest new`:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
uv run austial new my-app
|
|
130
|
+
cd my-app
|
|
131
|
+
uv sync
|
|
132
|
+
cp .env.example .env
|
|
133
|
+
uv run austial serve
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
> New projects depend on the published `austial` package from PyPI by
|
|
137
|
+
> default. If you're developing the framework itself, point new projects at
|
|
138
|
+
> your local checkout instead with `--link`:
|
|
139
|
+
> `austial new my-app --link /path/to/austial-py` -- this adds an editable
|
|
140
|
+
> `[tool.uv.sources]` entry so `uv sync` resolves `austial` from your local
|
|
141
|
+
> checkout rather than PyPI.
|
|
142
|
+
|
|
143
|
+
`austial generate` (alias `austial g`) adds artifacts to an existing project,
|
|
144
|
+
patching `src/app_module.py`'s imports/`imports=[...]` array automatically --
|
|
145
|
+
just like `nest g`:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
uv run austial generate module cats # src/modules/cats/cats_module.py
|
|
149
|
+
uv run austial generate controller cats # src/modules/cats/cats_controller.py
|
|
150
|
+
uv run austial generate service cats # src/modules/cats/cats_service.py
|
|
151
|
+
uv run austial generate resource cats # module + full CRUD controller/service/dto/entity
|
|
152
|
+
uv run austial g resource cats # same as above, short alias
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Repository layout
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
austial-py/
|
|
159
|
+
├── pyproject.toml # package "austial", console-script entry point
|
|
160
|
+
├── LICENSE # MIT
|
|
161
|
+
├── assets/ # logo.svg (+ logo-dark.svg for dark mode) / icon.svg / apple-icon.png
|
|
162
|
+
├── austial/ # the framework (installable library) -- this *is* the package
|
|
163
|
+
│ ├── common/ # decorators, guards, interceptors, pipes, filters,
|
|
164
|
+
│ │ # middleware, exceptions, logger
|
|
165
|
+
│ ├── core/ # metadata/reflector, DI container, router builder,
|
|
166
|
+
│ │ # AustialFactory / AustialApplication
|
|
167
|
+
│ ├── config/ # ConfigModule / ConfigService
|
|
168
|
+
│ ├── database/ # DatabaseModule (SQLAlchemy async)
|
|
169
|
+
│ ├── terminus/ # HealthCheckService + health indicators
|
|
170
|
+
│ ├── testing/ # Test.create_testing_module(...)
|
|
171
|
+
│ └── cli/ # `austial new` / `generate` / `serve` + templates
|
|
172
|
+
└── tests/ # tests for the framework itself -- no sample app needed;
|
|
173
|
+
├── unit/ # test modules/controllers are defined inline per test file
|
|
174
|
+
└── e2e/
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
There's deliberately no `src/` here -- that's what `austial new` generates for
|
|
178
|
+
*your* project, kept out of the framework's own repo. Test files use Nest's
|
|
179
|
+
`*.spec.ts` naming convention (`*_spec.py` here); `pyproject.toml`'s
|
|
180
|
+
`[tool.pytest.ini_options]` is configured to discover them.
|
|
181
|
+
|
|
182
|
+
## Request lifecycle
|
|
183
|
+
|
|
184
|
+
Every route runs through the same pipeline Nest uses:
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
guards -> pipes -> interceptors (wrapping) -> handler -> exception filters
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
from austial import Controller, Get, UseGuards, UseInterceptors
|
|
192
|
+
from austial.common.interceptors import TransformInterceptor
|
|
193
|
+
|
|
194
|
+
from .api_key_guard import ApiKeyGuard
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@Controller("cats")
|
|
198
|
+
class CatsController:
|
|
199
|
+
@Get(":id")
|
|
200
|
+
@UseGuards(ApiKeyGuard)
|
|
201
|
+
@UseInterceptors(TransformInterceptor())
|
|
202
|
+
async def find_one(self, id: int):
|
|
203
|
+
...
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Note path params use Nest's `:id` syntax (translated to FastAPI's `{id}`
|
|
207
|
+
under the hood), so route strings read exactly like their Nest counterparts.
|
|
208
|
+
|
|
209
|
+
## License
|
|
210
|
+
|
|
211
|
+
[MIT](./LICENSE)
|
|
Binary file
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
|
2
|
+
<!-- Round ink background keeps the mark visible on both light and dark browser chrome -->
|
|
3
|
+
<circle cx="256" cy="256" r="256" fill="#0a0a0a"/>
|
|
4
|
+
<g fill="#ffffff" transform="translate(256 256) scale(1.06) translate(-256 -214)">
|
|
5
|
+
<path d="M 159.25,219.52 C 149.80,240.98 140.46,262.10 131.12,283.23 C 119.40,283.23 107.97,283.23 96.00,283.23 C 113.81,243.82 131.47,204.77 149.38,165.16 C 150.13,166.45 150.74,167.31 151.17,168.26 C 155.82,178.57 160.39,188.92 165.10,199.20 C 166.20,201.60 166.49,203.66 165.23,206.15 C 163.08,210.39 161.30,214.82 159.25,219.52 z"/>
|
|
6
|
+
<path d="M 232.05,214.32 C 220.99,190.00 209.93,165.69 199.00,141.66 C 187.46,141.66 176.53,141.66 165.10,141.66 C 186.10,189.14 206.93,236.21 227.65,283.04 C 239.78,283.04 251.31,283.04 263.31,283.04 C 252.85,260.05 242.60,237.52 232.05,214.32 z"/>
|
|
7
|
+
<path d="M 310.10,180.06 C 310.10,193.37 310.08,206.19 310.10,219.01 C 310.10,224.61 310.47,230.16 312.20,235.55 C 315.39,245.45 321.73,252.24 331.71,255.44 C 339.68,257.99 347.77,258.44 355.98,256.71 C 370.25,253.69 379.97,242.86 381.31,228.33 C 381.61,225.07 381.73,221.78 381.73,218.50 C 381.76,194.34 381.73,170.18 381.72,146.02 C 381.72,144.72 381.72,143.43 381.72,141.41 C 393.07,141.41 404.16,141.38 415.65,141.44 C 415.76,142.75 415.96,143.99 415.96,145.23 C 415.98,171.20 416.00,197.17 415.96,223.14 C 415.95,233.25 414.17,243.02 409.98,252.31 C 404.60,264.22 395.85,272.67 384.09,278.17 C 367.32,286.01 349.73,287.21 331.63,284.97 C 320.37,283.58 309.91,280.04 300.28,274.06 C 287.79,266.30 281.10,254.50 277.88,240.61 C 276.59,235.06 275.90,229.25 275.85,223.55 C 275.65,197.09 275.77,170.63 275.78,144.17 C 275.78,143.38 275.88,142.60 275.94,141.62 C 287.33,141.62 298.43,141.62 310.10,141.62 C 310.10,154.35 310.10,166.96 310.10,180.06 z"/>
|
|
8
|
+
</g>
|
|
9
|
+
</svg>
|