Name changes (#55)

* Rename View -> HTTPEndpoint

* Rename views doc file -> endpoints

* Rename request/response files -> requests/responses, update imports, update docs

* Rename WebSocketSession -> WebSocket, rename session -> websocket in docs

* Full module imports in tests and source, removing imports from __init__ file

* Fix name in testclient doc
This commit is contained in:
Jordan Eremieff 2018-09-05 19:29:04 +10:00 committed by Tom Christie
parent 9871fa6e30
commit f4fe0ec312
30 changed files with 129 additions and 152 deletions

View File

@ -40,7 +40,7 @@ $ pip3 install starlette
## Example
```python
from starlette.response import Response
from starlette.responses import Response
class App:

View File

@ -1,13 +1,13 @@
Starlette also includes an `App` class that nicely ties together all of
Starlette also includes an app class `Starlette` that nicely ties together all of
its other functionality.
```python
from starlette.app import App
from starlette.response import PlainTextResponse
from starlette.app import Starlette
from starlette.responses import PlainTextResponse
from starlette.staticfiles import StaticFiles
app = App()
app = Starlette()
app.debug = True
app.mount("/static", StaticFiles(directory="static"))
@ -23,15 +23,15 @@ def user(request, username):
@app.websocket_route('/ws')
async def websocket_endpoint(session):
await session.accept()
await session.send_text('Hello, websocket!')
await session.close()
async def websocket_endpoint(websocket):
await websocket.accept()
await websocket.send_text('Hello, websocket!')
await websocket.close()
```
### Instantiating the application
* `App(debug=False)` - Create a new Starlette application.
* `Starlette(debug=False)` - Create a new Starlette application.
### Adding routes to the application

View File

@ -1,44 +1,44 @@
Starlette includes a `View` class that provides a class-based view pattern which
Starlette includes an `HTTPEndpoint` class that provides a class-based view pattern which
handles HTTP method dispatching.
The `View` class can be used as an other ASGI application:
The `HTTPEndpoint` class can be used as an ASGI application:
```python
from starlette.response import PlainTextResponse
from starlette.views import View
from starlette.responses import PlainTextResponse
from starlette.endpoints import HTTPEndpoint
class App(View):
class App(HTTPEndpoint):
async def get(self, request):
return PlainTextResponse(f"Hello, world!")
```
If you're using a Starlette application instance to handle routing, you can
dispatch to a View class by using the `@app.route()` decorator, or the
dispatch to an `HTTPEndpoint` class by using the `@app.route()` decorator, or the
`app.add_route()` function. Make sure to dispatch to the class itself, rather
than to an instance of the class:
```python
from starlette.app import App
from starlette.app import Starlette
from starlette.response import PlainTextResponse
from starlette.views import View
from starlette.endpoints import HTTPEndpoint
app = App()
app = Starlette()
@app.route("/")
class Homepage(View):
class Homepage(HTTPEndpoint):
async def get(self, request):
return PlainTextResponse(f"Hello, world!")
@app.route("/{username}")
class User(View):
class User(HTTPEndpoint):
async def get(self, request, username):
return PlainTextResponse(f"Hello, {username}")
```
Class-based views will respond with "405 Method not allowed" responses for any
HTTP endpoint classes will respond with "405 Method not allowed" responses for any
request methods which do not map to a corresponding handler.

View File

@ -26,7 +26,7 @@ instead:
```python
from starlette.exceptions import ExceptionMiddleware, HTTPException
from starlette.response import JSONResponse
from starlette.responses import JSONResponse
class App:

View File

@ -39,7 +39,7 @@ $ pip3 install starlette
## Example
```python
from starlette.response import Response
from starlette.responses import Response
class App:

View File

@ -7,7 +7,7 @@ the incoming request, rather than accessing the ASGI scope and receive channel d
Signature: `Request(scope, receive=None)`
```python
from starlette.request import Request
from starlette.requests import Request
from starlette.response import Response
@ -66,8 +66,8 @@ The request body, parsed as JSON: `await request.json()`
You can also access the request body as a stream, using the `async for` syntax:
```python
from starlette.request import Request
from starlette.response import Response
from starlette.requests import Request
from starlette.responses import Response
class App:

View File

@ -19,7 +19,7 @@ Once you've instantiated a response, you can send it by calling it as an
ASGI application instance.
```python
from starlette.response import Response
from starlette.responses import Response
class App:
@ -36,7 +36,7 @@ class App:
Takes some text or bytes and returns an HTML response.
```python
from starlette.response import HTMLResponse
from starlette.responses import HTMLResponse
class App:
@ -53,7 +53,7 @@ class App:
Takes some text or bytes and returns an plain text response.
```python
from starlette.response import PlainTextResponse
from starlette.responses import PlainTextResponse
class App:
@ -70,7 +70,7 @@ class App:
Takes some data and returns an `application/json` encoded response.
```python
from starlette.response import JSONResponse
from starlette.responses import JSONResponse
class App:
@ -87,7 +87,7 @@ class App:
Returns an HTTP redirect. Uses a 302 status code by default.
```python
from starlette.response import PlainTextResponse, RedirectResponse
from starlette.responses import PlainTextResponse, RedirectResponse
class App:
@ -107,7 +107,7 @@ class App:
Takes an async generator and streams the response body.
```python
from starlette.response import StreamingResponse
from starlette.responses import StreamingResponse
import asyncio
@ -143,7 +143,7 @@ Takes a different set of arguments to instantiate than the other response types:
File responses will include appropriate `Content-Length`, `Last-Modified` and `ETag` headers.
```python
from starlette.response import FileResponse
from starlette.responses import FileResponse
class App:

View File

@ -51,9 +51,9 @@ You can also route based on the incoming protocol, using the `ProtocolRouter`
class.
```python
from starlette.response import Response
from starlette.responses import Response
from starlette.routing import ProtocolRouter
from starlette.websockets import WebSocketSession
from starlette.websockets import WebSocket
def http_endpoint(scope):
@ -62,10 +62,10 @@ def http_endpoint(scope):
def websocket_endpoint(scope):
async def asgi(receive, send):
session = WebSocketSession(scope, receive, send)
await session.accept()
await session.send_json({"hello": "world"})
await session.close()
websocket = WebSocket(scope, receive, send)
await websocket.accept()
await websocket.send_json({"hello": "world"})
await websocket.close()
return asgi

View File

@ -3,7 +3,7 @@ The test client allows you to make requests against your ASGI application,
using the `requests` library.
```python
from starlette.response import HTMLResponse
from starlette.responses import HTMLResponse
from starlette.testclient import TestClient
@ -44,7 +44,7 @@ websocket testing.
```python
from starlette.testclient import TestClient
from starlette.websockets import WebSocketSession
from starlette.websockets import WebSocket
class App:
@ -52,16 +52,16 @@ class App:
self.scope = scope
async def __call__(self, receive, send):
session = WebSocketSession(self.scope, receive=receive, send=send)
await session.accept()
await session.send_text('Hello, world!')
await session.close()
websocket = WebSocket(self.scope, receive=receive, send=send)
await websocket.accept()
await websocket.send_text('Hello, world!')
await websocket.close()
def test_app():
client = TestClient(App)
with client.websocket_connect('/') as session:
data = session.receive_text()
with client.websocket_connect('/') as websocket:
data = websocket.receive_text()
assert data == 'Hello, world!'
```

View File

@ -1,14 +1,13 @@
Starlette includes a `WebSocketSessions` class that fulfils a similar role
to the HTTP request, but that allows sending and receiving data on a websocket
session.
Starlette includes a `WebSocket` class that fulfils a similar role
to the HTTP request, but that allows sending and receiving data on a websocket.
### WebSocketSession
### WebSocket
Signature: `WebSocketSession(scope, receive=None, send=None)`
Signature: `WebSocket(scope, receive=None, send=None)`
```python
from starlette.websockets import WebSocketSession
from starlette.websockets import WebSocket
class App:
@ -16,66 +15,66 @@ class App:
self.scope = scope
async def __call__(self, receive, send):
session = WebSocketSession(self.scope, receive=receive, send=send)
await session.accept()
await session.send_text('Hello, world!')
await session.close()
websocket = WebSocket(self.scope, receive=receive, send=send)
await websocket.accept()
await websocket.send_text('Hello, world!')
await websocket.close()
```
Sessions present a mapping interface, so you can use them in the same
WebSockets present a mapping interface, so you can use them in the same
way as a `scope`.
For instance: `session['path']` will return the ASGI path.
For instance: `websocket['path']` will return the ASGI path.
#### URL
The session URL is accessed as `session.url`.
The websocket URL is accessed as `websocket.url`.
The property is actually a subclass of `str`, and also exposes all the
components that can be parsed out of the URL.
For example: `session.url.path`, `session.url.port`, `session.url.scheme`.
For example: `websocket.url.path`, `websocket.url.port`, `websocket.url.scheme`.
#### Headers
Headers are exposed as an immutable, case-insensitive, multi-dict.
For example: `session.headers['sec-websocket-version']`
For example: `websocket.headers['sec-websocket-version']`
#### Query Parameters
Headers are exposed as an immutable multi-dict.
For example: `session.query_params['abc']`
For example: `websocket.query_params['abc']`
### Accepting the connection
* `await session.accept(subprotocol=None)`
* `await websocket.accept(subprotocol=None)`
### Sending data
* `await session.send_text(data)`
* `await session.send_bytes(data)`
* `await session.send_json(data)`
* `await websocket.send_text(data)`
* `await websocket.send_bytes(data)`
* `await websocket.send_json(data)`
### Receiving data
* `await session.receive_text()`
* `await session.receive_bytes()`
* `await session.receive_json()`
* `await websocket.receive_text()`
* `await websocket.receive_bytes()`
* `await websocket.receive_json()`
May raise `starlette.websockets.Disconnect()`.
### Closing the connection
* `await session.close(code=1000)`
* `await websocket.close(code=1000)`
### Sending and receiving messages
If you need to send or receive raw ASGI messages then you should use
`session.send()` and `session.receive()` rather than using the raw `send` and
`receive` callables. This will ensure that the session's state is kept
`websocket.send()` and `websocket.receive()` rather than using the raw `send` and
`receive` callables. This will ensure that the websocket's state is kept
correctly updated.
* `await session.send(message)`
* `await session.receive()`
* `await websocket.send(message)`
* `await websocket.receive()`

View File

@ -19,7 +19,7 @@ nav:
- Applications: 'applications.md'
- Test Client: 'test_client.md'
- Debugging: 'debugging.md'
- Views: 'views.md'
- Endpoints: 'endpoints.md'
markdown_extensions:
- markdown.extensions.codehilite:

View File

@ -1,27 +1 @@
from starlette.app import App
from starlette.response import (
FileResponse,
HTMLResponse,
JSONResponse,
RedirectResponse,
Response,
PlainTextResponse,
StreamingResponse,
)
from starlette.request import Request
from starlette.testclient import TestClient
__all__ = (
"App",
"FileResponse",
"HTMLResponse",
"JSONResponse",
"PlainTextResponse",
"RedirectResponse",
"Response",
"StreamingResponse",
"Request",
"TestClient",
)
__version__ = "0.2.3"

View File

@ -1,8 +1,8 @@
from starlette.exceptions import ExceptionMiddleware
from starlette.request import Request
from starlette.requests import Request
from starlette.routing import Path, PathPrefix, Router
from starlette.types import ASGIApp, ASGIInstance, Receive, Scope, Send
from starlette.websockets import WebSocketSession
from starlette.websockets import WebSocket
import asyncio
import inspect
@ -36,7 +36,7 @@ def websocket_session(func):
def app(scope: Scope) -> ASGIInstance:
async def awaitable(receive: Receive, send: Send) -> None:
session = WebSocketSession(scope, receive=receive, send=send)
session = WebSocket(scope, receive=receive, send=send)
kwargs = scope.get("kwargs", {})
await func(session, **kwargs)
@ -45,7 +45,7 @@ def websocket_session(func):
return app
class App:
class Starlette:
def __init__(self, debug=False) -> None:
self.router = Router(routes=[])
self.exception_middleware = ExceptionMiddleware(self.router, debug=debug)

View File

@ -1,5 +1,5 @@
from starlette.request import Request
from starlette.response import HTMLResponse, PlainTextResponse
from starlette.requests import Request
from starlette.responses import HTMLResponse, PlainTextResponse
import html
import traceback

View File

@ -1,11 +1,11 @@
from starlette.exceptions import HTTPException
from starlette.request import Request
from starlette.response import Response, PlainTextResponse
from starlette.requests import Request
from starlette.responses import Response, PlainTextResponse
from starlette.types import Receive, Send, Scope
import asyncio
class View:
class HTTPEndpoint:
def __init__(self, scope: Scope):
self.scope = scope

View File

@ -1,6 +1,6 @@
from starlette.debug import get_debug_response
from starlette.request import Request
from starlette.response import PlainTextResponse, Response
from starlette.requests import Request
from starlette.responses import PlainTextResponse, Response
import asyncio
import http

View File

@ -1,5 +1,5 @@
from starlette.exceptions import HTTPException
from starlette.response import PlainTextResponse
from starlette.responses import PlainTextResponse
from starlette.types import Scope, ASGIApp, ASGIInstance
from starlette.websockets import WebSocketClose
import re

View File

@ -1,4 +1,4 @@
from starlette import PlainTextResponse, FileResponse
from starlette.responses import PlainTextResponse, FileResponse
from aiofiles.os import stat as aio_stat
import os
import stat

View File

@ -17,7 +17,7 @@ class WebSocketDisconnect(Exception):
self.code = code
class WebSocketSession(Mapping):
class WebSocket(Mapping):
def __init__(self, scope, receive=None, send=None):
assert scope["type"] == "websocket"
self._scope = scope

View File

@ -1,13 +1,13 @@
from starlette import App
from starlette.app import Starlette
from starlette.exceptions import HTTPException
from starlette.response import JSONResponse, PlainTextResponse
from starlette.responses import JSONResponse, PlainTextResponse
from starlette.staticfiles import StaticFiles
from starlette.testclient import TestClient
from starlette.views import View
from starlette.endpoints import HTTPEndpoint
import os
app = App()
app = Starlette()
@app.exception_handler(Exception)
@ -31,7 +31,7 @@ async def async_homepage(request):
@app.route("/class")
class Homepage(View):
class Homepage(HTTPEndpoint):
def get(self, request):
return PlainTextResponse("Hello, world!")
@ -114,8 +114,9 @@ def test_app_mount(tmpdir):
with open(path, "w") as file:
file.write("<file content>")
app = App()
app = Starlette()
app.mount("/static", StaticFiles(directory=tmpdir), methods=["GET", "HEAD"])
client = TestClient(app)
response = client.get("/static/example.txt")
@ -128,7 +129,7 @@ def test_app_mount(tmpdir):
def test_app_debug():
app = App()
app = Starlette()
app.debug = True
@app.route("/")

View File

@ -1,4 +1,5 @@
from starlette import Request, Response, TestClient
from starlette.responses import Response
from starlette.testclient import TestClient
from starlette.debug import DebugMiddleware
import pytest

View File

@ -1,11 +1,11 @@
import pytest
from starlette.response import PlainTextResponse
from starlette.responses import PlainTextResponse
from starlette.routing import Router, Path
from starlette.testclient import TestClient
from starlette.views import View
from starlette.endpoints import HTTPEndpoint
class Homepage(View):
class Homepage(HTTPEndpoint):
async def get(self, request, username=None):
if username is None:
return PlainTextResponse("Hello, world!")

View File

@ -1,5 +1,5 @@
from starlette.exceptions import ExceptionMiddleware, HTTPException
from starlette.response import PlainTextResponse
from starlette.responses import PlainTextResponse
from starlette.routing import Router, Path
from starlette.testclient import TestClient
import pytest

View File

@ -1,5 +1,6 @@
from starlette import Request, JSONResponse, TestClient
from starlette.request import ClientDisconnect
from starlette.responses import JSONResponse
from starlette.testclient import TestClient
from starlette.requests import Request, ClientDisconnect
import asyncio
import pytest

View File

@ -1,4 +1,4 @@
from starlette.response import (
from starlette.responses import (
FileResponse,
RedirectResponse,
Response,

View File

@ -1,7 +1,8 @@
from starlette import Response, TestClient
from starlette.responses import Response
from starlette.testclient import TestClient
from starlette.exceptions import ExceptionMiddleware
from starlette.routing import Path, PathPrefix, Router, ProtocolRouter
from starlette.websockets import WebSocketSession, WebSocketDisconnect
from starlette.websockets import WebSocket, WebSocketDisconnect
import pytest
@ -71,7 +72,7 @@ def http_endpoint(scope):
def websocket_endpoint(scope):
async def asgi(receive, send):
session = WebSocketSession(scope, receive, send)
session = WebSocket(scope, receive, send)
await session.accept()
await session.send_json({"hello": "world"})
await session.close()

View File

@ -1,4 +1,4 @@
from starlette import TestClient
from starlette.testclient import TestClient
from starlette.staticfiles import StaticFile, StaticFiles
import os
import pytest

View File

@ -1,12 +1,12 @@
import pytest
from starlette.testclient import TestClient
from starlette.websockets import WebSocketSession, WebSocketDisconnect
from starlette.websockets import WebSocket, WebSocketDisconnect
def test_session_url():
def app(scope):
async def asgi(receive, send):
session = WebSocketSession(scope, receive, send)
session = WebSocket(scope, receive, send)
await session.accept()
await session.send_json({"url": session.url})
await session.close()
@ -22,7 +22,7 @@ def test_session_url():
def test_session_query_params():
def app(scope):
async def asgi(receive, send):
session = WebSocketSession(scope, receive, send)
session = WebSocket(scope, receive, send)
query_params = dict(session.query_params)
await session.accept()
await session.send_json({"params": query_params})
@ -39,7 +39,7 @@ def test_session_query_params():
def test_session_headers():
def app(scope):
async def asgi(receive, send):
session = WebSocketSession(scope, receive, send)
session = WebSocket(scope, receive, send)
headers = dict(session.headers)
await session.accept()
await session.send_json({"headers": headers})
@ -65,7 +65,7 @@ def test_session_headers():
def test_session_port():
def app(scope):
async def asgi(receive, send):
session = WebSocketSession(scope, receive, send)
session = WebSocket(scope, receive, send)
await session.accept()
await session.send_json({"port": session.url.port})
await session.close()
@ -81,7 +81,7 @@ def test_session_port():
def test_session_send_and_receive_text():
def app(scope):
async def asgi(receive, send):
session = WebSocketSession(scope, receive, send)
session = WebSocket(scope, receive, send)
await session.accept()
data = await session.receive_text()
await session.send_text("Message was: " + data)
@ -99,7 +99,7 @@ def test_session_send_and_receive_text():
def test_session_send_and_receive_bytes():
def app(scope):
async def asgi(receive, send):
session = WebSocketSession(scope, receive, send)
session = WebSocket(scope, receive, send)
await session.accept()
data = await session.receive_bytes()
await session.send_bytes(b"Message was: " + data)
@ -117,7 +117,7 @@ def test_session_send_and_receive_bytes():
def test_session_send_and_receive_json():
def app(scope):
async def asgi(receive, send):
session = WebSocketSession(scope, receive, send)
session = WebSocket(scope, receive, send)
await session.accept()
data = await session.receive_json()
await session.send_json({"message": data})
@ -138,7 +138,7 @@ def test_client_close():
def app(scope):
async def asgi(receive, send):
nonlocal close_code
session = WebSocketSession(scope, receive, send)
session = WebSocket(scope, receive, send)
await session.accept()
try:
data = await session.receive_text()
@ -156,7 +156,7 @@ def test_client_close():
def test_application_close():
def app(scope):
async def asgi(receive, send):
session = WebSocketSession(scope, receive, send)
session = WebSocket(scope, receive, send)
await session.accept()
await session.close(1001)
@ -172,7 +172,7 @@ def test_application_close():
def test_rejected_connection():
def app(scope):
async def asgi(receive, send):
session = WebSocketSession(scope, receive, send)
session = WebSocket(scope, receive, send)
await session.close(1001)
return asgi
@ -186,7 +186,7 @@ def test_rejected_connection():
def test_subprotocol():
def app(scope):
async def asgi(receive, send):
session = WebSocketSession(scope, receive, send)
session = WebSocket(scope, receive, send)
assert session["subprotocols"] == ["soap", "wamp"]
await session.accept(subprotocol="wamp")
await session.close()
@ -213,7 +213,7 @@ def test_session_exception():
def test_duplicate_close():
def app(scope):
async def asgi(receive, send):
session = WebSocketSession(scope, receive, send)
session = WebSocket(scope, receive, send)
await session.accept()
await session.close()
await session.close()
@ -229,7 +229,7 @@ def test_duplicate_close():
def test_duplicate_disconnect():
def app(scope):
async def asgi(receive, send):
session = WebSocketSession(scope, receive, send)
session = WebSocket(scope, receive, send)
await session.accept()
message = await session.receive()
assert message["type"] == "websocket.disconnect"
@ -245,10 +245,10 @@ def test_duplicate_disconnect():
def test_session_scope_interface():
"""
A WebSocketSession can be instantiated with a scope, and presents a `Mapping`
A WebSocket can be instantiated with a scope, and presents a `Mapping`
interface.
"""
session = WebSocketSession({"type": "websocket", "path": "/abc/", "headers": []})
session = WebSocket({"type": "websocket", "path": "/abc/", "headers": []})
assert session["type"] == "websocket"
assert dict(session) == {"type": "websocket", "path": "/abc/", "headers": []}
assert len(session) == 3