encode / starlette

The little ASGI framework that shines. 🌟
https://www.starlette.io/
BSD 3-Clause "New" or "Revised" License
10.32k stars 948 forks source link

Python 3.13.0b2: KeyError: 'content-type' in `test_debug_html[asyncio]` and `test_debug_html[trio]` #2614

Closed befeleme closed 3 months ago

befeleme commented 5 months ago

Two tests fail with Python 3.13.0b2 on a missing key 'content-type'. I run then during the RPM build on version 0.37.2. @musicinmybrain reports it can be reproduced via a virtual environment: pip install -e .[full]; pip install -r requirements.txt; pip install --update trio; python -m pytest)

___________________________ test_debug_html[asyncio] ___________________________

test_client_factory = functools.partial(<class 'starlette.testclient.TestClient'>, backend='asyncio', backend_options={})

    def test_debug_html(test_client_factory: TestClientFactory) -> None:
        async def app(scope: Scope, receive: Receive, send: Send) -> None:
            raise RuntimeError("Something went wrong")

        app = ServerErrorMiddleware(app, debug=True)
        client = test_client_factory(app, raise_server_exceptions=False)
        response = client.get("/", headers={"Accept": "text/html, */*"})
        assert response.status_code == 500
>       assert response.headers["content-type"].startswith("text/html")

tests/middleware/test_errors.py:53: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = Headers({}), key = 'content-type'

    def __getitem__(self, key: str) -> str:
        """
        Return a single header value.

        If there are multiple headers with the same key, then we concatenate
        them with commas. See: https://tools.ietf.org/html/rfc7230#section-3.2.2
        """
        normalized_key = key.lower().encode(self.encoding)

        items = [
            header_value.decode(self.encoding)
            for _, header_key, header_value in self._list
            if header_key == normalized_key
        ]

        if items:
            return ", ".join(items)

>       raise KeyError(key)
E       KeyError: 'content-type'

/usr/lib/python3.13/site-packages/httpx/_models.py:228: KeyError
____________________________ test_debug_html[trio] _____________________________

test_client_factory = functools.partial(<class 'starlette.testclient.TestClient'>, backend='trio', backend_options={})

    def test_debug_html(test_client_factory: TestClientFactory) -> None:
        async def app(scope: Scope, receive: Receive, send: Send) -> None:
            raise RuntimeError("Something went wrong")

        app = ServerErrorMiddleware(app, debug=True)
        client = test_client_factory(app, raise_server_exceptions=False)
        response = client.get("/", headers={"Accept": "text/html, */*"})
        assert response.status_code == 500
>       assert response.headers["content-type"].startswith("text/html")

tests/middleware/test_errors.py:53: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = Headers({}), key = 'content-type'

    def __getitem__(self, key: str) -> str:
        """
        Return a single header value.

        If there are multiple headers with the same key, then we concatenate
        them with commas. See: https://tools.ietf.org/html/rfc7230#section-3.2.2
        """
        normalized_key = key.lower().encode(self.encoding)

        items = [
            header_value.decode(self.encoding)
            for _, header_key, header_value in self._list
            if header_key == normalized_key
        ]

        if items:
            return ", ".join(items)

>       raise KeyError(key)
E       KeyError: 'content-type'

/usr/lib/python3.13/site-packages/httpx/_models.py:228: KeyError

[!IMPORTANT]

  • We're using Polar.sh so you can upvote and help fund this issue.
  • We receive the funding once the issue is completed & confirmed by you.
  • Thank you in advance for helping prioritize & fund our backlog.

Fund with Polar

musicinmybrain commented 4 months ago

I confirm this still happens on Python 3.13.0b4.

Kludex commented 4 months ago

I don't even reach this...

❯ ./scripts/test            
+ '[' -z ']'
+ scripts/check
+ ./scripts/sync-version
+ venv/bin/ruff format --check --diff starlette tests
67 files already formatted
+ venv/bin/mypy starlette tests
Success: no issues found in 67 source files
+ venv/bin/ruff check starlette tests
+ venv/bin/coverage run -m pytest
ImportError while loading conftest '/Users/marcelotryle/dev/encode/starlette/tests/conftest.py'.
tests/conftest.py:8: in <module>
    from starlette.testclient import TestClient
starlette/testclient.py:33: in <module>
    import httpx
venv/lib/python3.13/site-packages/httpx/__init__.py:2: in <module>
    from ._api import delete, get, head, options, patch, post, put, request, stream
venv/lib/python3.13/site-packages/httpx/_api.py:6: in <module>
    from ._client import Client
venv/lib/python3.13/site-packages/httpx/_client.py:32: in <module>
    from ._transports.default import AsyncHTTPTransport, HTTPTransport
venv/lib/python3.13/site-packages/httpx/_transports/default.py:32: in <module>
    import httpcore
venv/lib/python3.13/site-packages/httpcore/__init__.py:1: in <module>
    from ._api import request, stream
venv/lib/python3.13/site-packages/httpcore/_api.py:5: in <module>
    from ._sync.connection_pool import ConnectionPool
venv/lib/python3.13/site-packages/httpcore/_sync/__init__.py:1: in <module>
    from .connection import HTTPConnection
venv/lib/python3.13/site-packages/httpcore/_sync/connection.py:12: in <module>
    from .._synchronization import Lock
venv/lib/python3.13/site-packages/httpcore/_synchronization.py:11: in <module>
    import trio
venv/lib/python3.13/site-packages/trio/__init__.py:75: in <module>
    from ._path import Path as Path
venv/lib/python3.13/site-packages/trio/_path.py:203: in <module>
    class Path(metaclass=AsyncAutoWrapperType):
venv/lib/python3.13/site-packages/trio/_path.py:147: in __init__
    type(cls).generate_forwards(cls, attrs)
venv/lib/python3.13/site-packages/trio/_path.py:164: in generate_forwards
    raise TypeError(attr_name, type(attr))
E   TypeError: ('parser', <class 'module'>)

But also... We have trio pinned on our requirements.

Kludex commented 3 months ago

I have a fix for this on https://github.com/encode/starlette/pull/2662.

Kludex commented 3 months ago

This is fixed on master. 👍