Skip to content

Commit

Permalink
Merge branch 'master' into feature/domain-in-sessionmiddleware
Browse files Browse the repository at this point in the history
  • Loading branch information
Kludex authored Nov 3, 2023
2 parents 1368fdc + e9f3f31 commit f0af32d
Show file tree
Hide file tree
Showing 10 changed files with 43 additions and 37 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
name: deploy

steps:
- uses: "actions/checkout@v3"
- uses: "actions/checkout@v4"
- uses: "actions/setup-python@v4"
with:
python-version: "3.11"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-suite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]

steps:
- uses: "actions/checkout@v3"
- uses: "actions/checkout@v4"
- uses: "actions/setup-python@v4"
with:
python-version: "${{ matrix.python-version }}"
Expand Down
3 changes: 0 additions & 3 deletions docs/js/chat.js

This file was deleted.

6 changes: 0 additions & 6 deletions docs/js/sidecar-1.5.0.js

This file was deleted.

4 changes: 0 additions & 4 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,3 @@ markdown_extensions:
- pymdownx.superfences
- pymdownx.tabbed:
alternate_style: true

extra_javascript:
- 'js/chat.js'
- 'js/sidecar-1.5.0.js'
22 changes: 11 additions & 11 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,24 @@
-e .[full]

# Testing
black==23.7.0
coverage==7.3.0
black==23.10.1
coverage==7.3.1
importlib-metadata==6.8.0
mypy==1.5.1
ruff==0.0.285
typing_extensions==4.7.1
types-contextvars==2.4.7.2
types-PyYAML==6.0.12.11
ruff==0.1.3
typing_extensions==4.8.0
types-contextvars==2.4.7.3
types-PyYAML==6.0.12.12
types-dataclasses==0.6.6
pytest==7.4.0
trio==0.21.0
anyio==3.7.1
trio==0.22.2
anyio==4.0.0

# Documentation
mkdocs==1.5.2
mkdocs-material==9.1.17
mkdocs==1.5.3
mkdocs-material==9.4.7
mkautodoc==0.2.0

# Packaging
build==0.10.0
build==1.0.3
twine==4.0.2
4 changes: 2 additions & 2 deletions starlette/testclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,8 @@ def send_json(self, data: typing.Any, mode: str = "text") -> None:
else:
self.send({"type": "websocket.receive", "bytes": text.encode("utf-8")})

def close(self, code: int = 1000) -> None:
self.send({"type": "websocket.disconnect", "code": code})
def close(self, code: int = 1000, reason: typing.Union[str, None] = None) -> None:
self.send({"type": "websocket.disconnect", "code": code, "reason": reason})

def receive(self) -> Message:
message = self._send_queue.get()
Expand Down
2 changes: 1 addition & 1 deletion starlette/websockets.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ async def accept(

def _raise_on_disconnect(self, message: Message) -> None:
if message["type"] == "websocket.disconnect":
raise WebSocketDisconnect(message["code"])
raise WebSocketDisconnect(message["code"], message.get("reason"))

async def receive_text(self) -> str:
if self.application_state != WebSocketState.CONNECTED:
Expand Down
23 changes: 19 additions & 4 deletions tests/middleware/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,18 @@ async def send(message):
def test_app_receives_http_disconnect_while_sending_if_discarded(test_client_factory):
class DiscardingMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
await call_next(request)
# As a matter of ordering, this test targets the case where the downstream
# app response is discarded while it is sending a response body.
# We need to wait for the downstream app to begin sending a response body
# before sending the middleware response that will overwrite the downstream
# response.
downstream_app_response = await call_next(request)
body_generator = downstream_app_response.body_iterator
try:
await body_generator.__anext__()
finally:
await body_generator.aclose()

return PlainTextResponse("Custom")

async def downstream_app(scope, receive, send):
Expand All @@ -411,17 +422,21 @@ async def downstream_app(scope, receive, send):
)
async with anyio.create_task_group() as task_group:

async def cancel_on_disconnect():
async def cancel_on_disconnect(*, task_status=anyio.TASK_STATUS_IGNORED):
task_status.started()
while True:
message = await receive()
if message["type"] == "http.disconnect":
task_group.cancel_scope.cancel()
break

task_group.start_soon(cancel_on_disconnect)
# Using start instead of start_soon to ensure that
# cancel_on_disconnect is scheduled by the event loop
# before we start returning the body
await task_group.start(cancel_on_disconnect)

# A timeout is set for 0.1 second in order to ensure that
# cancel_on_disconnect is scheduled by the event loop
# we never deadlock the test run in an infinite loop
with anyio.move_on_after(0.1):
while True:
await send(
Expand Down
12 changes: 8 additions & 4 deletions tests/test_websockets.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import sys
from typing import Any, MutableMapping
from typing import Any, Callable, MutableMapping

import anyio
import pytest
from anyio.abc import ObjectReceiveStream, ObjectSendStream

from starlette import status
from starlette.testclient import TestClient
from starlette.types import Receive, Scope, Send
from starlette.websockets import WebSocket, WebSocketDisconnect, WebSocketState

Expand Down Expand Up @@ -209,22 +210,25 @@ async def app(scope: Scope, receive: Receive, send: Send) -> None:
assert data == {"hello": "world"}


def test_client_close(test_client_factory):
def test_client_close(test_client_factory: Callable[..., TestClient]):
close_code = None
close_reason = None

async def app(scope: Scope, receive: Receive, send: Send) -> None:
nonlocal close_code
nonlocal close_code, close_reason
websocket = WebSocket(scope, receive=receive, send=send)
await websocket.accept()
try:
await websocket.receive_text()
except WebSocketDisconnect as exc:
close_code = exc.code
close_reason = exc.reason

client = test_client_factory(app)
with client.websocket_connect("/") as websocket:
websocket.close(code=status.WS_1001_GOING_AWAY)
websocket.close(code=status.WS_1001_GOING_AWAY, reason="Going Away")
assert close_code == status.WS_1001_GOING_AWAY
assert close_reason == "Going Away"


def test_application_close(test_client_factory):
Expand Down

0 comments on commit f0af32d

Please sign in to comment.