pyrustapi 0.3.34__tar.gz → 0.7.86__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.
Files changed (129) hide show
  1. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/.gitignore +1 -0
  2. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/Cargo.lock +1 -1
  3. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/Cargo.toml +1 -1
  4. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/PKG-INFO +2 -2
  5. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/README.md +1 -1
  6. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/ROADMAP.md +1 -0
  7. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/docs/docs.md +1 -1
  8. pyrustapi-0.7.86/docs/fastapi_compatibility_and_features.md +349 -0
  9. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/docs/swagger_ui_and_dx_deviations.md +26 -0
  10. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/pyproject.toml +1 -1
  11. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/python/rustapi/__init__.py +78 -2
  12. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/python/rustapi/__init__.pyi +7 -7
  13. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/python/rustapi/openapi/__init__.py +5 -0
  14. pyrustapi-0.7.86/python/rustapi/openapi/constants.py +3 -0
  15. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/python/rustapi/openapi/docs.py +4 -1
  16. pyrustapi-0.7.86/python/rustapi/openapi/models.py +222 -0
  17. pyrustapi-0.7.86/python/rustapi/openapi/utils.py +132 -0
  18. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/python/rustapi/resolver.py +16 -2
  19. pyrustapi-0.7.86/python/rustapi/security/__init__.py +42 -0
  20. pyrustapi-0.7.86/python/rustapi/security/api_key.py +70 -0
  21. pyrustapi-0.7.86/python/rustapi/security/base.py +14 -0
  22. pyrustapi-0.7.86/python/rustapi/security/http.py +171 -0
  23. pyrustapi-0.7.86/python/rustapi/security/oauth2.py +174 -0
  24. pyrustapi-0.7.86/python/rustapi/security/open_id_connect_url.py +42 -0
  25. pyrustapi-0.7.86/python/rustapi/security/utils.py +11 -0
  26. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/python/rustapi/uploads.py +8 -0
  27. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/src/lib.rs +441 -79
  28. pyrustapi-0.7.86/test-reports/junit.xml +1 -0
  29. pyrustapi-0.7.86/tests/test_auth_and_authorization.py +122 -0
  30. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/tests/test_fastapi_all_6_mismatches.py +38 -0
  31. pyrustapi-0.7.86/tests/test_python_ecosystem_compatibility.py +114 -0
  32. pyrustapi-0.7.86/tests/test_security_openapi_parity.py +53 -0
  33. pyrustapi-0.7.86/tests/test_swagger_ui_security.py +161 -0
  34. pyrustapi-0.3.34/docs/fastapi_compatibility_and_features.md +0 -171
  35. pyrustapi-0.3.34/python/rustapi/security/__init__.py +0 -178
  36. pyrustapi-0.3.34/test-reports/junit.xml +0 -1
  37. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/.github/workflows/CI.yml +0 -0
  38. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/.github/workflows/pages.yml +0 -0
  39. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/.github/workflows/release.yml +0 -0
  40. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/.github/workflows/static.yml +0 -0
  41. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/LICENSE +0 -0
  42. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/benchmark_dashboard.html +0 -0
  43. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/benchmarks/benchmark.sh +0 -0
  44. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/benchmarks/data/fastapi_app.py +0 -0
  45. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/benchmarks/data/fastapi_cpu_app.py +0 -0
  46. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/benchmarks/data/fastapi_edge_app.py +0 -0
  47. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/benchmarks/data/fastapi_oha.py +0 -0
  48. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/benchmarks/data/large.json +0 -0
  49. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/benchmarks/data/mixed.json +0 -0
  50. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/benchmarks/data/rustapi_app.py +0 -0
  51. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/benchmarks/data/rustapi_cpu_app.py +0 -0
  52. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/benchmarks/data/rustapi_edge_app.py +0 -0
  53. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/benchmarks/data/rustapi_multiworker_app.py +0 -0
  54. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/benchmarks/data/rustapi_oha.py +0 -0
  55. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/benchmarks/data/rustapi_tier3_app.py +0 -0
  56. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/benchmarks/data/tier_app.py +0 -0
  57. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/benchmarks/data/validate.json +0 -0
  58. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/benchmarks/run_3tier_benchmark.py +0 -0
  59. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/benchmarks/run_comparison_benchmark.py +0 -0
  60. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/benchmarks/run_cpu_stability_benchmark.py +0 -0
  61. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/benchmarks/run_edgecase_benchmark.py +0 -0
  62. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/benchmarks/run_native_tier3_benchmark.py +0 -0
  63. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/benchmarks/run_real_oha_benchmark.py +0 -0
  64. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/benchmarks/run_release_highspeed_benchmark.py +0 -0
  65. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/benchmarks.html +0 -0
  66. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/crud_benchmark/benchmark_db.sqlite +0 -0
  67. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/crud_benchmark/benchmark_db.sqlite-shm +0 -0
  68. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/crud_benchmark/benchmark_db.sqlite-wal +0 -0
  69. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/crud_benchmark/benchmark_results.md +0 -0
  70. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/crud_benchmark/fastapi_crud.py +0 -0
  71. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/crud_benchmark/run_crud_benchmark.py +0 -0
  72. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/crud_benchmark/rustapi_crud.py +0 -0
  73. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/docs/3tier_architecture_and_benchmarks.md +0 -0
  74. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/docs/DeepAnalytics.md +0 -0
  75. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/docs/api-reference.html +0 -0
  76. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/docs/benchmark_dashboard.html +0 -0
  77. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/docs/benchmarks.html +0 -0
  78. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/docs/database.html +0 -0
  79. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/docs/database_engine.md +0 -0
  80. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/docs/getting-started.html +0 -0
  81. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/docs/getting_started.md +0 -0
  82. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/docs/index.html +0 -0
  83. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/docs/mcp-server.html +0 -0
  84. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/docs/mcp_server.md +0 -0
  85. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/docs/native_business_logic.md +0 -0
  86. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/docs/performance_and_benchmarks.md +0 -0
  87. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/docs/power_primitives.md +0 -0
  88. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/docs/pyrustapi-logo (1).png +0 -0
  89. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/docs/report_01_.md +0 -0
  90. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/docs/response_types.md +0 -0
  91. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/docs/security-power-modules.html +0 -0
  92. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/examples/app.py +0 -0
  93. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/index.html +0 -0
  94. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/performance-reports/cpu_intensive_stability_report.md +0 -0
  95. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/performance-reports/fastapi_vs_hybrid_vs_native.md +0 -0
  96. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/performance-reports/fastapi_vs_rustapi_comparison.md +0 -0
  97. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/performance-reports/fastapi_vs_rustapi_crud_benchmark.md +0 -0
  98. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/performance-reports/performance_and_benchmarks.md +0 -0
  99. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/performance-reports/rust_native_edgecases_report.md +0 -0
  100. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/performance-reports/three_tier_architecture_evaluation.md +0 -0
  101. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/pyrustapi-logo.png +0 -0
  102. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/python/pyrustapi/__init__.py +0 -0
  103. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/python/rustapi/background.py +0 -0
  104. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/python/rustapi/depends.py +0 -0
  105. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/python/rustapi/encoders.py +0 -0
  106. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/python/rustapi/exceptions.py +0 -0
  107. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/python/rustapi/middleware/__init__.py +0 -0
  108. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/python/rustapi/middleware/cors.py +0 -0
  109. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/python/rustapi/param_functions.py +0 -0
  110. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/python/rustapi/py.typed +0 -0
  111. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/python/rustapi/responses/__init__.py +0 -0
  112. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/python/rustapi/router.py +0 -0
  113. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/python/rustapi/rust.rs +0 -0
  114. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/python/rustapi/sse.py +0 -0
  115. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/python/rustapi/staticfiles.py +0 -0
  116. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/python/rustapi/status.py +0 -0
  117. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/scratch/.gitkeep +0 -0
  118. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/tests/test_3tier_architecture.py +0 -0
  119. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/tests/test_basic.py +0 -0
  120. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/tests/test_database_engine.py +0 -0
  121. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/tests/test_fastapi_compatibility.py +0 -0
  122. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/tests/test_file_uploads_and_dx.py +0 -0
  123. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/tests/test_imports.py +0 -0
  124. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/tests/test_native_routes.py +0 -0
  125. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/tests/test_overrides_and_coercion.py +0 -0
  126. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/tests/test_reload_and_logging.py +0 -0
  127. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/tests/test_response_model.py +0 -0
  128. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/tests/test_responses_and_req_json.py +0 -0
  129. {pyrustapi-0.3.34 → pyrustapi-0.7.86}/tests/test_rust_power_modules.py +0 -0
