Skip to content

Commit 26ed45f

Browse files
committed
Fixed docs
1 parent cf9a554 commit 26ed45f

File tree

15 files changed

+57
-190
lines changed

15 files changed

+57
-190
lines changed

docs/advanced/architecture.md

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -273,23 +273,37 @@ class RequestData:
273273
**Extractor Interface:**
274274

275275
```python
276-
class BaseAsyncRequestDataExtractor:
277-
"""Base async extractor"""
276+
class BaseAsyncRequestDataExtractor(BaseRequestDataExtractor, ABC):
277+
"""Base async extractor — overrides only body/form/files as async"""
278+
279+
@classmethod
280+
@abstractmethod
281+
async def _get_body(cls, request: Any) -> bytes | str | dict: ...
282+
283+
@classmethod
284+
@abstractmethod
285+
async def _get_form_data(cls, request: Any) -> dict: ...
286+
287+
@classmethod
288+
@abstractmethod
289+
async def _get_files(cls, request: Any) -> dict: ...
278290

279291
@classmethod
280292
async def extract_request_data(cls, env: RequestEnvelope) -> RequestData:
281-
"""Extract data from framework request"""
293+
request = env.request
282294
return RequestData(
283-
path_params=await cls._get_path_params(env),
284-
query_params=await cls._get_query_params(env),
285-
headers=await cls._get_headers(env),
286-
cookies=await cls._get_cookies(env),
287-
body=await cls._get_body(env),
288-
form_data=await cls._get_form_data(env),
289-
files=await cls._get_files(env),
295+
path_params=env.path_params or cls._get_path_params(request),
296+
query_params=cls._get_query_params(request), # sync
297+
headers=cls._normalize_headers(cls._get_headers(request)), # sync
298+
cookies=cls._get_cookies(request), # sync
299+
body=await cls._get_body(request), # async
300+
form_data=await cls._get_form_data(request), # async
301+
files=await cls._get_files(request), # async
290302
)
291303
```
292304

305+
> **Note:** `_get_path_params`, `_get_query_params`, `_get_headers`, `_get_cookies` are **synchronous** methods inherited from `BaseRequestDataExtractor`. Only `_get_body`, `_get_form_data`, and `_get_files` are overridden as async. All `_get_*` methods receive the framework `request` object, not the `RequestEnvelope`.
306+
293307
Each framework provides its own extractor (e.g., `StarletteRequestDataExtractor`, `FlaskRequestDataExtractor`).
294308

295309
### 2. Parameter Resolver
@@ -374,6 +388,7 @@ class DependencyResolver:
374388
self._request_cache[request_data] = {
375389
"resolved": {},
376390
"resolving": set(), # For circular detection
391+
"generators": [], # For yield dependency cleanup
377392
}
378393

379394
try:
@@ -389,7 +404,7 @@ class DependencyResolver:
389404
- **Request-scoped caching** - Same dependency called twice = same instance
390405
- **Circular dependency detection** - Raises `CircularDependencyError`
391406
- **Security scopes validation** - Validates OAuth2 scopes
392-
- **Generator dependencies** - Planned support for `yield` setup/teardown (not yet implemented)
407+
- **Generator dependencies** - Supports `yield` setup/teardown for both sync and async generators with guaranteed cleanup
393408
- **Thread-safe** - Double-checked locking pattern
394409
- **Async support** - Separate async methods that handle both sync and async deps
395410

docs/advanced/architecture_quick_reference.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ To add a new framework, implement:
9595

9696
- [ ] **Extractor class** - Extract data from framework request
9797
- Inherit from `BaseAsyncRequestDataExtractor` or `BaseRequestDataExtractor`
98-
- Implement `_extract_*` methods
98+
- Implement `_get_*` methods (`_get_path_params`, `_get_query_params`, `_get_headers`, `_get_cookies`, `_get_body`, `_get_form_data`, `_get_files`)
9999

100100
- [ ] **Router class** - Integrate with framework
101101
- Inherit from `BaseAdapter`

docs/api_reference/responses.md

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -402,25 +402,6 @@ def start_task(task_data: dict):
402402

403403
## Response Model Options
404404

405-
### Exclude Unset Fields
406-
407-
```python
408-
from pydantic import BaseModel
409-
410-
class User(BaseModel):
411-
id: int
412-
username: str
413-
email: str | None = None
414-
full_name: str | None = None
415-
416-
@router.get("/users/{user_id}", response_model=User)
417-
def get_user(user_id: int):
418-
# Only returns fields that are set
419-
return User(id=user_id, username="john")
420-
# Returns: {"id": 1, "username": "john"}
421-
# email and full_name are excluded
422-
```
423-
424405
### Exclude None Fields
425406

