mirror of https://github.com/encode/starlette.git
Version 0.3 (#57)
* Renamings * Version 0.3.0 * Black formatting * Update docs for 0.3
This commit is contained in:
parent
f4fe0ec312
commit
78d1829758
84
README.md
84
README.md
|
@ -2,7 +2,7 @@
|
|||
<a href="https://www.starlette.io/"><img width="320" height="192" src="https://raw.githubusercontent.com/encode/starlette/master/docs/starlette.png" alt='starlette'></a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<em>✨ The little ASGI library that shines. ✨</em>
|
||||
<em>✨ The little ASGI framework that shines. ✨</em>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.org/encode/starlette">
|
||||
|
@ -22,10 +22,10 @@
|
|||
|
||||
---
|
||||
|
||||
Starlette is a small library for working with [ASGI](https://asgi.readthedocs.io/en/latest/).
|
||||
Starlette is a lightweight [ASGI](https://asgi.readthedocs.io/en/latest/) framework/toolkit.
|
||||
|
||||
It gives you `Request` and `Response` classes, request routing, websocket support,
|
||||
static files support, and a test client.
|
||||
It is ideal for building high performance asyncio services, and supports both
|
||||
HTTP and WebSockets.
|
||||
|
||||
## Requirements
|
||||
|
||||
|
@ -37,10 +37,46 @@ Python 3.6+
|
|||
$ pip3 install starlette
|
||||
```
|
||||
|
||||
You'll also want to install an ASGI server, such as [uvicorn](http://www.uvicorn.org/), [daphne](https://github.com/django/daphne/), or [hypercorn](https://pgjones.gitlab.io/hypercorn/).
|
||||
|
||||
```shell
|
||||
$ pip3 install uvicorn
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from starlette.responses import Response
|
||||
from starlette.applications import Starlette
|
||||
from starlette.responses import JSONResponse
|
||||
import uvicorn
|
||||
|
||||
app = Starlette()
|
||||
|
||||
@app.route('/')
|
||||
def homepage(request):
|
||||
return JSONResponse({'hello': 'world'})
|
||||
|
||||
if __name__ == '__main__':
|
||||
uvicorn.run(app, host='0.0.0.0', port=8001, access_log=False)
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
Starlette does not have any hard dependencies, but the following are optional:
|
||||
|
||||
* `requests` - Required if you want to use the `TestClient`.
|
||||
* `aiofiles` - Required if you want to use `FileResponse` or `StaticFiles`.
|
||||
* `ujson` - Optionally used for `JSONResponse`.
|
||||
|
||||
You can install all three of these with `pip3 install starlette[full]`.
|
||||
|
||||
## Framework or Toolkit
|
||||
|
||||
Starlette is designed to be used either as a complete framework, or as
|
||||
an ASGI toolkit. You can use any of its components independently.
|
||||
|
||||
```python
|
||||
from starlette.responses import PlainTextResponse
|
||||
|
||||
|
||||
class App:
|
||||
|
@ -48,20 +84,10 @@ class App:
|
|||
self.scope = scope
|
||||
|
||||
async def __call__(self, receive, send):
|
||||
response = Response('Hello, world!', media_type='text/plain')
|
||||
response = PlainTextResponse('Hello, world!')
|
||||
await response(receive, send)
|
||||
```
|
||||
|
||||
You can run the application with any ASGI server, including [uvicorn](http://www.uvicorn.org/), [daphne](https://github.com/django/daphne/), or [hypercorn](https://pgjones.gitlab.io/hypercorn/).
|
||||
|
||||
Install the Uvicorn ASGI server:
|
||||
|
||||
```shell
|
||||
$ pip3 install uvicorn
|
||||
[...]
|
||||
Successfully installed uvicorn
|
||||
```
|
||||
|
||||
Run the `App` application in `example.py`:
|
||||
|
||||
```shell
|
||||
|
@ -70,5 +96,31 @@ INFO: Started server process [11509]
|
|||
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
## Modularity
|
||||
|
||||
The modularity that Starlette is designed on promotes building re-usable
|
||||
components that can be shared between any ASGI framework. This should enable
|
||||
an ecosystem of shared middleware and mountable applications.
|
||||
|
||||
The clean API separation also means it's easier to understand each component
|
||||
in isolation.
|
||||
|
||||
## Performance
|
||||
|
||||
Our testing shows that Starlette applications running under Uvicorn as one of
|
||||
the fastest Python frameworks available. As an example, application performance
|
||||
should roughly equal or out-perform Sanic.
|
||||
|
||||
For high throughput loads you should:
|
||||
|
||||
* Make sure to install `ujson`.
|
||||
* Run using `uvicorn`, with access logging disabled.
|
||||
|
||||
Several of the ASGI servers also have pure Python implementations available,
|
||||
so you can also run under `PyPy` if your application code has parts that are
|
||||
CPU constrained.
|
||||
|
||||
Eg. `uvicorn.run(..., http='h11', loop='asyncio')`
|
||||
|
||||
<p align="center">— ⭐️ —</p>
|
||||
<p align="center"><i>Starlette is <a href="https://github.com/tomchristie/starlette/blob/master/LICENSE.md">BSD licensed</a> code. Designed & built in Brighton, England.</i></p>
|
||||
|
|
|
@ -2,7 +2,7 @@ Starlette also includes an app class `Starlette` that nicely ties together all o
|
|||
its other functionality.
|
||||
|
||||
```python
|
||||
from starlette.app import Starlette
|
||||
from starlette.applications import Starlette
|
||||
from starlette.responses import PlainTextResponse
|
||||
from starlette.staticfiles import StaticFiles
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<img width="320" height="192" src="https://raw.githubusercontent.com/encode/starlette/master/docs/starlette.png" alt='starlette'>
|
||||
</p>
|
||||
<p align="center">
|
||||
<em>✨ The little ASGI library that shines. ✨</em>
|
||||
<em>✨ The little ASGI framework that shines. ✨</em>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.org/encode/starlette">
|
||||
|
@ -21,10 +21,10 @@
|
|||
|
||||
# Introduction
|
||||
|
||||
Starlette is a small library for working with [ASGI](https://asgi.readthedocs.io/en/latest/).
|
||||
Starlette is a lightweight [ASGI](https://asgi.readthedocs.io/en/latest/) framework/toolkit.
|
||||
|
||||
It gives you `Request` and `Response` classes, request routing, websocket support,
|
||||
static files support, and a test client.
|
||||
It is ideal for building high performance asyncio services, and supports both
|
||||
HTTP and WebSockets.
|
||||
|
||||
## Requirements
|
||||
|
||||
|
@ -36,10 +36,46 @@ Python 3.6+
|
|||
$ pip3 install starlette
|
||||
```
|
||||
|
||||
You'll also want to install an ASGI server, such as [uvicorn](http://www.uvicorn.org/), [daphne](https://github.com/django/daphne/), or [hypercorn](https://pgjones.gitlab.io/hypercorn/).
|
||||
|
||||
```shell
|
||||
$ pip3 install uvicorn
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from starlette.responses import Response
|
||||
from starlette.applications import Starlette
|
||||
from starlette.responses import JSONResponse
|
||||
import uvicorn
|
||||
|
||||
app = Starlette()
|
||||
|
||||
@app.route('/')
|
||||
def homepage(request):
|
||||
return JSONResponse({'hello': 'world'})
|
||||
|
||||
if __name__ == '__main__':
|
||||
uvicorn.run(app, host='0.0.0.0', port=8001, access_log=False)
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
Starlette does not have any hard dependencies, but the following are optional:
|
||||
|
||||
* `requests` - Required if you want to use the `TestClient`.
|
||||
* `aiofiles` - Required if you want to use `FileResponse` or `StaticFiles`.
|
||||
* `ujson` - Optionally used for `JSONResponse`.
|
||||
|
||||
You can install all three of these with `pip3 install starlette[full]`.
|
||||
|
||||
## Framework or Toolkit
|
||||
|
||||
Starlette is designed to be used either as a complete framework, or as
|
||||
an ASGI toolkit. You can use any of its components independently.
|
||||
|
||||
```python
|
||||
from starlette.responses import PlainTextResponse
|
||||
|
||||
|
||||
class App:
|
||||
|
@ -47,20 +83,10 @@ class App:
|
|||
self.scope = scope
|
||||
|
||||
async def __call__(self, receive, send):
|
||||
response = Response('Hello, world!', media_type='text/plain')
|
||||
response = PlainTextResponse('Hello, world!')
|
||||
await response(receive, send)
|
||||
```
|
||||
|
||||
You can run the application with any ASGI server, including [uvicorn](http://www.uvicorn.org/), [daphne](https://github.com/django/daphne/), or [hypercorn](https://pgjones.gitlab.io/hypercorn/).
|
||||
|
||||
Install the Uvicorn ASGI server:
|
||||
|
||||
```shell
|
||||
$ pip3 install uvicorn
|
||||
[...]
|
||||
Successfully installed uvicorn
|
||||
```
|
||||
|
||||
Run the `App` application in `example.py`:
|
||||
|
||||
```shell
|
||||
|
@ -69,5 +95,31 @@ INFO: Started server process [11509]
|
|||
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
## Modularity
|
||||
|
||||
The modularity that Starlette is designed on promotes building re-usable
|
||||
components that can be shared between any ASGI framework. This should enable
|
||||
an ecosystem of shared middleware and mountable applications.
|
||||
|
||||
The clean API separation also means it's easier to understand each component
|
||||
in isolation.
|
||||
|
||||
## Performance
|
||||
|
||||
Our testing shows that Starlette applications running under Uvicorn as one of
|
||||
the fastest Python frameworks available. As an example, application performance
|
||||
should roughly equal or out-perform Sanic.
|
||||
|
||||
For high throughput loads you should:
|
||||
|
||||
* Make sure to install `ujson`.
|
||||
* Run using `uvicorn`, with access logging disabled.
|
||||
|
||||
Several of the ASGI servers also have pure Python implementations available,
|
||||
so you can also run under `PyPy` if your application code has parts that are
|
||||
CPU constrained.
|
||||
|
||||
Eg. `uvicorn.run(..., http='h11', loop='asyncio')`
|
||||
|
||||
<p align="center">— ⭐️ —</p>
|
||||
<p align="center"><i>Starlette is <a href="https://github.com/tomchristie/starlette/blob/master/LICENSE.md">BSD licensed</a> code. Designed & built in Brighton, England.</i></p>
|
||||
|
|
|
@ -10,15 +10,15 @@ edit_uri: ""
|
|||
|
||||
nav:
|
||||
- Introduction: 'index.md'
|
||||
- Responses: 'responses.md'
|
||||
- Requests: 'requests.md'
|
||||
- Responses: 'responses.md'
|
||||
- WebSockets: 'websockets.md'
|
||||
- Routing: 'routing.md'
|
||||
- Exceptions: 'exceptions.md'
|
||||
- Static Files: 'staticfiles.md'
|
||||
- Applications: 'applications.md'
|
||||
- Test Client: 'test_client.md'
|
||||
- Debugging: 'debugging.md'
|
||||
- Test Client: 'testclient.md'
|
||||
- Debugging: 'debug.md'
|
||||
- Endpoints: 'endpoints.md'
|
||||
|
||||
markdown_extensions:
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# Optionals
|
||||
aiofiles
|
||||
requests
|
||||
ujson
|
||||
|
||||
# Testing
|
||||
black
|
||||
|
@ -7,7 +9,6 @@ codecov
|
|||
pytest
|
||||
pytest-cov
|
||||
|
||||
|
||||
# Documentation
|
||||
mkdocs
|
||||
mkdocs-material
|
||||
|
|
11
setup.py
11
setup.py
|
@ -43,10 +43,13 @@ setup(
|
|||
author='Tom Christie',
|
||||
author_email='tom@tomchristie.com',
|
||||
packages=get_packages('starlette'),
|
||||
install_requires=[
|
||||
'aiofiles',
|
||||
'requests',
|
||||
],
|
||||
extras_require = {
|
||||
'full': [
|
||||
'aiofiles',
|
||||
'requests',
|
||||
'ujson'
|
||||
]
|
||||
},
|
||||
classifiers=[
|
||||
'Development Status :: 3 - Alpha',
|
||||
'Environment :: Web Environment',
|
||||
|
|
|
@ -1 +1 @@
|
|||
__version__ = "0.2.3"
|
||||
__version__ = "0.3.0"
|
||||
|
|
|
@ -1,16 +1,35 @@
|
|||
from aiofiles.os import stat as aio_stat
|
||||
from email.utils import formatdate
|
||||
from mimetypes import guess_type
|
||||
from starlette.datastructures import MutableHeaders
|
||||
from starlette.types import Receive, Send
|
||||
from urllib.parse import quote_plus
|
||||
import aiofiles
|
||||
import json
|
||||
import hashlib
|
||||
import os
|
||||
import stat
|
||||
import typing
|
||||
|
||||
try:
|
||||
import aiofiles
|
||||
from aiofiles.os import stat as aio_stat
|
||||
except ImportError: # pragma: nocover
|
||||
aiofiles = None
|
||||
aio_stat = None
|
||||
|
||||
try:
|
||||
import ujson as json
|
||||
|
||||
JSON_DUMPS_OPTIONS = {"ensure_ascii": False}
|
||||
except ImportError: # pragma: nocover
|
||||
import json
|
||||
|
||||
JSON_DUMPS_OPTIONS = {
|
||||
"ensure_ascii": False,
|
||||
"allow_nan": False,
|
||||
"indent": None,
|
||||
"separators": (",", ":"),
|
||||
}
|
||||
|
||||
|
||||
class Response:
|
||||
media_type = None
|
||||
|
@ -88,15 +107,9 @@ class PlainTextResponse(Response):
|
|||
|
||||
class JSONResponse(Response):
|
||||
media_type = "application/json"
|
||||
options = {
|
||||
"ensure_ascii": False,
|
||||
"allow_nan": False,
|
||||
"indent": None,
|
||||
"separators": (",", ":"),
|
||||
} # type: typing.Dict[str, typing.Any]
|
||||
|
||||
def render(self, content: typing.Any) -> bytes:
|
||||
return json.dumps(content, **self.options).encode("utf-8")
|
||||
return json.dumps(content, **JSON_DUMPS_OPTIONS).encode("utf-8")
|
||||
|
||||
|
||||
class RedirectResponse(Response):
|
||||
|
@ -144,6 +157,7 @@ class FileResponse(Response):
|
|||
filename: str = None,
|
||||
stat_result: os.stat_result = None,
|
||||
) -> None:
|
||||
assert aiofiles is not None, "'aiofiles' must be installed to use FileResponse"
|
||||
self.path = path
|
||||
self.status_code = 200
|
||||
self.filename = filename
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
123
|
|
@ -1,4 +1,4 @@
|
|||
from starlette.app import Starlette
|
||||
from starlette.applications import Starlette
|
||||
from starlette.exceptions import HTTPException
|
||||
from starlette.responses import JSONResponse, PlainTextResponse
|
||||
from starlette.staticfiles import StaticFiles
|
|
@ -3,52 +3,52 @@ from starlette.testclient import TestClient
|
|||
from starlette.websockets import WebSocket, WebSocketDisconnect
|
||||
|
||||
|
||||
def test_session_url():
|
||||
def test_websocket_url():
|
||||
def app(scope):
|
||||
async def asgi(receive, send):
|
||||
session = WebSocket(scope, receive, send)
|
||||
await session.accept()
|
||||
await session.send_json({"url": session.url})
|
||||
await session.close()
|
||||
websocket = WebSocket(scope, receive, send)
|
||||
await websocket.accept()
|
||||
await websocket.send_json({"url": websocket.url})
|
||||
await websocket.close()
|
||||
|
||||
return asgi
|
||||
|
||||
client = TestClient(app)
|
||||
with client.websocket_connect("/123?a=abc") as session:
|
||||
data = session.receive_json()
|
||||
with client.websocket_connect("/123?a=abc") as websocket:
|
||||
data = websocket.receive_json()
|
||||
assert data == {"url": "ws://testserver/123?a=abc"}
|
||||
|
||||
|
||||
def test_session_query_params():
|
||||
def test_websocket_query_params():
|
||||
def app(scope):
|
||||
async def asgi(receive, send):
|
||||
session = WebSocket(scope, receive, send)
|
||||
query_params = dict(session.query_params)
|
||||
await session.accept()
|
||||
await session.send_json({"params": query_params})
|
||||
await session.close()
|
||||
websocket = WebSocket(scope, receive, send)
|
||||
query_params = dict(websocket.query_params)
|
||||
await websocket.accept()
|
||||
await websocket.send_json({"params": query_params})
|
||||
await websocket.close()
|
||||
|
||||
return asgi
|
||||
|
||||
client = TestClient(app)
|
||||
with client.websocket_connect("/?a=abc&b=456") as session:
|
||||
data = session.receive_json()
|
||||
with client.websocket_connect("/?a=abc&b=456") as websocket:
|
||||
data = websocket.receive_json()
|
||||
assert data == {"params": {"a": "abc", "b": "456"}}
|
||||
|
||||
|
||||
def test_session_headers():
|
||||
def test_websocket_headers():
|
||||
def app(scope):
|
||||
async def asgi(receive, send):
|
||||
session = WebSocket(scope, receive, send)
|
||||
headers = dict(session.headers)
|
||||
await session.accept()
|
||||
await session.send_json({"headers": headers})
|
||||
await session.close()
|
||||
websocket = WebSocket(scope, receive, send)
|
||||
headers = dict(websocket.headers)
|
||||
await websocket.accept()
|
||||
await websocket.send_json({"headers": headers})
|
||||
await websocket.close()
|
||||
|
||||
return asgi
|
||||
|
||||
client = TestClient(app)
|
||||
with client.websocket_connect("/") as session:
|
||||
with client.websocket_connect("/") as websocket:
|
||||
expected_headers = {
|
||||
"accept": "*/*",
|
||||
"accept-encoding": "gzip, deflate",
|
||||
|
@ -58,77 +58,77 @@ def test_session_headers():
|
|||
"sec-websocket-key": "testserver==",
|
||||
"sec-websocket-version": "13",
|
||||
}
|
||||
data = session.receive_json()
|
||||
data = websocket.receive_json()
|
||||
assert data == {"headers": expected_headers}
|
||||
|
||||
|
||||
def test_session_port():
|
||||
def test_websocket_port():
|
||||
def app(scope):
|
||||
async def asgi(receive, send):
|
||||
session = WebSocket(scope, receive, send)
|
||||
await session.accept()
|
||||
await session.send_json({"port": session.url.port})
|
||||
await session.close()
|
||||
websocket = WebSocket(scope, receive, send)
|
||||
await websocket.accept()
|
||||
await websocket.send_json({"port": websocket.url.port})
|
||||
await websocket.close()
|
||||
|
||||
return asgi
|
||||
|
||||
client = TestClient(app)
|
||||
with client.websocket_connect("ws://example.com:123/123?a=abc") as session:
|
||||
data = session.receive_json()
|
||||
with client.websocket_connect("ws://example.com:123/123?a=abc") as websocket:
|
||||
data = websocket.receive_json()
|
||||
assert data == {"port": 123}
|
||||
|
||||
|
||||
def test_session_send_and_receive_text():
|
||||
def test_websocket_send_and_receive_text():
|
||||
def app(scope):
|
||||
async def asgi(receive, send):
|
||||
session = WebSocket(scope, receive, send)
|
||||
await session.accept()
|
||||
data = await session.receive_text()
|
||||
await session.send_text("Message was: " + data)
|
||||
await session.close()
|
||||
websocket = WebSocket(scope, receive, send)
|
||||
await websocket.accept()
|
||||
data = await websocket.receive_text()
|
||||
await websocket.send_text("Message was: " + data)
|
||||
await websocket.close()
|
||||
|
||||
return asgi
|
||||
|
||||
client = TestClient(app)
|
||||
with client.websocket_connect("/") as session:
|
||||
session.send_text("Hello, world!")
|
||||
data = session.receive_text()
|
||||
with client.websocket_connect("/") as websocket:
|
||||
websocket.send_text("Hello, world!")
|
||||
data = websocket.receive_text()
|
||||
assert data == "Message was: Hello, world!"
|
||||
|
||||
|
||||
def test_session_send_and_receive_bytes():
|
||||
def test_websocket_send_and_receive_bytes():
|
||||
def app(scope):
|
||||
async def asgi(receive, send):
|
||||
session = WebSocket(scope, receive, send)
|
||||
await session.accept()
|
||||
data = await session.receive_bytes()
|
||||
await session.send_bytes(b"Message was: " + data)
|
||||
await session.close()
|
||||
websocket = WebSocket(scope, receive, send)
|
||||
await websocket.accept()
|
||||
data = await websocket.receive_bytes()
|
||||
await websocket.send_bytes(b"Message was: " + data)
|
||||
await websocket.close()
|
||||
|
||||
return asgi
|
||||
|
||||
client = TestClient(app)
|
||||
with client.websocket_connect("/") as session:
|
||||
session.send_bytes(b"Hello, world!")
|
||||
data = session.receive_bytes()
|
||||
with client.websocket_connect("/") as websocket:
|
||||
websocket.send_bytes(b"Hello, world!")
|
||||
data = websocket.receive_bytes()
|
||||
assert data == b"Message was: Hello, world!"
|
||||
|
||||
|
||||
def test_session_send_and_receive_json():
|
||||
def test_websocket_send_and_receive_json():
|
||||
def app(scope):
|
||||
async def asgi(receive, send):
|
||||
session = WebSocket(scope, receive, send)
|
||||
await session.accept()
|
||||
data = await session.receive_json()
|
||||
await session.send_json({"message": data})
|
||||
await session.close()
|
||||
websocket = WebSocket(scope, receive, send)
|
||||
await websocket.accept()
|
||||
data = await websocket.receive_json()
|
||||
await websocket.send_json({"message": data})
|
||||
await websocket.close()
|
||||
|
||||
return asgi
|
||||
|
||||
client = TestClient(app)
|
||||
with client.websocket_connect("/") as session:
|
||||
session.send_json({"hello": "world"})
|
||||
data = session.receive_json()
|
||||
with client.websocket_connect("/") as websocket:
|
||||
websocket.send_json({"hello": "world"})
|
||||
data = websocket.receive_json()
|
||||
assert data == {"message": {"hello": "world"}}
|
||||
|
||||
|
||||
|
@ -138,42 +138,42 @@ def test_client_close():
|
|||
def app(scope):
|
||||
async def asgi(receive, send):
|
||||
nonlocal close_code
|
||||
session = WebSocket(scope, receive, send)
|
||||
await session.accept()
|
||||
websocket = WebSocket(scope, receive, send)
|
||||
await websocket.accept()
|
||||
try:
|
||||
data = await session.receive_text()
|
||||
data = await websocket.receive_text()
|
||||
except WebSocketDisconnect as exc:
|
||||
close_code = exc.code
|
||||
|
||||
return asgi
|
||||
|
||||
client = TestClient(app)
|
||||
with client.websocket_connect("/") as session:
|
||||
session.close(code=1001)
|
||||
with client.websocket_connect("/") as websocket:
|
||||
websocket.close(code=1001)
|
||||
assert close_code == 1001
|
||||
|
||||
|
||||
def test_application_close():
|
||||
def app(scope):
|
||||
async def asgi(receive, send):
|
||||
session = WebSocket(scope, receive, send)
|
||||
await session.accept()
|
||||
await session.close(1001)
|
||||
websocket = WebSocket(scope, receive, send)
|
||||
await websocket.accept()
|
||||
await websocket.close(1001)
|
||||
|
||||
return asgi
|
||||
|
||||
client = TestClient(app)
|
||||
with client.websocket_connect("/") as session:
|
||||
with client.websocket_connect("/") as websocket:
|
||||
with pytest.raises(WebSocketDisconnect) as exc:
|
||||
session.receive_text()
|
||||
websocket.receive_text()
|
||||
assert exc.value.code == 1001
|
||||
|
||||
|
||||
def test_rejected_connection():
|
||||
def app(scope):
|
||||
async def asgi(receive, send):
|
||||
session = WebSocket(scope, receive, send)
|
||||
await session.close(1001)
|
||||
websocket = WebSocket(scope, receive, send)
|
||||
await websocket.close(1001)
|
||||
|
||||
return asgi
|
||||
|
||||
|
@ -186,19 +186,19 @@ def test_rejected_connection():
|
|||
def test_subprotocol():
|
||||
def app(scope):
|
||||
async def asgi(receive, send):
|
||||
session = WebSocket(scope, receive, send)
|
||||
assert session["subprotocols"] == ["soap", "wamp"]
|
||||
await session.accept(subprotocol="wamp")
|
||||
await session.close()
|
||||
websocket = WebSocket(scope, receive, send)
|
||||
assert websocket["subprotocols"] == ["soap", "wamp"]
|
||||
await websocket.accept(subprotocol="wamp")
|
||||
await websocket.close()
|
||||
|
||||
return asgi
|
||||
|
||||
client = TestClient(app)
|
||||
with client.websocket_connect("/", subprotocols=["soap", "wamp"]) as session:
|
||||
assert session.accepted_subprotocol == "wamp"
|
||||
with client.websocket_connect("/", subprotocols=["soap", "wamp"]) as websocket:
|
||||
assert websocket.accepted_subprotocol == "wamp"
|
||||
|
||||
|
||||
def test_session_exception():
|
||||
def test_websocket_exception():
|
||||
def app(scope):
|
||||
async def asgi(receive, send):
|
||||
assert False
|
||||
|
@ -213,42 +213,42 @@ def test_session_exception():
|
|||
def test_duplicate_close():
|
||||
def app(scope):
|
||||
async def asgi(receive, send):
|
||||
session = WebSocket(scope, receive, send)
|
||||
await session.accept()
|
||||
await session.close()
|
||||
await session.close()
|
||||
websocket = WebSocket(scope, receive, send)
|
||||
await websocket.accept()
|
||||
await websocket.close()
|
||||
await websocket.close()
|
||||
|
||||
return asgi
|
||||
|
||||
client = TestClient(app)
|
||||
with pytest.raises(RuntimeError):
|
||||
with client.websocket_connect("/") as session:
|
||||
with client.websocket_connect("/") as websocket:
|
||||
pass
|
||||
|
||||
|
||||
def test_duplicate_disconnect():
|
||||
def app(scope):
|
||||
async def asgi(receive, send):
|
||||
session = WebSocket(scope, receive, send)
|
||||
await session.accept()
|
||||
message = await session.receive()
|
||||
websocket = WebSocket(scope, receive, send)
|
||||
await websocket.accept()
|
||||
message = await websocket.receive()
|
||||
assert message["type"] == "websocket.disconnect"
|
||||
message = await session.receive()
|
||||
message = await websocket.receive()
|
||||
|
||||
return asgi
|
||||
|
||||
client = TestClient(app)
|
||||
with pytest.raises(RuntimeError):
|
||||
with client.websocket_connect("/") as session:
|
||||
session.close()
|
||||
with client.websocket_connect("/") as websocket:
|
||||
websocket.close()
|
||||
|
||||
|
||||
def test_session_scope_interface():
|
||||
def test_websocket_scope_interface():
|
||||
"""
|
||||
A WebSocket can be instantiated with a scope, and presents a `Mapping`
|
||||
interface.
|
||||
"""
|
||||
session = WebSocket({"type": "websocket", "path": "/abc/", "headers": []})
|
||||
assert session["type"] == "websocket"
|
||||
assert dict(session) == {"type": "websocket", "path": "/abc/", "headers": []}
|
||||
assert len(session) == 3
|
||||
websocket = WebSocket({"type": "websocket", "path": "/abc/", "headers": []})
|
||||
assert websocket["type"] == "websocket"
|
||||
assert dict(websocket) == {"type": "websocket", "path": "/abc/", "headers": []}
|
||||
assert len(websocket) == 3
|
||||
|
|
Loading…
Reference in New Issue