Skip to content

Commit

Permalink
Merge branch 'master' into improve-types/types-file
Browse files Browse the repository at this point in the history
  • Loading branch information
TechNiick authored May 7, 2024
2 parents 807e8a8 + 9f16bf5 commit 9cbf5f4
Show file tree
Hide file tree
Showing 9 changed files with 28 additions and 22 deletions.
4 changes: 3 additions & 1 deletion docs/middleware.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,9 @@ The following arguments are supported:
* `session_cookie` - Defaults to "session".
* `max_age` - Session expiry time in seconds. Defaults to 2 weeks. If set to `None` then the cookie will last as long as the browser session.
* `same_site` - SameSite flag prevents the browser from sending session cookie along with cross-site requests. Defaults to `'lax'`.
* `path` - The path set for the session cookie. Defaults to `'/'`.
* `https_only` - Indicate that Secure flag should be set (can be used with HTTPS only). Defaults to `False`.
* `domain` - Domain of the cookie used to share cookie between subdomains or cross-domains. The browser defaults the domain to the same host that set the cookie, excluding subdomains [refrence](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#domain_attribute).
* `domain` - Domain of the cookie used to share cookie between subdomains or cross-domains. The browser defaults the domain to the same host that set the cookie, excluding subdomains ([reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#domain_attribute)).


```python
Expand Down Expand Up @@ -167,6 +168,7 @@ The following arguments are supported:
* `allowed_hosts` - A list of domain names that should be allowed as hostnames. Wildcard
domains such as `*.example.com` are supported for matching subdomains. To allow any
hostname either use `allowed_hosts=["*"]` or omit the middleware.
* `www_redirect` - If set to True, requests to non-www versions of the allowed hosts will be redirected to their www counterparts. Defaults to `True`.

If an incoming request does not validate correctly then a 400 response will be sent.

Expand Down
3 changes: 1 addition & 2 deletions docs/requests.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,7 @@ For example: `request.path_params['username']`

#### Client Address

The client's remote address is exposed as a named two-tuple `request.client`.
Either item in the tuple may be `None`.
The client's remote address is exposed as a named two-tuple `request.client` (or `None`).

The hostname or IP address: `request.client.host`

Expand Down
11 changes: 0 additions & 11 deletions docs/third-party-packages.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,17 +168,6 @@ while maintaining the integrity of the newly `lifespan` events of `Starlette`.
High performance, easy to learn, fast to code, ready for production web API framework.
Inspired by **APIStar**'s previous server system with type declarations for route parameters, based on the OpenAPI specification version 3.0.0+ (with JSON Schema), powered by **Pydantic** for the data handling.

### Esmerald

<a href="https://github.com/dymmond/esmerald" target="_blank">GitHub</a> |
<a href="https://www.esmerald.dev" target="_blank">Documentation</a>

Highly scalable, performant, easy to learn, easy to code and for every application web framework.
Inspired by a lot of frameworks out there, Esmerald provides what every application needs, from the
smallest to complex. Includes, routes, middlewares, permissions, scheduler, interceptors and lot more.

Powered by **Starlette** and **Pydantic** with OpenAPI specification.

### Flama

<a href="https://github.com/vortico/flama" target="_blank">GitHub</a> |
Expand Down
2 changes: 1 addition & 1 deletion starlette/requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ def cookies(self) -> dict[str, str]:

@property
def client(self) -> Address | None:
# client is a 2 item tuple of (host, port), None or missing
# client is a 2 item tuple of (host, port), None if missing
host_port = self.scope.get("client")
if host_port is not None:
return Address(*host_port)
Expand Down
4 changes: 2 additions & 2 deletions starlette/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ def __init__(
self.headers["location"] = quote(str(url), safe=":/%#?=@[]!$&'()*+,;")


Content = typing.Union[str, bytes]
Content = typing.Union[str, bytes, memoryview]
SyncContentStream = typing.Iterable[Content]
AsyncContentStream = typing.AsyncIterable[Content]
ContentStream = typing.Union[AsyncContentStream, SyncContentStream]
Expand Down Expand Up @@ -248,7 +248,7 @@ async def stream_response(self, send: Send) -> None:
}
)
async for chunk in self.body_iterator:
if not isinstance(chunk, bytes):
if not isinstance(chunk, (bytes, memoryview)):
chunk = chunk.encode(self.charset)
await send({"type": "http.response.body", "body": chunk, "more_body": True})

Expand Down
8 changes: 4 additions & 4 deletions starlette/routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,15 @@ def request_response(
Takes a function or coroutine `func(request) -> response`,
and returns an ASGI application.
"""
f: typing.Callable[[Request], typing.Awaitable[Response]] = (
func if is_async_callable(func) else functools.partial(run_in_threadpool, func) # type:ignore
)

async def app(scope: Scope, receive: Receive, send: Send) -> None:
request = Request(scope, receive, send)

async def app(scope: Scope, receive: Receive, send: Send) -> None:
if is_async_callable(func):
response = await func(request)
else:
response = await run_in_threadpool(func, request)
response = await f(request)
await response(scope, receive, send)

await wrap_app_handling_exceptions(app, request)(scope, receive, send)
Expand Down
4 changes: 3 additions & 1 deletion starlette/templating.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,9 @@ def __init__(
DeprecationWarning,
)
assert jinja2 is not None, "jinja2 must be installed to use Jinja2Templates"
assert directory or env, "either 'directory' or 'env' arguments must be passed"
assert bool(directory) ^ bool(
env
), "either 'directory' or 'env' arguments must be passed"
self.context_processors = context_processors or []
if directory is not None:
self.env = self._create_env(directory, **env_options)
Expand Down
7 changes: 7 additions & 0 deletions tests/test_responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,13 @@ def test_streaming_response_known_size(
assert response.headers["content-length"] == "10"


def test_streaming_response_memoryview(test_client_factory: TestClientFactory) -> None:
app = StreamingResponse(content=iter([memoryview(b"hello"), memoryview(b"world")]))
client: TestClient = test_client_factory(app)
response = client.get("/")
assert response.text == "helloworld"


@pytest.mark.anyio
async def test_streaming_response_stops_if_receiving_http_disconnect() -> None:
streamed = 0
Expand Down
7 changes: 7 additions & 0 deletions tests/test_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,13 @@ def test_templates_require_directory_or_environment() -> None:
Jinja2Templates() # type: ignore[call-overload]


def test_templates_require_directory_or_enviroment_not_both() -> None:
with pytest.raises(
AssertionError, match="either 'directory' or 'env' arguments must be passed"
):
Jinja2Templates(directory="dir", env=jinja2.Environment())


def test_templates_with_directory(tmpdir: Path) -> None:
path = os.path.join(tmpdir, "index.html")
with open(path, "w") as file:
Expand Down

0 comments on commit 9cbf5f4

Please sign in to comment.