swiftpy-framework 0.1.0__py3-none-any.whl

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.
Files changed (211) hide show
  1. swiftpy/.gitignore +0 -0
  2. swiftpy/__init__.py +572 -0
  3. swiftpy/admin/__init__.py +50 -0
  4. swiftpy/admin/auth.py +522 -0
  5. swiftpy/admin/registry.py +567 -0
  6. swiftpy/admin/views.py +1583 -0
  7. swiftpy/ai/__init__.py +199 -0
  8. swiftpy/ai/_deterministic.py +40 -0
  9. swiftpy/ai/_fake.py +604 -0
  10. swiftpy/ai/agents/__init__.py +30 -0
  11. swiftpy/ai/agents/base_agent.py +506 -0
  12. swiftpy/ai/agents/memory.py +864 -0
  13. swiftpy/ai/client.py +1011 -0
  14. swiftpy/ai/conversation.py +171 -0
  15. swiftpy/ai/embeddings.py +321 -0
  16. swiftpy/ai/exceptions.py +75 -0
  17. swiftpy/ai/history_strategies.py +157 -0
  18. swiftpy/ai/middleware.py +918 -0
  19. swiftpy/ai/observability/__init__.py +28 -0
  20. swiftpy/ai/observability/drivers.py +763 -0
  21. swiftpy/ai/observability/observer.py +364 -0
  22. swiftpy/ai/pool.py +102 -0
  23. swiftpy/ai/pricing.py +101 -0
  24. swiftpy/ai/prompts/__init__.py +6 -0
  25. swiftpy/ai/prompts/loader.py +343 -0
  26. swiftpy/ai/providers/__init__.py +22 -0
  27. swiftpy/ai/providers/anthropic_provider.py +353 -0
  28. swiftpy/ai/providers/base.py +273 -0
  29. swiftpy/ai/providers/openai_provider.py +275 -0
  30. swiftpy/ai/rag/__init__.py +53 -0
  31. swiftpy/ai/rag/context_formatter.py +263 -0
  32. swiftpy/ai/rag/pipeline_builder.py +544 -0
  33. swiftpy/ai/rag/reranker.py +484 -0
  34. swiftpy/ai/rag/result.py +78 -0
  35. swiftpy/ai/rag/retriever.py +315 -0
  36. swiftpy/ai/redis_store.py +227 -0
  37. swiftpy/ai/streaming.py +115 -0
  38. swiftpy/ai/tools/__init__.py +22 -0
  39. swiftpy/ai/tools/base_tool.py +489 -0
  40. swiftpy/ai/tools/first_party.py +384 -0
  41. swiftpy/ai/tools/tool_loop.py +417 -0
  42. swiftpy/ai/vectorstore/__init__.py +46 -0
  43. swiftpy/ai/vectorstore/base.py +542 -0
  44. swiftpy/ai/vectorstore/chroma_adapter.py +435 -0
  45. swiftpy/ai/vectorstore/in_memory_adapter.py +321 -0
  46. swiftpy/ai/vectorstore/lancedb_adapter.py +370 -0
  47. swiftpy/ai/vectorstore/pgvector_adapter.py +496 -0
  48. swiftpy/ai/vectorstore/pinecone_adapter.py +445 -0
  49. swiftpy/ai/vectorstore/qdrant_adapter.py +418 -0
  50. swiftpy/auth/__init__.py +50 -0
  51. swiftpy/auth/api_key_guard.py +544 -0
  52. swiftpy/auth/auth_manager.py +259 -0
  53. swiftpy/auth/exceptions.py +71 -0
  54. swiftpy/auth/jwt_guard.py +230 -0
  55. swiftpy/auth/jwt_issuer.py +658 -0
  56. swiftpy/auth/token_blacklist.py +300 -0
  57. swiftpy/cache/__init__.py +14 -0
  58. swiftpy/cache/cache.py +251 -0
  59. swiftpy/cache/driver.py +216 -0
  60. swiftpy/cache/drivers/memory.py +199 -0
  61. swiftpy/cache/drivers/redis.py +421 -0
  62. swiftpy/console/__init__.py +0 -0
  63. swiftpy/console/cli.py +68 -0
  64. swiftpy/console/commands/__init__.py +0 -0
  65. swiftpy/console/commands/cache.py +164 -0
  66. swiftpy/console/commands/make.py +935 -0
  67. swiftpy/console/commands/migrate.py +280 -0
  68. swiftpy/console/commands/new.py +1806 -0
  69. swiftpy/console/commands/queue.py +306 -0
  70. swiftpy/console/commands/seed.py +252 -0
  71. swiftpy/console/commands/start.py +171 -0
  72. swiftpy/console/commands/storage.py +104 -0
  73. swiftpy/console/loader.py +21 -0
  74. swiftpy/console/stubs/__init__.py +26 -0
  75. swiftpy/console/stubs/controller.stub +14 -0
  76. swiftpy/console/stubs/event.stub +14 -0
  77. swiftpy/console/stubs/job.stub +22 -0
  78. swiftpy/console/stubs/listener.stub +14 -0
  79. swiftpy/console/stubs/mail.stub +20 -0
  80. swiftpy/console/stubs/middleware.stub +16 -0
  81. swiftpy/console/stubs/migration.alter.stub +17 -0
  82. swiftpy/console/stubs/migration.api_keys.stub +22 -0
  83. swiftpy/console/stubs/migration.blank.stub +11 -0
  84. swiftpy/console/stubs/migration.create.stub +15 -0
  85. swiftpy/console/stubs/migration.stub +11 -0
  86. swiftpy/console/stubs/migration.update.stub +17 -0
  87. swiftpy/console/stubs/model.stub +17 -0
  88. swiftpy/console/stubs/provider.stub +18 -0
  89. swiftpy/console/stubs/repository.stub +10 -0
  90. swiftpy/console/stubs/request.stub +16 -0
  91. swiftpy/console/stubs/resource.stub +21 -0
  92. swiftpy/console/stubs/schema.stub +12 -0
  93. swiftpy/console/stubs/seeder.stub +14 -0
  94. swiftpy/console/stubs/service.stub +10 -0
  95. swiftpy/core/__init__.py +0 -0
  96. swiftpy/core/application.py +1306 -0
  97. swiftpy/core/bootstrap.py +30 -0
  98. swiftpy/core/config.py +432 -0
  99. swiftpy/core/container.py +596 -0
  100. swiftpy/core/container_exceptions.py +18 -0
  101. swiftpy/core/context.py +148 -0
  102. swiftpy/core/exceptions.py +352 -0
  103. swiftpy/core/providers/base.py +35 -0
  104. swiftpy/database/__init__.py +41 -0
  105. swiftpy/database/exceptions.py +42 -0
  106. swiftpy/database/identifiers.py +105 -0
  107. swiftpy/database/migrations/__init__.py +36 -0
  108. swiftpy/database/migrations/blueprint.py +431 -0
  109. swiftpy/database/migrations/lock.py +104 -0
  110. swiftpy/database/migrations/migration.py +22 -0
  111. swiftpy/database/migrations/migrator.py +507 -0
  112. swiftpy/database/migrations/schema.py +435 -0
  113. swiftpy/database/orm/__init__.py +33 -0
  114. swiftpy/database/orm/fields.py +92 -0
  115. swiftpy/database/orm/model.py +2034 -0
  116. swiftpy/database/orm/registry.py +33 -0
  117. swiftpy/database/orm/relationships.py +836 -0
  118. swiftpy/database/pagination/__init__.py +16 -0
  119. swiftpy/database/pagination/contracts.py +60 -0
  120. swiftpy/database/pagination/paginator.py +112 -0
  121. swiftpy/database/pagination/simple_paginator.py +97 -0
  122. swiftpy/database/pool.py +218 -0
  123. swiftpy/database/query_builder.py +1803 -0
  124. swiftpy/database/seeder.py +39 -0
  125. swiftpy/database/transaction.py +276 -0
  126. swiftpy/events/__init__.py +10 -0
  127. swiftpy/events/dispatcher.py +308 -0
  128. swiftpy/events/redis_bus.py +552 -0
  129. swiftpy/filesystem/__init__.py +227 -0
  130. swiftpy/filesystem/disk.py +378 -0
  131. swiftpy/filesystem/drivers/__init__.py +11 -0
  132. swiftpy/filesystem/drivers/base.py +156 -0
  133. swiftpy/filesystem/drivers/local.py +559 -0
  134. swiftpy/filesystem/drivers/s3.py +655 -0
  135. swiftpy/filesystem/events.py +37 -0
  136. swiftpy/filesystem/exceptions.py +127 -0
  137. swiftpy/filesystem/manager.py +138 -0
  138. swiftpy/filesystem/metadata.py +22 -0
  139. swiftpy/filesystem/paths.py +99 -0
  140. swiftpy/filesystem/service_provider.py +17 -0
  141. swiftpy/filesystem/uploads/__init__.py +13 -0
  142. swiftpy/filesystem/uploads/handlers.py +362 -0
  143. swiftpy/filesystem/uploads/uploaded_file.py +222 -0
  144. swiftpy/filesystem/uploads/validators.py +186 -0
  145. swiftpy/filesystem/visibility.py +22 -0
  146. swiftpy/helpers/__init__.py +42 -0
  147. swiftpy/helpers/helper.py +180 -0
  148. swiftpy/http/exceptions.py +41 -0
  149. swiftpy/http/middleware.py +307 -0
  150. swiftpy/http/request.py +375 -0
  151. swiftpy/http/response.py +442 -0
  152. swiftpy/http/router.py +1050 -0
  153. swiftpy/mail/__init__.py +58 -0
  154. swiftpy/mail/attachments.py +255 -0
  155. swiftpy/mail/config.py +71 -0
  156. swiftpy/mail/envelope.py +183 -0
  157. swiftpy/mail/exceptions.py +90 -0
  158. swiftpy/mail/facade.py +312 -0
  159. swiftpy/mail/mailable.py +53 -0
  160. swiftpy/mail/manager.py +240 -0
  161. swiftpy/mail/message.py +186 -0
  162. swiftpy/mail/models.py +19 -0
  163. swiftpy/mail/queue/__init__.py +5 -0
  164. swiftpy/mail/queue/job.py +279 -0
  165. swiftpy/mail/service_provider.py +19 -0
  166. swiftpy/mail/templates/__init__.py +5 -0
  167. swiftpy/mail/templates/renderer.py +173 -0
  168. swiftpy/mail/transports/__init__.py +21 -0
  169. swiftpy/mail/transports/api/__init__.py +13 -0
  170. swiftpy/mail/transports/api/base.py +194 -0
  171. swiftpy/mail/transports/api/mailgun.py +158 -0
  172. swiftpy/mail/transports/api/resend.py +156 -0
  173. swiftpy/mail/transports/api/sendgrid.py +172 -0
  174. swiftpy/mail/transports/base.py +24 -0
  175. swiftpy/mail/transports/console.py +91 -0
  176. swiftpy/mail/transports/sendmail.py +128 -0
  177. swiftpy/mail/transports/smtp.py +466 -0
  178. swiftpy/queue/__init__.py +13 -0
  179. swiftpy/queue/dispatcher.py +813 -0
  180. swiftpy/queue/job.py +541 -0
  181. swiftpy/queue/worker.py +556 -0
  182. swiftpy/support/__init__.py +12 -0
  183. swiftpy/support/keys.py +108 -0
  184. swiftpy/support/redis_config.py +223 -0
  185. swiftpy/support/strings.py +114 -0
  186. swiftpy/testing/__init__.py +20 -0
  187. swiftpy/testing/fakes/__init__.py +11 -0
  188. swiftpy/testing/fakes/ai_fake.py +21 -0
  189. swiftpy/testing/fakes/vectorstore_fake.py +194 -0
  190. swiftpy/testing/fixtures.py +124 -0
  191. swiftpy/testing/http_client.py +730 -0
  192. swiftpy/testing/test_case.py +126 -0
  193. swiftpy/validation/__init__.py +97 -0
  194. swiftpy/validation/exceptions.py +26 -0
  195. swiftpy/validation/form_request.py +302 -0
  196. swiftpy/validation/protocols.py +33 -0
  197. swiftpy/validation/rules/__init__.py +89 -0
  198. swiftpy/validation/rules/builtin.py +391 -0
  199. swiftpy/validation/rules/exists.py +86 -0
  200. swiftpy/validation/rules/registry.py +290 -0
  201. swiftpy/validation/rules/required.py +55 -0
  202. swiftpy/validation/rules/unique.py +138 -0
  203. swiftpy/validation/validator.py +612 -0
  204. swiftpy/websockets/__init__.py +19 -0
  205. swiftpy/websockets/connection.py +317 -0
  206. swiftpy/websockets/manager.py +271 -0
  207. swiftpy_framework-0.1.0.dist-info/METADATA +603 -0
  208. swiftpy_framework-0.1.0.dist-info/RECORD +211 -0
  209. swiftpy_framework-0.1.0.dist-info/WHEEL +4 -0
  210. swiftpy_framework-0.1.0.dist-info/entry_points.txt +2 -0
  211. swiftpy_framework-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,603 @@
