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 2025-11-01 23:34:24
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-starlette (Old)
 and      /work/SRC/openSUSE:Factory/.python-starlette.new.1980 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-starlette"

Sat Nov  1 23:34:24 2025 rev:35 rq:1314383 version:0.49.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-starlette/python-starlette.changes        
2025-10-18 14:37:06.980665912 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-starlette.new.1980/python-starlette.changes  
    2025-11-01 23:34:26.362028297 +0100
@@ -1,0 +2,15 @@
+Wed Oct 29 09:14:32 UTC 2025 - Nico Krapp <[email protected]>
+
+- Update to 0.49.1 (fixes CVE-2025-62727, bsc#1252805)
+  * This release fixes a security vulnerability in the parsing logic of the
+    Range header in FileResponse. You can view the full security advisory:
+    GHSA-7f5h-v6xp-fcq8
+  * Optimize the HTTP ranges parsing logic
+- Update to 0.49.0
+  * Add encoding parameter to Config class
+  * Support multiple cookie headers in Request.cookies
+  * Use Literal type for WebSocketEndpoint encoding values
+  * Do not pollute exception context in Middleware when using
+    BaseHTTPMiddleware
+
+-------------------------------------------------------------------

Old:
----
  starlette-0.48.0.tar.gz

New:
----
  starlette-0.49.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-starlette.spec ++++++
--- /var/tmp/diff_new_pack.GgD7c0/_old  2025-11-01 23:34:27.114059741 +0100
+++ /var/tmp/diff_new_pack.GgD7c0/_new  2025-11-01 23:34:27.118059909 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-starlette
 #
-# Copyright (c) 2025 SUSE LLC
+# Copyright (c) 2025 SUSE LLC and contributors
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -27,7 +27,7 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-starlette%{psuffix}
-Version:        0.48.0
+Version:        0.49.1
 Release:        0
 Summary:        Lightweight ASGI framework/toolkit
 License:        BSD-3-Clause

++++++ starlette-0.48.0.tar.gz -> starlette-0.49.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/.github/workflows/main.yml 
new/starlette-0.49.1/.github/workflows/main.yml
--- old/starlette-0.48.0/.github/workflows/main.yml     2025-09-13 
10:39:11.000000000 +0200
+++ new/starlette-0.49.1/.github/workflows/main.yml     2025-10-28 
18:31:53.000000000 +0100
@@ -20,7 +20,7 @@
       - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # 
v5.0.0
 
       - name: Install uv
-        uses: astral-sh/setup-uv@557e51de59eb14aaaba2ed9621916900a91d50c6 # 
v6.6.1
+        uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # 
v6.8.0
         with:
           python-version: ${{ matrix.python-version }}
           enable-cache: true
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/.github/workflows/publish.yml 
new/starlette-0.49.1/.github/workflows/publish.yml
--- old/starlette-0.48.0/.github/workflows/publish.yml  2025-09-13 
10:39:11.000000000 +0200
+++ new/starlette-0.49.1/.github/workflows/publish.yml  2025-10-28 
18:31:53.000000000 +0100
@@ -4,6 +4,7 @@
   push:
     tags:
       - '*'
+  workflow_dispatch:
 
 jobs:
   build:
@@ -13,7 +14,7 @@
       - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # 
v5.0.0
 
       - name: Install uv
-        uses: astral-sh/setup-uv@557e51de59eb14aaaba2ed9621916900a91d50c6 # 
v6.6.1
+        uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # 
v6.8.0
         with:
           python-version: "3.11"
           enable-cache: true
@@ -39,6 +40,7 @@
   pypi-publish:
     runs-on: ubuntu-latest
     needs: build
+    if: success() && startsWith(github.ref, 'refs/tags/')
 
     permissions:
       id-token: write
@@ -77,12 +79,39 @@
           git config user.name github-actions[bot]
           git config user.email 
41898282+github-actions[bot]@users.noreply.github.com
 
-      - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # 
v6.0.0
+      - name: Install uv
+        uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # 
v6.8.0
         with:
           python-version: "3.12"
+          enable-cache: true
 
       - name: Install dependencies
         run: scripts/install
 
       - name: Publish documentation πŸ“š to GitHub Pages
-        run: mkdocs gh-deploy --force
+        run: uv run mkdocs gh-deploy --force
+
+  docs-cloudflare:
+    runs-on: ubuntu-latest
+    needs: build
+
+    environment:
+      name: cloudflare
+      url: https://starlette.dev
+
+    steps:
+      - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # 
v5.0.0
+      - name: Download artifacts
+        uses: 
actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
+        with:
+          name: documentation
+          path: site/
+
+      - uses: 
cloudflare/wrangler-action@da0e0dfe58b7a431659754fdf3f186c529afbe65 # v3.14.1
+        with:
+          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
+          command: >
+            pages deploy ./site
+            --project-name starlette
+            --commit-hash ${{ github.sha }}
+            --branch main
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/CITATION.cff 
new/starlette-0.49.1/CITATION.cff
--- old/starlette-0.48.0/CITATION.cff   2025-09-13 10:39:11.000000000 +0200
+++ new/starlette-0.49.1/CITATION.cff   2025-10-28 18:31:53.000000000 +0100
@@ -15,7 +15,7 @@
     family-names: Christie
     email: [email protected]
 repository-code: "https://github.com/Kludex/starlette";
-url: "https://www.starlette.io/";
+url: "https://starlette.dev/";
 abstract: Starlette is an ASGI web framework for Python.
 keywords:
   - asgi
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/README.md 
new/starlette-0.49.1/README.md
--- old/starlette-0.48.0/README.md      2025-09-13 10:39:11.000000000 +0200
+++ new/starlette-0.49.1/README.md      2025-10-28 18:31:53.000000000 +0100
@@ -15,10 +15,11 @@
 [![Build 
Status](https://github.com/Kludex/starlette/workflows/Test%20Suite/badge.svg)](https://github.com/Kludex/starlette/actions)
 [![Package 
version](https://badge.fury.io/py/starlette.svg)](https://pypi.python.org/pypi/starlette)
 [![Supported Python 
Version](https://img.shields.io/pypi/pyversions/starlette.svg?color=%2334D058)](https://pypi.org/project/starlette)
+[![Discord](https://img.shields.io/discord/1051468649518616576?logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/RxKUF5JuHs)
 
 ---
 
-**Documentation**: <a href="https://www.starlette.io/"; 
target="_blank">https://www.starlette.io</a>
+**Documentation**: <a href="https://starlette.dev/"; 
target="_blank">https://starlette.dev</a>
 
 **Source Code**: <a href="https://github.com/Kludex/starlette"; 
target="_blank">https://github.com/Kludex/starlette</a>
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/docs/config.md 
new/starlette-0.49.1/docs/config.md
--- old/starlette-0.48.0/docs/config.md 2025-09-13 10:39:11.000000000 +0200
+++ new/starlette-0.49.1/docs/config.md 2025-10-28 18:31:53.000000000 +0100
@@ -120,6 +120,18 @@
 ENVIRONMENT = config('ENVIRONMENT') # lookups APP_ENVIRONMENT, raises KeyError 
as variable is not defined
 ```
 
+## Custom encoding for environment files 
+
+By default, Starlette reads environment files using UTF-8 encoding. 
+You can specify a different encoding by setting `encoding` argument.
+
+```python title="myproject/settings.py"
+from starlette.config import Config
+
+# Using custom encoding for .env file
+config = Config(".env", encoding="latin-1")
+```
+
 ## A full example
 
 Structuring large applications can be complex. You need proper separation of
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/docs/contributing.md 
new/starlette-0.49.1/docs/contributing.md
--- old/starlette-0.48.0/docs/contributing.md   2025-09-13 10:39:11.000000000 
+0200
+++ new/starlette-0.49.1/docs/contributing.md   2025-10-28 18:31:53.000000000 
+0100
@@ -166,5 +166,3 @@
 
 Once created this release will be automatically uploaded to PyPI.
 
-If something goes wrong with the PyPI job the release can be published using 
the
-`scripts/publish` script.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/docs/exceptions.md 
new/starlette-0.49.1/docs/exceptions.md
--- old/starlette-0.48.0/docs/exceptions.md     2025-09-13 10:39:11.000000000 
+0200
+++ new/starlette-0.49.1/docs/exceptions.md     2025-10-28 18:31:53.000000000 
+0100
@@ -99,7 +99,7 @@
 }
 ```
 
-It's important to notice that in case a 
[`BackgroundTask`](https://www.starlette.io/background/) raises an exception,
+It's important to notice that in case a [`BackgroundTask`](background.md) 
raises an exception,
 it will be handled by the `handle_error` function, but at that point, the 
response was already sent. In other words,
 the response created by `handle_error` will be discarded. In case the error 
happens before the response was sent, then
 it will use the response object - in the above example, the returned 
`JSONResponse`.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/docs/index.md 
new/starlette-0.49.1/docs/index.md
--- old/starlette-0.48.0/docs/index.md  2025-09-13 10:39:11.000000000 +0200
+++ new/starlette-0.49.1/docs/index.md  2025-10-28 18:31:53.000000000 +0100
@@ -15,11 +15,14 @@
 <a href="https://pypi.org/project/starlette"; target="_blank">
     <img 
src="https://img.shields.io/pypi/pyversions/starlette.svg?color=%2334D058"; 
alt="Supported Python versions">
 </a>
+<a href="https://discord.gg/RxKUF5JuHs";>
+    <img 
src="https://img.shields.io/discord/1051468649518616576?logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2";
 alt="Discord">
+</a>
 </p>
 
 ---
 
-**Documentation**: <a href="https://www.starlette.io/"; 
target="_blank">https://www.starlette.io</a>
+**Documentation**: <a href="https://starlette.dev/"; 
target="_blank">https://starlette.dev</a>
 
 **Source Code**: <a href="https://github.com/Kludex/starlette"; 
target="_blank">https://github.com/Kludex/starlette</a>
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/docs/overrides/main.html 
new/starlette-0.49.1/docs/overrides/main.html
--- old/starlette-0.48.0/docs/overrides/main.html       1970-01-01 
01:00:00.000000000 +0100
+++ new/starlette-0.49.1/docs/overrides/main.html       2025-10-28 
18:31:53.000000000 +0100
@@ -0,0 +1,12 @@
+{% extends "base.html" %}
+
+{% block extrahead %}
+  {{ super() }}
+  <script>
+    // Redirect starlette.io to starlette.dev
+    if (window.location.hostname === 'www.starlette.io' || 
window.location.hostname === 'starlette.io') {
+      const newUrl = 
window.location.href.replace(/^https?:\/\/(www\.)?starlette\.io/, 
'https://starlette.dev');
+      window.location.replace(newUrl);
+    }
+  </script>
+{% endblock %}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/docs/release-notes.md 
new/starlette-0.49.1/docs/release-notes.md
--- old/starlette-0.48.0/docs/release-notes.md  2025-09-13 10:39:11.000000000 
+0200
+++ new/starlette-0.49.1/docs/release-notes.md  2025-10-28 18:31:53.000000000 
+0100
@@ -2,6 +2,28 @@
 toc_depth: 2
 ---
 
+## 0.49.1 (October 28, 2025)
+
+This release fixes a security vulnerability in the parsing logic of the 
`Range` header in `FileResponse`.
+
+You can view the full security advisory: 
[GHSA-7f5h-v6xp-fcq8](https://github.com/Kludex/starlette/security/advisories/GHSA-7f5h-v6xp-fcq8)
+
+#### Fixed
+
+* Optimize the HTTP ranges parsing logic 
[4ea6e22b489ec388d6004cfbca52dd5b147127c5](https://github.com/Kludex/starlette/commit/4ea6e22b489ec388d6004cfbca52dd5b147127c5)
+
+## 0.49.0 (October 28, 2025)
+
+#### Added
+
+* Add `encoding` parameter to `Config` class 
[#2996](https://github.com/Kludex/starlette/pull/2996).
+* Support multiple cookie headers in `Request.cookies` 
[#3029](https://github.com/Kludex/starlette/pull/3029).
+* Use `Literal` type for `WebSocketEndpoint` encoding values 
[#3027](https://github.com/Kludex/starlette/pull/3027).
+
+#### Changed
+
+* Do not pollute exception context in `Middleware` when using 
`BaseHTTPMiddleware` [#2976](https://github.com/Kludex/starlette/pull/2976).
+
 ## 0.48.0 (September 13, 2025)
 
 #### Added
@@ -731,7 +753,7 @@
 ### Removed
 
 * `UJSONResponse` was removed (this change was intended to be included in 
0.14.0). Please see the
-  
[documentation](https://www.starlette.io/responses/#custom-json-serialization) 
for how to
+  [documentation](https://starlette.dev/responses/#custom-json-serialization) 
for how to
   implement responses using custom JSON serialization -
   [#1074](https://github.com/Kludex/starlette/pull/1047).
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/docs/sponsorship.md 
new/starlette-0.49.1/docs/sponsorship.md
--- old/starlette-0.48.0/docs/sponsorship.md    2025-09-13 10:39:11.000000000 
+0200
+++ new/starlette-0.49.1/docs/sponsorship.md    2025-10-28 18:31:53.000000000 
+0100
@@ -59,8 +59,8 @@
 
 ## Sponsorship Tiers 🎁
 
-<div style="display: grid; grid-template-columns: repeat(auto-fit, 
minmax(250px, 1fr)); gap: 1.5rem; margin: 2rem 0;">
-    <div style="padding: 1.5rem; border: 1px solid #e1e4e8; border-radius: 
6px; background: #fff; display: flex; flex-direction: column;">
+<div style="display: grid; grid-template-columns: repeat(auto-fit, 
minmax(250px, 1fr)); gap: 1.5rem; margin: 2rem 0; color: #fff">
+    <div style="padding: 1.5rem; border: 1px solid #e1e4e8; border-radius: 
6px; background: linear-gradient(135deg, #6e5494, #24292e); display: flex; 
flex-direction: column;">
         <h3 style="color: #cd7f32;">πŸ₯‰ Bronze Sponsor</h3>
         <div style="font-size: 1.5em; margin: 1rem 0;">$100<span 
style="font-size: 0.6em;">/month</span></div>
         <ul style="list-style: none; padding: 0; margin-bottom: 1rem; 
min-height: 90px;">
@@ -74,7 +74,7 @@
             </a>
         </div>
     </div>
-    <div style="padding: 1.5rem; border: 1px solid #e1e4e8; border-radius: 
6px; background: #fff; display: flex; flex-direction: column;">
+    <div style="padding: 1.5rem; border: 1px solid #e1e4e8; border-radius: 
6px; background: linear-gradient(135deg, #6e5494, #24292e); display: flex; 
flex-direction: column;">
         <h3 style="color: #c0c0c0;">πŸ₯ˆ Silver Sponsor</h3>
         <div style="font-size: 1.5em; margin: 1rem 0;">$250<span 
style="font-size: 0.6em;">/month</span></div>
         <ul style="list-style: none; padding: 0; margin-bottom: 1rem; 
min-height: 90px;">
@@ -88,7 +88,7 @@
             </a>
         </div>
     </div>
-    <div style="padding: 1.5rem; border: 1px solid #e1e4e8; border-radius: 
6px; background: #fff; position: relative; overflow: hidden; display: flex; 
flex-direction: column;">
+    <div style="padding: 1.5rem; border: 1px solid #e1e4e8; border-radius: 
6px; background: linear-gradient(135deg, #6e5494, #24292e); position: relative; 
overflow: hidden; display: flex; flex-direction: column;">
         <div style="position: absolute; top: 10px; right: -25px; background: 
#238636; color: white; padding: 5px 30px; transform: rotate(45deg);">
             Popular
         </div>
@@ -148,7 +148,7 @@
 
 ## Alternative Sponsorship Platforms
 
-<div style="background: #f6f8fa; padding: 1.5rem; border-radius: 8px; margin: 
2rem 0;">
+<div style="background: linear-gradient(135deg, 
var(--md-default-fg-color--lighter), var(--md-default-fg-color--lightest)); 
padding: 1.5rem; border-radius: 8px; margin: 2rem 0;">
     <h3>πŸ“’ We Want Your Input!</h3>
     <p>We are currently evaluating whether to expand our sponsorship options 
beyond GitHub Sponsors. If your company would be interested in sponsoring 
Starlette and Uvicorn but prefers to use a different platform (e.g., Open 
Collective, direct invoicing), please let us know!</p>
     <p>Your feedback is invaluable in helping us make sponsorship as 
accessible as possible. Share your thoughts by:</p>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/docs/third-party-packages.md 
new/starlette-0.49.1/docs/third-party-packages.md
--- old/starlette-0.48.0/docs/third-party-packages.md   2025-09-13 
10:39:11.000000000 +0200
+++ new/starlette-0.49.1/docs/third-party-packages.md   2025-10-28 
18:31:53.000000000 +0100
@@ -252,7 +252,7 @@
 
 <a href="https://github.com/DavidVentura/starlette-static-resources"; 
target="_blank">GitHub</a>
 
-Allows mounting [package 
resources](https://docs.python.org/3/library/importlib.resources.html#module-importlib.resources)
 for static data, similar to 
[StaticFiles](https://www.starlette.io/staticfiles/).
+Allows mounting [package 
resources](https://docs.python.org/3/library/importlib.resources.html#module-importlib.resources)
 for static data, similar to [StaticFiles](staticfiles.md).
 
 ### Sentry
 
@@ -266,4 +266,4 @@
 <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
+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.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/mkdocs.yml 
new/starlette-0.49.1/mkdocs.yml
--- old/starlette-0.48.0/mkdocs.yml     2025-09-13 10:39:11.000000000 +0200
+++ new/starlette-0.49.1/mkdocs.yml     2025-10-28 18:31:53.000000000 +0100
@@ -1,6 +1,6 @@
 site_name: Starlette
 site_description: The little ASGI library that shines.
-site_url: https://www.starlette.io
+site_url: https://starlette.dev
 
 repo_name: Kludex/starlette
 repo_url: https://github.com/Kludex/starlette
@@ -62,6 +62,17 @@
   analytics:
     provider: google
     property: G-Z37GTYBR6M
+  social:
+    - icon: fontawesome/brands/github-alt
+      link: https://github.com/Kludex/starlette
+    - icon: fontawesome/brands/discord
+      link: https://discord.com/invite/RxKUF5JuHs
+    - icon: fontawesome/brands/twitter
+      link: https://x.com/marcelotryle
+    - icon: fontawesome/brands/linkedin
+      link: https://www.linkedin.com/in/marcelotryle
+    - icon: fontawesome/solid/globe
+      link: https://fastapiexpert.com
 
 markdown_extensions:
   - attr_list
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/pyproject.toml 
new/starlette-0.49.1/pyproject.toml
--- old/starlette-0.48.0/pyproject.toml 2025-09-13 10:39:11.000000000 +0200
+++ new/starlette-0.49.1/pyproject.toml 2025-10-28 18:31:53.000000000 +0100
@@ -8,17 +8,19 @@
 description = "The little ASGI library that shines."
 readme = "README.md"
 license = "BSD-3-Clause"
+license-files = ["LICENSE.md"]
 requires-python = ">=3.9"
 authors = [
     { name = "Tom Christie", email = "[email protected]" },
-    { name = "Marcelo Trylesinski", email = "[email protected]" }
+]
+maintainers = [
+    { name = "Marcelo Trylesinski", email = "[email protected]" },
 ]
 classifiers = [
     "Development Status :: 3 - Alpha",
     "Environment :: Web Environment",
     "Framework :: AnyIO",
     "Intended Audience :: Developers",
-    "License :: OSI Approved :: BSD License",
     "Operating System :: OS Independent",
     "Programming Language :: Python :: 3",
     "Programming Language :: Python :: 3.9",
@@ -70,8 +72,8 @@
 
 [project.urls]
 Homepage = "https://github.com/Kludex/starlette";
-Documentation = "https://www.starlette.io/";
-Changelog = "https://www.starlette.io/release-notes/";
+Documentation = "https://starlette.dev/";
+Changelog = "https://starlette.dev/release-notes/";
 Funding = "https://github.com/sponsors/Kludex";
 Source = "https://github.com/Kludex/starlette";
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/scripts/README.md 
new/starlette-0.49.1/scripts/README.md
--- old/starlette-0.48.0/scripts/README.md      2025-09-13 10:39:11.000000000 
+0200
+++ new/starlette-0.49.1/scripts/README.md      2025-10-28 18:31:53.000000000 
+0100
@@ -6,6 +6,5 @@
 * `scripts/check` - Run the code linting, checking that it passes.
 * `scripts/coverage` - Check that code coverage is complete.
 * `scripts/build` - Build source and wheel packages.
-* `scripts/publish` - Publish the latest version to PyPI.
 
 Styled after GitHub's ["Scripts to Rule Them 
All"](https://github.com/github/scripts-to-rule-them-all).
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/scripts/publish 
new/starlette-0.49.1/scripts/publish
--- old/starlette-0.48.0/scripts/publish        2025-09-13 10:39:11.000000000 
+0200
+++ new/starlette-0.49.1/scripts/publish        1970-01-01 01:00:00.000000000 
+0100
@@ -1,20 +0,0 @@
-#!/bin/sh -e
-
-VERSION_FILE="starlette/__init__.py"
-
-if [ ! -z "$GITHUB_ACTIONS" ]; then
-  git config --local user.email 
"41898282+github-actions[bot]@users.noreply.github.com"
-  git config --local user.name "GitHub Action"
-
-  VERSION=`grep __version__ ${VERSION_FILE} | grep -o '[0-9][^"]*'`
-
-  if [ "refs/tags/${VERSION}" != "${GITHUB_REF}" ] ; then
-    echo "GitHub Ref '${GITHUB_REF}' did not match package version 
'${VERSION}'"
-    exit 1
-  fi
-fi
-
-set -x
-
-uv run twine upload dist/*
-uv run mkdocs gh-deploy --force
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/starlette/__init__.py 
new/starlette-0.49.1/starlette/__init__.py
--- old/starlette-0.48.0/starlette/__init__.py  2025-09-13 10:39:11.000000000 
+0200
+++ new/starlette-0.49.1/starlette/__init__.py  2025-10-28 18:31:53.000000000 
+0100
@@ -1 +1 @@
-__version__ = "0.48.0"
+__version__ = "0.49.1"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/starlette/applications.py 
new/starlette-0.49.1/starlette/applications.py
--- old/starlette-0.48.0/starlette/applications.py      2025-09-13 
10:39:11.000000000 +0200
+++ new/starlette-0.49.1/starlette/applications.py      2025-10-28 
18:31:53.000000000 +0100
@@ -166,7 +166,7 @@
     def exception_handler(self, exc_class_or_status_code: int | 
type[Exception]) -> Callable:  # type: ignore[type-arg]
         warnings.warn(
             "The `exception_handler` decorator is deprecated, and will be 
removed in version 1.0.0. "
-            "Refer to https://www.starlette.io/exceptions/ for the recommended 
approach.",
+            "Refer to https://starlette.dev/exceptions/ for the recommended 
approach.",
             DeprecationWarning,
         )
 
@@ -192,7 +192,7 @@
         """
         warnings.warn(
             "The `route` decorator is deprecated, and will be removed in 
version 1.0.0. "
-            "Refer to https://www.starlette.io/routing/ for the recommended 
approach.",
+            "Refer to https://starlette.dev/routing/ for the recommended 
approach.",
             DeprecationWarning,
         )
 
@@ -218,7 +218,7 @@
         """
         warnings.warn(
             "The `websocket_route` decorator is deprecated, and will be 
removed in version 1.0.0. "
-            "Refer to https://www.starlette.io/routing/#websocket-routing for 
the recommended approach.",
+            "Refer to https://starlette.dev/routing/#websocket-routing for the 
recommended approach.",
             DeprecationWarning,
         )
 
@@ -238,7 +238,7 @@
         """
         warnings.warn(
             "The `middleware` decorator is deprecated, and will be removed in 
version 1.0.0. "
-            "Refer to https://www.starlette.io/middleware/#using-middleware 
for recommended approach.",
+            "Refer to https://starlette.dev/middleware/#using-middleware for 
recommended approach.",
             DeprecationWarning,
         )
         assert middleware_type == "http", 'Currently only middleware("http") 
is supported.'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/starlette/config.py 
new/starlette-0.49.1/starlette/config.py
--- old/starlette-0.48.0/starlette/config.py    2025-09-13 10:39:11.000000000 
+0200
+++ new/starlette-0.49.1/starlette/config.py    2025-10-28 18:31:53.000000000 
+0100
@@ -52,6 +52,7 @@
         env_file: str | Path | None = None,
         environ: Mapping[str, str] = environ,
         env_prefix: str = "",
+        encoding: str = "utf-8",
     ) -> None:
         self.environ = environ
         self.env_prefix = env_prefix
@@ -60,7 +61,7 @@
             if not os.path.isfile(env_file):
                 warnings.warn(f"Config file '{env_file}' not found.")
             else:
-                self.file_values = self._read_file(env_file)
+                self.file_values = self._read_file(env_file, encoding)
 
     @overload
     def __call__(self, key: str, *, default: None) -> str | None: ...
@@ -107,9 +108,9 @@
             return self._perform_cast(key, default, cast)
         raise KeyError(f"Config '{key}' is missing, and has no default.")
 
-    def _read_file(self, file_name: str | Path) -> dict[str, str]:
+    def _read_file(self, file_name: str | Path, encoding: str) -> dict[str, 
str]:
         file_values: dict[str, str] = {}
-        with open(file_name) as input_file:
+        with open(file_name, encoding=encoding) as input_file:
             for line in input_file.readlines():
                 line = line.strip()
                 if "=" in line and not line.startswith("#"):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/starlette/endpoints.py 
new/starlette-0.49.1/starlette/endpoints.py
--- old/starlette-0.48.0/starlette/endpoints.py 2025-09-13 10:39:11.000000000 
+0200
+++ new/starlette-0.49.1/starlette/endpoints.py 2025-10-28 18:31:53.000000000 
+0100
@@ -2,7 +2,7 @@
 
 import json
 from collections.abc import Generator
-from typing import Any, Callable
+from typing import Any, Callable, Literal
 
 from starlette import status
 from starlette._utils import is_async_callable
@@ -52,7 +52,7 @@
 
 
 class WebSocketEndpoint:
-    encoding: str | None = None  # May be "text", "bytes", or "json".
+    encoding: Literal["text", "bytes", "json"] | None = None
 
     def __init__(self, scope: Scope, receive: Receive, send: Send) -> None:
         assert scope["type"] == "websocket"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/starlette/middleware/base.py 
new/starlette-0.49.1/starlette/middleware/base.py
--- old/starlette-0.48.0/starlette/middleware/base.py   2025-09-13 
10:39:11.000000000 +0200
+++ new/starlette-0.49.1/starlette/middleware/base.py   2025-10-28 
18:31:53.000000000 +0100
@@ -156,7 +156,16 @@
                 if app_exc is not None:
                     nonlocal exception_already_raised
                     exception_already_raised = True
-                    raise app_exc
+                    # Prevent `anyio.EndOfStream` from polluting app exception 
context.
+                    # If both cause and context are None then the context is 
suppressed
+                    # and `anyio.EndOfStream` is not present in the exception 
traceback.
+                    # If exception cause is not None then it is propagated with
+                    # reraising here.
+                    # If exception has no cause but has context set then the 
context is
+                    # propagated as a cause with the reraise. This is 
necessary in order
+                    # to prevent `anyio.EndOfStream` from polluting the 
exception
+                    # context.
+                    raise app_exc from app_exc.__cause__ or app_exc.__context__
                 raise RuntimeError("No response returned.")
 
             assert message["type"] == "http.response.start"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/starlette/requests.py 
new/starlette-0.49.1/starlette/requests.py
--- old/starlette-0.48.0/starlette/requests.py  2025-09-13 10:39:11.000000000 
+0200
+++ new/starlette-0.49.1/starlette/requests.py  2025-10-28 18:31:53.000000000 
+0100
@@ -140,10 +140,11 @@
     def cookies(self) -> dict[str, str]:
         if not hasattr(self, "_cookies"):
             cookies: dict[str, str] = {}
-            cookie_header = self.headers.get("cookie")
+            cookie_headers = self.headers.getlist("cookie")
+
+            for header in cookie_headers:
+                cookies.update(cookie_parser(header))
 
-            if cookie_header:
-                cookies = cookie_parser(cookie_header)
             self._cookies = cookies
         return self._cookies
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/starlette/responses.py 
new/starlette-0.49.1/starlette/responses.py
--- old/starlette-0.48.0/starlette/responses.py 2025-09-13 10:39:11.000000000 
+0200
+++ new/starlette-0.49.1/starlette/responses.py 2025-10-28 18:31:53.000000000 
+0100
@@ -4,7 +4,6 @@
 import http.cookies
 import json
 import os
-import re
 import stat
 import sys
 import warnings
@@ -291,9 +290,6 @@
         self.max_size = max_size
 
 
-_RANGE_PATTERN = re.compile(r"(\d*)-(\d*)")
-
-
 class FileResponse(Response):
     chunk_size = 64 * 1024
 
@@ -455,8 +451,8 @@
     def _should_use_range(self, http_if_range: str) -> bool:
         return http_if_range == self.headers["last-modified"] or http_if_range 
== self.headers["etag"]
 
-    @staticmethod
-    def _parse_range_header(http_range: str, file_size: int) -> 
list[tuple[int, int]]:
+    @classmethod
+    def _parse_range_header(cls, http_range: str, file_size: int) -> 
list[tuple[int, int]]:
         ranges: list[tuple[int, int]] = []
         try:
             units, range_ = http_range.split("=", 1)
@@ -468,14 +464,7 @@
         if units != "bytes":
             raise MalformedRangeHeader("Only support bytes range")
 
-        ranges = [
-            (
-                int(_[0]) if _[0] else file_size - int(_[1]),
-                int(_[1]) + 1 if _[0] and _[1] and int(_[1]) < file_size else 
file_size,
-            )
-            for _ in _RANGE_PATTERN.findall(range_)
-            if _ != ("", "")
-        ]
+        ranges = cls._parse_ranges(range_, file_size)
 
         if len(ranges) == 0:
             raise MalformedRangeHeader("Range header: range must be requested")
@@ -507,6 +496,35 @@
 
         return result
 
+    @classmethod
+    def _parse_ranges(cls, range_: str, file_size: int) -> list[tuple[int, 
int]]:
+        ranges: list[tuple[int, int]] = []
+
+        for part in range_.split(","):
+            part = part.strip()
+
+            # If the range is empty or a single dash, we ignore it.
+            if not part or part == "-":
+                continue
+
+            # If the range is not in the format "start-end", we ignore it.
+            if "-" not in part:
+                continue
+
+            start_str, end_str = part.split("-", 1)
+            start_str = start_str.strip()
+            end_str = end_str.strip()
+
+            try:
+                start = int(start_str) if start_str else file_size - 
int(end_str)
+                end = int(end_str) + 1 if start_str and end_str and 
int(end_str) < file_size else file_size
+                ranges.append((start, end))
+            except ValueError:
+                # If the range is not numeric, we ignore it.
+                continue
+
+        return ranges
+
     def generate_multipart(
         self,
         ranges: Sequence[tuple[int, int]],
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/starlette/routing.py 
new/starlette-0.49.1/starlette/routing.py
--- old/starlette-0.48.0/starlette/routing.py   2025-09-13 10:39:11.000000000 
+0200
+++ new/starlette-0.49.1/starlette/routing.py   2025-10-28 18:31:53.000000000 
+0100
@@ -601,7 +601,7 @@
             warnings.warn(
                 "The on_startup and on_shutdown parameters are deprecated, and 
they "
                 "will be removed on version 1.0. Use the lifespan parameter 
instead. "
-                "See more about it on https://www.starlette.io/lifespan/.";,
+                "See more about it on https://starlette.dev/lifespan/.";,
                 DeprecationWarning,
             )
             if lifespan:
@@ -818,7 +818,7 @@
         """
         warnings.warn(
             "The `route` decorator is deprecated, and will be removed in 
version 1.0.0."
-            "Refer to https://www.starlette.io/routing/#http-routing for the 
recommended approach.",
+            "Refer to https://starlette.dev/routing/#http-routing for the 
recommended approach.",
             DeprecationWarning,
         )
 
@@ -844,7 +844,7 @@
         """
         warnings.warn(
             "The `websocket_route` decorator is deprecated, and will be 
removed in version 1.0.0. Refer to "
-            "https://www.starlette.io/routing/#websocket-routing for the 
recommended approach.",
+            "https://starlette.dev/routing/#websocket-routing for the 
recommended approach.",
             DeprecationWarning,
         )
 
@@ -865,7 +865,7 @@
     def on_event(self, event_type: str) -> Callable:  # type: ignore[type-arg]
         warnings.warn(
             "The `on_event` decorator is deprecated, and will be removed in 
version 1.0.0. "
-            "Refer to https://www.starlette.io/lifespan/ for recommended 
approach.",
+            "Refer to https://starlette.dev/lifespan/ for recommended 
approach.",
             DeprecationWarning,
         )
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/tests/middleware/test_base.py 
new/starlette-0.49.1/tests/middleware/test_base.py
--- old/starlette-0.48.0/tests/middleware/test_base.py  2025-09-13 
10:39:11.000000000 +0200
+++ new/starlette-0.49.1/tests/middleware/test_base.py  2025-10-28 
18:31:53.000000000 +0100
@@ -1243,3 +1243,62 @@
     assert len(events) == 2
     assert events[0]["type"] == "http.response.start"
     assert events[1]["type"] == "http.response.pathsend"
+
+
+def test_error_context_propagation(test_client_factory: TestClientFactory) -> 
None:
+    class PassthroughMiddleware(BaseHTTPMiddleware):
+        async def dispatch(
+            self,
+            request: Request,
+            call_next: RequestResponseEndpoint,
+        ) -> Response:
+            return await call_next(request)
+
+    def exception_without_context(request: Request) -> None:
+        raise Exception("Exception")
+
+    def exception_with_context(request: Request) -> None:
+        try:
+            raise Exception("Inner exception")
+        except Exception:
+            raise Exception("Outer exception")
+
+    def exception_with_cause(request: Request) -> None:
+        try:
+            raise Exception("Inner exception")
+        except Exception as e:
+            raise Exception("Outer exception") from e
+
+    app = Starlette(
+        routes=[
+            Route("/exception-without-context", 
endpoint=exception_without_context),
+            Route("/exception-with-context", endpoint=exception_with_context),
+            Route("/exception-with-cause", endpoint=exception_with_cause),
+        ],
+        middleware=[Middleware(PassthroughMiddleware)],
+    )
+    client = test_client_factory(app)
+
+    # For exceptions without context the context is filled with the 
`anyio.EndOfStream`
+    # but it is suppressed therefore not propagated to traceback.
+    with pytest.raises(Exception) as ctx:
+        client.get("/exception-without-context")
+    assert str(ctx.value) == "Exception"
+    assert ctx.value.__cause__ is None
+    assert ctx.value.__context__ is not None
+    assert ctx.value.__suppress_context__ is True
+
+    # For exceptions with context the context is propagated as a cause to avoid
+    # `anyio.EndOfStream` error from overwriting it.
+    with pytest.raises(Exception) as ctx:
+        client.get("/exception-with-context")
+    assert str(ctx.value) == "Outer exception"
+    assert ctx.value.__cause__ is not None
+    assert str(ctx.value.__cause__) == "Inner exception"
+
+    # For exceptions with cause check that it gets correctly propagated.
+    with pytest.raises(Exception) as ctx:
+        client.get("/exception-with-cause")
+    assert str(ctx.value) == "Outer exception"
+    assert ctx.value.__cause__ is not None
+    assert str(ctx.value.__cause__) == "Inner exception"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/tests/test_config.py 
new/starlette-0.49.1/tests/test_config.py
--- old/starlette-0.48.0/tests/test_config.py   2025-09-13 10:39:11.000000000 
+0200
+++ new/starlette-0.49.1/tests/test_config.py   2025-10-28 18:31:53.000000000 
+0100
@@ -140,3 +140,10 @@
 
     with pytest.raises(KeyError):
         config.get("ENVIRONMENT")
+
+
+def test_config_with_encoding(tmpdir: Path) -> None:
+    path = tmpdir / ".env"
+    path.write_text("MESSAGE=Hello δΈ–η•Œ\n", encoding="utf-8")
+    config = Config(path, encoding="utf-8")
+    assert config.get("MESSAGE") == "Hello δΈ–η•Œ"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/tests/test_requests.py 
new/starlette-0.49.1/tests/test_requests.py
--- old/starlette-0.48.0/tests/test_requests.py 2025-09-13 10:39:11.000000000 
+0200
+++ new/starlette-0.49.1/tests/test_requests.py 2025-10-28 18:31:53.000000000 
+0100
@@ -450,6 +450,19 @@
     assert result["cookies"] == expected
 
 
+def test_multiple_cookie_headers(test_client_factory: TestClientFactory) -> 
None:
+    async def app(scope: Scope, receive: Receive, send: Send) -> None:
+        scope["headers"] = [(b"cookie", b"a=abc"), (b"cookie", b"b=def"), 
(b"cookie", b"c=ghi")]
+        request = Request(scope, receive)
+        response = JSONResponse({"cookies": request.cookies})
+        await response(scope, receive, send)
+
+    client = test_client_factory(app)
+    response = client.get("/")
+    result = response.json()
+    assert result["cookies"] == {"a": "abc", "b": "def", "c": "ghi"}
+
+
 def test_chunked_encoding(test_client_factory: TestClientFactory) -> None:
     async def app(scope: Scope, receive: Receive, send: Send) -> None:
         request = Request(scope, receive)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/tests/test_responses.py 
new/starlette-0.49.1/tests/test_responses.py
--- old/starlette-0.48.0/tests/test_responses.py        2025-09-13 
10:39:11.000000000 +0200
+++ new/starlette-0.49.1/tests/test_responses.py        2025-10-28 
18:31:53.000000000 +0100
@@ -798,6 +798,34 @@
     ]
 
 
+def test_file_response_range_without_dash(file_response_client: TestClient) -> 
None:
+    response = file_response_client.get("/", headers={"Range": "bytes=100, 
0-50"})
+    assert response.status_code == 206
+    assert response.headers["content-range"] == f"bytes 
0-50/{len(README.encode('utf8'))}"
+
+
+def test_file_response_range_empty_start_and_end(file_response_client: 
TestClient) -> None:
+    response = file_response_client.get("/", headers={"Range": "bytes= - , 
0-50"})
+    assert response.status_code == 206
+    assert response.headers["content-range"] == f"bytes 
0-50/{len(README.encode('utf8'))}"
+
+
+def test_file_response_range_ignore_non_numeric(file_response_client: 
TestClient) -> None:
+    response = file_response_client.get("/", headers={"Range": "bytes=abc-def, 
0-50"})
+    assert response.status_code == 206
+    assert response.headers["content-range"] == f"bytes 
0-50/{len(README.encode('utf8'))}"
+
+
+def test_file_response_suffix_range(file_response_client: TestClient) -> None:
+    # Test suffix range (last N bytes) - line 523 with empty start_str
+    response = file_response_client.get("/", headers={"Range": "bytes=-100"})
+    assert response.status_code == 206
+    file_size = len(README.encode("utf8"))
+    assert response.headers["content-range"] == f"bytes {file_size - 
100}-{file_size - 1}/{file_size}"
+    assert response.headers["content-length"] == "100"
+    assert response.content == README.encode("utf8")[-100:]
+
+
 @pytest.mark.anyio
 async def test_file_response_multi_small_chunk_size(readme_file: Path) -> None:
     class SmallChunkSizeFileResponse(FileResponse):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/starlette-0.48.0/tests/test_status.py 
new/starlette-0.49.1/tests/test_status.py
--- old/starlette-0.48.0/tests/test_status.py   2025-09-13 10:39:11.000000000 
+0200
+++ new/starlette-0.49.1/tests/test_status.py   2025-10-28 18:31:53.000000000 
+0100
@@ -21,3 +21,11 @@
         getattr(importlib.import_module("starlette.status"), constant)
         assert len(record) == 1
         assert msg in str(record.list[0])
+
+
+def test_unknown_status() -> None:
+    with pytest.raises(
+        AttributeError,
+        match="module 'starlette.status' has no attribute 
'HTTP_999_UNKNOWN_STATUS_CODE'",
+    ):
+        getattr(importlib.import_module("starlette.status"), 
"HTTP_999_UNKNOWN_STATUS_CODE")

Reply via email to