[BUG] TestClient crashes on websocket endpoints with dependencies that require fastapi.Request
See original GitHub issueFirst check
- I added a very descriptive title to this issue.
- I used the GitHub search to find a similar issue and didn’t find it.
- I searched the FastAPI documentation, with the integrated search.
- I already searched in Google “How to X in FastAPI” and didn’t find any information.
- I already read and followed all the tutorial in the docs and didn’t find an answer.
- I already checked if it is not related to FastAPI but to Pydantic.
- I already checked if it is not related to FastAPI but to Swagger UI.
- I already checked if it is not related to FastAPI but to ReDoc.
- After submitting this, I commit to one of:
- Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.
- I already hit the “watch” button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.
- Implement a Pull Request for a confirmed bug.
Example
Here’s a self-contained, minimal, reproducible, example with my use case:
from fastapi import WebSocket, Depends, FastAPI
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from starlette.testclient import TestClient
app = FastAPI()
@app.websocket('/')
async def socket(
websocket: WebSocket,
bearer_token: HTTPAuthorizationCredentials = Depends(HTTPBearer())
):
await websocket.accept()
await websocket.send_text("hello there")
await websocket.close()
def test_socket():
client = TestClient(app)
with client.websocket_connect("/") as ws:
ws.read_text()
ws.close()
Description
Adding any dependency that requires additional parameters makes this endpoint crash with a TypeError __call__() missing 1 required positional argument: 'request'
when used from the TestClient
.
It works fine when running with uvicorn.
Traceback:
test_websocket_depends.py:17 (test_socket)
def test_socket():
client = TestClient(app)
> with client.websocket_connect("/") as ws:
test_websocket_depends.py:20:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../venv/lib64/python3.8/site-packages/starlette/testclient.py:444: in websocket_connect
super().request("GET", url, **kwargs)
../../venv/lib64/python3.8/site-packages/requests/sessions.py:530: in request
resp = self.send(prep, **send_kwargs)
../../venv/lib64/python3.8/site-packages/requests/sessions.py:643: in send
r = adapter.send(request, **kwargs)
../../venv/lib64/python3.8/site-packages/starlette/testclient.py:145: in send
session = WebSocketTestSession(self.app, scope)
../../venv/lib64/python3.8/site-packages/starlette/testclient.py:277: in __init__
message = self.receive()
../../venv/lib64/python3.8/site-packages/starlette/testclient.py:339: in receive
raise message
../../venv/lib64/python3.8/site-packages/starlette/testclient.py:300: in _run
self._loop.run_until_complete(self.app(scope, receive, send))
/usr/lib64/python3.8/asyncio/base_events.py:616: in run_until_complete
return future.result()
../../venv/lib64/python3.8/site-packages/fastapi/applications.py:179: in __call__
await super().__call__(scope, receive, send)
../../venv/lib64/python3.8/site-packages/starlette/applications.py:111: in __call__
await self.middleware_stack(scope, receive, send)
../../venv/lib64/python3.8/site-packages/starlette/middleware/errors.py:146: in __call__
await self.app(scope, receive, send)
../../venv/lib64/python3.8/site-packages/starlette/exceptions.py:58: in __call__
await self.app(scope, receive, send)
../../venv/lib64/python3.8/site-packages/starlette/routing.py:566: in __call__
await route.handle(scope, receive, send)
../../venv/lib64/python3.8/site-packages/starlette/routing.py:283: in handle
await self.app(scope, receive, send)
../../venv/lib64/python3.8/site-packages/starlette/routing.py:57: in app
await func(session)
../../venv/lib64/python3.8/site-packages/fastapi/routing.py:218: in app
solved_result = await solve_dependencies(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
async def solve_dependencies(
*,
request: Union[Request, WebSocket],
dependant: Dependant,
body: Optional[Union[Dict[str, Any], FormData]] = None,
background_tasks: Optional[BackgroundTasks] = None,
response: Optional[Response] = None,
dependency_overrides_provider: Optional[Any] = None,
dependency_cache: Optional[Dict[Tuple[Callable, Tuple[str]], Any]] = None,
) -> Tuple[
Dict[str, Any],
List[ErrorWrapper],
Optional[BackgroundTasks],
Response,
Dict[Tuple[Callable, Tuple[str]], Any],
]:
values: Dict[str, Any] = {}
errors: List[ErrorWrapper] = []
response = response or Response(
content=None,
status_code=None, # type: ignore
headers=None,
media_type=None,
background=None,
)
dependency_cache = dependency_cache or {}
sub_dependant: Dependant
for sub_dependant in dependant.dependencies:
sub_dependant.call = cast(Callable, sub_dependant.call)
sub_dependant.cache_key = cast(
Tuple[Callable, Tuple[str]], sub_dependant.cache_key
)
call = sub_dependant.call
use_sub_dependant = sub_dependant
if (
dependency_overrides_provider
and dependency_overrides_provider.dependency_overrides
):
original_call = sub_dependant.call
call = getattr(
dependency_overrides_provider, "dependency_overrides", {}
).get(original_call, original_call)
use_path: str = sub_dependant.path # type: ignore
use_sub_dependant = get_dependant(
path=use_path,
call=call,
name=sub_dependant.name,
security_scopes=sub_dependant.security_scopes,
)
use_sub_dependant.security_scopes = sub_dependant.security_scopes
solved_result = await solve_dependencies(
request=request,
dependant=use_sub_dependant,
body=body,
background_tasks=background_tasks,
response=response,
dependency_overrides_provider=dependency_overrides_provider,
dependency_cache=dependency_cache,
)
(
sub_values,
sub_errors,
background_tasks,
_, # the subdependency returns the same response we have
sub_dependency_cache,
) = solved_result
dependency_cache.update(sub_dependency_cache)
if sub_errors:
errors.extend(sub_errors)
continue
if sub_dependant.use_cache and sub_dependant.cache_key in dependency_cache:
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
solved = await solve_generator(
call=call, stack=stack, sub_values=sub_values
)
elif is_coroutine_callable(call):
> solved = await call(**sub_values)
E TypeError: __call__() missing 1 required positional argument: 'request'
../../venv/lib64/python3.8/site-packages/fastapi/dependencies/utils.py:550: TypeError
Environment
- OS: [e.g. Linux / Windows / macOS]: Fedora 32
- FastAPI Version [e.g. 0.3.0]: 0.61.1
- Python version: Python 3.8.5
Issue Analytics
- State:
- Created 3 years ago
- Reactions:7
- Comments:10
Top Results From Across the Web
Testing WebSockets - FastAPI
Testing WebSockets¶. You can use the same TestClient to test WebSockets. For this, you use the TestClient in a with statement, connecting to...
Read more >Websocket getting closed immediately after connecting to ...
The connection is closed by either end (client or server), as shown from your code snippets. You would need to have a loop...
Read more >Building Data Science Applications with FastAPI - EBIN.PUB
Use a dependency on a path decorator 153 ... Defining WebSockets for Two-Way Interactive ... Creating an Efficient Prediction API Endpoint with FastAPI....
Read more >Download Diff File
Unless required by applicable law or - agreed to in writing, Licensor provides ... Fewer bugs. ... -fastapi/dependencies/__pycache__/models.cpython-38.pyc,, ...
Read more >Simple index - piwheels
... girder-large-image recurringfunction bise-catalogueindexer slthost django-jet-4 dcurl vista-dna-mscp-snop-demand-forecaster django-google-integrations ...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
I was using OAuth2PasswordBearer in my dependency chain for authentication/authorization, and I did something similar to the above:
Just want to add that I was also getting the same issue with all clients and the solution above that @mosheduminer gave is working.