question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[BUG] TestClient crashes on websocket endpoints with dependencies that require fastapi.Request

See original GitHub issue

First 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:open
  • Created 3 years ago
  • Reactions:7
  • Comments:10

github_iconTop GitHub Comments

9reactions
mosheduminercommented, Feb 24, 2021

I was using OAuth2PasswordBearer in my dependency chain for authentication/authorization, and I did something similar to the above:

from fastapi import Request, WebSocket
from fastapi.security import OAuth2PasswordBearer


class CustomOAuth2PasswordBearer(OAuth2PasswordBearer):
    async def __call__(self, request: Request = None, websocket: WebSocket = None):
        return await super().__call__(request or websocket)


oauth2_scheme = CustomOAuth2PasswordBearer(tokenUrl="login")
1reaction
shashank-556commented, Feb 26, 2022

I was using OAuth2PasswordBearer in my dependency chain for authentication/authorization, and I did something similar to the above:

from fastapi import Request, WebSocket
from fastapi.security import OAuth2PasswordBearer


class CustomOAuth2PasswordBearer(OAuth2PasswordBearer):
    async def __call__(self, request: Request = None, websocket: WebSocket = None):
        return await super().__call__(request or websocket)


oauth2_scheme = CustomOAuth2PasswordBearer(tokenUrl="login")

Just want to add that I was also getting the same issue with all clients and the solution above that @mosheduminer gave is working.

Read more comments on GitHub >

github_iconTop 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 >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found