Browse Source

️ Restore the parts from master that should stay the same

pull/14099/head
Sebastián Ramírez 3 weeks ago
parent
commit
3652d7dc83
  1. 30
      docs_src/dependencies/tutorial008b.py
  2. 31
      docs_src/dependencies/tutorial008b_an.py
  3. 32
      docs_src/dependencies/tutorial008b_an_py39.py
  4. 1
      fastapi/concurrency.py
  5. 4
      fastapi/dependencies/utils.py
  6. 9
      pyproject.toml
  7. 20
      tests/test_dependency_contextmanager.py
  8. 39
      tests/test_tutorial/test_dependencies/test_tutorial008b.py

30
docs_src/dependencies/tutorial008b.py

@ -0,0 +1,30 @@
from fastapi import Depends, FastAPI, HTTPException
app = FastAPI()
data = {
"plumbus": {"description": "Freshly pickled plumbus", "owner": "Morty"},
"portal-gun": {"description": "Gun to create portals", "owner": "Rick"},
}
class OwnerError(Exception):
pass
def get_username():
try:
yield "Rick"
except OwnerError as e:
raise HTTPException(status_code=400, detail=f"Owner error: {e}")
@app.get("/items/{item_id}")
def get_item(item_id: str, username: str = Depends(get_username)):
if item_id not in data:
raise HTTPException(status_code=404, detail="Item not found")
item = data[item_id]
if item["owner"] != username:
raise OwnerError(username)
return item

31
docs_src/dependencies/tutorial008b_an.py

@ -0,0 +1,31 @@
from fastapi import Depends, FastAPI, HTTPException
from typing_extensions import Annotated
app = FastAPI()
data = {
"plumbus": {"description": "Freshly pickled plumbus", "owner": "Morty"},
"portal-gun": {"description": "Gun to create portals", "owner": "Rick"},
}
class OwnerError(Exception):
pass
def get_username():
try:
yield "Rick"
except OwnerError as e:
raise HTTPException(status_code=400, detail=f"Owner error: {e}")
@app.get("/items/{item_id}")
def get_item(item_id: str, username: Annotated[str, Depends(get_username)]):
if item_id not in data:
raise HTTPException(status_code=404, detail="Item not found")
item = data[item_id]
if item["owner"] != username:
raise OwnerError(username)
return item

32
docs_src/dependencies/tutorial008b_an_py39.py

@ -0,0 +1,32 @@
from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException
app = FastAPI()
data = {
"plumbus": {"description": "Freshly pickled plumbus", "owner": "Morty"},
"portal-gun": {"description": "Gun to create portals", "owner": "Rick"},
}
class OwnerError(Exception):
pass
def get_username():
try:
yield "Rick"
except OwnerError as e:
raise HTTPException(status_code=400, detail=f"Owner error: {e}")
@app.get("/items/{item_id}")
def get_item(item_id: str, username: Annotated[str, Depends(get_username)]):
if item_id not in data:
raise HTTPException(status_code=404, detail="Item not found")
item = data[item_id]
if item["owner"] != username:
raise OwnerError(username)
return item

1
fastapi/concurrency.py

@ -1,4 +1,3 @@
from contextlib import AsyncExitStack as AsyncExitStack # noqa
from contextlib import asynccontextmanager as asynccontextmanager from contextlib import asynccontextmanager as asynccontextmanager
from typing import AsyncGenerator, ContextManager, TypeVar from typing import AsyncGenerator, ContextManager, TypeVar

4
fastapi/dependencies/utils.py

