diff --git a/docs/en/docs/tutorial/dependencies/dependencies-with-yield.md b/docs/en/docs/tutorial/dependencies/dependencies-with-yield.md
index 3388a0828c193..82553afae52dd 100644
--- a/docs/en/docs/tutorial/dependencies/dependencies-with-yield.md
+++ b/docs/en/docs/tutorial/dependencies/dependencies-with-yield.md
@@ -7,15 +7,6 @@ To do this, use `yield` instead of `return`, and write the extra steps after.
!!! tip
Make sure to use `yield` one single time.
-!!! info
- For this to work, you need to use **Python 3.7** or above, or in **Python 3.6**, install the "backports":
-
- ```
- pip install async-exit-stack async-generator
- ```
-
- This installs async-exit-stack and async-generator.
-
!!! note "Technical Details"
Any function that is valid to use with:
diff --git a/docs/en/docs/tutorial/sql-databases.md b/docs/en/docs/tutorial/sql-databases.md
index c623fad298380..e8ebb29c8c776 100644
--- a/docs/en/docs/tutorial/sql-databases.md
+++ b/docs/en/docs/tutorial/sql-databases.md
@@ -441,17 +441,6 @@ You can find an example of Alembic in a FastAPI project in the templates from [P
### Create a dependency
-!!! info
- For this to work, you need to use **Python 3.7** or above, or in **Python 3.6**, install the "backports":
-
- ```console
- $ pip install async-exit-stack async-generator
- ```
-
- This installs async-exit-stack and async-generator.
-
- You can also use the alternative method with a "middleware" explained at the end.
-
Now use the `SessionLocal` class we created in the `sql_app/databases.py` file to create a dependency.
We need to have an independent database session/connection (`SessionLocal`) per request, use the same session through all the request and then close it after the request is finished.
diff --git a/fastapi/concurrency.py b/fastapi/concurrency.py
index d1fdfe5f60647..7ac82cfe80180 100644
--- a/fastapi/concurrency.py
+++ b/fastapi/concurrency.py
@@ -1,46 +1,34 @@
-from typing import Any, Callable
-
-from starlette.concurrency import iterate_in_threadpool as iterate_in_threadpool # noqa
-from starlette.concurrency import run_in_threadpool as run_in_threadpool # noqa
-from starlette.concurrency import ( # noqa
- run_until_first_complete as run_until_first_complete,
+import sys
+from typing import AsyncGenerator, ContextManager, TypeVar
+
+__all__ = [
+ "iterate_in_threadpool",
+ "run_in_threadpool",
+ "run_until_first_complete",
+ "contextmanager_in_threadpool",
+ "asynccontextmanager",
+ "AsyncExitStack",
+]
+
+from starlette.concurrency import (
+ iterate_in_threadpool,
+ run_in_threadpool,
+ run_until_first_complete,
)
-asynccontextmanager_error_message = """
-FastAPI's contextmanager_in_threadpool require Python 3.7 or above,
-or the backport for Python 3.6, installed with:
- pip install async-generator
-"""
-
-
-def _fake_asynccontextmanager(func: Callable[..., Any]) -> Callable[..., Any]:
- def raiser(*args: Any, **kwargs: Any) -> Any:
- raise RuntimeError(asynccontextmanager_error_message)
-
- return raiser
+if sys.version_info >= (3, 7):
+ from contextlib import AsyncExitStack, asynccontextmanager
+else:
+ from contextlib2 import AsyncExitStack, asynccontextmanager
-try:
- from contextlib import asynccontextmanager as asynccontextmanager # type: ignore
-except ImportError:
- try:
- from async_generator import ( # type: ignore # isort: skip
- asynccontextmanager as asynccontextmanager,
- )
- except ImportError: # pragma: no cover
- asynccontextmanager = _fake_asynccontextmanager
-
-try:
- from contextlib import AsyncExitStack as AsyncExitStack # type: ignore
-except ImportError:
- try:
- from async_exit_stack import AsyncExitStack as AsyncExitStack # type: ignore
- except ImportError: # pragma: no cover
- AsyncExitStack = None # type: ignore
+_T = TypeVar("_T")
-@asynccontextmanager # type: ignore
-async def contextmanager_in_threadpool(cm: Any) -> Any:
+@asynccontextmanager
+async def contextmanager_in_threadpool(
+ cm: ContextManager[_T],
+) -> AsyncGenerator[_T, None]:
try:
yield await run_in_threadpool(cm.__enter__)
except Exception as e:
diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py
index 95049d40ea82b..04dc2aec29981 100644
--- a/fastapi/dependencies/utils.py
+++ b/fastapi/dependencies/utils.py
@@ -20,7 +20,6 @@
from fastapi import params
from fastapi.concurrency import (
AsyncExitStack,
- _fake_asynccontextmanager,
asynccontextmanager,
contextmanager_in_threadpool,
)
@@ -266,18 +265,6 @@ def get_typed_annotation(param: inspect.Parameter, globalns: Dict[str, Any]) ->
return annotation
-async_contextmanager_dependencies_error = """
-FastAPI dependencies with yield require Python 3.7 or above,
-or the backports for Python 3.6, installed with:
- pip install async-exit-stack async-generator
-"""
-
-
-def check_dependency_contextmanagers() -> None:
- if AsyncExitStack is None or asynccontextmanager == _fake_asynccontextmanager:
- raise RuntimeError(async_contextmanager_dependencies_error) # pragma: no cover
-
-
def get_dependant(
*,
path: str,
@@ -289,8 +276,6 @@ def get_dependant(
path_param_names = get_path_param_names(path)
endpoint_signature = get_typed_signature(call)
signature_params = endpoint_signature.parameters
- if is_gen_callable(call) or is_async_gen_callable(call):
- check_dependency_contextmanagers()
dependant = Dependant(call=call, name=name, path=path, use_cache=use_cache)
for param_name, param in signature_params.items():
if isinstance(param.default, params.Depends):
@@ -452,14 +437,6 @@ async def solve_generator(
if is_gen_callable(call):
cm = contextmanager_in_threadpool(contextmanager(call)(**sub_values))
elif is_async_gen_callable(call):
- if not inspect.isasyncgenfunction(call):
- # asynccontextmanager from the async_generator backfill pre python3.7
- # does not support callables that are not functions or methods.
- # See https://github.com/python-trio/async_generator/issues/32
- #
- # Expand the callable class into its __call__ method before decorating it.
- # This approach will work on newer python versions as well.
- call = getattr(call, "__call__", None)
cm = asynccontextmanager(call)(**sub_values)
return await stack.enter_async_context(cm)
@@ -539,10 +516,7 @@ async def solve_dependencies(
solved = dependency_cache[sub_dependant.cache_key]
elif is_gen_callable(call) or is_async_gen_callable(call):
stack = request.scope.get("fastapi_astack")
- if stack is None:
- raise RuntimeError(
- async_contextmanager_dependencies_error
- ) # pragma: no cover
+ assert isinstance(stack, AsyncExitStack)
solved = await solve_generator(
call=call, stack=stack, sub_values=sub_values
)
diff --git a/pyproject.toml b/pyproject.toml
index 58382690b7512..77a1032006021 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -58,8 +58,6 @@ test = [
"databases[sqlite] >=0.3.2,<0.4.0",
"orjson >=3.2.1,<4.0.0",
"ujson >=4.0.1,<5.0.0",
- "async_exit_stack >=1.0.1,<2.0.0",
- "async_generator >=1.10,<2.0.0",
"python-multipart >=0.0.5,<0.0.6",
"flask >=1.1.2,<2.0.0"
]
@@ -90,8 +88,6 @@ all = [
"orjson >=3.2.1,<4.0.0",
"email_validator >=1.1.1,<2.0.0",
"uvicorn[standard] >=0.12.0,<0.14.0",
- "async_exit_stack >=1.0.1,<2.0.0",
- "async_generator >=1.10,<2.0.0"
]
[tool.isort]
diff --git a/tests/test_fakeasync.py b/tests/test_fakeasync.py
deleted file mode 100644
index 4e146b0ff2328..0000000000000
--- a/tests/test_fakeasync.py
+++ /dev/null
@@ -1,12 +0,0 @@
-import pytest
-from fastapi.concurrency import _fake_asynccontextmanager
-
-
-@_fake_asynccontextmanager
-def never_run():
- pass # pragma: no cover
-
-
-def test_fake_async():
- with pytest.raises(RuntimeError):
- never_run()