Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-starlette for openSUSE:Factory checked in at 2024-08-15 09:57:21 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-starlette (Old) and /work/SRC/openSUSE:Factory/.python-starlette.new.7232 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "python-starlette" Thu Aug 15 09:57:21 2024 rev:25 rq:1193696 version:0.38.2 Changes: -------- --- /work/SRC/openSUSE:Factory/python-starlette/python-starlette.changes 2024-04-30 17:26:04.755377224 +0200 +++ /work/SRC/openSUSE:Factory/.python-starlette.new.7232/python-starlette.changes 2024-08-15 09:57:23.376621654 +0200 @@ -1,0 +2,16 @@ +Tue Aug 13 09:41:53 UTC 2024 - Daniel Garcia <daniel.garcia@suse.com> + +- Update to 0.38.2: + * Fix routing.get_name() not to assume all routines have __name__ #2648 +- 0.38.1: + * Revert "Add support for ASGI pathsend extension" #2649. +- 0.38.0: + * Allow use of memoryview in StreamingResponse and Response #2576 + and #2577. + * Send 404 instead of 500 when filename requested is too long on + StaticFiles #2583. + * Fail fast on invalid Jinja2Template instantiation parameters #2568. + * Check endpoint handler is async only once #2536. + * Add proper synchronization to WebSocketTestSession #2597. + +------------------------------------------------------------------- Old: ---- starlette-0.37.2.tar.gz New: ---- starlette-0.38.2.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-starlette.spec ++++++ --- /var/tmp/diff_new_pack.0OI44Y/_old 2024-08-15 09:57:24.180655304 +0200 +++ /var/tmp/diff_new_pack.0OI44Y/_new 2024-08-15 09:57:24.180655304 +0200 @@ -27,7 +27,7 @@ %{?sle15_python_module_pythons} Name: python-starlette%{psuffix} -Version: 0.37.2 +Version: 0.38.2 Release: 0 Summary: Lightweight ASGI framework/toolkit License: BSD-3-Clause ++++++ starlette-0.37.2.tar.gz -> starlette-0.38.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/README.md new/starlette-0.38.2/README.md --- old/starlette-0.37.2/README.md 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/README.md 2024-07-27 19:55:52.000000000 +0200 @@ -45,7 +45,7 @@ $ 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/). +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://hypercorn.readthedocs.io/en/latest/). ```shell $ pip3 install uvicorn diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/docs/index.md new/starlette-0.38.2/docs/index.md --- old/starlette-0.37.2/docs/index.md 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/docs/index.md 2024-07-27 19:55:52.000000000 +0200 @@ -46,7 +46,7 @@ $ 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/). +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://hypercorn.readthedocs.io/en/latest/). ```shell $ pip3 install uvicorn diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/docs/middleware.md new/starlette-0.38.2/docs/middleware.md --- old/starlette-0.37.2/docs/middleware.md 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/docs/middleware.md 2024-07-27 19:55:52.000000000 +0200 @@ -104,8 +104,9 @@ * `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 @@ -167,6 +168,7 @@ * `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. @@ -185,7 +187,7 @@ routes = ... middleware = [ - Middleware(GZipMiddleware, minimum_size=1000) + Middleware(GZipMiddleware, minimum_size=1000, compresslevel=9) ] app = Starlette(routes=routes, middleware=middleware) @@ -194,6 +196,7 @@ The following arguments are supported: * `minimum_size` - Do not GZip responses that are smaller than this minimum size in bytes. Defaults to `500`. +* `compresslevel` - Used during GZip compression. It is an integer ranging from 1 to 9. Defaults to `9`. Lower value results in faster compression but larger file sizes, while higher value results in slower compression but smaller file sizes. The middleware won't GZip responses that already have a `Content-Encoding` set, to prevent them from being encoded twice. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/docs/release-notes.md new/starlette-0.38.2/docs/release-notes.md --- old/starlette-0.37.2/docs/release-notes.md 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/docs/release-notes.md 2024-07-27 19:55:52.000000000 +0200 @@ -1,3 +1,38 @@ +## 0.38.2 + +July 27, 2024 + +#### Fixed + +* Not assume all routines have `__name__` on `routing.get_name()` [#2648](https://github.com/encode/starlette/pull/2648). + +## 0.38.1 + +July 23, 2024 + +#### Removed + +* Revert "Add support for ASGI pathsend extension" [#2649](https://github.com/encode/starlette/pull/2649). + +## 0.38.0 + +July 20, 2024 + +#### Added + +* Allow use of `memoryview` in `StreamingResponse` and `Response` [#2576](https://github.com/encode/starlette/pull/2576) + and [#2577](https://github.com/encode/starlette/pull/2577). +* Send 404 instead of 500 when filename requested is too long on `StaticFiles` [#2583](https://github.com/encode/starlette/pull/2583). + +#### Changed + +* Fail fast on invalid `Jinja2Template` instantiation parameters [#2568](https://github.com/encode/starlette/pull/2568). +* Check endpoint handler is async only once [#2536](https://github.com/encode/starlette/pull/2536). + +#### Fixed + +* Add proper synchronization to `WebSocketTestSession` [#2597](https://github.com/encode/starlette/pull/2597). + ## 0.37.2 March 5, 2024 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/docs/requests.md new/starlette-0.38.2/docs/requests.md --- old/starlette-0.37.2/docs/requests.md 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/docs/requests.md 2024-07-27 19:55:52.000000000 +0200 @@ -60,8 +60,7 @@ #### 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` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/docs/third-party-packages.md new/starlette-0.38.2/docs/third-party-packages.md --- old/starlette-0.37.2/docs/third-party-packages.md 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/docs/third-party-packages.md 2024-07-27 19:55:52.000000000 +0200 @@ -66,6 +66,13 @@ Document your REST API built with Starlette by declaring OpenAPI (Swagger) schemas in YAML format in your endpoint's docstrings. +### Starlette Compress + +<a href="https://github.com/Zaczero/starlette-compress" target="_blank">GitHub</a> + +Starlette-Compress is a fast and simple middleware for compressing responses in Starlette. +It adds ZStd, Brotli, and GZip compression support with sensible default configuration. + ### Starlette Context <a href="https://github.com/tomwojcik/starlette-context" target="_blank">GitHub</a> @@ -148,6 +155,12 @@ to quickly generate fully customizable admin interface for your models. You can export your data to many formats (*CSV*, *PDF*, *Excel*, etc), filter your data with complex query including `AND` and `OR` conditions, upload files, ... +### Vellox + +<a href="https://github.com/junah201/vellox" target="_blank">GitHub</a> + +Serverless ASGI adapter for GCP Cloud Functions. + ## Starlette Bridge <a href="https://github.com/tarsil/starlette-bridge" target="_blank">GitHub</a> | @@ -168,17 +181,6 @@ 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> | @@ -258,3 +260,10 @@ <a href="https://docs.sentry.io/platforms/python/guides/starlette/" target="_blank">Documentation</a> Sentry is a software error detection tool. It offers actionable insights for resolving performance issues and errors, allowing users to diagnose, fix, and optimize Python debugging. Additionally, it integrates seamlessly with Starlette for Python application development. Sentry's capabilities include error tracking, performance insights, contextual information, and alerts/notifications. + +### Shiny + +<a href="https://github.com/posit-dev/py-shiny" target="_blank">GitHub</a> | +<a href="https://shiny.posit.co/py/" target="_blank">Documentation</a> + +Leveraging Starlette and asyncio, Shiny allows developers to create effortless Python web applications using the power of reactive programming. Shiny eliminates the hassle of manual state management, automatically determining the best execution path for your app at runtime while simultaneously minimizing re-rendering. This means that Shiny can support everything from the simplest dashboard to full-featured web apps. \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/pyproject.toml new/starlette-0.38.2/pyproject.toml --- old/starlette-0.37.2/pyproject.toml 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/pyproject.toml 2024-07-27 19:55:52.000000000 +0200 @@ -51,6 +51,7 @@ [tool.ruff.lint] select = ["E", "F", "I", "FA", "UP"] +ignore = ["UP031"] [tool.ruff.lint.isort] combine-as-imports = true @@ -65,7 +66,7 @@ implicit_optional = true [tool.pytest.ini_options] -addopts = "-rxXs --strict-config --strict-markers" +addopts = "-rXs --strict-config --strict-markers" xfail_strict = true filterwarnings = [ # Turn warnings that aren't filtered into exceptions @@ -78,6 +79,8 @@ "ignore: The `allow_redirects` argument is deprecated. Use `follow_redirects` instead.:DeprecationWarning", "ignore: 'cgi' is deprecated and slated for removal in Python 3.13:DeprecationWarning", "ignore: You seem to already have a custom sys.excepthook handler installed. I'll skip installing Trio's custom handler, but this means MultiErrors will not show full tracebacks.:RuntimeWarning", + # TODO: This warning appeared when we bumped anyio to 4.4.0. + "ignore: Unclosed .MemoryObject(Send|Receive)Stream.:ResourceWarning", ] [tool.coverage.run] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/requirements.txt new/starlette-0.38.2/requirements.txt --- old/starlette-0.37.2/requirements.txt 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/requirements.txt 2024-07-27 19:55:52.000000000 +0200 @@ -1,23 +1,26 @@ # Optionals -e .[full] +# TODO: We need to delete the following line when fixing the test suite for anyio 4.4.0. +anyio==4.4.0 + # Testing -coverage==7.4.3 -importlib-metadata==7.0.1 -mypy==1.8.0 -ruff==0.1.15 -typing_extensions==4.10.0 +coverage==7.5.4 +importlib-metadata==8.0.0 +mypy==1.10.1 +ruff==0.5.0 +typing_extensions==4.12.2 types-contextvars==2.4.7.3 -types-PyYAML==6.0.12.12 +types-PyYAML==6.0.12.20240311 types-dataclasses==0.6.6 -pytest==8.0.2 -trio==0.24.0 +pytest==8.2.2 +trio==0.25.1 # Documentation -mkdocs==1.5.3 -mkdocs-material==9.5.12 +mkdocs==1.6.0 +mkdocs-material==9.5.27 mkautodoc==0.2.0 # Packaging -build==1.1.1 -twine==5.0.0 +build==1.2.1 +twine==5.1.1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/scripts/check new/starlette-0.38.2/scripts/check --- old/starlette-0.37.2/scripts/check 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/scripts/check 2024-07-27 19:55:52.000000000 +0200 @@ -10,7 +10,5 @@ ./scripts/sync-version ${PREFIX}ruff format --check --diff $SOURCE_FILES -# TODO: Use `[[tool.mypy.overrides]]` on the `pyproject.toml` when the mypy issue is solved: -# github.com/python/mypy/issues/10045. Check github.com/encode/starlette/pull/2180 for more info. ${PREFIX}mypy $SOURCE_FILES ${PREFIX}ruff check $SOURCE_FILES diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/scripts/lint new/starlette-0.38.2/scripts/lint --- old/starlette-0.37.2/scripts/lint 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/scripts/lint 2024-07-27 19:55:52.000000000 +0200 @@ -9,4 +9,4 @@ set -x ${PREFIX}ruff format $SOURCE_FILES -${PREFIX}ruff --fix $SOURCE_FILES +${PREFIX}ruff check --fix $SOURCE_FILES diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/starlette/__init__.py new/starlette-0.38.2/starlette/__init__.py --- old/starlette-0.37.2/starlette/__init__.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/starlette/__init__.py 2024-07-27 19:55:52.000000000 +0200 @@ -1 +1 @@ -__version__ = "0.37.2" +__version__ = "0.38.2" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/starlette/_utils.py new/starlette-0.38.2/starlette/_utils.py --- old/starlette-0.37.2/starlette/_utils.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/starlette/_utils.py 2024-07-27 19:55:52.000000000 +0200 @@ -26,13 +26,11 @@ @typing.overload -def is_async_callable(obj: AwaitableCallable[T]) -> TypeGuard[AwaitableCallable[T]]: - ... +def is_async_callable(obj: AwaitableCallable[T]) -> TypeGuard[AwaitableCallable[T]]: ... @typing.overload -def is_async_callable(obj: typing.Any) -> TypeGuard[AwaitableCallable[typing.Any]]: - ... +def is_async_callable(obj: typing.Any) -> TypeGuard[AwaitableCallable[typing.Any]]: ... def is_async_callable(obj: typing.Any) -> typing.Any: @@ -49,13 +47,11 @@ class AwaitableOrContextManager( typing.Awaitable[T_co], typing.AsyncContextManager[T_co], typing.Protocol[T_co] -): - ... +): ... class SupportsAsyncClose(typing.Protocol): - async def close(self) -> None: - ... # pragma: no cover + async def close(self) -> None: ... # pragma: no cover SupportsAsyncCloseType = typing.TypeVar( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/starlette/config.py new/starlette-0.38.2/starlette/config.py --- old/starlette-0.37.2/starlette/config.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/starlette/config.py 2024-07-27 19:55:52.000000000 +0200 @@ -68,16 +68,13 @@ self.file_values = self._read_file(env_file) @typing.overload - def __call__(self, key: str, *, default: None) -> str | None: - ... + def __call__(self, key: str, *, default: None) -> str | None: ... @typing.overload - def __call__(self, key: str, cast: type[T], default: T = ...) -> T: - ... + def __call__(self, key: str, cast: type[T], default: T = ...) -> T: ... @typing.overload - def __call__(self, key: str, cast: type[str] = ..., default: str = ...) -> str: - ... + def __call__(self, key: str, cast: type[str] = ..., default: str = ...) -> str: ... @typing.overload def __call__( @@ -85,12 +82,12 @@ key: str, cast: typing.Callable[[typing.Any], T] = ..., default: typing.Any = ..., - ) -> T: - ... + ) -> T: ... @typing.overload - def __call__(self, key: str, cast: type[str] = ..., default: T = ...) -> T | str: - ... + def __call__( + self, key: str, cast: type[str] = ..., default: T = ... + ) -> T | str: ... def __call__( self, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/starlette/middleware/__init__.py new/starlette-0.38.2/starlette/middleware/__init__.py --- old/starlette-0.37.2/starlette/middleware/__init__.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/starlette/middleware/__init__.py 2024-07-27 19:55:52.000000000 +0200 @@ -14,11 +14,13 @@ class _MiddlewareClass(Protocol[P]): - def __init__(self, app: ASGIApp, *args: P.args, **kwargs: P.kwargs) -> None: - ... # pragma: no cover + def __init__( + self, app: ASGIApp, *args: P.args, **kwargs: P.kwargs + ) -> None: ... # pragma: no cover - async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: - ... # pragma: no cover + async def __call__( + self, scope: Scope, receive: Receive, send: Send + ) -> None: ... # pragma: no cover class Middleware: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/starlette/requests.py new/starlette-0.38.2/starlette/requests.py --- old/starlette-0.37.2/starlette/requests.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/starlette/requests.py 2024-07-27 19:55:52.000000000 +0200 @@ -145,7 +145,7 @@ @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) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/starlette/responses.py new/starlette-0.38.2/starlette/responses.py --- old/starlette-0.37.2/starlette/responses.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/starlette/responses.py 2024-07-27 19:55:52.000000000 +0200 @@ -41,10 +41,10 @@ self.body = self.render(content) self.init_headers(headers) - def render(self, content: typing.Any) -> bytes: + def render(self, content: typing.Any) -> bytes | memoryview: if content is None: return b"" - if isinstance(content, bytes): + if isinstance(content, (bytes, memoryview)): return content return content.encode(self.charset) # type: ignore @@ -94,7 +94,7 @@ value: str = "", max_age: int | None = None, expires: datetime | str | int | None = None, - path: str = "/", + path: str | None = "/", domain: str | None = None, secure: bool = False, httponly: bool = False, @@ -207,7 +207,7 @@ 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] @@ -248,7 +248,7 @@ } ) 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}) @@ -299,12 +299,10 @@ if self.filename is not None: content_disposition_filename = quote(self.filename) if content_disposition_filename != self.filename: - content_disposition = "{}; filename*=utf-8''{}".format( - content_disposition_type, content_disposition_filename - ) + content_disposition = f"{content_disposition_type}; filename*=utf-8''{content_disposition_filename}" # noqa: E501 else: - content_disposition = '{}; filename="{}"'.format( - content_disposition_type, self.filename + content_disposition = ( + f'{content_disposition_type}; filename="{self.filename}"' ) self.headers.setdefault("content-disposition", content_disposition) self.stat_result = stat_result @@ -341,8 +339,6 @@ ) if scope["method"].upper() == "HEAD": await send({"type": "http.response.body", "body": b"", "more_body": False}) - elif "extensions" in scope and "http.response.pathsend" in scope["extensions"]: - await send({"type": "http.response.pathsend", "path": str(self.path)}) else: async with await anyio.open_file(self.path, mode="rb") as file: more_body = True diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/starlette/routing.py new/starlette-0.38.2/starlette/routing.py --- old/starlette-0.37.2/starlette/routing.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/starlette/routing.py 2024-07-27 19:55:52.000000000 +0200 @@ -63,15 +63,15 @@ 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) @@ -99,9 +99,7 @@ def get_name(endpoint: typing.Callable[..., typing.Any]) -> str: - if inspect.isroutine(endpoint) or inspect.isclass(endpoint): - return endpoint.__name__ - return endpoint.__class__.__name__ + return getattr(endpoint, "__name__", endpoint.__class__.__name__) def replace_params( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/starlette/staticfiles.py new/starlette-0.38.2/starlette/staticfiles.py --- old/starlette-0.37.2/starlette/staticfiles.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/starlette/staticfiles.py 2024-07-27 19:55:52.000000000 +0200 @@ -1,5 +1,6 @@ from __future__ import annotations +import errno import importlib.util import os import stat @@ -124,8 +125,12 @@ ) except PermissionError: raise HTTPException(status_code=401) - except OSError: - raise + except OSError as exc: + # Filename is too long, so it can't be a valid static file. + if exc.errno == errno.ENAMETOOLONG: + raise HTTPException(status_code=404) + + raise exc if stat_result and stat.S_ISREG(stat_result.st_mode): # We have a static file to serve. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/starlette/status.py new/starlette-0.38.2/starlette/status.py --- old/starlette-0.37.2/starlette/status.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/starlette/status.py 2024-07-27 19:55:52.000000000 +0200 @@ -5,6 +5,7 @@ And RFC 2324 - https://tools.ietf.org/html/rfc2324 """ + from __future__ import annotations import warnings diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/starlette/templating.py new/starlette-0.38.2/starlette/templating.py --- old/starlette-0.37.2/starlette/templating.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/starlette/templating.py 2024-07-27 19:55:52.000000000 +0200 @@ -66,15 +66,12 @@ @typing.overload def __init__( self, - directory: str - | PathLike[typing.AnyStr] - | typing.Sequence[str | PathLike[typing.AnyStr]], + directory: str | PathLike[str] | typing.Sequence[str | PathLike[str]], *, context_processors: list[typing.Callable[[Request], dict[str, typing.Any]]] | None = None, **env_options: typing.Any, - ) -> None: - ... + ) -> None: ... @typing.overload def __init__( @@ -83,14 +80,13 @@ env: jinja2.Environment, context_processors: list[typing.Callable[[Request], dict[str, typing.Any]]] | None = None, - ) -> None: - ... + ) -> None: ... def __init__( self, directory: str - | PathLike[typing.AnyStr] - | typing.Sequence[str | PathLike[typing.AnyStr]] + | PathLike[str] + | typing.Sequence[str | PathLike[str]] | None = None, *, context_processors: list[typing.Callable[[Request], dict[str, typing.Any]]] @@ -104,7 +100,9 @@ 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) @@ -115,9 +113,7 @@ def _create_env( self, - directory: str - | PathLike[typing.AnyStr] - | typing.Sequence[str | PathLike[typing.AnyStr]], + directory: str | PathLike[str] | typing.Sequence[str | PathLike[str]], **env_options: typing.Any, ) -> jinja2.Environment: loader = jinja2.FileSystemLoader(directory) @@ -152,8 +148,7 @@ headers: typing.Mapping[str, str] | None = None, media_type: str | None = None, background: BackgroundTask | None = None, - ) -> _TemplateResponse: - ... + ) -> _TemplateResponse: ... @typing.overload def TemplateResponse( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/starlette/testclient.py new/starlette-0.38.2/starlette/testclient.py --- old/starlette-0.37.2/starlette/testclient.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/starlette/testclient.py 2024-07-27 19:55:52.000000000 +0200 @@ -160,7 +160,8 @@ async def _asgi_receive(self) -> Message: while self._receive_queue.empty(): - await anyio.sleep(0) + self._queue_event = anyio.Event() + await self._queue_event.wait() return self._receive_queue.get() async def _asgi_send(self, message: Message) -> None: @@ -189,6 +190,8 @@ def send(self, message: Message) -> None: self._receive_queue.put(message) + if hasattr(self, "_queue_event"): + self.portal.start_task_soon(self._queue_event.set) def send_text(self, data: str) -> None: self.send({"type": "websocket.receive", "text": data}) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/conftest.py new/starlette-0.38.2/tests/conftest.py --- old/starlette-0.37.2/tests/conftest.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/tests/conftest.py 2024-07-27 19:55:52.000000000 +0200 @@ -1,13 +1,12 @@ from __future__ import annotations import functools -from typing import Any, Callable, Literal +from typing import Any, Literal import pytest from starlette.testclient import TestClient - -TestClientFactory = Callable[..., TestClient] +from tests.types import TestClientFactory @pytest.fixture diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/middleware/test_base.py new/starlette-0.38.2/tests/middleware/test_base.py --- old/starlette-0.37.2/tests/middleware/test_base.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/tests/middleware/test_base.py 2024-07-27 19:55:52.000000000 +0200 @@ -5,7 +5,6 @@ from typing import ( Any, AsyncGenerator, - Callable, Generator, ) @@ -23,8 +22,7 @@ from starlette.testclient import TestClient from starlette.types import ASGIApp, Message, Receive, Scope, Send from starlette.websockets import WebSocket - -TestClientFactory = Callable[[ASGIApp], TestClient] +from tests.types import TestClientFactory class CustomMiddleware(BaseHTTPMiddleware): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/middleware/test_cors.py new/starlette-0.38.2/tests/middleware/test_cors.py --- old/starlette-0.37.2/tests/middleware/test_cors.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/tests/middleware/test_cors.py 2024-07-27 19:55:52.000000000 +0200 @@ -1,15 +1,10 @@ -from typing import Callable - from starlette.applications import Starlette from starlette.middleware import Middleware from starlette.middleware.cors import CORSMiddleware from starlette.requests import Request from starlette.responses import PlainTextResponse from starlette.routing import Route -from starlette.testclient import TestClient -from starlette.types import ASGIApp - -TestClientFactory = Callable[[ASGIApp], TestClient] +from tests.types import TestClientFactory def test_cors_allow_all( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/middleware/test_errors.py new/starlette-0.38.2/tests/middleware/test_errors.py --- old/starlette-0.37.2/tests/middleware/test_errors.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/tests/middleware/test_errors.py 2024-07-27 19:55:52.000000000 +0200 @@ -1,4 +1,4 @@ -from typing import Any, Callable +from typing import Any import pytest @@ -8,10 +8,8 @@ from starlette.requests import Request from starlette.responses import JSONResponse, Response from starlette.routing import Route -from starlette.testclient import TestClient from starlette.types import Receive, Scope, Send - -TestClientFactory = Callable[..., TestClient] +from tests.types import TestClientFactory def test_handler( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/middleware/test_gzip.py new/starlette-0.38.2/tests/middleware/test_gzip.py --- old/starlette-0.37.2/tests/middleware/test_gzip.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/tests/middleware/test_gzip.py 2024-07-27 19:55:52.000000000 +0200 @@ -1,15 +1,10 @@ -from typing import Callable - from starlette.applications import Starlette from starlette.middleware import Middleware from starlette.middleware.gzip import GZipMiddleware from starlette.requests import Request from starlette.responses import ContentStream, PlainTextResponse, StreamingResponse from starlette.routing import Route -from starlette.testclient import TestClient -from starlette.types import ASGIApp - -TestClientFactory = Callable[[ASGIApp], TestClient] +from tests.types import TestClientFactory def test_gzip_responses(test_client_factory: TestClientFactory) -> None: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/middleware/test_https_redirect.py new/starlette-0.38.2/tests/middleware/test_https_redirect.py --- old/starlette-0.37.2/tests/middleware/test_https_redirect.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/tests/middleware/test_https_redirect.py 2024-07-27 19:55:52.000000000 +0200 @@ -1,14 +1,10 @@ -from typing import Callable - from starlette.applications import Starlette from starlette.middleware import Middleware from starlette.middleware.httpsredirect import HTTPSRedirectMiddleware from starlette.requests import Request from starlette.responses import PlainTextResponse from starlette.routing import Route -from starlette.testclient import TestClient - -TestClientFactory = Callable[..., TestClient] +from tests.types import TestClientFactory def test_https_redirect_middleware(test_client_factory: TestClientFactory) -> None: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/middleware/test_session.py new/starlette-0.38.2/tests/middleware/test_session.py --- old/starlette-0.37.2/tests/middleware/test_session.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/tests/middleware/test_session.py 2024-07-27 19:55:52.000000000 +0200 @@ -1,5 +1,4 @@ import re -from typing import Callable from starlette.applications import Starlette from starlette.middleware import Middleware @@ -8,8 +7,7 @@ from starlette.responses import JSONResponse from starlette.routing import Mount, Route from starlette.testclient import TestClient - -TestClientFactory = Callable[..., TestClient] +from tests.types import TestClientFactory def view_session(request: Request) -> JSONResponse: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/middleware/test_trusted_host.py new/starlette-0.38.2/tests/middleware/test_trusted_host.py --- old/starlette-0.37.2/tests/middleware/test_trusted_host.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/tests/middleware/test_trusted_host.py 2024-07-27 19:55:52.000000000 +0200 @@ -1,14 +1,10 @@ -from typing import Callable - from starlette.applications import Starlette from starlette.middleware import Middleware from starlette.middleware.trustedhost import TrustedHostMiddleware from starlette.requests import Request from starlette.responses import PlainTextResponse from starlette.routing import Route -from starlette.testclient import TestClient - -TestClientFactory = Callable[..., TestClient] +from tests.types import TestClientFactory def test_trusted_host_middleware(test_client_factory: TestClientFactory) -> None: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/middleware/test_wsgi.py new/starlette-0.38.2/tests/middleware/test_wsgi.py --- old/starlette-0.37.2/tests/middleware/test_wsgi.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/tests/middleware/test_wsgi.py 2024-07-27 19:55:52.000000000 +0200 @@ -5,10 +5,9 @@ from starlette._utils import collapse_excgroups from starlette.middleware.wsgi import WSGIMiddleware, build_environ -from starlette.testclient import TestClient +from tests.types import TestClientFactory WSGIResponse = Iterable[bytes] -TestClientFactory = Callable[..., TestClient] StartResponse = Callable[..., Any] Environment = Dict[str, Any] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/test__utils.py new/starlette-0.38.2/tests/test__utils.py --- old/starlette-0.37.2/tests/test__utils.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/tests/test__utils.py 2024-07-27 19:55:52.000000000 +0200 @@ -5,22 +5,18 @@ def test_async_func() -> None: - async def async_func() -> None: - ... # pragma: no cover + async def async_func() -> None: ... # pragma: no cover - def func() -> None: - ... # pragma: no cover + def func() -> None: ... # pragma: no cover assert is_async_callable(async_func) assert not is_async_callable(func) def test_async_partial() -> None: - async def async_func(a: Any, b: Any) -> None: - ... # pragma: no cover + async def async_func(a: Any, b: Any) -> None: ... # pragma: no cover - def func(a: Any, b: Any) -> None: - ... # pragma: no cover + def func(a: Any, b: Any) -> None: ... # pragma: no cover partial = functools.partial(async_func, 1) assert is_async_callable(partial) @@ -31,12 +27,10 @@ def test_async_method() -> None: class Async: - async def method(self) -> None: - ... # pragma: no cover + async def method(self) -> None: ... # pragma: no cover class Sync: - def method(self) -> None: - ... # pragma: no cover + def method(self) -> None: ... # pragma: no cover assert is_async_callable(Async().method) assert not is_async_callable(Sync().method) @@ -44,12 +38,10 @@ def test_async_object_call() -> None: class Async: - async def __call__(self) -> None: - ... # pragma: no cover + async def __call__(self) -> None: ... # pragma: no cover class Sync: - def __call__(self) -> None: - ... # pragma: no cover + def __call__(self) -> None: ... # pragma: no cover assert is_async_callable(Async()) assert not is_async_callable(Sync()) @@ -61,16 +53,14 @@ self, a: Any, b: Any, - ) -> None: - ... # pragma: no cover + ) -> None: ... # pragma: no cover class Sync: def __call__( self, a: Any, b: Any, - ) -> None: - ... # pragma: no cover + ) -> None: ... # pragma: no cover partial = functools.partial(Async(), 1) assert is_async_callable(partial) @@ -83,8 +73,7 @@ async def async_func( a: Any, b: Any, - ) -> None: - ... # pragma: no cover + ) -> None: ... # pragma: no cover partial = functools.partial(async_func, b=2) nested_partial = functools.partial(partial, a=1) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/test_applications.py new/starlette-0.38.2/tests/test_applications.py --- old/starlette-0.37.2/tests/test_applications.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/tests/test_applications.py 2024-07-27 19:55:52.000000000 +0200 @@ -1,7 +1,7 @@ import os from contextlib import asynccontextmanager from pathlib import Path -from typing import AsyncGenerator, AsyncIterator, Callable, Generator +from typing import AsyncGenerator, AsyncIterator, Generator import anyio import pytest @@ -20,8 +20,7 @@ from starlette.testclient import TestClient from starlette.types import ASGIApp, Receive, Scope, Send from starlette.websockets import WebSocket - -TestClientFactory = Callable[..., TestClient] +from tests.types import TestClientFactory async def error_500(request: Request, exc: HTTPException) -> JSONResponse: @@ -463,8 +462,7 @@ async def middleware( request: Request, call_next: RequestResponseEndpoint - ) -> None: - ... # pragma: no cover + ) -> None: ... # pragma: no cover app.middleware("http")(middleware) assert len(record) == 1 @@ -494,8 +492,7 @@ ) ) as record: - async def startup() -> None: - ... # pragma: no cover + async def startup() -> None: ... # pragma: no cover app.on_event("startup")(startup) assert len(record) == 1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/test_authentication.py new/starlette-0.38.2/tests/test_authentication.py --- old/starlette-0.37.2/tests/test_authentication.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/tests/test_authentication.py 2024-07-27 19:55:52.000000000 +0200 @@ -21,10 +21,9 @@ from starlette.requests import HTTPConnection, Request from starlette.responses import JSONResponse, Response from starlette.routing import Route, WebSocketRoute -from starlette.testclient import TestClient from starlette.websockets import WebSocket, WebSocketDisconnect +from tests.types import TestClientFactory -TestClientFactory = Callable[..., TestClient] AsyncEndpoint = Callable[..., Awaitable[Response]] SyncEndpoint = Callable[..., Response] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/test_background.py new/starlette-0.38.2/tests/test_background.py --- old/starlette-0.37.2/tests/test_background.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/tests/test_background.py 2024-07-27 19:55:52.000000000 +0200 @@ -1,13 +1,9 @@ -from typing import Callable - import pytest from starlette.background import BackgroundTask, BackgroundTasks from starlette.responses import Response -from starlette.testclient import TestClient from starlette.types import Receive, Scope, Send - -TestClientFactory = Callable[..., TestClient] +from tests.types import TestClientFactory def test_async_task(test_client_factory: TestClientFactory) -> None: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/test_concurrency.py new/starlette-0.38.2/tests/test_concurrency.py --- old/starlette-0.37.2/tests/test_concurrency.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/tests/test_concurrency.py 2024-07-27 19:55:52.000000000 +0200 @@ -1,5 +1,5 @@ from contextvars import ContextVar -from typing import Callable, Iterator +from typing import Iterator import anyio import pytest @@ -9,9 +9,7 @@ from starlette.requests import Request from starlette.responses import Response from starlette.routing import Route -from starlette.testclient import TestClient - -TestClientFactory = Callable[..., TestClient] +from tests.types import TestClientFactory @pytest.mark.anyio diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/test_convertors.py new/starlette-0.38.2/tests/test_convertors.py --- old/starlette-0.37.2/tests/test_convertors.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/tests/test_convertors.py 2024-07-27 19:55:52.000000000 +0200 @@ -1,5 +1,5 @@ from datetime import datetime -from typing import Callable, Iterator +from typing import Iterator import pytest @@ -8,9 +8,7 @@ from starlette.requests import Request from starlette.responses import JSONResponse from starlette.routing import Route, Router -from starlette.testclient import TestClient - -TestClientFactory = Callable[..., TestClient] +from tests.types import TestClientFactory @pytest.fixture(scope="module", autouse=True) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/test_datastructures.py new/starlette-0.38.2/tests/test_datastructures.py --- old/starlette-0.37.2/tests/test_datastructures.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/tests/test_datastructures.py 2024-07-27 19:55:52.000000000 +0200 @@ -58,6 +58,9 @@ url = URL("http://u:p@host:80") assert url.replace(port=88) == URL("http://u:p@host:88") + url = URL("http://host:80") + assert url.replace(username="u") == URL("http://u@host:80") + def test_url_query_params() -> None: u = URL("https://example.org/path/?page=3") @@ -70,6 +73,10 @@ assert str(u) == "https://example.org/path/?order=name" u = u.remove_query_params("order") assert str(u) == "https://example.org/path/" + u = u.include_query_params(page=4, search="testing") + assert str(u) == "https://example.org/path/?page=4&search=testing" + u = u.remove_query_params(["page", "search"]) + assert str(u) == "https://example.org/path/" def test_hidden_password() -> None: @@ -138,6 +145,21 @@ assert u == "https://example.org/path/to/somewhere?abc=123" assert repr(u) == "URL('https://example.org/path/to/somewhere?abc=123')" + u = URL( + scope={ + "scheme": "http", + "path": "/some/path", + "query_string": b"query=string", + "headers": [ + (b"content-type", b"text/html"), + (b"host", b"example.com:8000"), + (b"accept", b"text/html"), + ], + } + ) + assert u == "http://example.com:8000/some/path?query=string" + assert repr(u) == "URL('http://example.com:8000/some/path?query=string')" + def test_headers() -> None: h = Headers(raw=[(b"a", b"123"), (b"a", b"456"), (b"b", b"789")]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/test_endpoints.py new/starlette-0.38.2/tests/test_endpoints.py --- old/starlette-0.37.2/tests/test_endpoints.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/tests/test_endpoints.py 2024-07-27 19:55:52.000000000 +0200 @@ -1,4 +1,4 @@ -from typing import Callable, Iterator +from typing import Iterator import pytest @@ -8,8 +8,7 @@ from starlette.routing import Route, Router from starlette.testclient import TestClient from starlette.websockets import WebSocket - -TestClientFactory = Callable[..., TestClient] +from tests.types import TestClientFactory class Homepage(HTTPEndpoint): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/test_exceptions.py new/starlette-0.38.2/tests/test_exceptions.py --- old/starlette-0.37.2/tests/test_exceptions.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/tests/test_exceptions.py 2024-07-27 19:55:52.000000000 +0200 @@ -1,5 +1,5 @@ import warnings -from typing import Callable, Generator +from typing import Generator import pytest @@ -10,8 +10,7 @@ from starlette.routing import Route, Router, WebSocketRoute from starlette.testclient import TestClient from starlette.types import Receive, Scope, Send - -TestClientFactory = Callable[..., TestClient] +from tests.types import TestClientFactory def raise_runtime_error(request: Request) -> None: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/test_formparsers.py new/starlette-0.38.2/tests/test_formparsers.py --- old/starlette-0.37.2/tests/test_formparsers.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/tests/test_formparsers.py 2024-07-27 19:55:52.000000000 +0200 @@ -13,10 +13,8 @@ from starlette.requests import Request from starlette.responses import JSONResponse from starlette.routing import Mount -from starlette.testclient import TestClient from starlette.types import ASGIApp, Receive, Scope, Send - -TestClientFactory = typing.Callable[..., TestClient] +from tests.types import TestClientFactory class ForceMultipartDict(typing.Dict[typing.Any, typing.Any]): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/test_requests.py new/starlette-0.38.2/tests/test_requests.py --- old/starlette-0.37.2/tests/test_requests.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/tests/test_requests.py 2024-07-27 19:55:52.000000000 +0200 @@ -1,7 +1,7 @@ from __future__ import annotations import sys -from typing import Any, Callable, Iterator +from typing import Any, Iterator import anyio import pytest @@ -9,10 +9,8 @@ from starlette.datastructures import Address, State from starlette.requests import ClientDisconnect, Request from starlette.responses import JSONResponse, PlainTextResponse, Response -from starlette.testclient import TestClient from starlette.types import Message, Receive, Scope, Send - -TestClientFactory = Callable[..., TestClient] +from tests.types import TestClientFactory def test_request_url(test_client_factory: TestClientFactory) -> None: @@ -269,8 +267,8 @@ def test_request_is_disconnected(test_client_factory: TestClientFactory) -> None: """ - If a client disconnect occurs while reading request body - then ClientDisconnect should be raised. + If a client disconnect occurs after reading request body + then request will be set disconnected properly. """ disconnected_after_response = None @@ -278,15 +276,15 @@ nonlocal disconnected_after_response request = Request(scope, receive) - await request.body() + body = await request.body() disconnected = await request.is_disconnected() - response = JSONResponse({"disconnected": disconnected}) + response = JSONResponse({"body": body.decode(), "disconnected": disconnected}) await response(scope, receive, send) disconnected_after_response = await request.is_disconnected() client = test_client_factory(app) - response = client.get("/") - assert response.json() == {"disconnected": False} + response = client.post("/", content="foo") + assert response.json() == {"body": "foo", "disconnected": False} assert disconnected_after_response diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/test_responses.py new/starlette-0.38.2/tests/test_responses.py --- old/starlette-0.37.2/tests/test_responses.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/tests/test_responses.py 2024-07-27 19:55:52.000000000 +0200 @@ -5,7 +5,7 @@ import time from http.cookies import SimpleCookie from pathlib import Path -from typing import AsyncIterator, Callable, Iterator +from typing import AsyncIterator, Iterator import anyio import pytest @@ -23,8 +23,7 @@ ) from starlette.testclient import TestClient from starlette.types import Message, Receive, Scope, Send - -TestClientFactory = Callable[..., TestClient] +from tests.types import TestClientFactory def test_text_response(test_client_factory: TestClientFactory) -> None: @@ -350,38 +349,6 @@ FileResponse(path=tmpdir, filename="example.png", method="GET") -@pytest.mark.anyio -async def test_file_response_with_pathsend(tmpdir: Path) -> None: - path = os.path.join(tmpdir, "xyz") - content = b"<file content>" * 1000 - with open(path, "wb") as file: - file.write(content) - - app = FileResponse(path=path, filename="example.png") - - async def receive() -> Message: # type: ignore[empty-body] - ... # pragma: no cover - - async def send(message: Message) -> None: - if message["type"] == "http.response.start": - assert message["status"] == status.HTTP_200_OK - headers = Headers(raw=message["headers"]) - assert headers["content-type"] == "image/png" - assert "content-length" in headers - assert "content-disposition" in headers - assert "last-modified" in headers - assert "etag" in headers - elif message["type"] == "http.response.pathsend": - assert message["path"] == str(path) - - # Since the TestClient doesn't support `pathsend`, we need to test this directly. - await app( - {"type": "http", "method": "get", "extensions": {"http.response.pathsend": {}}}, - receive, - send, - ) - - def test_set_cookie( test_client_factory: TestClientFactory, monkeypatch: pytest.MonkeyPatch ) -> None: @@ -541,6 +508,20 @@ assert response.headers["content-length"] == "10" +def test_response_memoryview(test_client_factory: TestClientFactory) -> None: + app = Response(content=memoryview(b"\xc0")) + client: TestClient = test_client_factory(app) + response = client.get("/") + assert response.content == b"\xc0" + + +def test_streaming_response_memoryview(test_client_factory: TestClientFactory) -> None: + app = StreamingResponse(content=iter([memoryview(b"\xc0"), memoryview(b"\xf5")])) + client: TestClient = test_client_factory(app) + response = client.get("/") + assert response.content == b"\xc0\xf5" + + @pytest.mark.anyio async def test_streaming_response_stops_if_receiving_http_disconnect() -> None: streamed = 0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/test_routing.py new/starlette-0.38.2/tests/test_routing.py --- old/starlette-0.37.2/tests/test_routing.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/tests/test_routing.py 2024-07-27 19:55:52.000000000 +0200 @@ -17,8 +17,7 @@ from starlette.testclient import TestClient from starlette.types import ASGIApp, Message, Receive, Scope, Send from starlette.websockets import WebSocket, WebSocketDisconnect - -TestClientFactory = typing.Callable[..., TestClient] +from tests.types import TestClientFactory def homepage(request: Request) -> Response: @@ -909,19 +908,15 @@ class Endpoint: - async def my_method(self, request: Request) -> None: - ... # pragma: no cover + async def my_method(self, request: Request) -> None: ... # pragma: no cover @classmethod - async def my_classmethod(cls, request: Request) -> None: - ... # pragma: no cover + async def my_classmethod(cls, request: Request) -> None: ... # pragma: no cover @staticmethod - async def my_staticmethod(request: Request) -> None: - ... # pragma: no cover + async def my_staticmethod(request: Request) -> None: ... # pragma: no cover - def __call__(self, request: Request) -> None: - ... # pragma: no cover + def __call__(self, request: Request) -> None: ... # pragma: no cover @pytest.mark.parametrize( @@ -1254,8 +1249,7 @@ with pytest.deprecated_call(): - async def startup() -> None: - ... # pragma: nocover + async def startup() -> None: ... # pragma: nocover router.on_event("startup")(startup) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/test_schemas.py new/starlette-0.38.2/tests/test_schemas.py --- old/starlette-0.37.2/tests/test_schemas.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/tests/test_schemas.py 2024-07-27 19:55:52.000000000 +0200 @@ -1,20 +1,16 @@ -from typing import Callable - from starlette.applications import Starlette from starlette.endpoints import HTTPEndpoint from starlette.requests import Request from starlette.responses import Response from starlette.routing import Host, Mount, Route, Router, WebSocketRoute from starlette.schemas import SchemaGenerator -from starlette.testclient import TestClient from starlette.websockets import WebSocket +from tests.types import TestClientFactory schemas = SchemaGenerator( {"openapi": "3.0.0", "info": {"title": "Example API", "version": "1.0"}} ) -TestClientFactory = Callable[..., TestClient] - def ws(session: WebSocket) -> None: """ws""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/test_staticfiles.py new/starlette-0.38.2/tests/test_staticfiles.py --- old/starlette-0.37.2/tests/test_staticfiles.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/tests/test_staticfiles.py 2024-07-27 19:55:52.000000000 +0200 @@ -16,9 +16,7 @@ from starlette.responses import Response from starlette.routing import Mount from starlette.staticfiles import StaticFiles -from starlette.testclient import TestClient - -TestClientFactory = typing.Callable[..., TestClient] +from tests.types import TestClientFactory def test_staticfiles(tmpdir: Path, test_client_factory: TestClientFactory) -> None: @@ -468,6 +466,19 @@ assert response.status_code == 404 assert response.text == "Not Found" + +def test_staticfiles_filename_too_long( + tmpdir: Path, test_client_factory: TestClientFactory +) -> None: + routes = [Mount("/", app=StaticFiles(directory=tmpdir), name="static")] + app = Starlette(routes=routes) + client = test_client_factory(app) + + path_max_size = os.pathconf("/", "PC_PATH_MAX") + response = client.get(f"/{'a' * path_max_size}.txt") + assert response.status_code == 404 + assert response.text == "Not Found" + def test_staticfiles_unhandled_os_error_returns_500( tmpdir: Path, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/test_templates.py new/starlette-0.38.2/tests/test_templates.py --- old/starlette-0.37.2/tests/test_templates.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/tests/test_templates.py 2024-07-27 19:55:52.000000000 +0200 @@ -1,7 +1,6 @@ from __future__ import annotations import os -import typing from pathlib import Path from unittest import mock @@ -16,9 +15,7 @@ from starlette.responses import Response from starlette.routing import Route from starlette.templating import Jinja2Templates -from starlette.testclient import TestClient - -TestClientFactory = typing.Callable[..., TestClient] +from tests.types import TestClientFactory def test_templates(tmpdir: Path, test_client_factory: TestClientFactory) -> None: @@ -143,6 +140,13 @@ 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: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/test_testclient.py new/starlette-0.38.2/tests/test_testclient.py --- old/starlette-0.37.2/tests/test_testclient.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/tests/test_testclient.py 2024-07-27 19:55:52.000000000 +0200 @@ -4,7 +4,7 @@ import sys from asyncio import Task, current_task as asyncio_current_task from contextlib import asynccontextmanager -from typing import Any, AsyncGenerator, Callable +from typing import Any, AsyncGenerator import anyio import anyio.lowlevel @@ -20,8 +20,7 @@ from starlette.testclient import ASGIInstance, TestClient from starlette.types import ASGIApp, Receive, Scope, Send from starlette.websockets import WebSocket, WebSocketDisconnect - -TestClientFactory = Callable[..., TestClient] +from tests.types import TestClientFactory def mock_service_endpoint(request: Request) -> JSONResponse: @@ -212,7 +211,7 @@ return inner - client = test_client_factory(app) + client = test_client_factory(app) # type: ignore response = client.get("/") assert response.text == "Hello, world!" @@ -252,7 +251,7 @@ return asgi - client = test_client_factory(app) + client = test_client_factory(app) # type: ignore with client.websocket_connect("/") as websocket: data = websocket.receive_json() assert data == {"message": "test"} @@ -268,7 +267,7 @@ return asgi - client = test_client_factory(app) + client = test_client_factory(app) # type: ignore with client.websocket_connect("/") as websocket: ... assert websocket.should_close.is_set() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/test_websockets.py new/starlette-0.38.2/tests/test_websockets.py --- old/starlette-0.37.2/tests/test_websockets.py 2024-03-05 17:08:44.000000000 +0100 +++ new/starlette-0.38.2/tests/test_websockets.py 2024-07-27 19:55:52.000000000 +0200 @@ -1,5 +1,5 @@ import sys -from typing import Any, Callable, MutableMapping +from typing import Any, MutableMapping import anyio import pytest @@ -7,11 +7,10 @@ from starlette import status from starlette.responses import Response -from starlette.testclient import TestClient, WebSocketDenialResponse +from starlette.testclient import WebSocketDenialResponse from starlette.types import Message, Receive, Scope, Send from starlette.websockets import WebSocket, WebSocketDisconnect, WebSocketState - -TestClientFactory = Callable[..., TestClient] +from tests.types import TestClientFactory def test_websocket_url(test_client_factory: TestClientFactory) -> None: @@ -492,8 +491,7 @@ async def mock_receive() -> Message: # type: ignore ... # pragma: no cover - async def mock_send(message: Message) -> None: - ... # pragma: no cover + async def mock_send(message: Message) -> None: ... # pragma: no cover websocket = WebSocket( {"type": "websocket", "path": "/abc/", "headers": []}, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.37.2/tests/types.py new/starlette-0.38.2/tests/types.py --- old/starlette-0.37.2/tests/types.py 1970-01-01 01:00:00.000000000 +0100 +++ new/starlette-0.38.2/tests/types.py 2024-07-27 19:55:52.000000000 +0200 @@ -0,0 +1,26 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Protocol + +import httpx + +from starlette.testclient import TestClient +from starlette.types import ASGIApp + +if TYPE_CHECKING: + + class TestClientFactory(Protocol): # pragma: no cover + def __call__( + self, + app: ASGIApp, + base_url: str = "http://testserver", + raise_server_exceptions: bool = True, + root_path: str = "", + cookies: httpx._types.CookieTypes | None = None, + headers: dict[str, str] | None = None, + follow_redirects: bool = True, + ) -> TestClient: ... +else: # pragma: no cover + + class TestClientFactory: + __test__ = False