Your message dated Fri, 29 Nov 2024 13:12:50 +0100
with message-id <[email protected]>
and subject line ACK NMU
has caused the Debian Bug report #1087211,
regarding python-falcon: diff for NMU version 3.1.1-4.1
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact [email protected]
immediately.)


-- 
1087211: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1087211
Debian Bug Tracking System
Contact [email protected] with problems
--- Begin Message ---
Package: python-falcon
Version: 3.1.1-4
Severity: normal
Tags: patch  pending

Dear maintainer,

I've prepared an NMU for python-falcon (versioned as 3.1.1-4.1) and
uploaded it to DELAYED/10. Please feel free to tell me if I
should delay it longer.

I expect the Python 3.13 transition to kick off very soon, raising this
bug to RC, and I plan to reschedule this to 0-day, if that happens.

Regards

Stefano
diff -Nru python-falcon-3.1.1/debian/changelog python-falcon-3.1.1/debian/changelog
--- python-falcon-3.1.1/debian/changelog	2024-05-01 23:37:02.000000000 -0700
+++ python-falcon-3.1.1/debian/changelog	2024-11-09 11:10:04.000000000 -0800
@@ -1,3 +1,10 @@
+python-falcon (3.1.1-4.1) unstable; urgency=medium
+
+  * Non-maintainer upload.
+  * Patch: Python 3.13 support. (Closes: #1081629, #1084623).
+
+ -- Stefano Rivera <[email protected]>  Sat, 09 Nov 2024 11:10:04 -0800
+
 python-falcon (3.1.1-4) unstable; urgency=medium
 
   * Drop extraneous python3-six dependency (Closes: #1070180).
diff -Nru python-falcon-3.1.1/debian/patches/python3.13.patch python-falcon-3.1.1/debian/patches/python3.13.patch
--- python-falcon-3.1.1/debian/patches/python3.13.patch	1969-12-31 16:00:00.000000000 -0800
+++ python-falcon-3.1.1/debian/patches/python3.13.patch	2024-11-09 11:10:04.000000000 -0800
@@ -0,0 +1,363 @@
+From: Vytautas Liuolia <[email protected]>
+Date: Wed, 3 Apr 2024 22:27:30 +0200
+Subject: feat(parse_header): provide our own implementation of
+ `parse_header()` (#2217)
+
+* feat(parse_header): provide our own implementation of `parse_header()`
+
+* docs(newsfragments): add a newsfragment
++ address 1 review comment
+
+* test(test_mediatypes.py): add tests for multiple parameters
+
+Bug-Debian: https://bugs.debian.org/1081629
+Bug-Upstream: https://github.com/falconry/falcon/issues/2066
+Origin: upstream, https://github.com/falconry/falcon/pull/2217
+---
+ docs/api/util.rst                    |  5 ++
+ docs/user/recipes/pretty-json.rst    |  3 +-
+ falcon/__init__.py                   |  1 +
+ falcon/asgi/multipart.py             |  5 +-
+ falcon/media/multipart.py            | 10 ++--
+ falcon/testing/helpers.py            |  4 +-
+ falcon/util/__init__.py              |  1 +
+ falcon/util/mediatypes.py            | 89 ++++++++++++++++++++++++++++++++++++
+ falcon/vendor/mimeparse/mimeparse.py |  4 +-
+ tests/test_mediatypes.py             | 41 +++++++++++++++++
+ 10 files changed, 149 insertions(+), 14 deletions(-)
+ create mode 100644 falcon/util/mediatypes.py
+ create mode 100644 tests/test_mediatypes.py
+
+diff --git a/docs/api/util.rst b/docs/api/util.rst
+index 53216ae..97759dd 100644
+--- a/docs/api/util.rst
++++ b/docs/api/util.rst
+@@ -34,6 +34,11 @@ HTTP Status
+ .. autofunction:: falcon.code_to_http_status
+ .. autofunction:: falcon.get_http_status
+ 
++Media types
++-----------
++
++.. autofunction:: falcon.parse_header
++
+ Async
+ -----
+ 
+diff --git a/docs/user/recipes/pretty-json.rst b/docs/user/recipes/pretty-json.rst
+index b6e5e4d..5faf59e 100644
+--- a/docs/user/recipes/pretty-json.rst
++++ b/docs/user/recipes/pretty-json.rst
+@@ -52,7 +52,6 @@ implemented with a :ref:`custom media handler <custom-media-handler-type>`:
+ 
+ .. code:: python
+ 
+-    import cgi
+     import json
+ 
+     import falcon
+@@ -66,7 +65,7 @@ implemented with a :ref:`custom media handler <custom-media-handler-type>`:
+             return json.loads(data.decode())
+ 
+         def serialize(self, media, content_type):
+-            _, params = cgi.parse_header(content_type)
++            _, params = falcon.parse_header(content_type)
+             indent = params.get('indent')
+             if indent is not None:
+                 try:
+diff --git a/falcon/__init__.py b/falcon/__init__.py
+index ff30097..a2a557e 100644
+--- a/falcon/__init__.py
++++ b/falcon/__init__.py
+@@ -75,6 +75,7 @@ from falcon.util import http_status_to_code
+ from falcon.util import IS_64_BITS
+ from falcon.util import is_python_func
+ from falcon.util import misc
++from falcon.util import parse_header
+ from falcon.util import reader
+ from falcon.util import runs_sync
+ from falcon.util import secure_filename
+diff --git a/falcon/asgi/multipart.py b/falcon/asgi/multipart.py
+index d58069f..57561f4 100644
+--- a/falcon/asgi/multipart.py
++++ b/falcon/asgi/multipart.py
+@@ -14,11 +14,10 @@
+ 
+ """ASGI multipart form media handler components."""
+ 
+-import cgi
+-
+ from falcon.asgi.reader import BufferedReader
+ from falcon.errors import DelimiterError
+ from falcon.media import multipart
++from falcon.util.mediatypes import parse_header
+ 
+ _ALLOWED_CONTENT_HEADERS = multipart._ALLOWED_CONTENT_HEADERS
+ _CRLF = multipart._CRLF
+@@ -54,7 +53,7 @@ class BodyPart(multipart.BodyPart):
+         return self._media
+ 
+     async def get_text(self):
+-        content_type, options = cgi.parse_header(self.content_type)
++        content_type, options = parse_header(self.content_type)
+         if content_type != 'text/plain':
+             return None
+ 
+diff --git a/falcon/media/multipart.py b/falcon/media/multipart.py
+index a194f1d..778cd66 100644
+--- a/falcon/media/multipart.py
++++ b/falcon/media/multipart.py
+@@ -14,7 +14,6 @@
+ 
+ """Multipart form media handler."""
+ 
+-import cgi
+ import re
+ from urllib.parse import unquote_to_bytes
+ 
+@@ -24,6 +23,7 @@ from falcon.stream import BoundedStream
+ from falcon.util import BufferedReader
+ from falcon.util import misc
+ from falcon.util.deprecation import deprecated_args
++from falcon.util.mediatypes import parse_header
+ 
+ 
+ # TODO(vytas):
+@@ -279,7 +279,7 @@ class BodyPart:
+             str: The part decoded as a text string provided the part is
+             encoded as ``text/plain``, ``None`` otherwise.
+         """
+-        content_type, options = cgi.parse_header(self.content_type)
++        content_type, options = parse_header(self.content_type)
+         if content_type != 'text/plain':
+             return None
+ 
+@@ -305,7 +305,7 @@ class BodyPart:
+ 
+             if self._content_disposition is None:
+                 value = self._headers.get(b'content-disposition', b'')
+-                self._content_disposition = cgi.parse_header(value.decode())
++                self._content_disposition = parse_header(value.decode())
+ 
+             _, params = self._content_disposition
+ 
+@@ -341,7 +341,7 @@ class BodyPart:
+ 
+             if self._content_disposition is None:
+                 value = self._headers.get(b'content-disposition', b'')
+-                self._content_disposition = cgi.parse_header(value.decode())
++                self._content_disposition = parse_header(value.decode())
+ 
+             _, params = self._content_disposition
+             self._name = params.get('name')
+@@ -526,7 +526,7 @@ class MultipartFormHandler(BaseHandler):
+         if not form_cls:
+             raise NotImplementedError
+ 
+-        _, options = cgi.parse_header(content_type)
++        _, options = parse_header(content_type)
+         try:
+             boundary = options['boundary']
+         except KeyError:
+diff --git a/falcon/testing/helpers.py b/falcon/testing/helpers.py
+index b11f7f6..c3c4b13 100644
+--- a/falcon/testing/helpers.py
++++ b/falcon/testing/helpers.py
+@@ -23,7 +23,6 @@ directly from the `testing` package::
+ """
+ 
+ import asyncio
+-import cgi
+ from collections import defaultdict
+ from collections import deque
+ import contextlib
+@@ -50,6 +49,7 @@ from falcon.asgi_spec import WSCloseCode
+ from falcon.constants import SINGLETON_HEADERS
+ import falcon.request
+ from falcon.util import uri
++from falcon.util.mediatypes import parse_header
+ 
+ # NOTE(kgriffs): Changed in 3.0 from 'curl/7.24.0 (x86_64-apple-darwin12.0)'
+ DEFAULT_UA = 'falcon-client/' + falcon.__version__
+@@ -798,7 +798,7 @@ def get_encoding_from_headers(headers):
+     if not content_type:
+         return None
+ 
+-    content_type, params = cgi.parse_header(content_type)
++    content_type, params = parse_header(content_type)
+ 
+     if 'charset' in params:
+         return params['charset'].strip('\'"')
+diff --git a/falcon/util/__init__.py b/falcon/util/__init__.py
+index cd3eca3..4232149 100644
+--- a/falcon/util/__init__.py
++++ b/falcon/util/__init__.py
+@@ -25,6 +25,7 @@ import sys
+ # Hoist misc. utils
+ from falcon.constants import PYTHON_VERSION
+ from falcon.util.deprecation import deprecated
++from falcon.util.mediatypes import parse_header
+ from falcon.util.misc import code_to_http_status
+ from falcon.util.misc import dt_to_http
+ from falcon.util.misc import get_argnames
+diff --git a/falcon/util/mediatypes.py b/falcon/util/mediatypes.py
+new file mode 100644
+index 0000000..c0dca51
+--- /dev/null
++++ b/falcon/util/mediatypes.py
+@@ -0,0 +1,89 @@
++# Copyright 2023-2024 by Vytautas Liuolia.
++#
++# Licensed under the Apache License, Version 2.0 (the "License");
++# you may not use this file except in compliance with the License.
++# You may obtain a copy of the License at
++#
++#    http://www.apache.org/licenses/LICENSE-2.0
++#
++# Unless required by applicable law or agreed to in writing, software
++# distributed under the License is distributed on an "AS IS" BASIS,
++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++# See the License for the specific language governing permissions and
++# limitations under the License.
++
++"""Media (aka MIME) type parsing and matching utilities."""
++
++import typing
++
++
++def _parse_param_old_stdlib(s):  # type: ignore
++    while s[:1] == ';':
++        s = s[1:]
++        end = s.find(';')
++        while end > 0 and (s.count('"', 0, end) - s.count('\\"', 0, end)) % 2:
++            end = s.find(';', end + 1)
++        if end < 0:
++            end = len(s)
++        f = s[:end]
++        yield f.strip()
++        s = s[end:]
++
++
++def _parse_header_old_stdlib(line):  # type: ignore
++    """Parse a Content-type like header.
++
++    Return the main content-type and a dictionary of options.
++
++    Note:
++        This method has been copied (almost) verbatim from CPython 3.8 stdlib.
++        It is slated for removal from the stdlib in 3.13.
++    """
++    parts = _parse_param_old_stdlib(';' + line)
++    key = parts.__next__()
++    pdict = {}
++    for p in parts:
++        i = p.find('=')
++        if i >= 0:
++            name = p[:i].strip().lower()
++            value = p[i + 1 :].strip()
++            if len(value) >= 2 and value[0] == value[-1] == '"':
++                value = value[1:-1]
++                value = value.replace('\\\\', '\\').replace('\\"', '"')
++            pdict[name] = value
++    return key, pdict
++
++
++def parse_header(line: str) -> typing.Tuple[str, dict]:
++    """Parse a Content-type like header.
++
++    Return the main content-type and a dictionary of options.
++
++    Args:
++        line: A header value to parse.
++
++    Returns:
++        tuple: (the main content-type, dictionary of options).
++
++    Note:
++        This function replaces an equivalent method previously available in the
++        stdlib as ``cgi.parse_header()``.
++        It was removed from the stdlib in Python 3.13.
++    """
++    if '"' not in line and '\\' not in line:
++        key, semicolon, parts = line.partition(';')
++        if not semicolon:
++            return (key.strip(), {})
++
++        pdict = {}
++        for part in parts.split(';'):
++            name, equals, value = part.partition('=')
++            if equals:
++                pdict[name.strip().lower()] = value.strip()
++
++        return (key.strip(), pdict)
++
++    return _parse_header_old_stdlib(line)
++
++
++__all__ = ['parse_header']
+diff --git a/falcon/vendor/mimeparse/mimeparse.py b/falcon/vendor/mimeparse/mimeparse.py
+index 0218553..f96e633 100755
+--- a/falcon/vendor/mimeparse/mimeparse.py
++++ b/falcon/vendor/mimeparse/mimeparse.py
+@@ -1,4 +1,4 @@
+-import cgi
++from falcon.util.mediatypes import parse_header
+ 
+ __version__ = '1.6.0'
+ __author__ = 'Joe Gregorio'
+@@ -23,7 +23,7 @@ def parse_mime_type(mime_type):
+ 
+     :rtype: (str,str,dict)
+     """
+-    full_type, params = cgi.parse_header(mime_type)
++    full_type, params = parse_header(mime_type)
+     # Java URLConnection class sends an Accept header that includes a
+     # single '*'. Turn it into a legal wildcard.
+     if full_type == '*':
+diff --git a/tests/test_mediatypes.py b/tests/test_mediatypes.py
+new file mode 100644
+index 0000000..0fae79b
+--- /dev/null
++++ b/tests/test_mediatypes.py
+@@ -0,0 +1,41 @@
++import pytest
++
++from falcon.util import mediatypes
++
++
[email protected](
++    'value,expected',
++    [
++        ('', ('', {})),
++        ('strange', ('strange', {})),
++        ('text/plain', ('text/plain', {})),
++        ('text/plain ', ('text/plain', {})),
++        (' text/plain', ('text/plain', {})),
++        (' text/plain ', ('text/plain', {})),
++        ('   text/plain   ', ('text/plain', {})),
++        (
++            'falcon/peregrine;  key1; key2=value; key3',
++            ('falcon/peregrine', {'key2': 'value'}),
++        ),
++        (
++            'audio/pcm;rate=48000;encoding=float;bits=32',
++            ('audio/pcm', {'bits': '32', 'encoding': 'float', 'rate': '48000'}),
++        ),
++        (
++            'falcon/*; genus=falco; family=falconidae; class=aves; ',
++            ('falcon/*', {'class': 'aves', 'family': 'falconidae', 'genus': 'falco'}),
++        ),
++        ('"falcon/peregrine" ; key="value"', ('"falcon/peregrine"', {'key': 'value'})),
++        ('falcon/peregrine; empty=""', ('falcon/peregrine', {'empty': ''})),
++        ('falcon/peregrine; quote="', ('falcon/peregrine', {'quote': '"'})),
++        ('text/plain; charset=utf-8', ('text/plain', {'charset': 'utf-8'})),
++        ('stuff/strange; missing-value; missing-another', ('stuff/strange', {})),
++        ('stuff/strange; missing-value\\missing-another', ('stuff/strange', {})),
++        (
++            'application/falcon; P1 = "key; value"; P2="\\""',
++            ('application/falcon', {'p1': 'key; value', 'p2': '"'}),
++        ),
++    ],
++)
++def test_parse_header(value, expected):
++    assert mediatypes.parse_header(value) == expected
diff -Nru python-falcon-3.1.1/debian/patches/series python-falcon-3.1.1/debian/patches/series
--- python-falcon-3.1.1/debian/patches/series	2024-05-01 23:37:02.000000000 -0700
+++ python-falcon-3.1.1/debian/patches/series	2024-11-09 11:10:04.000000000 -0800
@@ -1,2 +1,3 @@
 fix-non-ascii-in-doc.patch
 remove-test_cythonized_asgi.py.patch
+python3.13.patch

--- End Message ---
--- Begin Message ---
ACK NMU

--- End Message ---

Reply via email to