Skip to content

Commit a143854

Browse files
authored
Merge branch 'master' into feat/upload-file-max-size
2 parents 0db8744 + 427a8dc commit a143854

File tree

9 files changed

+74
-34
lines changed

9 files changed

+74
-34
lines changed

docs/applications.md

+8-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ Starlette includes an application class `Starlette` that nicely ties together al
33
its other functionality.
44

55
```python
6+
from contextlib import asynccontextmanager
7+
68
from starlette.applications import Starlette
79
from starlette.responses import PlainTextResponse
810
from starlette.routing import Route, Mount, WebSocketRoute
@@ -25,8 +27,11 @@ async def websocket_endpoint(websocket):
2527
await websocket.send_text('Hello, websocket!')
2628
await websocket.close()
2729

28-
def startup():
29-
print('Ready to go')
30+
@asyncontextmanager
31+
async def lifespan(app):
32+
print('Startup')
33+
yield
34+
print('Shutdown')
3035

3136

3237
routes = [
@@ -37,7 +42,7 @@ routes = [
3742
Mount('/static', StaticFiles(directory="static")),
3843
]
3944

40-
app = Starlette(debug=True, routes=routes, on_startup=[startup])
45+
app = Starlette(debug=True, routes=routes, lifespan=lifespan)
4146
```
4247

4348
### Instantiating the application

docs/index.md

+7-13
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
---
2-
hide: navigation
3-
---
4-
51
<p align="center">
62
<img width="400px" src="/img/starlette.svg#only-light" alt="starlette"/>
73
<img width="400px" src="/img/starlette_dark.svg#only-dark" alt="starlette"/>
@@ -52,18 +48,18 @@ It is production-ready, and gives you the following:
5248
## Installation
5349

5450
```shell
55-
$ pip install starlette
51+
pip install starlette
5652
```
5753

5854
You'll also want to install an ASGI server, such as [uvicorn](https://www.uvicorn.org/), [daphne](https://github.com/django/daphne/), or [hypercorn](https://hypercorn.readthedocs.io/en/latest/).
5955

6056
```shell
61-
$ pip install uvicorn
57+
pip install uvicorn
6258
```
6359

6460
## Example
6561

