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 2022-11-28 11:07:28
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-starlette (Old)
and /work/SRC/openSUSE:Factory/.python-starlette.new.1597 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-starlette"
Mon Nov 28 11:07:28 2022 rev:13 rq:1038588 version:0.22.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-starlette/python-starlette.changes
2022-09-29 18:15:17.119481642 +0200
+++
/work/SRC/openSUSE:Factory/.python-starlette.new.1597/python-starlette.changes
2022-11-28 11:07:34.751894604 +0100
@@ -1,0 +2,11 @@
+Sun Nov 27 22:53:40 UTC 2022 - Michael Ströder <[email protected]>
+
+- Update to 0.22.0
+ * Changed
+ - Bypass GZipMiddleware when response includes Content-Encoding #1901.
+ * Fixed
+ - Remove unneeded unquote() from query parameters on the TestClient #1953.
+ - Make sure MutableHeaders._list is actually a list #1917.
+ - Import compatibility with the next version of AnyIO #1936.
+
+-------------------------------------------------------------------
Old:
----
starlette-0.21.0.tar.gz
New:
----
starlette-0.22.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-starlette.spec ++++++
--- /var/tmp/diff_new_pack.gDcDMP/_old 2022-11-28 11:07:35.363897348 +0100
+++ /var/tmp/diff_new_pack.gDcDMP/_new 2022-11-28 11:07:35.375897402 +0100
@@ -27,7 +27,7 @@
%{?!python_module:%define python_module() python3-%{**}}
%define skip_python2 1
Name: python-starlette%{psuffix}
-Version: 0.21.0
+Version: 0.22.0
Release: 0
Summary: Lightweight ASGI framework/toolkit
License: BSD-3-Clause
++++++ starlette-0.21.0.tar.gz -> starlette-0.22.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/starlette-0.21.0/.github/workflows/test-suite.yml
new/starlette-0.22.0/.github/workflows/test-suite.yml
--- old/starlette-0.21.0/.github/workflows/test-suite.yml 2022-09-26
19:09:36.000000000 +0200
+++ new/starlette-0.22.0/.github/workflows/test-suite.yml 2022-11-17
07:24:34.000000000 +0100
@@ -14,7 +14,7 @@
strategy:
matrix:
- python-version: ["3.7", "3.8", "3.9", "3.10", "3.11-dev"]
+ python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
steps:
- uses: "actions/checkout@v3"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/starlette-0.21.0/docs/middleware.md
new/starlette-0.22.0/docs/middleware.md
--- old/starlette-0.21.0/docs/middleware.md 2022-09-26 19:09:36.000000000
+0200
+++ new/starlette-0.22.0/docs/middleware.md 2022-11-17 07:24:34.000000000
+0100
@@ -114,6 +114,7 @@
```python
from starlette.applications import Starlette
+from starlette.middleware import Middleware
from starlette.middleware.httpsredirect import HTTPSRedirectMiddleware
routes = ...
@@ -179,6 +180,8 @@
* `minimum_size` - Do not GZip responses that are smaller than this minimum
size in bytes. Defaults to `500`.
+The middleware won't GZip responses that already have a `Content-Encoding`
set, to prevent them from being encoded twice.
+
## BaseHTTPMiddleware
An abstract class that allows you to write ASGI middleware against a
request/response
@@ -242,7 +245,6 @@
Currently, the `BaseHTTPMiddleware` has some known limitations:
-- It's not possible to use `BackgroundTasks` with `BaseHTTPMiddleware`. Check
[#1438](https://github.com/encode/starlette/issues/1438) for more details.
- Using `BaseHTTPMiddleware` will prevent changes to
[`contextlib.ContextVar`](https://docs.python.org/3/library/contextvars.html#contextvars.ContextVar)s
from propagating upwards. That is, if you set a value for a `ContextVar` in
your endpoint and try to read it from a middleware you will find that the value
is not the same value you set in your endpoint (see [this
test](https://github.com/encode/starlette/blob/621abc747a6604825190b93467918a0ec6456a24/tests/middleware/test_base.py#L192-L223)
for an example of this behavior).
To overcome these limitations, use [pure ASGI
middleware](#pure-asgi-middleware), as shown below.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/starlette-0.21.0/docs/release-notes.md
new/starlette-0.22.0/docs/release-notes.md
--- old/starlette-0.21.0/docs/release-notes.md 2022-09-26 19:09:36.000000000
+0200
+++ new/starlette-0.22.0/docs/release-notes.md 2022-11-17 07:24:34.000000000
+0100
@@ -1,3 +1,15 @@
+## 0.22.0
+
+November 17, 2022
+
+### Changed
+* Bypass `GZipMiddleware` when response includes `Content-Encoding`
[#1901](https://github.com/encode/starlette/pull/1901).
+
+### Fixed
+* Remove unneeded `unquote()` from query parameters on the `TestClient`
[#1953](https://github.com/encode/starlette/pull/1953).
+* Make sure `MutableHeaders._list` is actually a `list`
[#1917](https://github.com/encode/starlette/pull/1917).
+* Import compatibility with the next version of `AnyIO`
[#1936](https://github.com/encode/starlette/pull/1936).
+
## 0.21.0
September 26, 2022
@@ -167,7 +179,7 @@
* The ClassVar `starlette.testclient.TestClient.async_backend` was removed,
the backend is now configured using constructor kwargs
[#1211](https://github.com/encode/starlette/pull/1211)
- * Passing an Async Generator Function or a Generator Function to
`starlette.router.Router(lifespan_context=)` is deprecated. You should wrap
your lifespan in `@contextlib.asynccontextmanager`.
+ * Passing an Async Generator Function or a Generator Function to
`starlette.routing.Router(lifespan=)` is deprecated. You should wrap your
lifespan in `@contextlib.asynccontextmanager`.
[#1227](https://github.com/encode/starlette/pull/1227)
[#1110](https://github.com/encode/starlette/pull/1110)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/starlette-0.21.0/docs/testclient.md
new/starlette-0.22.0/docs/testclient.md
--- old/starlette-0.21.0/docs/testclient.md 2022-09-26 19:09:36.000000000
+0200
+++ new/starlette-0.22.0/docs/testclient.md 2022-11-17 07:24:34.000000000
+0100
@@ -55,7 +55,7 @@
response = client.post("/form", files=files)
```
-For more information you can check the `requests`
[documentation](https://requests.readthedocs.io/en/master/user/advanced/).
+For more information you can check the `httpx`
[documentation](https://www.python-httpx.org/advanced/).
By default the `TestClient` will raise any exceptions that occur in the
application. Occasionally you might want to test the content of 500 error
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/starlette-0.21.0/docs/third-party-packages.md
new/starlette-0.22.0/docs/third-party-packages.md
--- old/starlette-0.21.0/docs/third-party-packages.md 2022-09-26
19:09:36.000000000 +0200
+++ new/starlette-0.22.0/docs/third-party-packages.md 2022-11-17
07:24:34.000000000 +0100
@@ -209,3 +209,9 @@
An extension to integrate Swagger/OpenAPI document easily for Starlette
project and provide [SwaggerUI](http://swagger.io/swagger-ui/) and
[RedocUI](https://rebilly.github.io/ReDoc/).
<a href="https://github.com/strongbugman/apiman" target="_blank">GitHub</a>
+
+### Starlette-Babel
+
+Provides translations, localization, and timezone support via Babel
integration.
+
+<a href="https://github.com/alex-oleshkevich/starlette_babel"
target="_blank">GitHub</a>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/starlette-0.21.0/mkdocs.yml
new/starlette-0.22.0/mkdocs.yml
--- old/starlette-0.21.0/mkdocs.yml 2022-09-26 19:09:36.000000000 +0200
+++ new/starlette-0.22.0/mkdocs.yml 2022-11-17 07:24:34.000000000 +0100
@@ -17,10 +17,12 @@
toggle:
icon: 'material/lightbulb-outline'
name: 'Switch to light mode'
+ icon:
+ repo: fontawesome/brands/github
repo_name: encode/starlette
repo_url: https://github.com/encode/starlette
-edit_uri: ""
+edit_uri: edit/master/docs/
nav:
- Introduction: 'index.md'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/starlette-0.21.0/pyproject.toml
new/starlette-0.22.0/pyproject.toml
--- old/starlette-0.21.0/pyproject.toml 2022-09-26 19:09:36.000000000 +0200
+++ new/starlette-0.22.0/pyproject.toml 2022-11-17 07:24:34.000000000 +0100
@@ -43,6 +43,10 @@
[project.urls]
Homepage = "https://github.com/encode/starlette"
+Documentation = "https://www.starlette.io/"
+Changelog = "https://www.starlette.io/release-notes/"
+Funding = "https://github.com/sponsors/encode"
+Source = "https://github.com/encode/starlette"
[tool.hatch.version]
path = "starlette/__init__.py"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/starlette-0.21.0/requirements.txt
new/starlette-0.22.0/requirements.txt
--- old/starlette-0.21.0/requirements.txt 2022-09-26 19:09:36.000000000
+0200
+++ new/starlette-0.22.0/requirements.txt 2022-11-17 07:24:34.000000000
+0100
@@ -4,24 +4,22 @@
# Testing
autoflake==1.5.3
black==22.8.0
-coverage==6.4.2
-databases[sqlite]==0.6.1
+coverage==6.5.0
flake8==3.9.2
+importlib-metadata==4.13.0
isort==5.10.1
mypy==0.971
-typing_extensions==4.3.0
+typing_extensions==4.4.0
types-contextvars==2.4.7
-types-PyYAML==6.0.11
+types-PyYAML==6.0.12
types-dataclasses==0.6.6
-pytest==7.1.2
+pytest==7.2.0
trio==0.21.0
-# NOTE: Remove once greenlet releases 2.0.0.
-greenlet==2.0.0a2; python_version >= "3.11"
# Documentation
-mkdocs==1.3.1
-mkdocs-material==8.4.2
-mkautodoc==0.1.0
+mkdocs==1.4.0
+mkdocs-material==8.5.7
+mkautodoc==0.2.0
# Packaging
build==0.8.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/starlette-0.21.0/scripts/install
new/starlette-0.22.0/scripts/install
--- old/starlette-0.21.0/scripts/install 2022-09-26 19:09:36.000000000
+0200
+++ new/starlette-0.22.0/scripts/install 2022-11-17 07:24:34.000000000
+0100
@@ -15,5 +15,5 @@
PIP="pip"
fi
+"$PIP" install -U pip
"$PIP" install -r "$REQUIREMENTS"
-"$PIP" install -e .
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/starlette-0.21.0/setup.cfg
new/starlette-0.22.0/setup.cfg
--- old/starlette-0.21.0/setup.cfg 2022-09-26 19:09:36.000000000 +0200
+++ new/starlette-0.22.0/setup.cfg 2022-11-17 07:24:34.000000000 +0100
@@ -7,6 +7,7 @@
ignore_missing_imports = True
no_implicit_optional = True
show_error_codes = True
+enable_error_code = ignore-without-code
[mypy-starlette.testclient]
no_implicit_optional = False
@@ -35,6 +36,7 @@
ignore: Use 'content=<...>' to upload raw bytes/text
content.:DeprecationWarning
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
[coverage:run]
source_pkgs = starlette, tests
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/starlette-0.21.0/starlette/__init__.py
new/starlette-0.22.0/starlette/__init__.py
--- old/starlette-0.21.0/starlette/__init__.py 2022-09-26 19:09:36.000000000
+0200
+++ new/starlette-0.22.0/starlette/__init__.py 2022-11-17 07:24:34.000000000
+0100
@@ -1 +1 @@
-__version__ = "0.21.0"
+__version__ = "0.22.0"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/starlette-0.21.0/starlette/datastructures.py
new/starlette-0.22.0/starlette/datastructures.py
--- old/starlette-0.21.0/starlette/datastructures.py 2022-09-26
19:09:36.000000000 +0200
+++ new/starlette-0.22.0/starlette/datastructures.py 2022-11-17
07:24:34.000000000 +0100
@@ -410,7 +410,7 @@
parse_qsl(value.decode("latin-1"), keep_blank_values=True),
**kwargs
)
else:
- super().__init__(*args, **kwargs) # type: ignore
+ super().__init__(*args, **kwargs) # type: ignore[arg-type]
self._list = [(str(k), str(v)) for k, v in self._list]
self._dict = {str(k): str(v) for k, v in self._dict.items()}
@@ -443,7 +443,7 @@
self.filename = filename
self.content_type = content_type
if file is None:
- self.file =
tempfile.SpooledTemporaryFile(max_size=self.spool_max_size) # type: ignore #
noqa: E501
+ self.file =
tempfile.SpooledTemporaryFile(max_size=self.spool_max_size) # type:
ignore[assignment] # noqa: E501
else:
self.file = file
self.headers = headers or Headers()
@@ -508,7 +508,7 @@
self,
headers: typing.Optional[typing.Mapping[str, str]] = None,
raw: typing.Optional[typing.List[typing.Tuple[bytes, bytes]]] = None,
- scope: typing.Optional[typing.Mapping[str, typing.Any]] = None,
+ scope: typing.Optional[typing.MutableMapping[str, typing.Any]] = None,
) -> None:
self._list: typing.List[typing.Tuple[bytes, bytes]] = []
if headers is not None:
@@ -522,19 +522,21 @@
assert scope is None, 'Cannot set both "raw" and "scope".'
self._list = raw
elif scope is not None:
- self._list = scope["headers"]
+ # scope["headers"] isn't necessarily a list
+ # it might be a tuple or other iterable
+ self._list = scope["headers"] = list(scope["headers"])
@property
def raw(self) -> typing.List[typing.Tuple[bytes, bytes]]:
return list(self._list)
- def keys(self) -> typing.List[str]: # type: ignore
+ def keys(self) -> typing.List[str]: # type: ignore[override]
return [key.decode("latin-1") for key, value in self._list]
- def values(self) -> typing.List[str]: # type: ignore
+ def values(self) -> typing.List[str]: # type: ignore[override]
return [value.decode("latin-1") for key, value in self._list]
- def items(self) -> typing.List[typing.Tuple[str, str]]: # type: ignore
+ def items(self) -> typing.List[typing.Tuple[str, str]]: # type:
ignore[override]
return [
(key.decode("latin-1"), value.decode("latin-1"))
for key, value in self._list
@@ -593,7 +595,7 @@
set_key = key.lower().encode("latin-1")
set_value = value.encode("latin-1")
- found_indexes = []
+ found_indexes: "typing.List[int]" = []
for idx, (item_key, item_value) in enumerate(self._list):
if item_key == set_key:
found_indexes.append(idx)
@@ -613,7 +615,7 @@
"""
del_key = key.lower().encode("latin-1")
- pop_indexes = []
+ pop_indexes: "typing.List[int]" = []
for idx, (item_key, item_value) in enumerate(self._list):
if item_key == del_key:
pop_indexes.append(idx)
@@ -621,13 +623,13 @@
for idx in reversed(pop_indexes):
del self._list[idx]
- def __ior__(self, other: typing.Mapping) -> "MutableHeaders":
+ def __ior__(self, other: typing.Mapping[str, str]) -> "MutableHeaders":
if not isinstance(other, typing.Mapping):
raise TypeError(f"Expected a mapping but got
{other.__class__.__name__}")
self.update(other)
return self
- def __or__(self, other: typing.Mapping) -> "MutableHeaders":
+ def __or__(self, other: typing.Mapping[str, str]) -> "MutableHeaders":
if not isinstance(other, typing.Mapping):
raise TypeError(f"Expected a mapping but got
{other.__class__.__name__}")
new = self.mutablecopy()
@@ -652,7 +654,7 @@
self._list.append((set_key, set_value))
return value
- def update(self, other: typing.Mapping) -> None:
+ def update(self, other: typing.Mapping[str, str]) -> None:
for key, val in other.items():
self[key] = val
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/starlette-0.21.0/starlette/middleware/errors.py
new/starlette-0.22.0/starlette/middleware/errors.py
--- old/starlette-0.21.0/starlette/middleware/errors.py 2022-09-26
19:09:36.000000000 +0200
+++ new/starlette-0.22.0/starlette/middleware/errors.py 2022-11-17
07:24:34.000000000 +0100
@@ -198,7 +198,9 @@
def generate_frame_html(self, frame: inspect.FrameInfo, is_collapsed:
bool) -> str:
code_context = "".join(
- self.format_line(index, line, frame.lineno, frame.index) # type:
ignore
+ self.format_line(
+ index, line, frame.lineno, frame.index # type:
ignore[arg-type]
+ )
for index, line in enumerate(frame.code_context or [])
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/starlette-0.21.0/starlette/middleware/gzip.py
new/starlette-0.22.0/starlette/middleware/gzip.py
--- old/starlette-0.21.0/starlette/middleware/gzip.py 2022-09-26
19:09:36.000000000 +0200
+++ new/starlette-0.22.0/starlette/middleware/gzip.py 2022-11-17
07:24:34.000000000 +0100
@@ -33,6 +33,7 @@
self.send: Send = unattached_send
self.initial_message: Message = {}
self.started = False
+ self.content_encoding_set = False
self.gzip_buffer = io.BytesIO()
self.gzip_file = gzip.GzipFile(
mode="wb", fileobj=self.gzip_buffer, compresslevel=compresslevel
@@ -48,6 +49,13 @@
# Don't send the initial message until we've determined how to
# modify the outgoing headers correctly.
self.initial_message = message
+ headers = Headers(raw=self.initial_message["headers"])
+ self.content_encoding_set = "content-encoding" in headers
+ elif message_type == "http.response.body" and
self.content_encoding_set:
+ if not self.started:
+ self.started = True
+ await self.send(self.initial_message)
+ await self.send(message)
elif message_type == "http.response.body" and not self.started:
self.started = True
body = message.get("body", b"")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/starlette-0.21.0/starlette/responses.py
new/starlette-0.22.0/starlette/responses.py
--- old/starlette-0.21.0/starlette/responses.py 2022-09-26 19:09:36.000000000
+0200
+++ new/starlette-0.22.0/starlette/responses.py 2022-11-17 07:24:34.000000000
+0100
@@ -23,7 +23,7 @@
from typing_extensions import Literal
# Workaround for adding samesite support to pre 3.8 python
-http.cookies.Morsel._reserved["samesite"] = "SameSite" # type: ignore
+http.cookies.Morsel._reserved["samesite"] = "SameSite" # type:
ignore[attr-defined]
# Compatibility wrapper for `mimetypes.guess_type` to support `os.PathLike` on
<py3.8
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/starlette-0.21.0/starlette/schemas.py
new/starlette-0.22.0/starlette/schemas.py
--- old/starlette-0.21.0/starlette/schemas.py 2022-09-26 19:09:36.000000000
+0200
+++ new/starlette-0.22.0/starlette/schemas.py 2022-11-17 07:24:34.000000000
+0100
@@ -9,7 +9,7 @@
try:
import yaml
except ImportError: # pragma: nocover
- yaml = None # type: ignore
+ yaml = None # type: ignore[assignment]
class OpenAPIResponse(Response):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/starlette-0.21.0/starlette/templating.py
new/starlette-0.22.0/starlette/templating.py
--- old/starlette-0.21.0/starlette/templating.py 2022-09-26
19:09:36.000000000 +0200
+++ new/starlette-0.22.0/starlette/templating.py 2022-11-17
07:24:34.000000000 +0100
@@ -17,7 +17,7 @@
else: # pragma: nocover
pass_context = jinja2.contextfunction # type: ignore[attr-defined]
except ImportError: # pragma: nocover
- jinja2 = None # type: ignore
+ jinja2 = None # type: ignore[assignment]
class _TemplateResponse(Response):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/starlette-0.21.0/starlette/testclient.py
new/starlette-0.22.0/starlette/testclient.py
--- old/starlette-0.21.0/starlette/testclient.py 2022-09-26
19:09:36.000000000 +0200
+++ new/starlette-0.22.0/starlette/testclient.py 2022-11-17
07:24:34.000000000 +0100
@@ -12,6 +12,7 @@
from urllib.parse import unquote, urljoin
import anyio
+import anyio.from_thread
import httpx
from anyio.streams.stapled import StapledObjectStream
@@ -33,6 +34,9 @@
ASGI3App = typing.Callable[[Scope, Receive, Send], typing.Awaitable[None]]
+_RequestData = typing.Mapping[str, typing.Union[str, typing.Iterable[str]]]
+
+
def _is_asgi3(app: typing.Union[ASGI2App, ASGI3App]) -> bool:
if inspect.isclass(app):
return hasattr(app, "__await__")
@@ -192,10 +196,10 @@
def handle_request(self, request: httpx.Request) -> httpx.Response:
scheme = request.url.scheme
- netloc = unquote(request.url.netloc.decode(encoding="ascii"))
+ netloc = request.url.netloc.decode(encoding="ascii")
path = request.url.path
raw_path = request.url.raw_path
- query = unquote(request.url.query.decode(encoding="ascii"))
+ query = request.url.query.decode(encoding="ascii")
default_port = {"http": 80, "ws": 80, "https": 443, "wss": 443}[scheme]
@@ -395,7 +399,9 @@
if self.portal is not None:
yield self.portal
else:
- with anyio.start_blocking_portal(**self.async_backend) as portal:
+ with anyio.from_thread.start_blocking_portal(
+ **self.async_backend
+ ) as portal:
yield portal
def _choose_redirect_arg(
@@ -426,22 +432,22 @@
method: str,
url: httpx._types.URLTypes,
*,
- content: httpx._types.RequestContent = None,
- data: httpx._types.RequestData = None,
- files: httpx._types.RequestFiles = None,
+ content: typing.Optional[httpx._types.RequestContent] = None,
+ data: typing.Optional[_RequestData] = None,
+ files: typing.Optional[httpx._types.RequestFiles] = None,
json: typing.Any = None,
- params: httpx._types.QueryParamTypes = None,
- headers: httpx._types.HeaderTypes = None,
- cookies: httpx._types.CookieTypes = None,
+ params: typing.Optional[httpx._types.QueryParamTypes] = None,
+ headers: typing.Optional[httpx._types.HeaderTypes] = None,
+ cookies: typing.Optional[httpx._types.CookieTypes] = None,
auth: typing.Union[
httpx._types.AuthTypes, httpx._client.UseClientDefault
] = httpx._client.USE_CLIENT_DEFAULT,
- follow_redirects: bool = None,
- allow_redirects: bool = None,
+ follow_redirects: typing.Optional[bool] = None,
+ allow_redirects: typing.Optional[bool] = None,
timeout: typing.Union[
httpx._client.TimeoutTypes, httpx._client.UseClientDefault
] = httpx._client.USE_CLIENT_DEFAULT,
- extensions: dict = None,
+ extensions: typing.Optional[typing.Dict[str, typing.Any]] = None,
) -> httpx.Response:
url = self.base_url.join(url)
redirect = self._choose_redirect_arg(follow_redirects, allow_redirects)
@@ -449,7 +455,7 @@
method,
url,
content=content,
- data=data,
+ data=data, # type: ignore[arg-type]
files=files,
json=json,
params=params,
@@ -465,18 +471,18 @@
self,
url: httpx._types.URLTypes,
*,
- params: httpx._types.QueryParamTypes = None,
- headers: httpx._types.HeaderTypes = None,
- cookies: httpx._types.CookieTypes = None,
+ params: typing.Optional[httpx._types.QueryParamTypes] = None,
+ headers: typing.Optional[httpx._types.HeaderTypes] = None,
+ cookies: typing.Optional[httpx._types.CookieTypes] = None,
auth: typing.Union[
httpx._types.AuthTypes, httpx._client.UseClientDefault
] = httpx._client.USE_CLIENT_DEFAULT,
- follow_redirects: bool = None,
- allow_redirects: bool = None,
+ follow_redirects: typing.Optional[bool] = None,
+ allow_redirects: typing.Optional[bool] = None,
timeout: typing.Union[
httpx._client.TimeoutTypes, httpx._client.UseClientDefault
] = httpx._client.USE_CLIENT_DEFAULT,
- extensions: dict = None,
+ extensions: typing.Optional[typing.Dict[str, typing.Any]] = None,
) -> httpx.Response:
redirect = self._choose_redirect_arg(follow_redirects, allow_redirects)
return super().get(
@@ -494,18 +500,18 @@
self,
url: httpx._types.URLTypes,
*,
- params: httpx._types.QueryParamTypes = None,
- headers: httpx._types.HeaderTypes = None,
- cookies: httpx._types.CookieTypes = None,
+ params: typing.Optional[httpx._types.QueryParamTypes] = None,
+ headers: typing.Optional[httpx._types.HeaderTypes] = None,
+ cookies: typing.Optional[httpx._types.CookieTypes] = None,
auth: typing.Union[
httpx._types.AuthTypes, httpx._client.UseClientDefault
] = httpx._client.USE_CLIENT_DEFAULT,
- follow_redirects: bool = None,
- allow_redirects: bool = None,
+ follow_redirects: typing.Optional[bool] = None,
+ allow_redirects: typing.Optional[bool] = None,
timeout: typing.Union[
httpx._client.TimeoutTypes, httpx._client.UseClientDefault
] = httpx._client.USE_CLIENT_DEFAULT,
- extensions: dict = None,
+ extensions: typing.Optional[typing.Dict[str, typing.Any]] = None,
) -> httpx.Response:
redirect = self._choose_redirect_arg(follow_redirects, allow_redirects)
return super().options(
@@ -523,18 +529,18 @@
self,
url: httpx._types.URLTypes,
*,
- params: httpx._types.QueryParamTypes = None,
- headers: httpx._types.HeaderTypes = None,
- cookies: httpx._types.CookieTypes = None,
+ params: typing.Optional[httpx._types.QueryParamTypes] = None,
+ headers: typing.Optional[httpx._types.HeaderTypes] = None,
+ cookies: typing.Optional[httpx._types.CookieTypes] = None,
auth: typing.Union[
httpx._types.AuthTypes, httpx._client.UseClientDefault
] = httpx._client.USE_CLIENT_DEFAULT,
- follow_redirects: bool = None,
- allow_redirects: bool = None,
+ follow_redirects: typing.Optional[bool] = None,
+ allow_redirects: typing.Optional[bool] = None,
timeout: typing.Union[
httpx._client.TimeoutTypes, httpx._client.UseClientDefault
] = httpx._client.USE_CLIENT_DEFAULT,
- extensions: dict = None,
+ extensions: typing.Optional[typing.Dict[str, typing.Any]] = None,
) -> httpx.Response:
redirect = self._choose_redirect_arg(follow_redirects, allow_redirects)
return super().head(
@@ -552,28 +558,28 @@
self,
url: httpx._types.URLTypes,
*,
- content: httpx._types.RequestContent = None,
- data: httpx._types.RequestData = None,
- files: httpx._types.RequestFiles = None,
+ content: typing.Optional[httpx._types.RequestContent] = None,
+ data: typing.Optional[_RequestData] = None,
+ files: typing.Optional[httpx._types.RequestFiles] = None,
json: typing.Any = None,
- params: httpx._types.QueryParamTypes = None,
- headers: httpx._types.HeaderTypes = None,
- cookies: httpx._types.CookieTypes = None,
+ params: typing.Optional[httpx._types.QueryParamTypes] = None,
+ headers: typing.Optional[httpx._types.HeaderTypes] = None,
+ cookies: typing.Optional[httpx._types.CookieTypes] = None,
auth: typing.Union[
httpx._types.AuthTypes, httpx._client.UseClientDefault
] = httpx._client.USE_CLIENT_DEFAULT,
- follow_redirects: bool = None,
- allow_redirects: bool = None,
+ follow_redirects: typing.Optional[bool] = None,
+ allow_redirects: typing.Optional[bool] = None,
timeout: typing.Union[
httpx._client.TimeoutTypes, httpx._client.UseClientDefault
] = httpx._client.USE_CLIENT_DEFAULT,
- extensions: dict = None,
+ extensions: typing.Optional[typing.Dict[str, typing.Any]] = None,
) -> httpx.Response:
redirect = self._choose_redirect_arg(follow_redirects, allow_redirects)
return super().post(
url,
content=content,
- data=data,
+ data=data, # type: ignore[arg-type]
files=files,
json=json,
params=params,
@@ -589,28 +595,28 @@
self,
url: httpx._types.URLTypes,
*,
- content: httpx._types.RequestContent = None,
- data: httpx._types.RequestData = None,
- files: httpx._types.RequestFiles = None,
+ content: typing.Optional[httpx._types.RequestContent] = None,
+ data: typing.Optional[_RequestData] = None,
+ files: typing.Optional[httpx._types.RequestFiles] = None,
json: typing.Any = None,
- params: httpx._types.QueryParamTypes = None,
- headers: httpx._types.HeaderTypes = None,
- cookies: httpx._types.CookieTypes = None,
+ params: typing.Optional[httpx._types.QueryParamTypes] = None,
+ headers: typing.Optional[httpx._types.HeaderTypes] = None,
+ cookies: typing.Optional[httpx._types.CookieTypes] = None,
auth: typing.Union[
httpx._types.AuthTypes, httpx._client.UseClientDefault
] = httpx._client.USE_CLIENT_DEFAULT,
- follow_redirects: bool = None,
- allow_redirects: bool = None,
+ follow_redirects: typing.Optional[bool] = None,
+ allow_redirects: typing.Optional[bool] = None,
timeout: typing.Union[
httpx._client.TimeoutTypes, httpx._client.UseClientDefault
] = httpx._client.USE_CLIENT_DEFAULT,
- extensions: dict = None,
+ extensions: typing.Optional[typing.Dict[str, typing.Any]] = None,
) -> httpx.Response:
redirect = self._choose_redirect_arg(follow_redirects, allow_redirects)
return super().put(
url,
content=content,
- data=data,
+ data=data, # type: ignore[arg-type]
files=files,
json=json,
params=params,
@@ -626,28 +632,28 @@
self,
url: httpx._types.URLTypes,
*,
- content: httpx._types.RequestContent = None,
- data: httpx._types.RequestData = None,
- files: httpx._types.RequestFiles = None,
+ content: typing.Optional[httpx._types.RequestContent] = None,
+ data: typing.Optional[_RequestData] = None,
+ files: typing.Optional[httpx._types.RequestFiles] = None,
json: typing.Any = None,
- params: httpx._types.QueryParamTypes = None,
- headers: httpx._types.HeaderTypes = None,
- cookies: httpx._types.CookieTypes = None,
+ params: typing.Optional[httpx._types.QueryParamTypes] = None,
+ headers: typing.Optional[httpx._types.HeaderTypes] = None,
+ cookies: typing.Optional[httpx._types.CookieTypes] = None,
auth: typing.Union[
httpx._types.AuthTypes, httpx._client.UseClientDefault
] = httpx._client.USE_CLIENT_DEFAULT,
- follow_redirects: bool = None,
- allow_redirects: bool = None,
+ follow_redirects: typing.Optional[bool] = None,
+ allow_redirects: typing.Optional[bool] = None,
timeout: typing.Union[
httpx._client.TimeoutTypes, httpx._client.UseClientDefault
] = httpx._client.USE_CLIENT_DEFAULT,
- extensions: dict = None,
+ extensions: typing.Optional[typing.Dict[str, typing.Any]] = None,
) -> httpx.Response:
redirect = self._choose_redirect_arg(follow_redirects, allow_redirects)
return super().patch(
url,
content=content,
- data=data,
+ data=data, # type: ignore[arg-type]
files=files,
json=json,
params=params,
@@ -663,18 +669,18 @@
self,
url: httpx._types.URLTypes,
*,
- params: httpx._types.QueryParamTypes = None,
- headers: httpx._types.HeaderTypes = None,
- cookies: httpx._types.CookieTypes = None,
+ params: typing.Optional[httpx._types.QueryParamTypes] = None,
+ headers: typing.Optional[httpx._types.HeaderTypes] = None,
+ cookies: typing.Optional[httpx._types.CookieTypes] = None,
auth: typing.Union[
httpx._types.AuthTypes, httpx._client.UseClientDefault
] = httpx._client.USE_CLIENT_DEFAULT,
- follow_redirects: bool = None,
- allow_redirects: bool = None,
+ follow_redirects: typing.Optional[bool] = None,
+ allow_redirects: typing.Optional[bool] = None,
timeout: typing.Union[
httpx._client.TimeoutTypes, httpx._client.UseClientDefault
] = httpx._client.USE_CLIENT_DEFAULT,
- extensions: dict = None,
+ extensions: typing.Optional[typing.Dict[str, typing.Any]] = None,
) -> httpx.Response:
redirect = self._choose_redirect_arg(follow_redirects, allow_redirects)
return super().delete(
@@ -711,7 +717,7 @@
def __enter__(self) -> "TestClient":
with contextlib.ExitStack() as stack:
self.portal = portal = stack.enter_context(
- anyio.start_blocking_portal(**self.async_backend)
+ anyio.from_thread.start_blocking_portal(**self.async_backend)
)
@stack.callback
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/starlette-0.21.0/tests/conftest.py
new/starlette-0.22.0/tests/conftest.py
--- old/starlette-0.21.0/tests/conftest.py 2022-09-26 19:09:36.000000000
+0200
+++ new/starlette-0.22.0/tests/conftest.py 2022-11-17 07:24:34.000000000
+0100
@@ -6,12 +6,6 @@
@pytest.fixture
-def no_trio_support(anyio_backend_name):
- if anyio_backend_name == "trio":
- pytest.skip("Trio not supported (yet!)")
-
-
[email protected]
def test_client_factory(anyio_backend_name, anyio_backend_options):
# anyio_backend_name defined by:
#
https://anyio.readthedocs.io/en/stable/testing.html#specifying-the-backends-to-run-on
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/starlette-0.21.0/tests/middleware/test_gzip.py
new/starlette-0.22.0/tests/middleware/test_gzip.py
--- old/starlette-0.21.0/tests/middleware/test_gzip.py 2022-09-26
19:09:36.000000000 +0200
+++ new/starlette-0.22.0/tests/middleware/test_gzip.py 2022-11-17
07:24:34.000000000 +0100
@@ -76,3 +76,27 @@
assert response.text == "x" * 4000
assert response.headers["Content-Encoding"] == "gzip"
assert "Content-Length" not in response.headers
+
+
+def test_gzip_ignored_for_responses_with_encoding_set(test_client_factory):
+ def homepage(request):
+ async def generator(bytes, count):
+ for index in range(count):
+ yield bytes
+
+ streaming = generator(bytes=b"x" * 400, count=10)
+ return StreamingResponse(
+ streaming, status_code=200, headers={"Content-Encoding": "br"}
+ )
+
+ app = Starlette(
+ routes=[Route("/", endpoint=homepage)],
+ middleware=[Middleware(GZipMiddleware)],
+ )
+
+ client = test_client_factory(app)
+ response = client.get("/", headers={"accept-encoding": "gzip, br"})
+ assert response.status_code == 200
+ assert response.text == "x" * 4000
+ assert response.headers["Content-Encoding"] == "br"
+ assert "Content-Length" not in response.headers
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/starlette-0.21.0/tests/test_database.py
new/starlette-0.22.0/tests/test_database.py
--- old/starlette-0.21.0/tests/test_database.py 2022-09-26 19:09:36.000000000
+0200
+++ new/starlette-0.22.0/tests/test_database.py 1970-01-01 01:00:00.000000000
+0100
@@ -1,175 +0,0 @@
-import databases
-import pytest
-import sqlalchemy
-
-from starlette.applications import Starlette
-from starlette.requests import Request
-from starlette.responses import JSONResponse
-from starlette.routing import Route
-
-DATABASE_URL = "sqlite:///test.db"
-
-metadata = sqlalchemy.MetaData()
-
-notes = sqlalchemy.Table(
- "notes",
- metadata,
- sqlalchemy.Column("id", sqlalchemy.Integer, primary_key=True),
- sqlalchemy.Column("text", sqlalchemy.String(length=100)),
- sqlalchemy.Column("completed", sqlalchemy.Boolean),
-)
-
-
-pytestmark = pytest.mark.usefixtures("no_trio_support")
-
-
[email protected](autouse=True, scope="module")
-def create_test_database():
- engine = sqlalchemy.create_engine(DATABASE_URL)
- metadata.create_all(engine)
- yield
- metadata.drop_all(engine)
-
-
-database = databases.Database(DATABASE_URL, force_rollback=True)
-
-
-async def startup():
- await database.connect()
-
-
-async def shutdown():
- await database.disconnect()
-
-
-async def list_notes(request: Request):
- query = notes.select()
- results = await database.fetch_all(query)
- content = [
- {"text": result["text"], "completed": result["completed"]} for result
in results
- ]
- return JSONResponse(content)
-
-
[email protected]()
-async def add_note(request: Request):
- data = await request.json()
- query = notes.insert().values(text=data["text"],
completed=data["completed"])
- await database.execute(query)
- if "raise_exc" in request.query_params:
- raise RuntimeError()
- return JSONResponse({"text": data["text"], "completed": data["completed"]})
-
-
-async def bulk_create_notes(request: Request):
- data = await request.json()
- query = notes.insert()
- await database.execute_many(query, data)
- return JSONResponse({"notes": data})
-
-
-async def read_note(request: Request):
- note_id = request.path_params["note_id"]
- query = notes.select().where(notes.c.id == note_id)
- result = await database.fetch_one(query)
- assert result is not None
- content = {"text": result["text"], "completed": result["completed"]}
- return JSONResponse(content)
-
-
-async def read_note_text(request: Request):
- note_id = request.path_params["note_id"]
- query = sqlalchemy.select([notes.c.text]).where(notes.c.id == note_id)
- result = await database.fetch_one(query)
- assert result is not None
- return JSONResponse(result[0])
-
-
-app = Starlette(
- routes=[
- Route("/notes", endpoint=list_notes, methods=["GET"]),
- Route("/notes", endpoint=add_note, methods=["POST"]),
- Route("/notes/bulk_create", endpoint=bulk_create_notes,
methods=["POST"]),
- Route("/notes/{note_id:int}", endpoint=read_note, methods=["GET"]),
- Route("/notes/{note_id:int}/text", endpoint=read_note_text,
methods=["GET"]),
- ],
- on_startup=[startup],
- on_shutdown=[shutdown],
-)
-
-
-def test_database(test_client_factory):
- with test_client_factory(app) as client:
- response = client.post(
- "/notes", json={"text": "buy the milk", "completed": True}
- )
- assert response.status_code == 200
-
- with pytest.raises(RuntimeError):
- response = client.post(
- "/notes",
- json={"text": "you wont see me", "completed": False},
- params={"raise_exc": "true"},
- )
-
- response = client.post(
- "/notes", json={"text": "walk the dog", "completed": False}
- )
- assert response.status_code == 200
-
- response = client.get("/notes")
- assert response.status_code == 200
- assert response.json() == [
- {"text": "buy the milk", "completed": True},
- {"text": "walk the dog", "completed": False},
- ]
-
- response = client.get("/notes/1")
- assert response.status_code == 200
- assert response.json() == {"text": "buy the milk", "completed": True}
-
- response = client.get("/notes/1/text")
- assert response.status_code == 200
- assert response.json() == "buy the milk"
-
-
-def test_database_execute_many(test_client_factory):
- with test_client_factory(app) as client:
- data = [
- {"text": "buy the milk", "completed": True},
- {"text": "walk the dog", "completed": False},
- ]
- response = client.post("/notes/bulk_create", json=data)
- assert response.status_code == 200
-
- response = client.get("/notes")
- assert response.status_code == 200
- assert response.json() == [
- {"text": "buy the milk", "completed": True},
- {"text": "walk the dog", "completed": False},
- ]
-
-
-def test_database_isolated_during_test_cases(test_client_factory):
- """
- Using `TestClient` as a context manager
- """
- with test_client_factory(app) as client:
- response = client.post(
- "/notes", json={"text": "just one note", "completed": True}
- )
- assert response.status_code == 200
-
- response = client.get("/notes")
- assert response.status_code == 200
- assert response.json() == [{"text": "just one note", "completed":
True}]
-
- with test_client_factory(app) as client:
- response = client.post(
- "/notes", json={"text": "just one note", "completed": True}
- )
- assert response.status_code == 200
-
- response = client.get("/notes")
- assert response.status_code == 200
- assert response.json() == [{"text": "just one note", "completed":
True}]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/starlette-0.21.0/tests/test_datastructures.py
new/starlette-0.22.0/tests/test_datastructures.py
--- old/starlette-0.21.0/tests/test_datastructures.py 2022-09-26
19:09:36.000000000 +0200
+++ new/starlette-0.22.0/tests/test_datastructures.py 2022-11-17
07:24:34.000000000 +0100
@@ -201,9 +201,9 @@
def test_mutable_headers_merge_not_mapping():
h = MutableHeaders()
with pytest.raises(TypeError):
- h |= {"not_mapping"} # type: ignore
+ h |= {"not_mapping"} # type: ignore[arg-type]
with pytest.raises(TypeError):
- h | {"not_mapping"} # type: ignore
+ h | {"not_mapping"} # type: ignore[operator]
def test_headers_mutablecopy():
@@ -214,6 +214,16 @@
assert c.items() == [("a", "abc"), ("b", "789")]
+def test_mutable_headers_from_scope():
+ # "headers" in scope must not necessarily be a list
+ h = MutableHeaders(scope={"headers": ((b"a", b"1"),)})
+ assert dict(h) == {"a": "1"}
+ h.update({"b": "2"})
+ assert dict(h) == {"a": "1", "b": "2"}
+ assert list(h.items()) == [("a", "1"), ("b", "2")]
+ assert list(h.raw) == [(b"a", b"1"), (b"b", b"2")]
+
+
def test_url_blank_params():
q = QueryParams("a=123&abc&def&b=456")
assert "a" in q
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/starlette-0.21.0/tests/test_testclient.py
new/starlette-0.22.0/tests/test_testclient.py
--- old/starlette-0.21.0/tests/test_testclient.py 2022-09-26
19:09:36.000000000 +0200
+++ new/starlette-0.22.0/tests/test_testclient.py 2022-11-17
07:24:34.000000000 +0100
@@ -9,7 +9,7 @@
from starlette.applications import Starlette
from starlette.middleware import Middleware
-from starlette.responses import JSONResponse
+from starlette.responses import JSONResponse, Response
from starlette.routing import Route
from starlette.websockets import WebSocket, WebSocketDisconnect
@@ -240,3 +240,14 @@
client = test_client_factory(app)
response = client.get("/")
assert response.json() == {"host": "testclient", "port": 50000}
+
+
[email protected]("param", ("2020-07-14T00:00:00+00:00", "España",
"voilà "))
+def test_query_params(test_client_factory, param: str):
+ def homepage(request):
+ return Response(request.query_params["param"])
+
+ app = Starlette(routes=[Route("/", endpoint=homepage)])
+ client = test_client_factory(app)
+ response = client.get("/", params={"param": param})
+ assert response.text == param