1
+ Metadata-Version: 2.5
2
+ Name: swiftpy-framework
3
+ Version: 0.1.0
4
+ Summary: SwiftPY is a new generation async-first Python framework purpose built for the modern API and AI economy.
5
+ Project-URL: Homepage, https://github.com/obrainwave/swiftpy
6
+ Project-URL: Repository, https://github.com/obrainwave/swiftpy
7
+ Project-URL: Documentation, https://github.com/obrainwave/swiftpy#readme
8
+ Project-URL: Issues, https://github.com/obrainwave/swiftpy/issues
9
+ Author: Akeem O. Salau
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: asgi,async,framework,orm,python,web-framework
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Framework :: AsyncIO
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
22
+ Requires-Python: >=3.12
23
+ Requires-Dist: asyncpg>=0.30.0
24
+ Requires-Dist: bcrypt>=5.0.0
25
+ Requires-Dist: cryptography>=50.0.2
26
+ Requires-Dist: httpx>=0.27.0
27
+ Requires-Dist: redis>=8.1.0
28
+ Requires-Dist: uvicorn[standard]>=0.52.4
29
+ Description-Content-Type: text/markdown
30
+
31
+ <p align="center">
32
+ <img src="./red-black.png" alt="SwiftPY Logo" width="420" />
33
+ </p>
34
+
35
+ # SwiftPY
36
+
37
+ **An ASGI-native, Eloquent-style Python web framework combining the developer experience of FastAPI with the elegance and structure of Laravel.**
38
+
39
+ [![CI](https://github.com/obrainwave/swiftpy/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/obrainwave/swiftpy/actions)
40
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
41
+ [![Python 3.12+](https://img.shields.io/badge/python-3.12%2B-blue.svg)](https://www.python.org/downloads/)
42
+ [![Status: Production-Hardened](https://img.shields.io/badge/status-production--hardened-green.svg)](./DEVLOG.md)
43
+ [![Code Style: Ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](https://github.com/astral-sh/ruff)
44
+ [![Types: Mypy Strict](https://img.shields.io/badge/types-mypy%20strict-blue.svg)](https://mypy-lang.org/)
45
+ [![Tests: Automated Suite](https://img.shields.io/badge/tests-pytest%20suite-brightgreen.svg)](./DEVLOG.md)
46
+
47
+ ---
48
+
49
+ ## Overview
50
+
51
+ SwiftPY brings the joy and productivity of Laravel to the modern asynchronous Python ecosystem. It eliminates boilerplate and fragmented third-party glue code by providing a unified, cohesive framework built natively on Python 3.12+ and high-performance [`asyncpg`](https://github.com/MagicStack/asyncpg).
52
+
53
+ - **FastAPI-Grade Simplicity**: Clean, intuitive entrypoint (`app = Application()`), async-first request/response cycle, automatic path parameter type coercion, and scoped route groups with middleware pipelines.
54
+ - **Eloquent-Caliber Active Record ORM**: Typed models declaring class attributes via standard Python type hints, automatic irregular-aware table pluralization, dirty-tracking `save()`, expressive relationships (`HasMany`, `BelongsTo`, `HasOne`, `BelongsToMany`), and eager loading with strict N+1 query prevention (via model-level `prevent_lazy_loading = True`, raising `LazyLoadingDisabledError` on un-eager-loaded accesses).
55
+ - **Query Builder, GROUP BY & Dynamic Pagination**: Full length-aware `paginate()` and lightweight `simple_paginate()`, `limit()`/`offset()` (with `take()`/`skip()`), group-aware subquery counting, and aggregate selectors (`select_count`, `select_sum`, etc.) with `having` and `or_having` logical filtering.
56
+ - **Multi-Disk Storage & Streaming Engine**: Flysystem-inspired `Storage` abstraction supporting local and S3 disks, high-throughput streaming uploads/downloads, magic-byte validation, and `storage:link`.
57
+ - **Form Request & Input Hardening**: Automatic 422 JSON errors, configurable body streaming limits, Content-Length checks, and JSON recursion depth caps.
58
+ - **Database Engine & Native Transactions**: Connection pool management, automatic transaction propagation across context variables, nested transaction savepoints, and strict SQL injection prevention with full identifier quoting for reserved words.
59
+ - **Fluent Migration DSL & Schema Builder**: Laravel-inspired `Blueprint` table definitions, safe statement ordering (drops executed before adds), column comments, unsigned checks, composite keys, and distributed PostgreSQL advisory locks.
60
+ - **At-Least-Once Queues & Signed Bus**: Robust background jobs with Redis visibility timeout leases, HMAC-SHA256 envelope signing, dead-letter routing, and a cryptographically protected cross-worker event bus.
61
+ - **WebSocket Gateway**: Structured WebSocket connection management, topic subscriptions, broadcast fan-out, ping/pong heartbeats, and strict same-origin handshake validation.
62
+ - **Zero-Config Provider Auto-Discovery**: Automatic service provider discovery in `app/providers/` that registers container dependencies behind the scenes without manual entrypoint registration.
63
+ - **Developer CLI (`swiftpy`)**: Built-in CLI for scaffolding new projects (`new`) and components (`make:*`), running/rolling back/resetting/seeding migrations (`migrate*`, `db:seed`), linking public storage, managing queue workers and failed jobs, precompiling caches, and starting the ASGI server.
64
+
65
+ ---
66
+
67
+ ## Status & Roadmap
68
+
69
+ All core architectural, storage, AI, and security hardening phases have been completed and verified against an automated PostgreSQL + Redis pytest suite:
70
+
71
+ | Milestone | Component Focus | Status |
72
+ |---|---|---|
73
+ | **Phase 0: Testing Harness & Quality Baseline** | CI harness, container test environments, strict ruff/mypy configurations | ✅ Completed |
74
+ | **Phase 1: Architecture & Foundation** | ASGI HTTP kernel, async router, route groups, IoC container, task isolation, middleware pipeline | ✅ Completed |
75
+ | **Phase 2: Authentication & Security** | Cryptographic JWT issuer, HMAC API key guards, server-side peppers, constant-time comparisons | ✅ Completed |
76
+ | **Phase 3: Database & Identifier Safety** | asyncpg connection pool, strict SQL identifier validation/quoting, nested transactions | ✅ Completed |
77
+ | **Phase 4: HTTP Kernel & Error Pipeline** | Request streaming limits (2 MiB default), typed response factories, fail-closed error pipeline | ✅ Completed |
78
+ | **Phase 5: Concurrency & DI Scoping** | ContextVar-backed scoped DI, async lifecycle hooks (`startup`/`shutdown`), graceful teardown | ✅ Completed |
79
+ | **Phase 5: Filesystem & Cloud Storage** | Multi-disk abstraction (`local`, `s3`), streaming uploads/downloads, magic-byte validation, `storage:link` | ✅ Completed |
80
+ | **Phase 6: Queues & Reliability** | At-least-once Redis queue leases, HMAC envelope signing, namespace allowlists, signed event bus | ✅ Completed |
81
+ | **Phase 7: Active Record ORM Hardening** | Mass assignment protection (`__fillable__`/`__guarded__`), eager loading N+1 prevention, dirty tracking | ✅ Completed |
82
+ | **Phase 8: Validation DSL Hardening** | Fail-closed FormRequest validation, hardened rule composition, input sanitization | ✅ Completed |
83
+ | **Phase 9: Real-time WebSockets** | Handshake validation, same-origin protection (D-14), bounded broadcast fan-out, lifecycle hooks | ✅ Completed |
84
+ | **Phase 10: Infra, CI & Hygiene** | Multi-stage Docker, pinned CI workflow, hardened compose, repo hygiene, CLI options | ✅ Completed |
85
+ | **Week 26: Security Audit & Hardening** | SQL injection audit, JWT & bcrypt API keys, JSON depth & streaming limits, traceback suppression | ✅ Completed |
86
+ | **Week 26: Pagination & Aggregation Subsystem** | Limit/offset parameterized SQL, length-aware `paginate()`, `simple_paginate()`, `group_by()` & `having()` | ✅ Completed |
87
+
88
+ *See [`DEVLOG.md`](./DEVLOG.md) for detailed test reports and verified architectural guarantees.*
89
+
90
+ ---
91
+
92
+ ## Verified Performance Benchmarks
93
+
94
+ All benchmarks were measured under concurrent load using `wrk` against the containerized production runtime in Docker with PostgreSQL and Redis connections active. See [`DEVLOG.md`](./DEVLOG.md) for full reproduction commands and raw latency distributions.
95
+
96
+ | Workload | Endpoint | Target Requirement | Measured Throughput / Latency | Status |
97
+ |---|---|---|---|---|
98
+ | **Simple JSON** | `GET /benchmarks/json` | $\ge$ 3,000 req/s | **6,507 req/s** (P50: 14.04ms, P90: 28.25ms) | 🚀 **Exceeded (+116.9%)** |
99
+ | **Authenticated DB Endpoint** | `GET /benchmarks/auth-db` | $\ge$ 1,000 req/s | **1,065 req/s** (P50: 78.02ms, P90: 164.12ms) | 🎯 **Target Met** |
100
+ | **RAG Pipeline (Vector + Prompt)** | `GET /benchmarks/rag` | < 900ms P95 latency | **P95: ~170ms** (268 req/s, P50: 61.41ms) | 🚀 **Exceeded (5.3x lower P95)** |
101
+ | **Streaming SSE Endpoint** | `GET /benchmarks/stream` | Chunked response | **4,114 req/s** (P50: 7.97ms, P90: 14.90ms) | ✅ **Production Ready** |
102
+ | **Authenticated Endpoint** | `GET /benchmarks/auth` | JWT verification | **2,939 req/s** (P50: 29.87ms, P90: 70.03ms) | ✅ **Production Ready** |
103
+ | **Database Read** | `GET /benchmarks/db` | QueryBuilder query | **1,476 req/s** (P50: 30.60ms, P90: 108.25ms) | ✅ **Production Ready** |
104
+
105
+ ### Key Optimizations:
106
+ - **$O(1)$ Static Routing**: Direct dictionary hash lookup for non-parameterized routes bypasses regex scanning.
107
+ - **DI Reflection Caching**: Container parameter reflection (`inspect.signature` / `get_type_hints`) is cached permanently after the first call.
108
+ - **Redis Pipelining & Token Cache**: Batching blacklist commands into atomic pipelines with in-memory TTL caching speeds up JWT validation.
109
+ - **Precompiled Production Cache (`swiftpy cache:build`)**: Precompiles configuration (`bootstrap/cache/config.json`) and prompt templates (`bootstrap/cache/prompts.json`) into serialized JSON files to eliminate startup file parsing overhead (note: legacy `RouteCache` was removed under Decision `G-16` in favor of direct `$O(1)$` static route registration).
110
+
111
+ ---
112
+
113
+ ## What It Looks Like
114
+
115
+ All code examples below are verified by an automated test suite (`tests/docs/test_readme_examples.py`).
116
+
117
+ ### 1. Minimal Application Entrypoint
118
+
119
+ No boilerplate or manual bootstrapping required—create your application and declare routes:
120
+
121
+ ```python
122
+ # main.py
123
+ from swiftpy import (
124
+ CORS,
125
+ Application,
126
+ CurrentUser,
127
+ JWTGuard,
128
+ Request,
129
+ RequestId,
130
+ Response,
131
+ RouteGroup,
132
+ SecurityHeaders,
133
+ json_response,
134
+ )
135
+
136
+ app = Application(title="SwiftPY Store API", version="1.0.0")
137
+ app.middleware([RequestId, SecurityHeaders, CORS])
138
+
139
+
140
+ @app.get("/ping")
141
+ async def ping(request: Request) -> dict[str, str]:
142
+ # Plain dict/list return values are automatically serialized to JSON (Decision G-16)
143
+ return {"message": "pong"}
144
+
145
+
146
+ @app.group("/api/v1")
147
+ def v1_routes(group: RouteGroup) -> None:
148
+ @group.get("/users/{user_id}")
149
+ async def show_user(request: Request, user_id: int) -> Response:
150
+ return json_response({"user_id": user_id, "ok": True})
151
+
152
+
153
+ @app.group("/api/v1/account", middleware=[JWTGuard])
154
+ def account_routes(group: RouteGroup) -> None:
155
+ @group.get("/me")
156
+ async def me(user: CurrentUser) -> Response:
157
+ return json_response({"user": dict(user)})
158
+ ```
159
+
160
+ ### 2. Active Record Models & Relationships
161
+
162
+ Declare models using Python type hints. Guard sensitive fields with `__fillable__` or `__guarded__`. Prevent silent N+1 queries using `.with_()` for explicit eager loading:
163
+
164
+ ```python
165
+ # app/models/user.py
166
+ from swiftpy.database.orm import HasMany, HasOne, Model
167
+
168
+
169
+ class Post(Model):
170
+ __table__ = "posts"
171
+ __fillable__ = ["title", "user_id"]
172
+
173
+ id: int
174
+ title: str
175
+ user_id: int
176
+
177
+
178
+ class Profile(Model):
179
+ __table__ = "profiles"
180
+ __fillable__ = ["bio", "user_id"]
181
+
182
+ id: int
183
+ bio: str
184
+ user_id: int
185
+
186
+
187
+ class User(Model):
188
+ __table__ = "users"
189
+ __fillable__ = ["name", "email"]
190
+
191
+ id: int
192
+ name: str
193
+ email: str
194
+
195
+ posts = HasMany(Post, foreign_key="user_id")
196
+ profile = HasOne(Profile, foreign_key="user_id")
197
+
198
+
199
+ # Querying and eager loading:
200
+ users = await User.with_("posts", "profile").where("name", "Alice").get()
201
+ for user in users:
202
+ print(user.name, len(user.posts), user.profile.bio)
203
+
204
+ # Dirty-tracking save (updates only changed attributes and maintains updated_at):
205
+ user = await User.find(1)
206
+ if user is not None:
207
+ user.name = "Alice Cooper"
208
+ await user.save()
209
+ ```
210
+
211
+ ### 3. Fluent Query Builder, GROUP BY & Dynamic Pagination
212
+
213
+ Execute complex aggregated queries, group by columns with `HAVING`, and paginate results using length-aware `paginate()` or high-performance `simple_paginate()`:
214
+
215
+ ```python
216
+ from app.models.user import User
217
+ from swiftpy.database.pagination import Paginator, SimplePaginator
218
+ from swiftpy.database.query_builder import Query
219
+
220
+ # 1. Length-aware pagination with metadata & total group count:
221
+ paginated_users: Paginator[User] = (
222
+ await User.where("status", "active")
223
+ .order_by("created_at", "DESC")
224
+ .paginate(per_page=20, page=1)
225
+ )
226
+
227
+ print(paginated_users.total) # e.g., 145
228
+ print(paginated_users.last_page) # 8
229
+ print(paginated_users.has_more_pages) # True
230
+
231
+ # Export standard JSON payload with navigation metadata:
232
+ payload = paginated_users.to_dict()
233
+ # {
234
+ # "data": [...],
235
+ # "current_page": 1,
236
+ # "per_page": 20,
237
+ # "total": 145,
238
+ # "last_page": 8,
239
+ # "from": 1,
240
+ # "to": 20,
241
+ # "has_more_pages": True,
242
+ # "has_previous_page": False
243
+ # }
244
+
245
+ # 2. Fast simple pagination (avoids COUNT(*) on large tables by querying per_page + 1):
246
+ simple_users: SimplePaginator[User] = (
247
+ await User.order_by("id", "ASC").simple_paginate(per_page=20, page=1)
248
+ )
249
+
250
+ # 3. GROUP BY and HAVING with subquery group counting:
251
+ order_stats = (
252
+ await Query.table("orders")
253
+ .select("country")
254
+ .select_count("id", alias="total_orders")
255
+ .select_sum("amount", alias="total_revenue")
256
+ .group_by("country")
257
+ .having("total_orders", ">", 10)
258
+ .order_by("total_revenue", "DESC")
259
+ .paginate(per_page=5, page=1)
260
+ )
261
+ ```
262
+
263
+ ### 4. Multi-Disk Storage & Streaming Engine
264
+
265
+ Flysystem-inspired file storage abstraction supporting multi-disk scoping (`local`, `public`, `s3`), streaming I/O, and secure downloads:
266
+
267
+ ```python
268
+ from swiftpy import Request, Response, json_response
269
+ from swiftpy.filesystem import Storage
270
+
271
+ # 1. Put file bytes or binary streams to configured disks:
272
+ await Storage.disk("local").put("reports/annual_2026.pdf", pdf_bytes)
273
+
274
+ # 2. Inspect existence, size, and metadata:
275
+ if await Storage.exists("reports/annual_2026.pdf"):
276
+ size = await Storage.size("reports/annual_2026.pdf")
277
+ mime = await Storage.mime_type("reports/annual_2026.pdf")
278
+
279
+ # 3. High-throughput chunked stream download:
280
+ @app.get("/reports/download")
281
+ async def download_report(request: Request) -> Response:
282
+ return await Storage.download(
283
+ "reports/annual_2026.pdf", name="Annual_Report_2026.pdf"
284
+ )
285
+ ```
286
+
287
+ ### 5. Form Request Validation & Hardened Input
288
+
289
+ Declare typed validation schemas that fail closed before your controller handler is invoked:
290
+
291
+ ```python
292
+ from app.models.user import User
293
+ from swiftpy import Response, json_response
294
+ from swiftpy.validation import FormRequest
295
+ from swiftpy.validation.rules import email, min_, required, string, unique
296
+
297
+
298
+ class RegisterRequest(FormRequest):
299
+ def rules(self) -> dict[str, object]:
300
+ return {
301
+ "name": [required(), string(), min_(3)],
302
+ "email": [required(), email(), unique("users", "email")],
303
+ "password": [required(), string(), min_(8)],
304
+ }
305
+
306
+
307
+ @app.post("/register")
308
+ async def register(form: RegisterRequest) -> Response:
309
+ # Automatically validated before reaching handler; invalid input yields HTTP 422 JSON
310
+ data = form.validated()
311
+ user = await User.create(name=data["name"], email=data["email"])
312
+ return json_response(user.to_dict(), status_code=201)
313
+ ```
314
+
315
+ ### 6. Fluent Migrations & Schema Builder
316
+
317
+ Define and alter tables using the expressive `Blueprint` DSL with safe identifier quoting and constraint generation:
318
+
319
+ ```python
320
+ # database/migrations/2026_10_18_000001_create_posts_table.py
321
+ from swiftpy.database.migrations import Blueprint, Migration, Schema
322
+
323
+
324
+ class CreatePostsTable(Migration):
325
+ async def up(self) -> None:
326
+ def define(table: Blueprint) -> None:
327
+ table.id()
328
+ table.big_integer("user_id").unsigned()
329
+ table.string("title", 200)
330
+ table.text("body").nullable()
331
+ table.boolean("is_published").default(False)
332
+ table.timestamps()
333
+ table.foreign_key(
334
+ "user_id", references="id", on="users", on_delete="CASCADE"
335
+ )
336
+ table.index(["user_id", "is_published"], name="idx_posts_user_published")
337
+
338
+ await Schema.create("posts", define)
339
+
340
+ async def down(self) -> None:
341
+ await Schema.drop_if_exists("posts")
342
+ ```
343
+
344
+ ### 7. Asynchronous Queues & Background Jobs
345
+
346
+ Define background jobs with automatic JSON serialization, HMAC envelope signing, retry with backoff, and dead letter queue routing:
347
+
348
+ ```python
349
+ # app/jobs/send_welcome_email.py
350
+ from swiftpy.queue import Job
351
+
352
+
353
+ class SendWelcomeEmail(Job):
354
+ tries = 3
355
+ backoff = (1.0, 5.0, 15.0)
356
+
357
+ def __init__(self, user_id: int, email: str) -> None:
358
+ super().__init__(user_id=user_id, email=email)
359
+ self.user_id = user_id
360
+ self.email = email
361
+
362
+ async def handle(self) -> None:
363
+ # Business logic executed by the queue worker
364
+ print(f"Sending welcome email to {self.email} for user {self.user_id}")
365
+
366
+ async def failed(self, exception: BaseException) -> None:
367
+ # Hook called when retries are exhausted and job moves to Dead Letter Queue
368
+ print(f"Failed permanently for user {self.user_id}: {exception}")
369
+
370
+
371
+ # Dispatching jobs:
372
+ await SendWelcomeEmail(user_id=42, email="alice@example.com").dispatch()
373
+
374
+ # Or dispatch with delay:
375
+ await SendWelcomeEmail(user_id=42, email="alice@example.com").dispatch(delay=60.0)
376
+ ```
377
+
378
+ ---
379
+
380
+ ## Command Line Interface (`swiftpy`)
381
+
382
+ SwiftPY provides a unified CLI entrypoint for common development and production workflows:
383
+
384
+ ```bash
385
+ # Scaffold a new SwiftPY project
386
+ swiftpy new my_service
387
+
388
+ # Start the ASGI server (production default: reload=False)
389
+ swiftpy start --host 0.0.0.0 --port 8000 --workers 4
390
+
391
+ # Start local dev server with auto-reload
392
+ swiftpy start --reload
393
+
394
+ # Start the background queue worker & inspect/retry Dead Letter Queue jobs
395
+ swiftpy queue:work --queue default --sleep 1.0
396
+ swiftpy queue:failed --limit 50
397
+ swiftpy queue:retry --all
398
+ swiftpy queue:flush
399
+
400
+ # Run, inspect, rollback, reset, and seed database migrations
401
+ swiftpy migrate
402
+ swiftpy migrate:status
403
+ swiftpy migrate:rollback
404
+ swiftpy migrate:reset
405
+ swiftpy db:seed
406
+
407
+ # Scaffold migrations, models, requests, API keys, mailables, and more
408
+ swiftpy make:migration create_posts_table
409
+ swiftpy make:model Post --migration
410
+ swiftpy make:request RegisterRequest
411
+ swiftpy make:mail WelcomeEmail
412
+ swiftpy make:api-key --name billing-client
413
+
414
+ # Create symbolic links from public/storage to storage/app/public
415
+ swiftpy storage:link
416
+
417
+ # Precompile configuration and prompt templates (bootstrap/cache/)
418
+ swiftpy cache:build
419
+
420
+ # Clear precompiled application caches
421
+ swiftpy cache:clear
422
+ ```
423
+
424
+ ---
425
+
426
+ ## Configuration Reference
427
+
428
+ SwiftPY reads environment variables and YAML/Python configuration files with strict validation at runtime:
429
+
430
+ | Environment Variable | Config Path | Default | Description |
431
+ |---|---|---|---|
432
+ | `APP_ENV` | `app.env` | `"local"` | Environment (`"local"`, `"testing"`, `"production"`). In production, `debug=True` is prohibited. |
433
+ | `APP_DEBUG` | `app.debug` | `False` | Enable debug mode (`true`/`false`); fails closed in production if enabled. |
434
+ | `APP_KEY` | `app.key` | `""` | Primary `>= 32`-byte application secret key for HMAC queue/event-bus signing and signed local URLs. |
435
+ | `JWT_SECRET` | `auth.guards.jwt.secret` | `None` | Secret key for JWT HS256 signing (`>= 32` bytes required when non-debug/production). |
436
+ | `AUTH_KEY_PEPPER` | `auth.guards.api_key.pepper` | `None` | Server pepper for API key HMAC-SHA256 verification (`>= 32` bytes required when non-debug/production). |
437
+ | `FILESYSTEM_DISK` | `filesystems.default` | `"local"` | Default disk driver (`"local"`, `"public"`, `"s3"`). |
438
+ | `MAIL_MAILER` | `mail.default` | `"smtp"` | Default mail transport (`"smtp"`, `"sendmail"`, `"resend"`, `"sendgrid"`, `"mailgun"`, `"console"`, `"array"`). Templates resolve under `views/`. |
439
+ | — | `http.max_body_size` | `2097152` (2 MiB) | Maximum allowed HTTP request body size in bytes (configured via `Config` or `Application(...)` / route). Returns 413 when exceeded. |
440
+ | — | `http.max_content_length` | `None` | Optional strict `Content-Length` ceiling in bytes before buffering chunks (configured via `Config`, `Application(...)`, or per-route). |
441
+ | — | `http.max_json_depth` | `32` | Maximum allowed JSON nesting depth before rejecting with HTTP 400 (configured via `Config`, `Application(...)`, or per-route). |
442
+ | — | `websockets.allowed_origins` | `None` | Allowed WebSocket origins (`Config` or `Application(allowed_origins=...)`). When unset, enforces strict same-origin policy against `Host` (D-14). |
443
+ | — | `websockets.max_message_size` | `1048576` (1 MiB) | Maximum incoming WebSocket message size in bytes (`Config`); exceeding closes connection with code 1009. |
444
+ | `DB_CONNECTION` | `database.default` | `"postgresql"` | Default database connection key (`"postgresql"`). |
445
+ | `DB_SSL` | `database.connections.postgresql.ssl` | `"disable"` | PostgreSQL SSL mode (`"disable"`, `"require"`, `"verify-ca"`, `"verify-full"`). |
446
+ | `DB_SSL_CA_FILE` | `database.connections.postgresql.ssl_ca_file` | `""` | Filepath to CA certificate when `DB_SSL` is `"verify-ca"` or `"verify-full"`. |
447
+ | `CORS_ALLOWED_ORIGINS` | `cors.allowed_origins` | `[]` | Comma-separated list of allowed CORS origins (never pair `"*"` with `CORS_ALLOW_CREDENTIALS=true`). |
448
+ | `CORS_ALLOW_CREDENTIALS` | `cors.allow_credentials` | `False` | Whether cross-origin requests may include credentials/cookies. |
449
+
450
+ ---
451
+
452
+ ## Lifecycle Hooks & Application Architecture
453
+
454
+ ### ServiceProvider Asynchronous Lifecycle
455
+ SwiftPY manages startup and shutdown sequences through registered `ServiceProvider` instances with strict error isolation and rollback guarantees (Decision `D-7`):
456
+
457
+ ```python
458
+ from swiftpy.core.providers.base import ServiceProvider
459
+
460
+ class BillingServiceProvider(ServiceProvider):
461
+ def register(self) -> None:
462
+ # Register bindings, singletons, and factories into the IoC container
463
+ self.container.singleton(PaymentGateway, StripeGateway)
464
+
465
+ def boot(self) -> None:
466
+ # Synchronous boot tasks after all providers are registered
467
+ pass
468
+
469
+ async def startup(self) -> None:
470
+ # Asynchronous initialization (e.g. connecting to external APIs, warming caches)
471
+ await self.container.resolve(PaymentGateway).connect()
472
+
473
+ async def shutdown(self) -> None:
474
+ # Graceful asynchronous cleanup executed in reverse registration order
475
+ await self.container.resolve(PaymentGateway).disconnect()
476
+ ```
477
+
478
+ When `app.startup()` runs (or ASGI lifespan starts), providers initialize sequentially. If any provider raises an error during `startup()`, already-started providers are immediately rolled back and shut down in reverse order. During server shutdown, database pools and Redis connections are closed cleanly.
479
+
480
+ ### Queue Delivery Semantics & At-Least-Once Guarantees
481
+ Background jobs run with **at-least-once** delivery guarantees:
482
+ - **Visibility Leases (`pop_and_lease`)**: When a worker acquires a job, an atomic Lua script moves the job from the ready queue to that worker's private in-flight processing list and creates an expiration timestamp lease in a sorted set.
483
+ - **Crash Recovery (`reap_expired_leases`)**: If a worker process is killed or crashes mid-job, remaining workers automatically reap expired leases and re-queue the job (or move it to DLQ if max attempts were reached).
484
+ - **Idempotency Requirement**: Because transient network timeouts or crashes can cause a job to be re-delivered, all `Job.handle()` methods must be implemented idempotently.
485
+ - **Transactional Dispatch (`after_commit=True`)**: Calling `job.dispatch(after_commit=True)` defers pushing the job until the active database transaction successfully commits. If the transaction rolls back, the job is dropped.
486
+
487
+ ---
488
+
489
+ ## Breaking Changes & Migration Guide
490
+
491
+ Across the hardening and audit remediation phases, architectural decisions (`D-1`–`D-14` and approval gates `G-1`–`G-16`, summarized in [`CHANGELOG.md`](./CHANGELOG.md)) enforce secure, fail-closed contracts:
492
+
493
+ - **D-1 (JWT Secret Enforcement)**: Weak or missing JWT secrets (< 32 bytes) raise `ConfigurationError` in production environments; hardcoded fallback secret removed from `config/auth.py`.
494
+ - **D-2 (API Key Format & Server Pepper)**: API keys follow `<prefix>.<secret>` (e.g. `sk_live_<hex>.<secret>`), look up single row by indexed prefix, and verify constant-time HMAC-SHA256 requiring `AUTH_KEY_PEPPER` (min 32 bytes) in production.
495
+ - **D-3 (Queue Envelope Signing & Processing Leases)**: Redis queues utilize visibility timeout leases to prevent message loss on worker failure. Payloads are signed with HMAC-SHA256 using `APP_KEY` and restricted to trusted namespaces (`get_trusted_namespaces()`).
496
+ - **D-4 (`__fillable__` Secure Default)**: Mass assignment denies all fields by default unless `__fillable__` or `__guarded__` is explicitly declared on the model (`__guarded__ = []` opt-out).
497
+ - **D-5 (`Model.to_dict()`, `__hidden__`, and `__repr__`)**: Models support `__hidden__` to exclude fields from serialization; `__repr__` displays only the primary key to prevent credential leakage.
498
+ - **D-6 (Route Precedence)**: Static route segments take precedence over dynamic parameter segments regardless of registration order.
499
+ - **D-7 (Container Re-binding Semantics)**: Re-binding a singleton into the container replaces any cached resolved instance and logs at debug level.
500
+ - **D-8 (Config Environment Variable Coercion)**: Coercion preserves strings unless the default type is known (e.g. `"0012"` is not silently cast to integer `12`).
501
+ - **D-9 (`FormRequest` Fail-Closed Validation)**: If `rules()` raises `TypeError` or any exception, the request fails closed with `ConfigurationError` (HTTP 500) rather than falling back to unvalidated `{}`.
502
+ - **D-10 (`update()`/`delete()` Without `WHERE`)**: Mass write/delete queries without a `WHERE` clause raise `QueryError` unless `.allow_all()` is explicitly chained.
503
+ - **G-1 (`swiftpy/ai` & Wheel Packaging)**: `swiftpy/ai` (LLM clients, embeddings, vector stores, RAG pipelines, agents, tools, prompt templates, observability), `swiftpy/admin`, `swiftpy/mail`, `swiftpy/filesystem`, and `swiftpy/validation` are fully implemented and distributed in the main `swiftpy-framework` wheel (dev test utilities remain in `swiftpy/testing`). `swiftpy/__init__.py` uses PEP 562 lazy exports so lightweight CLI/app imports avoid heavy optional subpackages until accessed.
504
+ - **G-16 (`dict` Auto-JSON & Cache Cleanup)**: Route handlers returning plain `dict` or `list` values are automatically serialized to JSON; legacy unbootstrapped `RouteCache` was removed (`swiftpy cache:build` compiles `config.json` and `prompts.json`); `/docs/` and `AGENTS.md` remain gitignored.
505
+ - **D-12 (Repository Hygiene)**: `.coverage` and cache artifacts untracked from git; `docs/`, `AGENTS.md`, and `DEVLOG.md` maintained untracked at repo root.
506
+ - **D-13 (Default `max_body_size`)**: Default 2 MiB request body limit (`413 Payload Too Large`) enforced across all buffered and streaming HTTP requests, configurable per app and per route.
507
+ - **D-14 (WebSocket Origin Validation)**: Defaults to strict same-origin checking against the `Host` header unless `WEBSOCKETS_ALLOWED_ORIGINS` is explicitly configured.
508
+ - **CLI Development Server Default (`swiftpy start`)**: `--reload` defaults to `False` for production safety (Decision under `P10-C1`; pass `--reload` for dev hot-reloading; `--reload` is refused when `--workers > 1`).
509
+
510
+ ---
511
+
512
+ ## Project Structure
513
+
514
+ SwiftPY follows a clean, intuitive layout inspired by modern application architectures:
515
+
516
+ ```text
517
+ ├── app/
518
+ │ ├── exceptions/ # Application-level exceptions
519
+ │ ├── http/ # Controllers, middleware, and request handlers
520
+ │ ├── models/ # Active Record ORM models
521
+ │ ├── providers/ # Service providers (auto-discovered on boot)
522
+ │ │ ├── app_service_provider.py
523
+ │ │ └── database_service_provider.py
524
+ │ └── services/ # Domain business logic
525
+ ├── config/ # Environment & framework configuration
526
+ │ ├── ai.py
527
+ │ ├── app.py
528
+ │ ├── auth.py
529
+ │ ├── cache.py
530
+ │ ├── cors.py
531
+ │ ├── database.py
532
+ │ ├── events.py
533
+ │ ├── filesystems.py
534
+ │ ├── logging.py
535
+ │ ├── mail.py
536
+ │ └── queue.py
537
+ ├── database/
538
+ │ └── migrations/ # Timestamped database migrations
539
+ ├── views/ # Sandboxed Jinja2 HTML templates (admin/, mail/)
540
+ ├── swiftpy/ # SwiftPY framework core
541
+ │ ├── admin/ # Auto-introspecting admin site, RBAC & session auth
542
+ │ ├── ai/ # AI client, embeddings, vectorstores, RAG, agents, tools, prompts
543
+ │ ├── auth/ # JWT issuer, API key guards, token blacklist
544
+ │ ├── cache/ # Redis and memory cache drivers
545
+ │ ├── console/ # CLI commands, scaffolding (new/make:*), app loader
546
+ │ ├── core/ # Application kernel, DI container, config repository
547
+ │ ├── database/ # Pool, query builder, ORM, migrations, seeders, advisory lock
548
+ │ ├── events/ # Signed Redis event bus and local dispatcher
549
+ │ ├── filesystem/ # Multi-disk Storage (local, public, s3) and uploads
550
+ │ ├── helpers/ # Inflection (pluralize/snake_case), global helpers
551
+ │ ├── http/ # Router, request/response, middleware pipeline
552
+ │ ├── mail/ # Mailables, SMTP/Sendmail/API transports, templates
553
+ │ ├── queue/ # At-least-once queue workers, job serialization
554
+ │ ├── support/ # Shared identifiers, keys, Redis config, string inflection
555
+ │ ├── testing/ # TestClient, AIFake, MailFake, StorageFake, DB fixtures
556
+ │ ├── validation/ # FormRequest, fail-closed Validator, builtin & DB rules
557
+ │ └── websockets/ # WebSocket manager, connections, broadcast
558
+ ├── tests/ # Automated pytest suite
559
+ ├── docker-compose.yml # Hardened local PostgreSQL & Redis environment
560
+ ├── Dockerfile # Multi-stage production container
561
+ ├── main.py # Application entrypoint
562
+ └── pyproject.toml # Project configuration & dependencies
563
+ ```
564
+
565
+ ---
566
+
567
+ ## Development Setup
568
+
569
+ ### Prerequisites
570
+ - Python 3.12+ (`uv` recommended)
571
+ - Docker and Docker Compose
572
+
573
+ ### 1. Clone & Start Containers
574
+ ```bash
575
+ git clone https://github.com/obrainwave/swiftpy.git
576
+ cd swiftpy
577
+ cp .env.example .env # set strong APP_KEY, DB_PASSWORD, REDIS_PASSWORD, JWT_SECRET, AUTH_KEY_PEPPER
578
+ docker compose up -d
579
+ ```
580
+
581
+ ### 2. Run Migrations
582
+ ```bash
583
+ docker compose exec app swiftpy migrate
584
+ ```
585
+
586
+ ### 3. Run Test Suites & Quality Checks
587
+ ```bash
588
+ # Run full pytest test suite (requires PostgreSQL + Redis running)
589
+ uv run pytest -vv -ra --timeout=120 --timeout-method=thread
590
+
591
+ # Static type checking with Mypy in strict mode
592
+ uv run --with jinja2 --with aiosmtplib mypy swiftpy
593
+ uv run mypy --explicit-package-bases .
594
+
595
+ # Linting with Ruff
596
+ uv run ruff check .
597
+ ```
598
+
599
+ ---
600
+
601
+ ## License
602
+
603
+ SwiftPY is open-source software licensed under the [MIT License](./LICENSE).