@@ -24,3 +24,4 @@ build/
24
24
 
25
25
  # Local docs & references
26
26
  fastapi_docs/
27
+ fastapi/
@@ -1427,7 +1427,7 @@ dependencies = [
1427
1427
 
1428
1428
  [[package]]
1429
1429
  name = "rustapi"
1430
- version = "0.3.34"
1430
+ version = "0.7.86"
1431
1431
  dependencies = [
1432
1432
  "argon2",
1433
1433
  "base64 0.23.0",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "rustapi"
3
- version = "0.3.34"
3
+ version = "0.7.86"
4
4
  edition = "2021"
5
5
  description = "A FastAPI-style Python web framework with a Rust tokio/hyper core, plus a built-in MCP server (JSON-RPC over Streamable HTTP)."
6
6
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyrustapi
3
- Version: 0.3.34
3
+ Version: 0.7.86
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Programming Language :: Rust
6
6
  Classifier: Programming Language :: Python :: 3
@@ -49,7 +49,7 @@ Project-URL: Repository, https://github.com/rajboopathiking/rustapi
49
49
 
50
50
  | Feature Area | Implementation Details |
51
51
  | :--- | :--- |
52
- | **FastAPI Compatibility** | `FastAPI` & `Request` aliases, `status` codes, `EventSourceResponse` (SSE), `jsonable_encoder`, `Query`/`Body`/`Path`, `OAuth2PasswordBearer`, `app.frontend()`. |
52
+ | **FastAPI Compatibility** | `FastAPI` & `Request` aliases, `status` codes, `EventSourceResponse` (SSE), `jsonable_encoder`, `Query`/`Body`/`Path`, `OAuth2PasswordBearer`, `HTTPBearer`, `APIKeyHeader`, `OpenIdConnect`, `get_openapi`, `app.frontend()`. |
53
53
  | **HTTP & Routing** | FastAPI-style `@app.get`, `@app.post`, `APIRouter`, `Depends`, `app.dependency_overrides`, `response_model`, `reload=True`. |
54
54
  | **Rust-Native Tier 3 Routes** | `app.add_native_route()` zero-GIL C-speed fast-paths (**50,000+ req/sec**). |
55
55
  | **Database Engine** | `sqlx` SQLite & PostgreSQL pool (`app.connect_db()`) with zero-copy JSON streaming (`db.query_json()`). |
@@ -16,7 +16,7 @@
16
16
 
17
17
  | Feature Area | Implementation Details |
18
18
  | :--- | :--- |
19
- | **FastAPI Compatibility** | `FastAPI` & `Request` aliases, `status` codes, `EventSourceResponse` (SSE), `jsonable_encoder`, `Query`/`Body`/`Path`, `OAuth2PasswordBearer`, `app.frontend()`. |
19
+ | **FastAPI Compatibility** | `FastAPI` & `Request` aliases, `status` codes, `EventSourceResponse` (SSE), `jsonable_encoder`, `Query`/`Body`/`Path`, `OAuth2PasswordBearer`, `HTTPBearer`, `APIKeyHeader`, `OpenIdConnect`, `get_openapi`, `app.frontend()`. |
20
20
  | **HTTP & Routing** | FastAPI-style `@app.get`, `@app.post`, `APIRouter`, `Depends`, `app.dependency_overrides`, `response_model`, `reload=True`. |
21
21
  | **Rust-Native Tier 3 Routes** | `app.add_native_route()` zero-GIL C-speed fast-paths (**50,000+ req/sec**). |
22
22
  | **Database Engine** | `sqlx` SQLite & PostgreSQL pool (`app.connect_db()`) with zero-copy JSON streaming (`db.query_json()`). |
@@ -38,6 +38,7 @@ RustAPI follows a **3-Tier Architecture** that combines FastAPI's intuitive Pyth
38
38
  - [x] **Strict Parameter Type Coercion:** Automatic casting of path and query params (`int`, `float`, `bool`) with structured 422 HTTP validation error responses.
39
39
  - [x] **Request & Response Ergonomics:** `req.json()`, `req.body`, `req.headers`, `req.cookies`, `JSONResponse`, `HTMLResponse`, `PlainTextResponse`, `RedirectResponse`, `StreamingResponse`, `UploadFile`, and `WebSocket`.
40
40
  - [x] **OpenAPI & Interactive Docs:** Automatic `/openapi.json` generation and embedded Swagger UI at `/docs`.
41
+ - [x] **Swagger UI Security & Interactive Authorize (v0.7.86):** Automatic `components.securitySchemes` generation and interactive top **Authorize** lock button (🔒) in Swagger UI (`/docs`) for all 8 `rustapi.security` schemes.
41
42
  - [x] **Native Telemetry & Access Logging:** Real-time terminal request access logs (`INFO: 127.0.0.1 - "GET /docs HTTP/1.1" 200 - 0.85ms`).
42
43
  - [x] **Model Context Protocol (MCP) Server:** Embedded MCP tools (`@app.tool()`), resources (`@app.resource()`), and prompts (`@app.prompt()`) accessible at `POST /mcp`.
43
44
 
@@ -21,7 +21,7 @@
21
21
 
22
22
  | Section | Content Covered | Developer Benefit |
23
23
  | :--- | :--- | :--- |
24
- | **FastAPI Compatibility** | `from rustapi import FastAPI, Request, status`, `get_swagger_ui_html`, `EventSourceResponse` (SSE), `jsonable_encoder`, `Query`/`Body`/`Path`, `app.frontend()`. | Complete FastAPI drop-in compatibility and SSE/SPA helpers (see [`fastapi_compatibility_and_features.md`](fastapi_compatibility_and_features.md)). |
24
+ | **FastAPI Compatibility** | `from rustapi import FastAPI, Request, status`, `from rustapi.security import OAuth2PasswordBearer, HTTPBearer, APIKeyHeader, OpenIdConnect, SecurityScopes`, `from rustapi.openapi import get_swagger_ui_html, get_openapi, models`. | 100% FastAPI drop-in compatibility, security dependency modules, OpenAPI 3.1.0 models & generators, and SSE/SPA helpers (see [`fastapi_compatibility_and_features.md`](fastapi_compatibility_and_features.md)). |
25
25
  | **§1. Core Engine & Routing** | `Engine()`, `@app.get`, `@app.post`, `@app.put`, `@app.delete`, `@app.patch`, `sync` and `async def` handlers. | Quick reference for routing syntax & handler types. |
26
26
  | **§2. Request Metadata & Errors** | `req` (`PyRequest`) object, custom `Response(..., status_code, headers)`, `HTTPException`. | Inspect headers, query/path params, and raise clean HTTP errors. |
27
27
  | **💡 FastAPI `Request` vs `req`** | Full property comparison table (`method`, `path`, `headers`, `cookies`, `form`, `files`, `body`, `json()`). | Eliminates confusion when migrating from FastAPI to RustAPI. |
@@ -0,0 +1,349 @@
1
+ # 🚀 FastAPI Compatibility & Feature Guide in RustAPI (`pyrustapi`)
2
+
3
+ RustAPI (`pyrustapi`) provides **1:1 FastAPI compatibility**, enabling developers to migrate existing FastAPI applications or write clean FastAPI-style code backed by a high-performance **Rust Tokio / Hyper core**.
4
+
5
+ ---
6
+
7
+ ## 📌 Summary of Features & Compatibility Layer
8
+
9
+ | Feature | Import Path | Description |
10
+ | :--- | :--- | :--- |
11
+ | **`FastAPI` Alias** | `from rustapi import FastAPI` | Class alias for `Engine` for 100% drop-in FastAPI compatibility. |
12
+ | **`Request` Alias** | `from rustapi import Request` | Class alias for `PyRequest`. |
13
+ | **Status Codes** | `from rustapi import status` | Starlette/FastAPI status code constants (`status.HTTP_200_OK`, `status.HTTP_404_NOT_FOUND`, etc.). |
14
+ | **OpenAPI & Specs** | `from rustapi.openapi import get_swagger_ui_html, get_redoc_html, get_openapi, models` | Interactive Swagger UI (`/docs`), ReDoc (`/redoc`), and OpenAPI 3.1.0 specification models & generators. |
15
+ | **Response Classes** | `from rustapi.responses import FileResponse, HTMLResponse, JSONResponse, PlainTextResponse, RedirectResponse, StreamingResponse` | Full range of FastAPI response classes. |
16
+ | **CORS Middleware** | `from rustapi.middleware.cors import CORSMiddleware` | Middleware class & `app.add_middleware()` for configuring cross-origin requests. |
17
+ | **Server-Sent Events** | `from rustapi import EventSourceResponse, ServerSentEvent, format_sse_event` | Real-time SSE streaming for AI/LLM tokens and Model Context Protocol (MCP). |
18
+ | **Data Encoders** | `from rustapi import jsonable_encoder` | Convert Pydantic models, Dataclasses, Datetime, UUID, and dicts to JSON-serializable primitives. |
19
+ | **Parameter Markers** | `from rustapi import Body, Query, Path, Header, Cookie, Form, File, Security` | Location markers for dependency injection and parameter validation. |
20
+ | **Security Modules** | `from rustapi.security import OAuth2PasswordBearer, OAuth2AuthorizationCodeBearer, HTTPBearer, HTTPBasic, APIKeyHeader, APIKeyQuery, APIKeyCookie, OpenIdConnect, SecurityScopes, SecurityBase` | 100% FastAPI-compatible security authentication dependency helpers. |
21
+ | **Frontend Serving** | `app.frontend("/", directory="dist")` / `router.frontend(...)` | Serve built React, Vue, Svelte, or Vite single-page apps with client-side routing fallback. |
22
+ | **WebSocket Exceptions**| `from rustapi import WebSocketDisconnect, WebSocketException` | Exception classes for handling WebSocket disconnection events. |
23
+
24
+ ---
25
+
26
+ ## 1. FastAPI Aliases & Status Codes
27
+
28
+ Drop-in FastAPI imports work directly out of the box:
29
+
30
+ ```python
31
+ from rustapi import FastAPI, Request, status, HTTPException
32
+
33
+ app = FastAPI()
34
+
35
+ @app.get("/health", status_code=status.HTTP_200_OK)
36
+ def health_check(request: Request):
37
+ if not request:
38
+ raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Bad request")
39
+ return {"status": "healthy"}
40
+ ```
41
+
42
+ ---
43
+
44
+ ## 2. Interactive API Documentation (`Swagger UI` & `ReDoc`)
45
+
46
+ Serve custom interactive documentation endpoints using `get_swagger_ui_html` and `get_redoc_html`:
47
+
48
+ ```python
49
+ from rustapi import FastAPI
50
+ from rustapi.openapi import get_swagger_ui_html, get_redoc_html, get_openapi
51
+
52
+ app = FastAPI()
53
+
54
+ @app.get("/docs", include_in_schema=False)
55
+ def custom_swagger_ui():
56
+ return get_swagger_ui_html(
57
+ openapi_url="/openapi.json",
58
+ title="My Custom API Docs",
59
+ )
60
+
61
+ @app.get("/openapi.json", include_in_schema=False)
62
+ def get_custom_openapi():
63
+ return get_openapi(
64
+ title="RustAPI Application",
65
+ version="1.0.0",
66
+ description="OpenAPI 3.1.0 specification generated dynamically.",
67
+ routes=app.routes,
68
+ )
69
+ ```
70
+
71
+ ### Security Modules (`rustapi.security`)
72
+
73
+ `rustapi.security` provides complete 1:1 FastAPI compatibility for security dependency schemes:
74
+
75
+ ```python
76
+ from rustapi import Depends, FastAPI
77
+ from rustapi.security import (
78
+ OAuth2PasswordBearer,
79
+ OAuth2PasswordRequestForm,
80
+ HTTPBearer,
81
+ HTTPBasic,
82
+ APIKeyHeader,
83
+ SecurityScopes,
84
+ )
85
+
86
+ app = FastAPI()
87
+ oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
88
+ api_key_scheme = APIKeyHeader(name="X-API-Key")
89
+
90
+ @app.post("/token")
91
+ def login(form_data: OAuth2PasswordRequestForm = Depends()):
92
+ return {"access_token": form_data.username, "token_type": "bearer"}
93
+
94
+ @app.get("/protected")
95
+ def protected_route(token: str = Depends(oauth2_scheme), api_key: str = Depends(api_key_scheme)):
96
+ return {"token": token, "api_key": api_key}
97
+ ```
98
+
99
+ @app.get("/redoc", include_in_schema=False)
100
+ def custom_redoc_ui():
101
+ return get_redoc_html(
102
+ openapi_url="/openapi.json",
103
+ title="My ReDoc API",
104
+ )
105
+ ```
106
+
107
+ ---
108
+
109
+ ## 3. Server-Sent Events (SSE) for AI & Streaming
110
+
111
+ Stream AI/LLM tokens or real-time event updates over HTTP using `EventSourceResponse` and `ServerSentEvent`:
112
+
113
+ ```python
114
+ from collections.abc import AsyncIterable
115
+ from rustapi import FastAPI, EventSourceResponse, ServerSentEvent
116
+
117
+ app = FastAPI()
118
+
119
+ @app.get("/ai-stream", response_class=EventSourceResponse)
120
+ async def stream_ai_tokens():
121
+ tokens = ["Hello", " world", " from", " RustAPI", " SSE!"]
122
+ for token in tokens:
123
+ yield ServerSentEvent(data={"token": token}, event="message")
124
+ ```
125
+
126
+ Or format raw SSE wire bytes using `format_sse_event`:
127
+
128
+ ```python
129
+ from rustapi import format_sse_event
130
+
131
+ raw_bytes = format_sse_event(
132
+ data_str='{"status": "processing"}',
133
+ event="ping",
134
+ id="evt_001",
135
+ retry=3000,
136
+ )
137
+ ```
138
+
139
+ ---
140
+
141
+ ## 4. Frontend Single-Page App (SPA) Serving
142
+
143
+ Serve compiled React, Vue, Svelte, or Vite frontend build directories directly from `rustapi`:
144
+
145
+ ```python
146
+ from rustapi import FastAPI
147
+
148
+ app = FastAPI()
149
+
150
+ # Serves 'dist/index.html' for '/' and static assets from 'dist/'
151
+ app.frontend("/", directory="dist")
152
+
153
+ # Or attach frontend serving to sub-routers
154
+ from rustapi import APIRouter
155
+ admin_router = APIRouter(prefix="/admin")
156
+ admin_router.frontend("/", directory="admin-dist")
157
+ app.include_router(admin_router)
158
+ ```
159
+
160
+ ---
161
+
162
+ ## 5. Security & Authentication Dependencies
163
+
164
+ Use pre-packaged security helpers for OAuth2, HTTP Bearer, and API Keys:
165
+
166
+ ```python
167
+ from rustapi import FastAPI, Depends, HTTPException, status
168
+ from rustapi.security import OAuth2PasswordBearer, HTTPBearer, APIKeyHeader
169
+
170
+ app = FastAPI()
171
+
172
+ oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
173
+ bearer_scheme = HTTPBearer()
174
+ api_key_scheme = APIKeyHeader(name="X-API-Key")
175
+
176
+ @app.get("/users/me")
177
+ def read_current_user(token: str = Depends(oauth2_scheme)):
178
+ if not token:
179
+ raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")
180
+ return {"user": "alice", "token": token}
181
+ ```
182
+
183
+ ---
184
+
185
+ ## 6. Parameter Markers & `jsonable_encoder`
186
+
187
+ Use standard FastAPI parameter placement markers and data converters:
188
+
189
+ ```python
190
+ from dataclasses import dataclass
191
+ from rustapi import FastAPI, Query, Path, Body, jsonable_encoder
192
+
193
+ app = FastAPI()
194
+
195
+ @dataclass
196
+ class User:
197
+ id: int
198
+ username: str
199
+
200
+ @app.get("/items/{item_id}")
201
+ def get_item(
202
+ item_id: int = Path(..., ge=1),
203
+ q: str = Query(None, max_length=50),
204
+ ):
205
+ user = User(id=1, username="bob")
206
+ # Convert dataclass/Pydantic model to JSON dict
207
+ user_dict = jsonable_encoder(user)
208
+ return {"item_id": item_id, "query": q, "user": user_dict}
209
+
210
+
211
+ ---
212
+
213
+ ## 7. Python Ecosystem Compatibility Matrix
214
+
215
+ `rustapi` is built to seamlessly integrate with standard Python database, machine learning, security, and utility libraries:
216
+
217
+ | Library Domain | Package | Verified Feature & Usage | Status |
218
+ | :--- | :--- | :--- | :--- |
219
+ | **Data Validation** | `pydantic` v2 | `BaseModel`, `Field`, `validator`, `EmailStr` in route bodies | ✅ Verified |
220
+ | **HTTP Clients** | `httpx`, `requests` | `httpx.AsyncClient` & `requests` testing, multi-part uploads & streams | ✅ Verified |
221
+ | **Databases & ORMs** | `sqlite3`, `sqlalchemy` | Session context managers & query execution inside `Depends(get_db)` | ✅ Verified |
222
+ | **Image Processing** | `PIL` / `Pillow` | Image decoding & matrix transformations on `UploadFile` streams | ✅ Verified |
223
+ | **JWT & Security** | `pyjwt`, `passlib`, `argon2` | `jwt.encode`, `jwt.decode`, password hashing inside auth endpoints | ✅ Verified |
224
+ | **Numeric & ML** | `numpy` | `np.array` operations on request payloads & image byte arrays | ✅ Verified |
225
+
226
+ ```python
227
+ import io
228
+ import jwt
229
+ import numpy as np
230
+ from PIL import Image
231
+ from pydantic import BaseModel, Field
232
+ from rustapi import FastAPI, Request, Depends, HTTPException
233
+ from rustapi.uploads import UploadFile
234
+
235
+ app = FastAPI()
236
+
237
+ class AnalysisModel(BaseModel):
238
+ project_name: str = Field(..., min_length=2)
239
+
240
+ @app.post("/analyze")
241
+ async def analyze(req: Request):
242
+ if "photo" not in req.files:
243
+ raise HTTPException(status_code=400, detail="No photo uploaded")
244
+
245
+ file_obj: UploadFile = req.files["photo"][0]
246
+ img_bytes = await file_obj.read() # Both await file.read() and file.read() work
247
+
248
+ # Process image with Pillow & NumPy
249
+ image = Image.open(io.BytesIO(img_bytes)).convert("L")
250
+ array = np.array(image)
251
+ brightness = float(np.mean(array))
252
+
253
+ # Encode JWT response
254
+ token = jwt.encode({"brightness": brightness}, "secret", algorithm="HS256")
255
+ return {"status": "success", "token": token}
256
+ ```
257
+
258
+ ---
259
+
260
+ ## 9. Full Python Ecosystem Interoperability & Release v0.7.86
261
+
262
+ `pyrustapi` features zero-limitation compatibility with the Python library ecosystem:
263
+
264
+ | Library Category | Supported Packages | Integration Behavior |
265
+ | :--- | :--- | :--- |
266
+ | **Databases & Async ORMs** | `SQLAlchemy`, `asyncpg`, `aiosqlite`, `tortoise-orm`, `peewee` | Context managers & `yield` generator sessions in `Depends(get_db)` |
267
+ | **Data Validation** | `pydantic` v2, `msgspec`, `attrs`, `dataclasses` | Full request body parsing, field validation & 422 coercion |
268
+ | **Security & Auth** | `pyjwt`, `python-jose`, `passlib`, `argon2-cffi`, `cryptography` | Bearer token validation, password hashing & custom claims |
269
+ | **Machine Learning & AI** | `torch`, `tensorflow`, `scikit-learn`, `onnxruntime`, `xgboost` | Model inference & tensor computation on upload byte streams |
270
+ | **HTTP & Async Testing** | `httpx`, `requests`, `aiohttp`, `starlette.testclient` | ASGI 3.0 (`app(scope, receive, send)`) & `ASGITransport` support |
271
+ | **Async Task Queues** | `celery`, `redis`, `rq`, `dramatiq` | Background task enqueueing inside async route handlers |
272
+ | **Domain Specific & Rare** | `biopython`, `rdkit`, `geopandas`, `cv2`, `librosa`, `numba` | Native C-extensions, CUDA acceleration & JIT compiled functions |
273
+
274
+ ### Support for Rare & Native C-Extension Libraries
275
+
276
+ `pyrustapi` is **not** an interpreter transpile layer; it executes directly inside the native CPython runtime via PyO3 C-bindings. Therefore, **100% of Python libraries work with zero limitations**:
277
+
278
+ - **C / C++ / Cython Extensions** (`ctypes`, `cffi`, `pybind11`, `.so` / `.dylib`): Linked directly to standard CPython ABI memory.
279
+ - **Scientific & ML Accelerators** (`torch`, `cuda`, `scikit-learn`, `numba`, `scipy`): GPU tensor execution & JIT function evaluation operate seamlessly.
280
+ - **Domain-Specific Packages** (`biopython`, `rdkit`, `astropy`, `cv2`, `librosa`): Native binary dependencies load and execute without modification.
281
+
282
+ ### Authentication & Authorization Code Pattern (v0.7.86)
283
+
284
+ ```python
285
+ import jwt
286
+ from rustapi import FastAPI, Request, Depends, HTTPException, status
287
+ from rustapi.security import (
288
+ HTTPBearer,
289
+ HTTPAuthorizationCredentials,
290
+ OAuth2PasswordBearer,
291
+ APIKeyHeader,
292
+ )
293
+
294
+ app = FastAPI(title="Production Auth API", version="0.7.86")
295
+
296
+ SECRET_KEY = "your-256-bit-secret-key"
297
+ ALGORITHM = "HS256"
298
+
299
+ # Security scheme declarations
300
+ bearer_scheme = HTTPBearer()
301
+ oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/token")
302
+ api_key_scheme = APIKeyHeader(name="X-API-Key")
303
+
304
+ async def get_current_user(token: str = Depends(oauth2_scheme)) -> dict:
305
+ if not token:
306
+ raise HTTPException(
307
+ status_code=status.HTTP_401_UNAUTHORIZED,
308
+ detail="Authorization token required",
309
+ )
310
+ try:
311
+ payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
312
+ return {"username": payload["sub"], "role": payload["role"]}
313
+ except Exception:
314
+ raise HTTPException(
315
+ status_code=status.HTTP_401_UNAUTHORIZED,
316
+ detail="Invalid or expired token",
317
+ )
318
+
319
+ async def get_admin_user(current_user: dict = Depends(get_current_user)) -> dict:
320
+ if current_user["role"] not in ("admin", "sysadmin"):
321
+ raise HTTPException(
322
+ status_code=status.HTTP_403_FORBIDDEN,
323
+ detail="Admin privileges required",
324
+ )
325
+ return current_user
326
+
327
+ @app.get("/users/me")
328
+ async def read_current_user(user: dict = Depends(get_current_user)):
329
+ return {"user": user["username"], "role": user["role"]}
330
+
331
+ @app.get("/admin/audit-logs")
332
+ async def read_audit_logs(admin: dict = Depends(get_admin_user)):
333
+ return {"logs": [], "accessed_by": admin["username"]}
334
+ ```
335
+
336
+ ### Key Improvements in Release v0.7.86:
337
+
338
+ 1. **Swagger UI Security & Interactive Authorize (Top Lock Button 🔒)**:
339
+ Automatically registers `components.securitySchemes` in `/openapi.json` and attaches `security` operation requirements across all 8 security schemes (`HTTPBearer`, `OAuth2PasswordBearer`, `OAuth2AuthorizationCodeBearer`, `APIKeyHeader`, `APIKeyQuery`, `APIKeyCookie`, `HTTPBasic`, `HTTPDigest`, `OpenIdConnect`), rendering the top **Authorize** lock button in `/docs`.
340
+ 2. **Recursive Sub-Dependency OpenAPI SecuritySchemes**:
341
+ Automatically unrolls nested dependencies (`Depends(get_admin_user)` $\rightarrow$ `Depends(get_current_user)` $\rightarrow$ `Depends(oauth2_scheme)`) to output `components.securitySchemes` in `/openapi.json`, enabling interactive auth testing in Swagger UI (`/docs`).
342
+ 3. **HTTPException Status Code Preservation**:
343
+ Directly propagates custom status codes (`401 Unauthorized`, `403 Forbidden`, `404 Not Found`, `422 Unprocessable Entity`) without falling back to 500 Internal Server Errors.
344
+ 4. **Recursive Dependency & Request Injection**:
345
+ Resolves nested `Depends(sub_dep)` chains and injects `request: Request` automatically across all dependency tiers.
346
+ 5. **Dual Async / Sync Payload Helpers**:
347
+ Provides `AwaitableDict` for `req.json()` and `AwaitableBytes` for `UploadFile.read()`, allowing both `await req.json()` and `req.json()` inside sync/async ML route handlers.
348
+
349
+
@@ -105,6 +105,32 @@ Visiting `http://127.0.0.1:8000/docs` will render the customized Swagger UI inte
105
105
 
106
106
  ---
107
107
 
108
+ ### Swagger UI Security & Interactive Authorize (Top Lock Button) (v0.7.86)
109
+
110
+ RustAPI automatically detects all 8 security dependencies from `rustapi.security` (`HTTPBearer`, `OAuth2PasswordBearer`, `OAuth2AuthorizationCodeBearer`, `APIKeyHeader`, `APIKeyQuery`, `APIKeyCookie`, `HTTPBasic`, `HTTPDigest`, `OpenIdConnect`) across direct and nested dependencies (`Depends(...)`).
111
+
112
+ When security dependencies are declared on route handlers:
113
+ 1. **OpenAPI SecuritySchemes**: RustAPI auto-populates `components.securitySchemes` in `/openapi.json`.
114
+ 2. **Path Operation Security**: Each protected route receives `"security": [{ "<scheme_name>": [] }]`.
115
+ 3. **Top Authorize Lock Button (🔒)**: Swagger UI served at `/docs` displays the interactive **Authorize** top lock button. Developers can click Authorize, enter Bearer tokens, API keys, or OAuth2 credentials, and test protected endpoints directly within Swagger UI!
116
+
117
+ ```python
118
+ from rustapi import FastAPI, Depends
119
+ from rustapi.security import OAuth2PasswordBearer, HTTPBearer, APIKeyHeader
120
+
121
+ app = FastAPI(title="Secure API", version="0.7.86")
122
+
123
+ oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
124
+ bearer_scheme = HTTPBearer()
125
+ api_key_scheme = APIKeyHeader(name="X-API-Key")
126
+
127
+ @app.get("/users/me")
128
+ def read_user(token: str = Depends(oauth2_scheme)):
129
+ return {"token": token}
130
+ ```
131
+
132
+ ---
133
+
108
134
  ## 2. Route Handler Signature (req vs Parameter Injection)
109
135
 
110
136
  ### FastAPI DX
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "pyrustapi"
7
- version = "0.3.34"
7
+ version = "0.7.86"
8
8
  description = "A FastAPI-style Python web framework with a Rust tokio/hyper core, and a built-in MCP server."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -2,6 +2,7 @@ from typing import Any, Dict, Optional
2
2
  from .status import status
3
3
  from ._rustapi import (
4
4
  Engine,
5
+ Route,
5
6
  PyRequest,
6
7
  UploadFile,
7
8
  WebSocket,
@@ -30,7 +31,7 @@ from .param_functions import (
30
31
  )
31
32
 
32
33
  from .responses import FileResponse
33
- from . import responses, middleware
34
+ from . import responses, middleware, security, openapi
34
35
 
35
36
  class FastAPI(Engine):
36
37
  """FastAPI-compatible application class wrapping the Rust Tokio core engine."""
@@ -64,6 +65,78 @@ class FastAPI(Engine):
64
65
  """Add middleware (such as CORSMiddleware) to application configuration."""
65
66
  self.middlewares.append((middleware_cls, kwargs))
66
67
 
68
+ async def __call__(self, scope: Dict[str, Any], receive: Any, send: Any):
69
+ """ASGI 3.0 interface implementation for ASGITransport, TestClient, uvicorn, and pytest."""
70
+ import inspect, json
71
+ if scope["type"] == "lifespan":
72
+ while True:
73
+ message = await receive()
74
+ if message["type"] == "lifespan.startup":
75
+ for handler in getattr(self, "startup_handlers", []):
76
+ if inspect.iscoroutinefunction(handler):
77
+ await handler()
78
+ else:
79
+ handler()
80
+ await send({"type": "lifespan.startup.complete"})
81
+ elif message["type"] == "lifespan.shutdown":
82
+ for handler in getattr(self, "shutdown_handlers", []):
83
+ if inspect.iscoroutinefunction(handler):
84
+ await handler()
85
+ else:
86
+ handler()
87
+ await send({"type": "lifespan.shutdown.complete"})
88
+ break
89
+ return
90
+
91
+ if scope["type"] != "http":
92
+ return
93
+
94
+ method = scope.get("method", "GET")
95
+ path = scope.get("path", "/")
96
+ query_string = scope.get("query_string", b"").decode("latin1")
97
+ headers = {k.decode("latin1").lower(): v.decode("latin1") for k, v in scope.get("headers", [])}
98
+
99
+ body_bytes = bytearray()
100
+ while True:
101
+ msg = await receive()
102
+ if msg["type"] == "http.request":
103
+ body_bytes.extend(msg.get("body", b""))
104
+ if not msg.get("more_body", False):
105
+ break
106
+
107
+ body_str = body_bytes.decode("utf-8", errors="replace")
108
+
109
+ try:
110
+ status_code, response_body, resp_headers = await self.dispatch_request(
111
+ method, path, query_string, headers, body_str
112
+ )
113
+ except Exception as exc:
114
+ import logging
115
+ logging.getLogger("rustapi").error(f"Error handling ASGI request [{method} {path}]: {exc}", exc_info=True)
116
+ handler = self.exception_handlers.get(type(exc)) or self.exception_handlers.get(getattr(exc, "status_code", None))
117
+ if handler:
118
+ req = PyRequest(method=method, path=path, path_params={}, query_params={}, headers=headers, cookies={}, form={}, files={}, body=body_str)
119
+ resp = await handler(req, exc) if inspect.iscoroutinefunction(handler) else handler(req, exc)
120
+ status_code = getattr(resp, "status_code", 500)
121
+ response_body = getattr(resp, "content", str(exc))
122
+ resp_headers = getattr(resp, "headers", {"content-type": "application/json"})
123
+ else:
124
+ status_code = getattr(exc, "status_code", 500)
125
+ detail = getattr(exc, "detail", str(exc))
126
+ response_body = f'{{"detail": "{detail}"}}' if isinstance(detail, str) else json.dumps({"detail": detail})
127
+ resp_headers = {"content-type": "application/json"}
128
+
129
+ encoded_headers = [(k.encode("latin1"), v.encode("latin1")) for k, v in resp_headers.items()]
130
+ await send({
131
+ "type": "http.response.start",
132
+ "status": status_code,
133
+ "headers": encoded_headers,
134
+ })
135
+ await send({
136
+ "type": "http.response.body",
137
+ "body": response_body.encode("utf-8") if isinstance(response_body, str) else response_body,
138
+ })
139
+
67
140
  def exception_handler(self, exc_class_or_status_code: Any):
68
141
  """Register an exception handler decorator for an exception class or status code."""
69
142
  def decorator(func: Any):
@@ -152,10 +225,11 @@ class RedirectResponse(Response):
152
225
  return Response.__new__(cls, content="", status_code=status_code, headers=h)
153
226
 
154
227
 
155
- __version__ = "0.3.34"
228
+ __version__ = "0.7.86"
156
229
  __all__ = [
157
230
  "Engine",
158
231
  "FastAPI",
232
+ "Route",
159
233
  "PyRequest",
160
234
  "Request",
161
235
  "Response",
@@ -167,6 +241,8 @@ __all__ = [
167
241
  "FileResponse",
168
242
  "responses",
169
243
  "middleware",
244
+ "security",
245
+ "openapi",
170
246
  "EventSourceResponse",
171
247
  "ServerSentEvent",
172
248
  "format_sse_event",
@@ -19,13 +19,13 @@ class PyRequest:
19
19
  self,
20
20
  method: str,
21
21
  path: str,
22
- path_params: Dict[str, str],
23
- query_params: Dict[str, str],
24
- headers: Dict[str, str],
25
- cookies: Dict[str, str],
26
- form: Dict[str, str],
27
- files: Dict[str, List[UploadFile]],
28
- body: str,
22
+ path_params: Optional[Dict[str, str]] = ...,
23
+ query_params: Optional[Dict[str, str]] = ...,
24
+ headers: Optional[Dict[str, str]] = ...,
25
+ cookies: Optional[Dict[str, str]] = ...,
26
+ form: Optional[Dict[str, str]] = ...,
27
+ files: Optional[Dict[str, List[UploadFile]]] = ...,
28
+ body: Optional[str] = ...,
29
29
  ) -> None: ...
30
30
  def json(self) -> Any: ...
31
31
 
@@ -4,10 +4,15 @@ from .docs import (
4
4
  get_swagger_ui_oauth2_redirect_html,
5
5
  swagger_ui_default_parameters,
6
6
  )
7
+ from .utils import get_openapi
8
+ from . import models, utils
7
9
 
8
10
  __all__ = [
9
11
  "get_swagger_ui_html",
10
12
  "get_redoc_html",
11
13
  "get_swagger_ui_oauth2_redirect_html",
12
14
  "swagger_ui_default_parameters",
15
+ "get_openapi",
16
+ "models",
17
+ "utils",
13
18
  ]
@@ -0,0 +1,3 @@
1
+ METHODS_WITH_BODY = {"GET", "HEAD", "POST", "PUT", "DELETE", "PATCH"}
2
+ REF_PREFIX = "#/components/schemas/"
3
+ REF_TEMPLATE = "#/components/schemas/{model}"
@@ -1,6 +1,9 @@
1
1
  import json
2
2
  from typing import Any, Optional
3
- from .. import Response
3
+ try:
4
+ from .._rustapi import Response
5
+ except ImportError:
6
+ from .._rustapi import PyResponse as Response
4
7
 
5
8
  class HTMLResponse(Response):
6
9
  """HTML response wrapper setting Content-Type: text/html; charset=utf-8."""