diff --git a/docs/_newsfragments/2066.newandimproved.rst b/docs/_newsfragments/2066.newandimproved.rst new file mode 100644 index 000000000..8bbee4797 --- /dev/null +++ b/docs/_newsfragments/2066.newandimproved.rst @@ -0,0 +1,4 @@ +In Python 3.13, the ``cgi`` module is removed entirely from the stdlib, +including its ``parse_header()`` method. Falcon addresses the issue by shipping +an own implementation; :func:`falcon.parse_header` can also be used in your projects +affected by the removal. diff --git a/falcon/util/mediatypes.py b/falcon/util/mediatypes.py index 536fd19cf..c0dca5121 100644 --- a/falcon/util/mediatypes.py +++ b/falcon/util/mediatypes.py @@ -17,6 +17,19 @@ 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. @@ -26,20 +39,7 @@ def _parse_header_old_stdlib(line): # type: ignore This method has been copied (almost) verbatim from CPython 3.8 stdlib. It is slated for removal from the stdlib in 3.13. """ - - def _parseparam(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:] - - parts = _parseparam(';' + line) + parts = _parse_param_old_stdlib(';' + line) key = parts.__next__() pdict = {} for p in parts: @@ -63,7 +63,7 @@ def parse_header(line: str) -> typing.Tuple[str, dict]: line: A header value to parse. Returns: - A tuple containing the main content-type and a dictionary of options. + tuple: (the main content-type, dictionary of options). Note: This function replaces an equivalent method previously available in the