@ -2,8 +2,6 @@ import inspect
from contextlib import AsyncExitStack, contextmanager from contextlib import AsyncExitStack, contextmanager
from copy import copy, deepcopy from copy import copy, deepcopy
from dataclasses import dataclass from dataclasses import dataclass
from contextlib import contextmanager
from copy import deepcopy
from typing import ( from typing import (
Any, Any,
Callable, Callable,
@ -634,8 +632,6 @@ async def solve_dependencies(
if sub_dependant.use_cache and sub_dependant.cache_key in dependency_cache: if sub_dependant.use_cache and sub_dependant.cache_key in dependency_cache:
solved = dependency_cache[sub_dependant.cache_key] solved = dependency_cache[sub_dependant.cache_key]
elif is_gen_callable(call) or is_async_gen_callable(call): elif is_gen_callable(call) or is_async_gen_callable(call):
stack = request.scope.get("fastapi_astack")
assert isinstance(stack, AsyncExitStack)
solved = await solve_generator( solved = await solve_generator(
call=call, stack=async_exit_stack, sub_values=solved_result.values call=call, stack=async_exit_stack, sub_values=solved_result.values
) )

9
pyproject.toml

@ -154,6 +154,12 @@ module = "fastapi.tests.*"
ignore_missing_imports = true ignore_missing_imports = true
check_untyped_defs = true check_untyped_defs = true
[[tool.mypy.overrides]]
module = "docs_src.*"
disallow_incomplete_defs = false
disallow_untyped_defs = false
disallow_untyped_calls = false
[tool.pytest.ini_options] [tool.pytest.ini_options]
addopts = [ addopts = [
"--strict-config", "--strict-config",
@ -247,6 +253,9 @@ ignore = [
"docs_src/security/tutorial005_an_py39.py" = ["B904"] "docs_src/security/tutorial005_an_py39.py" = ["B904"]
"docs_src/security/tutorial005_py310.py" = ["B904"] "docs_src/security/tutorial005_py310.py" = ["B904"]
"docs_src/security/tutorial005_py39.py" = ["B904"] "docs_src/security/tutorial005_py39.py" = ["B904"]
"docs_src/dependencies/tutorial008b.py" = ["B904"]
"docs_src/dependencies/tutorial008b_an.py" = ["B904"]
"docs_src/dependencies/tutorial008b_an_py39.py" = ["B904"]
[tool.ruff.lint.isort] [tool.ruff.lint.isort]

20
tests/test_dependency_contextmanager.py

@ -1,7 +1,9 @@
import json
from typing import Dict from typing import Dict
import pytest import pytest
from fastapi import BackgroundTasks, Depends, FastAPI from fastapi import BackgroundTasks, Depends, FastAPI
from fastapi.responses import StreamingResponse
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
app = FastAPI() app = FastAPI()
@ -202,6 +204,13 @@ async def get_sync_context_b_bg(
return state return state
@app.middleware("http")
async def middleware(request, call_next):
response: StreamingResponse = await call_next(request)
response.headers["x-state"] = json.dumps(state.copy())
return response
client = TestClient(app) client = TestClient(app)
@ -276,9 +285,13 @@ def test_background_tasks():
assert data["context_b"] == "started b" assert data["context_b"] == "started b"
assert data["context_a"] == "started a" assert data["context_a"] == "started a"
assert data["bg"] == "not set" assert data["bg"] == "not set"
middleware_state = json.loads(response.headers["x-state"])
assert middleware_state["context_b"] == "finished b with a: started a"
assert middleware_state["context_a"] == "finished a"
assert middleware_state["bg"] == "not set"
assert state["context_b"] == "finished b with a: started a" assert state["context_b"] == "finished b with a: started a"
assert state["context_a"] == "finished a" assert state["context_a"] == "finished a"
assert state["bg"] == "bg set - b: started b - a: started a" assert state["bg"] == "bg set - b: finished b with a: started a - a: finished a"
def test_sync_raise_raises(): def test_sync_raise_raises():
@ -384,4 +397,7 @@ def test_sync_background_tasks():
assert data["sync_bg"] == "not set" assert data["sync_bg"] == "not set"
assert state["context_b"] == "finished b with a: started a" assert state["context_b"] == "finished b with a: started a"
assert state["context_a"] == "finished a" assert state["context_a"] == "finished a"
assert state["sync_bg"] == "sync_bg set - b: started b - a: started a" assert (
state["sync_bg"]
== "sync_bg set - b: finished b with a: started a - a: finished a"
)

39
tests/test_tutorial/test_dependencies/test_tutorial008b.py

@ -0,0 +1,39 @@
import importlib
import pytest
from fastapi.testclient import TestClient
from ...utils import needs_py39
@pytest.fixture(
name="client",
params=[
"tutorial008b",
"tutorial008b_an",
pytest.param("tutorial008b_an_py39", marks=needs_py39),
],
)
def get_client(request: pytest.FixtureRequest):
mod = importlib.import_module(f"docs_src.dependencies.{request.param}")
client = TestClient(mod.app)
return client
def test_get_no_item(client: TestClient):
response = client.get("/items/foo")
assert response.status_code == 404, response.text
assert response.json() == {"detail": "Item not found"}
def test_owner_error(client: TestClient):
response = client.get("/items/plumbus")
assert response.status_code == 400, response.text
assert response.json() == {"detail": "Owner error: Rick"}
def test_get_item(client: TestClient):
response = client.get("/items/portal-gun")
assert response.status_code == 200, response.text
assert response.json() == {"description": "Gun to create portals", "owner": "Rick"}
Loading…
Cancel
Save