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 <mich...@stroeder.com>
+
+- 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!)")
-
-
-@pytest.fixture
 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")
-
-
-@pytest.fixture(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)
-
-
-@database.transaction()
-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}
+
+
+@pytest.mark.parametrize("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

Reply via email to