426407
```python

docs/examples/async_tasks.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,7 @@ if __name__ == "__main__":
494494
import asyncio
495495
from datetime import datetime, timedelta
496496
from collections import defaultdict
497-
from fastopenapi import Depends
497+
from fastopenapi import Depends, Header
498498
from fastopenapi.errors import AuthorizationError
499499

500500
class AsyncRateLimiter:

docs/examples/file_uploads.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ curl -X POST http://localhost:5000/upload-multiple \
108108

109109
```python
110110
from pydantic import BaseModel
111+
from fastopenapi import File, FileUpload, Form
111112

112113
class DocumentMetadata(BaseModel):
113114
title: str
@@ -371,6 +372,7 @@ def upload_csv(file: FileUpload = File(...)):
371372
## File Download
372373

373374
```python
375+
from fastopenapi import Response
374376
from fastopenapi.errors import ResourceNotFoundError
375377

376378
@router.get("/download/{filename}", tags=["Files"])
@@ -418,7 +420,7 @@ def download_file(filename: str):
418420
from flask import Flask
419421
from pydantic import BaseModel
420422
from datetime import datetime
421-
from fastopenapi import File, FileUpload
423+
from fastopenapi import File, FileUpload, Form, Response
422424
from fastopenapi.routers import FlaskRouter
423425
from fastopenapi.errors import ResourceNotFoundError, BadRequestError
424426
import os

docs/examples/multi_router.md

61 Bytes
Binary file not shown.

docs/frameworks/sanic.md

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -107,20 +107,6 @@ async def upload_file(file: FileUpload = File(...)):
107107

108108
## Sanic-Specific Features
109109

110-
### Using Sanic Request
111-
112-
```python
113-
from sanic import request
114-
115-
@router.get("/request-info")
116-
async def get_request_info():
117-
return {
118-
"ip": request.ip,
119-
"host": request.host,
120-
"scheme": request.scheme
121-
}
122-
```
123-
124110
### Using Sanic Response
125111

126112
```python

docs/frameworks/starlette.md

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -108,20 +108,6 @@ async def upload_file(file: FileUpload = File(...)):
108108

109109
## Starlette-Specific Features
110110

111-
### Using Starlette Request
112-
113-
```python
114-
from starlette.requests import Request
115-
116-
@router.get("/request-info")
117-
async def get_request_info(request: Request):
118-
return {
119-
"method": request.method,
120-
"url": str(request.url),
121-
"client": request.client.host
122-
}
123-
```
124-
125111
### Using Starlette Response
126112

127113
```python
@@ -513,26 +499,6 @@ app.add_middleware(
513499
)
514500
```
515501

516-
## Session Management
517-
518-
```python
519-
from starlette.middleware.sessions import SessionMiddleware
520-
521-
app.add_middleware(SessionMiddleware, secret_key="your-secret-key")
522-
523-
@router.post("/login")
524-
async def login(request: Request, username: str = Form(...)):
525-
request.session["user"] = username
526-
return {"message": "Logged in"}
527-
528-
@router.get("/profile")
529-
async def profile(request: Request):
530-
user = request.session.get("user")
531-
if not user:
532-
raise AuthenticationError("Not logged in")
533-
return {"user": user}
534-
```
535-
536502
## Next Steps
537503

538504
- [Core Concepts](../getting_started/core_concepts.md)

docs/getting_started/installation.md

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -101,16 +101,10 @@ To contribute to FastOpenAPI or run tests, clone the repository and install in d
101101
```bash
102102
git clone https://github.com/mr-fatalyst/fastopenapi.git
103103
cd fastopenapi
104-
pip install -e ".[dev]"
104+
poetry install --all-extras
105105
```
106106

107-
This installs FastOpenAPI in editable mode with all development dependencies including:
108-
109-
- pytest - Testing framework
110-
- black - Code formatter
111-
- flake8 - Linter
112-
- mypy - Type checker
113-
- All supported frameworks for testing
107+
This installs FastOpenAPI with all development dependencies and framework extras.
114108

115109
## Verifying Installation
116110

docs/guide/dependencies.md

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -415,26 +415,6 @@ def list_items(user_id: int | None = Depends(get_optional_token)):
415415
return get_public_items()
416416
```
417417

418-
### Conditional Dependencies
419-
420-
```python
421-
def require_auth_if_private(is_private: bool = Query(False)):
422-
if is_private:
423-
return Depends(get_current_user)
424-
return None
425-
426-
@router.get("/content/{content_id}")
427-
def get_content(
428-
content_id: int,
429-
is_private: bool = Query(False),
430-
user = Depends(require_auth_if_private(is_private))
431-
):
432-
if is_private and not user:
433-
raise AuthenticationError("Authentication required for private content")
434-
435-
return get_content_by_id(content_id)
436-
```
437-
438418
### Dependency Composition
439419

440420
```python

0 commit comments

Comments
 (0)