Skip to content

Commit

Permalink
Merge branch 'main' into response-cookie-extraction-change
Browse files Browse the repository at this point in the history
  • Loading branch information
JacobCoffee authored Jan 31, 2024
2 parents e85a48c + f90a12d commit a1e59c9
Show file tree
Hide file tree
Showing 12 changed files with 616 additions and 60 deletions.
35 changes: 35 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,41 @@ jobs:
coverage: ${{ (matrix.python-version == '3.12' || matrix.python-version == '3.8') }}
python-version: ${{ matrix.python-version }}

test_integration:
name: Test server integration
runs-on: ubuntu-latest
strategy:
matrix:
uvicorn-version: ["uvicorn<0.27.0", "uvicorn>=0.27.0"]
steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up python 3.11
uses: actions/setup-python@v5
with:
python-version: 3.11

- uses: pdm-project/setup-pdm@v4
name: Set up PDM
with:
python-version: 3.11
allow-python-prereleases: false
cache: true
cache-dependency-path: |
./pdm.lock
- name: Install dependencies
run: |
pdm install -G:all
pip install -U "${{ matrix.uvicorn-version }}"
- name: Set PYTHONPATH
run: echo "PYTHONPATH=$PWD" >> $GITHUB_ENV

- name: Test
run: pdm run pytest tests -m server_integration

upload-test-coverage:
runs-on: ubuntu-latest
needs: test
Expand Down
15 changes: 15 additions & 0 deletions docs/release-notes/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,21 @@
2.x Changelog
=============

.. changelog:: 2.5.3
:date: 2024/01/29

.. change:: Handle diverging ASGI ``root_path`` behaviour
:type: bugfix
:pr: 3039
:issue: 3041

Uvicorn `0.26.0 <https://github.com/encode/uvicorn/releases/tag/0.26.0>`_
introduced a breaking change in its handling of the ASGI ``root_path`` behaviour,
which, while adhering to the spec, diverges from the interpretation of other
ASGI servers of this aspect of the spec (e.g. hypercorn and daphne do not follow
uvicorn's interpretation as of today). A fix was introduced that ensures
consistent behaviour of applications in any case.

.. changelog:: 2.5.2
:date: 2024/01/27

Expand Down
7 changes: 6 additions & 1 deletion litestar/_asgi/asgi_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,12 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
The main entry point to the Router class.
"""
scope.setdefault("path_params", {})
normalized_path = normalize_path(scope["path"])

path = scope["path"]
if root_path := scope["root_path"]:
path = path.split(root_path, maxsplit=1)[-1]
normalized_path = normalize_path(path)

asgi_app, scope["route_handler"], scope["path"], scope["path_params"] = self.handle_routing(
path=normalized_path, method=scope.get("method")
)
Expand Down
10 changes: 5 additions & 5 deletions litestar/response/sse.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

if TYPE_CHECKING:
from litestar.background_tasks import BackgroundTask, BackgroundTasks
from litestar.types import ResponseCookies, ResponseHeaders, StreamType
from litestar.types import ResponseCookies, ResponseHeaders, SSEData, StreamType

_LINE_BREAK_RE = re.compile(r"\r\n|\r|\n")
DEFAULT_SEPARATOR = "\r\n"
Expand All @@ -21,11 +21,11 @@
class _ServerSentEventIterator(AsyncIteratorWrapper[bytes]):
__slots__ = ("content_async_iterator", "event_id", "event_type", "retry_duration", "comment_message")

content_async_iterator: AsyncIteratorWrapper[bytes | str] | AsyncIterable[str | bytes] | AsyncIterator[str | bytes]
content_async_iterator: AsyncIterable[SSEData]

def __init__(
self,
content: str | bytes | StreamType[str | bytes],
content: str | bytes | StreamType[SSEData],
event_type: str | None = None,
event_id: int | str | None = None,
retry_duration: int | None = None,
Expand Down Expand Up @@ -56,7 +56,7 @@ def __init__(
if isinstance(content, (str, bytes)):
self.content_async_iterator = AsyncIteratorWrapper([content])
elif isinstance(content, (Iterable, Iterator)):
self.content_async_iterator = AsyncIteratorWrapper(content) # type: ignore[arg-type]
self.content_async_iterator = AsyncIteratorWrapper(content)
elif isinstance(content, (AsyncIterable, AsyncIterator, AsyncIteratorWrapper)):
self.content_async_iterator = content
else:
Expand Down Expand Up @@ -131,7 +131,7 @@ def encode(self) -> bytes:
class ServerSentEvent(Stream):
def __init__(
self,
content: str | bytes | StreamType[str | bytes],
content: str | bytes | StreamType[SSEData],
*,
background: BackgroundTask | BackgroundTasks | None = None,
cookies: ResponseCookies | None = None,
Expand Down
3 changes: 2 additions & 1 deletion litestar/types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
)
from .empty import Empty, EmptyType
from .file_types import FileInfo, FileSystemProtocol
from .helper_types import AnyIOBackend, MaybePartial, OptionalSequence, StreamType, SyncOrAsyncUnion
from .helper_types import AnyIOBackend, MaybePartial, OptionalSequence, SSEData, StreamType, SyncOrAsyncUnion
from .internal_types import (
ControllerRouterHandler,
ReservedKwargs,
Expand Down Expand Up @@ -155,6 +155,7 @@
"Send",
"Serializer",
"StreamType",
"SSEData",
"SyncOrAsyncUnion",
"TypeDecodersSequence",
"TypeEncodersMap",
Expand Down
10 changes: 9 additions & 1 deletion litestar/types/helper_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
from functools import partial
from typing import (
TYPE_CHECKING,
Any,
AsyncIterable,
AsyncIterator,
Awaitable,
Dict,
Iterable,
Iterator,
Literal,
Expand All @@ -18,9 +20,12 @@
if TYPE_CHECKING:
from typing_extensions import TypeAlias

from litestar.response.sse import ServerSentEventMessage


T = TypeVar("T")

__all__ = ("OptionalSequence", "SyncOrAsyncUnion", "AnyIOBackend", "StreamType", "MaybePartial")
__all__ = ("OptionalSequence", "SyncOrAsyncUnion", "AnyIOBackend", "StreamType", "MaybePartial", "SSEData")

OptionalSequence: TypeAlias = Optional[Sequence[T]]
"""Types 'T' as union of Sequence[T] and None."""
Expand All @@ -37,3 +42,6 @@

MaybePartial: TypeAlias = Union[T, partial]
"""A potentially partial callable."""

SSEData: TypeAlias = Union[int, str, bytes, Dict[str, Any], "ServerSentEventMessage"]
"""A type alias for SSE data."""
Loading

0 comments on commit a1e59c9

Please sign in to comment.