From 4e5df827b7259e1f7c9a76eadbf46b0714302565 Mon Sep 17 00:00:00 2001 From: Dan Radez Date: Tue, 9 Jan 2024 11:27:05 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A7=20Stop=20using=20the=20`cgi`=20std?= =?UTF-8?q?lib=20module?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit the module was deprecatied in py 3.11 and removed in py 3.13 there are examples of using the email module to resolve this: PEP 594 https://github.com/python-babel/babel/issues/873 https://stackoverflow.com/questions/69068527/python-3-cgi-parse-header This method doesn't seem to work for cherrypy. The email.message module is trying to create a higher level object that is intelligent about the headers it has defined. The cgi.parse_header function is a very low level unintelligent function that simply parses header content based on structure and does not inspect the contents. Because of the intelligence of the email.message object cherrypy can't use it in the very generic way that the cgi.parse_header function was being used. Fix #2014 --- CHANGES.rst | 6 ++++++ cherrypy/_cpcompat.py | 36 ++++++++++++++++++++++++++++++++++++ cherrypy/lib/covercp.py | 6 +++--- cherrypy/lib/httputil.py | 2 +- pytest.ini | 3 --- 5 files changed, 46 insertions(+), 7 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index cad61837b..a5a433e21 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v(next) +------- + +* Removed the use of :mod:`cgi` deprecated in Python 3.11 + -- by :user:`radez`. + v18.9.0 ------- diff --git a/cherrypy/_cpcompat.py b/cherrypy/_cpcompat.py index a43f6d369..cebdf84e2 100644 --- a/cherrypy/_cpcompat.py +++ b/cherrypy/_cpcompat.py @@ -20,6 +20,42 @@ import http.client +def _cgi_parseparam(s): + 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 cgi_parse_header(line): + """Parse a Content-type like header. + + Return the main content-type and a dictionary of options. + + Copied from removed stdlib cgi module. Couldn't find + something to replace it with that provided same functionality + from the email module as suggested. + """ + parts = _cgi_parseparam(';' + 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 ntob(n, encoding='ISO-8859-1'): """Return the given native string as a byte string in the given diff --git a/cherrypy/lib/covercp.py b/cherrypy/lib/covercp.py index 6c3871fc9..f22cce742 100644 --- a/cherrypy/lib/covercp.py +++ b/cherrypy/lib/covercp.py @@ -22,7 +22,7 @@ import re import sys -import cgi +import html import os import os.path import urllib.parse @@ -352,9 +352,9 @@ def annotated_file(self, filename, statements, excluded, missing): buffer.append((lineno, line)) if empty_the_buffer: for lno, pastline in buffer: - yield template % (lno, cgi.escape(pastline)) + yield template % (lno, html.escape(pastline)) buffer = [] - yield template % (lineno, cgi.escape(line)) + yield template % (lineno, html.escape(line)) @cherrypy.expose def report(self, name): diff --git a/cherrypy/lib/httputil.py b/cherrypy/lib/httputil.py index 62bc1bd04..e72bbf8e7 100644 --- a/cherrypy/lib/httputil.py +++ b/cherrypy/lib/httputil.py @@ -12,7 +12,7 @@ import re import builtins from binascii import b2a_base64 -from cgi import parse_header +from cherrypy._cpcompat import cgi_parse_header as parse_header from email.header import decode_header from http.server import BaseHTTPRequestHandler from urllib.parse import unquote_plus diff --git a/pytest.ini b/pytest.ini index 53e18e200..bcecc9bd5 100644 --- a/pytest.ini +++ b/pytest.ini @@ -59,9 +59,6 @@ filterwarnings = ignore:the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses:DeprecationWarning ignore:the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses:PendingDeprecationWarning - # TODO: Replace the use of `cgi`. It seems untrivial. - ignore:'cgi' is deprecated and slated for removal in Python 3.13:DeprecationWarning - # TODO: Remove once `cheroot.webtest._open_url_once` is fixed to release # TODO: connection properly. ignore:unclosed