66-
```python title="example.py"
62+
```python title="main.py"
6763
from starlette.applications import Starlette
6864
from starlette.responses import JSONResponse
6965
from starlette.routing import Route
@@ -81,11 +77,9 @@ app = Starlette(debug=True, routes=[
8177
Then run the application...
8278

8379
```shell
84-
$ uvicorn example:app
80+
uvicorn main:app
8581
```
8682

87-
For a more complete example, [see here](https://github.com/encode/starlette-example).
88-
8983
## Dependencies
9084

9185
Starlette only requires `anyio`, and the following dependencies are optional:
@@ -103,7 +97,7 @@ You can install all of these with `pip install starlette[full]`.
10397
Starlette is designed to be used either as a complete framework, or as
10498
an ASGI toolkit. You can use any of its components independently.
10599

106-
```python
100+
```python title="main.py"
107101
from starlette.responses import PlainTextResponse
108102

109103

@@ -113,10 +107,10 @@ async def app(scope, receive, send):
113107
await response(scope, receive, send)
114108
```
115109

116-
Run the `app` application in `example.py`:
110+
Run the `app` application in `main.py`:
117111

118112
```shell
119-
$ uvicorn example:app
113+
$ uvicorn main:app
120114
INFO: Started server process [11509]
121115
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
122116
```

docs/release-notes.md

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
---
2-
hide: navigation
32
toc_depth: 2
43
---
54

mkdocs.yml

+1-2
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,14 @@ theme:
2121
repo: fontawesome/brands/github
2222
features:
2323
- content.code.copy
24-
- navigation.tabs
2524
- toc.follow
2625

2726
repo_name: encode/starlette
2827
repo_url: https://github.com/encode/starlette
2928
edit_uri: edit/master/docs/
3029

3130
nav:
32-
- Home: "index.md"
31+
- Introduction: "index.md"
3332
- Features:
3433
- Applications: "applications.md"
3534
- Requests: "requests.md"

starlette/applications.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from typing_extensions import ParamSpec
1111

1212
from starlette.datastructures import State, URLPath
13-
from starlette.middleware import Middleware, _MiddlewareClass
13+
from starlette.middleware import Middleware, _MiddlewareFactory
1414
from starlette.middleware.base import BaseHTTPMiddleware
1515
from starlette.middleware.errors import ServerErrorMiddleware
1616
from starlette.middleware.exceptions import ExceptionMiddleware
@@ -96,7 +96,7 @@ def build_middleware_stack(self) -> ASGIApp:
9696

9797
app = self.router
9898
for cls, args, kwargs in reversed(middleware):
99-
app = cls(app=app, *args, **kwargs)
99+
app = cls(app, *args, **kwargs)
100100
return app
101101

102102
@property
@@ -123,7 +123,7 @@ def host(self, host: str, app: ASGIApp, name: str | None = None) -> None:
123123

124124
def add_middleware(
125125
self,
126-
middleware_class: type[_MiddlewareClass[P]],
126+
middleware_class: _MiddlewareFactory[P],
127127
*args: P.args,
128128
**kwargs: P.kwargs,
129129
) -> None:

starlette/middleware/__init__.py

+6-7
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,19 @@
88
else: # pragma: no cover
99
from typing_extensions import ParamSpec
1010

11-
from starlette.types import ASGIApp, Receive, Scope, Send
11+
from starlette.types import ASGIApp
1212

1313
P = ParamSpec("P")
1414

1515

16-
class _MiddlewareClass(Protocol[P]):
17-
def __init__(self, app: ASGIApp, *args: P.args, **kwargs: P.kwargs) -> None: ... # pragma: no cover
18-
19-
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: ... # pragma: no cover
16+
class _MiddlewareFactory(Protocol[P]):
17+
def __call__(self, app: ASGIApp, *args: P.args, **kwargs: P.kwargs) -> ASGIApp: ... # pragma: no cover
2018

2119

2220
class Middleware:
2321
def __init__(
2422
self,
25-
cls: type[_MiddlewareClass[P]],
23+
cls: _MiddlewareFactory[P],
2624
*args: P.args,
2725
**kwargs: P.kwargs,
2826
) -> None:
@@ -38,5 +36,6 @@ def __repr__(self) -> str:
3836
class_name = self.__class__.__name__
3937
args_strings = [f"{value!r}" for value in self.args]
4038
option_strings = [f"{key}={value!r}" for key, value in self.kwargs.items()]
41-
args_repr = ", ".join([self.cls.__name__] + args_strings + option_strings)
39+
name = getattr(self.cls, "__name__", "")
40+
args_repr = ", ".join([name] + args_strings + option_strings)
4241
return f"{class_name}({args_repr})"

starlette/routing.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ def __init__(
236236

237237
if middleware is not None:
238238
for cls, args, kwargs in reversed(middleware):
239-
self.app = cls(app=self.app, *args, **kwargs)
239+
self.app = cls(self.app, *args, **kwargs)
240240

241241
if methods is None:
242242
self.methods = None
@@ -328,7 +328,7 @@ def __init__(
328328

329329
if middleware is not None:
330330
for cls, args, kwargs in reversed(middleware):
331-
self.app = cls(app=self.app, *args, **kwargs)
331+
self.app = cls(self.app, *args, **kwargs)
332332

333333
self.path_regex, self.path_format, self.param_convertors = compile_path(path)
334334

@@ -388,7 +388,7 @@ def __init__(
388388
self.app = self._base_app
389389
if middleware is not None:
390390
for cls, args, kwargs in reversed(middleware):
391-
self.app = cls(app=self.app, *args, **kwargs)
391+
self.app = cls(self.app, *args, **kwargs)
392392
self.name = name
393393
self.path_regex, self.path_format, self.param_convertors = compile_path(self.path + "/{path:path}")
394394

tests/middleware/test_base.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
from starlette.applications import Starlette
1212
from starlette.background import BackgroundTask
13-
from starlette.middleware import Middleware, _MiddlewareClass
13+
from starlette.middleware import Middleware, _MiddlewareFactory
1414
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
1515
from starlette.requests import ClientDisconnect, Request
1616
from starlette.responses import PlainTextResponse, Response, StreamingResponse
@@ -232,7 +232,7 @@ async def dispatch(
232232
)
233233
def test_contextvars(
234234
test_client_factory: TestClientFactory,
235-
middleware_cls: type[_MiddlewareClass[Any]],
235+
middleware_cls: _MiddlewareFactory[Any],
236236
) -> None:
237237
# this has to be an async endpoint because Starlette calls run_in_threadpool
238238
# on sync endpoints which has it's own set of peculiarities w.r.t propagating

tests/test_applications.py

+44
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import os
24
from contextlib import asynccontextmanager
35
from pathlib import Path
@@ -533,6 +535,48 @@ def get_app() -> ASGIApp:
533535
assert SimpleInitializableMiddleware.counter == 2
534536

535537

538+
def test_middleware_args(test_client_factory: TestClientFactory) -> None:
539+
calls: list[str] = []
540+
541+
class MiddlewareWithArgs:
542+
def __init__(self, app: ASGIApp, arg: str) -> None:
543+
self.app = app
544+
self.arg = arg
545+
546+
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
547+
calls.append(self.arg)
548+
await self.app(scope, receive, send)
549+
550+
app = Starlette()
551+
app.add_middleware(MiddlewareWithArgs, "foo")
552+
app.add_middleware(MiddlewareWithArgs, "bar")
553+
554+
with test_client_factory(app):
555+
pass
556+
557+
assert calls == ["bar", "foo"]
558+
559+
560+
def test_middleware_factory(test_client_factory: TestClientFactory) -> None:
561+
calls: list[str] = []
562+
563+
def _middleware_factory(app: ASGIApp, arg: str) -> ASGIApp:
564+
async def _app(scope: Scope, receive: Receive, send: Send) -> None:
565+
calls.append(arg)
566+
await app(scope, receive, send)
567+
568+
return _app
569+
570+
app = Starlette()
571+
app.add_middleware(_middleware_factory, arg="foo")
572+
app.add_middleware(_middleware_factory, arg="bar")
573+
574+
with test_client_factory(app):
575+
pass
576+
577+
assert calls == ["bar", "foo"]
578+
579+
536580
def test_lifespan_app_subclass() -> None:
537581
# This test exists to make sure that subclasses of Starlette
538582
# (like FastAPI) are compatible with the types hints for Lifespan

0 commit comments

Comments
 (0)