From 8af4c5fd7bbd35ea68ef532c554059f3598420dd Mon Sep 17 00:00:00 2001 From: DimitrisJim <d.f.hilliard@gmail.com> Date: Sun, 11 Jun 2023 17:27:59 +0300 Subject: [PATCH 1/8] Upgrade cmd.py and test_cmd.py from Python 3.11. --- Lib/cmd.py | 10 +++++----- Lib/test/test_cmd.py | 45 +++++++++++++++++++++++++++++++++----------- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/Lib/cmd.py b/Lib/cmd.py index 859e91096d..88ee7d3ddc 100644 --- a/Lib/cmd.py +++ b/Lib/cmd.py @@ -310,10 +310,10 @@ def do_help(self, arg): names = self.get_names() cmds_doc = [] cmds_undoc = [] - help = {} + topics = set() for name in names: if name[:5] == 'help_': - help[name[5:]]=1 + topics.add(name[5:]) names.sort() # There can be duplicates if routines overridden prevname = '' @@ -323,16 +323,16 @@ def do_help(self, arg): continue prevname = name cmd=name[3:] - if cmd in help: + if cmd in topics: cmds_doc.append(cmd) - del help[cmd] + topics.remove(cmd) elif getattr(self, name).__doc__: cmds_doc.append(cmd) else: cmds_undoc.append(cmd) self.stdout.write("%s\n"%str(self.doc_leader)) self.print_topics(self.doc_header, cmds_doc, 15,80) - self.print_topics(self.misc_header, list(help.keys()),15,80) + self.print_topics(self.misc_header, sorted(topics),15,80) self.print_topics(self.undoc_header, cmds_undoc, 15,80) def print_topics(self, header, cmds, cmdlen, maxcol): diff --git a/Lib/test/test_cmd.py b/Lib/test/test_cmd.py index c9ffad485f..319801c71f 100644 --- a/Lib/test/test_cmd.py +++ b/Lib/test/test_cmd.py @@ -6,10 +6,10 @@ import cmd import sys +import doctest import unittest import io from test import support -from test.support import import_helper class samplecmdclass(cmd.Cmd): """ @@ -70,7 +70,7 @@ class samplecmdclass(cmd.Cmd): >>> mycmd.complete_help("12") [] >>> sorted(mycmd.complete_help("")) - ['add', 'exit', 'help', 'shell'] + ['add', 'exit', 'help', 'life', 'meaning', 'shell'] Test for the function do_help(): >>> mycmd.do_help("testet") @@ -79,12 +79,20 @@ class samplecmdclass(cmd.Cmd): help text for add >>> mycmd.onecmd("help add") help text for add + >>> mycmd.onecmd("help meaning") # doctest: +NORMALIZE_WHITESPACE + Try and be nice to people, avoid eating fat, read a good book every + now and then, get some walking in, and try to live together in peace + and harmony with people of all creeds and nations. >>> mycmd.do_help("") <BLANKLINE> Documented commands (type help <topic>): ======================================== add help <BLANKLINE> + Miscellaneous help topics: + ========================== + life meaning + <BLANKLINE> Undocumented commands: ====================== exit shell @@ -115,17 +123,22 @@ class samplecmdclass(cmd.Cmd): This test includes the preloop(), postloop(), default(), emptyline(), parseline(), do_help() functions >>> mycmd.use_rawinput=0 - >>> mycmd.cmdqueue=["", "add", "add 4 5", "help", "help add","exit"] - >>> mycmd.cmdloop() + + >>> mycmd.cmdqueue=["add", "add 4 5", "", "help", "help add", "exit"] + >>> mycmd.cmdloop() # doctest: +REPORT_NDIFF Hello from preloop - help text for add *** invalid number of arguments 9 + 9 <BLANKLINE> Documented commands (type help <topic>): ======================================== add help <BLANKLINE> + Miscellaneous help topics: + ========================== + life meaning + <BLANKLINE> Undocumented commands: ====================== exit shell @@ -165,6 +178,17 @@ def help_add(self): print("help text for add") return + def help_meaning(self): + print("Try and be nice to people, avoid eating fat, read a " + "good book every now and then, get some walking in, " + "and try to live together in peace and harmony with " + "people of all creeds and nations.") + return + + def help_life(self): + print("Always look on the bright side of life") + return + def do_exit(self, arg): return True @@ -220,13 +244,12 @@ def test_input_reset_at_EOF(self): "(Cmd) *** Unknown syntax: EOF\n")) -def test_main(verbose=None): - from test import test_cmd - support.run_doctest(test_cmd, verbose) - support.run_unittest(TestAlternateInput) +def load_tests(loader, tests, pattern): + tests.addTest(doctest.DocTestSuite()) + return tests def test_coverage(coverdir): - trace = import_helper.import_module('trace') + trace = support.import_module('trace') tracer=trace.Trace(ignoredirs=[sys.base_prefix, sys.base_exec_prefix,], trace=0, count=1) tracer.run('import importlib; importlib.reload(cmd); test_main()') @@ -240,4 +263,4 @@ def test_coverage(coverdir): elif "-i" in sys.argv: samplecmdclass().cmdloop() else: - test_main() + unittest.main() From 3d76e1e1db5ed438cd4d9534ceb4c75289ec0b23 Mon Sep 17 00:00:00 2001 From: DimitrisJim <d.f.hilliard@gmail.com> Date: Sun, 11 Jun 2023 22:34:27 +0300 Subject: [PATCH 2/8] Upgrade aifc.py from Python 3.11. --- Lib/aifc.py | 65 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/Lib/aifc.py b/Lib/aifc.py index 1916e7ef8e..5254987e22 100644 --- a/Lib/aifc.py +++ b/Lib/aifc.py @@ -138,7 +138,11 @@ import builtins import warnings -__all__ = ["Error", "open", "openfp"] +__all__ = ["Error", "open"] + + +warnings._deprecated(__name__, remove=(3, 13)) + class Error(Exception): pass @@ -251,7 +255,9 @@ def _write_float(f, x): _write_ulong(f, himant) _write_ulong(f, lomant) -from chunk import Chunk +with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + from chunk import Chunk from collections import namedtuple _aifc_params = namedtuple('_aifc_params', @@ -447,21 +453,33 @@ def readframes(self, nframes): # def _alaw2lin(self, data): - import audioop + with warnings.catch_warnings(): + warnings.simplefilter('ignore', category=DeprecationWarning) + import audioop return audioop.alaw2lin(data, 2) def _ulaw2lin(self, data): - import audioop + with warnings.catch_warnings(): + warnings.simplefilter('ignore', category=DeprecationWarning) + import audioop return audioop.ulaw2lin(data, 2) def _adpcm2lin(self, data): - import audioop + with warnings.catch_warnings(): + warnings.simplefilter('ignore', category=DeprecationWarning) + import audioop if not hasattr(self, '_adpcmstate'): # first time self._adpcmstate = None data, self._adpcmstate = audioop.adpcm2lin(data, 2, self._adpcmstate) return data + def _sowt2lin(self, data): + with warnings.catch_warnings(): + warnings.simplefilter('ignore', category=DeprecationWarning) + import audioop + return audioop.byteswap(data, 2) + def _read_comm_chunk(self, chunk): self._nchannels = _read_short(chunk) self._nframes = _read_long(chunk) @@ -497,6 +515,8 @@ def _read_comm_chunk(self, chunk): self._convert = self._ulaw2lin elif self._comptype in (b'alaw', b'ALAW'): self._convert = self._alaw2lin + elif self._comptype in (b'sowt', b'SOWT'): + self._convert = self._sowt2lin else: raise Error('unsupported compression type') self._sampwidth = 2 @@ -659,7 +679,7 @@ def setcomptype(self, comptype, compname): if self._nframeswritten: raise Error('cannot change parameters after starting to write') if comptype not in (b'NONE', b'ulaw', b'ULAW', - b'alaw', b'ALAW', b'G722'): + b'alaw', b'ALAW', b'G722', b'sowt', b'SOWT'): raise Error('unsupported compression type') self._comptype = comptype self._compname = compname @@ -680,7 +700,7 @@ def setparams(self, params): if self._nframeswritten: raise Error('cannot change parameters after starting to write') if comptype not in (b'NONE', b'ulaw', b'ULAW', - b'alaw', b'ALAW', b'G722'): + b'alaw', b'ALAW', b'G722', b'sowt', b'SOWT'): raise Error('unsupported compression type') self.setnchannels(nchannels) self.setsampwidth(sampwidth) @@ -764,28 +784,43 @@ def close(self): # def _lin2alaw(self, data): - import audioop + with warnings.catch_warnings(): + warnings.simplefilter('ignore', category=DeprecationWarning) + import audioop return audioop.lin2alaw(data, 2) def _lin2ulaw(self, data): - import audioop + with warnings.catch_warnings(): + warnings.simplefilter('ignore', category=DeprecationWarning) + import audioop return audioop.lin2ulaw(data, 2) def _lin2adpcm(self, data): - import audioop + with warnings.catch_warnings(): + warnings.simplefilter('ignore', category=DeprecationWarning) + import audioop if not hasattr(self, '_adpcmstate'): self._adpcmstate = None data, self._adpcmstate = audioop.lin2adpcm(data, 2, self._adpcmstate) return data + def _lin2sowt(self, data): + with warnings.catch_warnings(): + warnings.simplefilter('ignore', category=DeprecationWarning) + import audioop + return audioop.byteswap(data, 2) + def _ensure_header_written(self, datasize): if not self._nframeswritten: - if self._comptype in (b'ULAW', b'ulaw', b'ALAW', b'alaw', b'G722'): + if self._comptype in (b'ULAW', b'ulaw', + b'ALAW', b'alaw', b'G722', + b'sowt', b'SOWT'): if not self._sampwidth: self._sampwidth = 2 if self._sampwidth != 2: raise Error('sample width must be 2 when compressing ' - 'with ulaw/ULAW, alaw/ALAW or G7.22 (ADPCM)') + 'with ulaw/ULAW, alaw/ALAW, sowt/SOWT ' + 'or G7.22 (ADPCM)') if not self._nchannels: raise Error('# channels not specified') if not self._sampwidth: @@ -801,6 +836,8 @@ def _init_compression(self): self._convert = self._lin2ulaw elif self._comptype in (b'alaw', b'ALAW'): self._convert = self._lin2alaw + elif self._comptype in (b'sowt', b'SOWT'): + self._convert = self._lin2sowt def _write_header(self, initlength): if self._aifc and self._comptype != b'NONE': @@ -920,10 +957,6 @@ def open(f, mode=None): else: raise Error("mode must be 'r', 'rb', 'w', or 'wb'") -def openfp(f, mode=None): - warnings.warn("aifc.openfp is deprecated since Python 3.7. " - "Use aifc.open instead.", DeprecationWarning, stacklevel=2) - return open(f, mode=mode) if __name__ == '__main__': import sys From 1be262db40c2867249dc153ce1807f5269f97110 Mon Sep 17 00:00:00 2001 From: DimitrisJim <d.f.hilliard@gmail.com> Date: Sun, 11 Jun 2023 23:46:59 +0300 Subject: [PATCH 3/8] Upgrade warnings.py from Python 3.11 --- Lib/warnings.py | 104 ++++++++++++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 39 deletions(-) diff --git a/Lib/warnings.py b/Lib/warnings.py index 81f9864778..7d8c440012 100644 --- a/Lib/warnings.py +++ b/Lib/warnings.py @@ -33,9 +33,8 @@ def _showwarnmsg_impl(msg): pass def _formatwarnmsg_impl(msg): - s = ("%s:%s: %s: %s\n" - % (msg.filename, msg.lineno, msg.category.__name__, - msg.message)) + category = msg.category.__name__ + s = f"{msg.filename}:{msg.lineno}: {category}: {msg.message}\n" if msg.line is None: try: @@ -55,11 +54,20 @@ def _formatwarnmsg_impl(msg): if msg.source is not None: try: import tracemalloc - tb = tracemalloc.get_object_traceback(msg.source) + # Logging a warning should not raise a new exception: + # catch Exception, not only ImportError and RecursionError. except Exception: - # When a warning is logged during Python shutdown, tracemalloc - # and the import machinery don't work anymore + # don't suggest to enable tracemalloc if it's not available + tracing = True tb = None + else: + tracing = tracemalloc.is_tracing() + try: + tb = tracemalloc.get_object_traceback(msg.source) + except Exception: + # When a warning is logged during Python shutdown, tracemalloc + # and the import machinery don't work anymore + tb = None if tb is not None: s += 'Object allocated at (most recent call last):\n' @@ -77,6 +85,9 @@ def _formatwarnmsg_impl(msg): if line: line = line.strip() s += ' %s\n' % line + elif not tracing: + s += (f'{category}: Enable tracemalloc to get the object ' + f'allocation traceback\n') return s # Keep a reference to check if the function was replaced @@ -113,7 +124,7 @@ def _formatwarnmsg(msg): if fw is not _formatwarning_orig: # warnings.formatwarning() was replaced return fw(msg.message, msg.category, - msg.filename, msg.lineno, line=msg.line) + msg.filename, msg.lineno, msg.line) return _formatwarnmsg_impl(msg) def filterwarnings(action, message="", category=Warning, module="", lineno=0, @@ -200,7 +211,6 @@ def _processoptions(args): # Helper for _processoptions() def _setoption(arg): - import re parts = arg.split(':') if len(parts) > 5: raise _OptionError("too many fields (max 5): %r" % (arg,)) @@ -209,11 +219,13 @@ def _setoption(arg): action, message, category, module, lineno = [s.strip() for s in parts] action = _getaction(action) - message = re.escape(message) category = _getcategory(category) - module = re.escape(module) + if message or module: + import re + if message: + message = re.escape(message) if module: - module = module + '$' + module = re.escape(module) + r'\Z' if lineno: try: lineno = int(lineno) @@ -237,26 +249,21 @@ def _getaction(action): # Helper for _setoption() def _getcategory(category): - import re if not category: return Warning - if re.match("^[a-zA-Z0-9_]+$", category): - try: - cat = eval(category) - except NameError: - raise _OptionError("unknown warning category: %r" % (category,)) from None + if '.' not in category: + import builtins as m + klass = category else: - i = category.rfind(".") - module = category[:i] - klass = category[i+1:] + module, _, klass = category.rpartition('.') try: m = __import__(module, None, None, [klass]) except ImportError: raise _OptionError("invalid module name: %r" % (module,)) from None - try: - cat = getattr(m, klass) - except AttributeError: - raise _OptionError("unknown warning category: %r" % (category,)) from None + try: + cat = getattr(m, klass) + except AttributeError: + raise _OptionError("unknown warning category: %r" % (category,)) from None if not issubclass(cat, Warning): raise _OptionError("invalid warning category: %r" % (category,)) return cat @@ -303,28 +310,16 @@ def warn(message, category=None, stacklevel=1, source=None): raise ValueError except ValueError: globals = sys.__dict__ + filename = "sys" lineno = 1 else: globals = frame.f_globals + filename = frame.f_code.co_filename lineno = frame.f_lineno if '__name__' in globals: module = globals['__name__'] else: module = "<string>" - filename = globals.get('__file__') - if filename: - fnl = filename.lower() - if fnl.endswith(".pyc"): - filename = filename[:-1] - else: - if module == "__main__": - try: - filename = sys.argv[0] - except AttributeError: - # embedded interpreters don't have sys.argv, see bug #839151 - filename = '__main__' - if not filename: - filename = module registry = globals.setdefault("__warningregistry__", {}) warn_explicit(message, category, filename, lineno, module, registry, globals, source) @@ -437,9 +432,13 @@ class catch_warnings(object): named 'warnings' and imported under that name. This argument is only useful when testing the warnings module itself. + If the 'action' argument is not None, the remaining arguments are passed + to warnings.simplefilter() as if it were called immediately on entering the + context. """ - def __init__(self, *, record=False, module=None): + def __init__(self, *, record=False, module=None, + action=None, category=Warning, lineno=0, append=False): """Specify whether to record warnings and if an alternative module should be used other than sys.modules['warnings']. @@ -450,6 +449,10 @@ def __init__(self, *, record=False, module=None): self._record = record self._module = sys.modules['warnings'] if module is None else module self._entered = False + if action is None: + self._filter = None + else: + self._filter = (action, category, lineno, append) def __repr__(self): args = [] @@ -469,6 +472,8 @@ def __enter__(self): self._module._filters_mutated() self._showwarning = self._module.showwarning self._showwarnmsg_impl = self._module._showwarnmsg_impl + if self._filter is not None: + simplefilter(*self._filter) if self._record: log = [] self._module._showwarnmsg_impl = log.append @@ -488,6 +493,27 @@ def __exit__(self, *exc_info): self._module._showwarnmsg_impl = self._showwarnmsg_impl +_DEPRECATED_MSG = "{name!r} is deprecated and slated for removal in Python {remove}" + +def _deprecated(name, message=_DEPRECATED_MSG, *, remove, _version=sys.version_info): + """Warn that *name* is deprecated or should be removed. + + RuntimeError is raised if *remove* specifies a major/minor tuple older than + the current Python version or the same version but past the alpha. + + The *message* argument is formatted with *name* and *remove* as a Python + version (e.g. "3.11"). + + """ + remove_formatted = f"{remove[0]}.{remove[1]}" + if (_version[:2] > remove) or (_version[:2] == remove and _version[3] != "alpha"): + msg = f"{name!r} was slated for removal after Python {remove_formatted} alpha" + raise RuntimeError(msg) + else: + msg = message.format(name=name, remove=remove_formatted) + warn(msg, DeprecationWarning, stacklevel=3) + + # Private utility function called by _PyErr_WarnUnawaitedCoroutine def _warn_unawaited_coroutine(coro): msg_lines = [ From 3150b4ded6b92c9dd2dc8cfcc542e784c95a7b92 Mon Sep 17 00:00:00 2001 From: DimitrisJim <d.f.hilliard@gmail.com> Date: Mon, 12 Jun 2023 00:08:49 +0300 Subject: [PATCH 4/8] Update cgi.py from Python 3.11 --- Lib/cgi.py | 46 +++++++++++++++++++++++++++++++------------- Lib/test/test_cgi.py | 19 ++++++++++-------- 2 files changed, 44 insertions(+), 21 deletions(-) diff --git a/Lib/cgi.py b/Lib/cgi.py index c22c71b387..8787567be7 100755 --- a/Lib/cgi.py +++ b/Lib/cgi.py @@ -13,6 +13,11 @@ This module defines a number of utilities for use by CGI scripts written in Python. + +The global variable maxlen can be set to an integer indicating the maximum size +of a POST request. POST requests larger than this size will result in a +ValueError being raised during parsing. The default value of this variable is 0, +meaning the request size is unlimited. """ # History @@ -41,12 +46,16 @@ import html import locale import tempfile +import warnings __all__ = ["MiniFieldStorage", "FieldStorage", "parse", "parse_multipart", "parse_header", "test", "print_exception", "print_environ", "print_form", "print_directory", "print_arguments", "print_environ_usage"] + +warnings._deprecated(__name__, remove=(3,13)) + # Logging support # =============== @@ -77,9 +86,11 @@ def initlog(*allargs): """ global log, logfile, logfp + warnings.warn("cgi.log() is deprecated as of 3.10. Use logging instead", + DeprecationWarning, stacklevel=2) if logfile and not logfp: try: - logfp = open(logfile, "a") + logfp = open(logfile, "a", encoding="locale") except OSError: pass if not logfp: @@ -115,7 +126,8 @@ def closelog(): # 0 ==> unlimited input maxlen = 0 -def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0): +def parse(fp=None, environ=os.environ, keep_blank_values=0, + strict_parsing=0, separator='&'): """Parse a query in the environment or from a file (default stdin) Arguments, all optional: @@ -134,6 +146,9 @@ def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0): strict_parsing: flag indicating what to do with parsing errors. If false (the default), errors are silently ignored. If true, errors raise a ValueError exception. + + separator: str. The symbol to use for separating the query arguments. + Defaults to &. """ if fp is None: fp = sys.stdin @@ -154,7 +169,7 @@ def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0): if environ['REQUEST_METHOD'] == 'POST': ctype, pdict = parse_header(environ['CONTENT_TYPE']) if ctype == 'multipart/form-data': - return parse_multipart(fp, pdict) + return parse_multipart(fp, pdict, separator=separator) elif ctype == 'application/x-www-form-urlencoded': clength = int(environ['CONTENT_LENGTH']) if maxlen and clength > maxlen: @@ -178,10 +193,10 @@ def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0): qs = "" environ['QUERY_STRING'] = qs # XXX Shouldn't, really return urllib.parse.parse_qs(qs, keep_blank_values, strict_parsing, - encoding=encoding) + encoding=encoding, separator=separator) -def parse_multipart(fp, pdict, encoding="utf-8", errors="replace"): +def parse_multipart(fp, pdict, encoding="utf-8", errors="replace", separator='&'): """Parse multipart input. Arguments: @@ -194,15 +209,18 @@ def parse_multipart(fp, pdict, encoding="utf-8", errors="replace"): value is a list of values for that field. For non-file fields, the value is a list of strings. """ - # RFC 2026, Section 5.1 : The "multipart" boundary delimiters are always + # RFC 2046, Section 5.1 : The "multipart" boundary delimiters are always # represented as 7bit US-ASCII. boundary = pdict['boundary'].decode('ascii') ctype = "multipart/form-data; boundary={}".format(boundary) headers = Message() headers.set_type(ctype) - headers['Content-Length'] = pdict['CONTENT-LENGTH'] + try: + headers['Content-Length'] = pdict['CONTENT-LENGTH'] + except KeyError: + pass fs = FieldStorage(fp, headers=headers, encoding=encoding, errors=errors, - environ={'REQUEST_METHOD': 'POST'}) + environ={'REQUEST_METHOD': 'POST'}, separator=separator) return {k: fs.getlist(k) for k in fs} def _parseparam(s): @@ -312,7 +330,7 @@ class FieldStorage: def __init__(self, fp=None, headers=None, outerboundary=b'', environ=os.environ, keep_blank_values=0, strict_parsing=0, limit=None, encoding='utf-8', errors='replace', - max_num_fields=None): + max_num_fields=None, separator='&'): """Constructor. Read multipart/* until last part. Arguments, all optional: @@ -360,6 +378,7 @@ def __init__(self, fp=None, headers=None, outerboundary=b'', self.keep_blank_values = keep_blank_values self.strict_parsing = strict_parsing self.max_num_fields = max_num_fields + self.separator = separator if 'REQUEST_METHOD' in environ: method = environ['REQUEST_METHOD'].upper() self.qs_on_post = None @@ -586,7 +605,7 @@ def read_urlencoded(self): query = urllib.parse.parse_qsl( qs, self.keep_blank_values, self.strict_parsing, encoding=self.encoding, errors=self.errors, - max_num_fields=self.max_num_fields) + max_num_fields=self.max_num_fields, separator=self.separator) self.list = [MiniFieldStorage(key, value) for key, value in query] self.skip_lines() @@ -602,7 +621,7 @@ def read_multi(self, environ, keep_blank_values, strict_parsing): query = urllib.parse.parse_qsl( self.qs_on_post, self.keep_blank_values, self.strict_parsing, encoding=self.encoding, errors=self.errors, - max_num_fields=self.max_num_fields) + max_num_fields=self.max_num_fields, separator=self.separator) self.list.extend(MiniFieldStorage(key, value) for key, value in query) klass = self.FieldStorageClass or self.__class__ @@ -646,7 +665,7 @@ def read_multi(self, environ, keep_blank_values, strict_parsing): else self.limit - self.bytes_read part = klass(self.fp, headers, ib, environ, keep_blank_values, strict_parsing, limit, - self.encoding, self.errors, max_num_fields) + self.encoding, self.errors, max_num_fields, self.separator) if max_num_fields is not None: max_num_fields -= 1 @@ -736,7 +755,8 @@ def read_lines_to_outerboundary(self): last_line_lfend = True _read = 0 while 1: - if self.limit is not None and _read >= self.limit: + + if self.limit is not None and 0 <= self.limit <= _read: break line = self.fp.readline(1<<16) # bytes self.bytes_read += len(line) diff --git a/Lib/test/test_cgi.py b/Lib/test/test_cgi.py index cc736572b0..43164cff31 100644 --- a/Lib/test/test_cgi.py +++ b/Lib/test/test_cgi.py @@ -1,4 +1,3 @@ -import cgi import os import sys import tempfile @@ -6,6 +5,10 @@ from collections import namedtuple from io import StringIO, BytesIO from test import support +from test.support import warnings_helper + +cgi = warnings_helper.import_deprecated("cgi") + class HackedSysModule: # The regression test will have real values in sys.argv, which @@ -50,7 +53,7 @@ def do_test(buf, method): return ComparableException(err) parse_strict_test_cases = [ - ("", ValueError("bad query field: ''")), + ("", {}), ("&", ValueError("bad query field: ''")), ("&&", ValueError("bad query field: ''")), # Should the next few really be valid? @@ -123,8 +126,6 @@ def test_parse_multipart(self): 'file': [b'Testing 123.\n'], 'title': ['']} self.assertEqual(result, expected) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_parse_multipart_without_content_length(self): POSTDATA = '''--JfISa01 Content-Disposition: form-data; name="submit-name" @@ -174,6 +175,8 @@ def test_fieldstorage_invalid(self): fs = cgi.FieldStorage(headers={'content-type':'text/plain'}) self.assertRaises(TypeError, bool, fs) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_strict(self): for orig, expect in parse_strict_test_cases: # Test basic parsing @@ -200,8 +203,6 @@ def test_strict(self): else: self.assertEqual(fs.getvalue(key), expect_val[0]) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_separator(self): parse_semicolon = [ ("x=1;y=2.0", {'x': ['1'], 'y': ['2.0']}), @@ -226,6 +227,7 @@ def test_separator(self): else: self.assertEqual(fs.getvalue(key), expect_val[0]) + @warnings_helper.ignore_warnings(category=DeprecationWarning) def test_log(self): cgi.log("Testing") @@ -578,8 +580,9 @@ def test_parse_header(self): ("form-data", {"name": "files", "filename": 'fo"o;bar'})) def test_all(self): - not_exported = {"logfile", "logfp", "initlog", "dolog", "nolog", - "closelog", "log", "maxlen", "valid_boundary"} + not_exported = { + "logfile", "logfp", "initlog", "dolog", "nolog", "closelog", "log", + "maxlen", "valid_boundary"} support.check__all__(self, cgi, not_exported=not_exported) From 854d3e4d807db8c82f621aa37711a27e7c481ae5 Mon Sep 17 00:00:00 2001 From: DimitrisJim <d.f.hilliard@gmail.com> Date: Mon, 12 Jun 2023 02:03:08 +0300 Subject: [PATCH 5/8] Update cgitb from Python 3.11 --- Lib/cgitb.py | 23 +++++++++++++++++------ Lib/test/test_cgitb.py | 15 +++++++++------ 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/Lib/cgitb.py b/Lib/cgitb.py index 4f81271be3..8ce0e833a9 100644 --- a/Lib/cgitb.py +++ b/Lib/cgitb.py @@ -31,6 +31,11 @@ import time import tokenize import traceback +import warnings +from html import escape as html_escape + +warnings._deprecated(__name__, remove=(3, 13)) + def reset(): """Return a string that resets the CGI and browser to a known state.""" @@ -105,10 +110,16 @@ def html(einfo, context=5): etype = etype.__name__ pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable date = time.ctime(time.time()) - head = '<body bgcolor="#f0f0f8">' + pydoc.html.heading( - '<big><big>%s</big></big>' % - strong(pydoc.html.escape(str(etype))), - '#ffffff', '#6622aa', pyver + '<br>' + date) + ''' + head = f''' +<body bgcolor="#f0f0f8"> +<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading"> +<tr bgcolor="#6622aa"> +<td valign=bottom> <br> +<font color="#ffffff" face="helvetica, arial"> <br> +<big><big><strong>{html_escape(str(etype))}</strong></big></big></font></td> +<td align=right valign=bottom> +<font color="#ffffff" face="helvetica, arial">{pyver}<br>{date}</font></td> +</tr></table> <p>A problem occurred in a Python script. Here is the sequence of function calls leading up to the error, in the order they occurred.</p>''' @@ -181,8 +192,8 @@ def reader(lnum=[lnum]): <!-- The above is a description of an error in a Python program, formatted - for a Web browser because the 'cgitb' module was enabled. In case you - are not reading this in a Web browser, here is the original traceback: + for a web browser because the 'cgitb' module was enabled. In case you + are not reading this in a web browser, here is the original traceback: %s --> diff --git a/Lib/test/test_cgitb.py b/Lib/test/test_cgitb.py index 5cbb054a3c..501c7fcce2 100644 --- a/Lib/test/test_cgitb.py +++ b/Lib/test/test_cgitb.py @@ -1,8 +1,9 @@ from test.support.os_helper import temp_dir from test.support.script_helper import assert_python_failure +from test.support.warnings_helper import import_deprecated import unittest import sys -import cgitb +cgitb = import_deprecated("cgitb") class TestCgitb(unittest.TestCase): @@ -31,7 +32,7 @@ def test_html(self): def test_text(self): try: raise ValueError("Hello World") - except ValueError as err: + except ValueError: text = cgitb.text(sys.exc_info()) self.assertIn("ValueError", text) self.assertIn("Hello World", text) @@ -41,8 +42,9 @@ def test_syshook_no_logdir_default_format(self): rc, out, err = assert_python_failure( '-c', ('import cgitb; cgitb.enable(logdir=%s); ' - 'raise ValueError("Hello World")') % repr(tracedir)) - out = out.decode(sys.getfilesystemencoding()) + 'raise ValueError("Hello World")') % repr(tracedir), + PYTHONIOENCODING='utf-8') + out = out.decode() self.assertIn("ValueError", out) self.assertIn("Hello World", out) self.assertIn("<strong><module></strong>", out) @@ -56,8 +58,9 @@ def test_syshook_no_logdir_text_format(self): rc, out, err = assert_python_failure( '-c', ('import cgitb; cgitb.enable(format="text", logdir=%s); ' - 'raise ValueError("Hello World")') % repr(tracedir)) - out = out.decode(sys.getfilesystemencoding()) + 'raise ValueError("Hello World")') % repr(tracedir), + PYTHONIOENCODING='utf-8') + out = out.decode() self.assertIn("ValueError", out) self.assertIn("Hello World", out) self.assertNotIn('<p>', out) From 7e3183ef160522e4c7cb41ac2c78690a343bf76a Mon Sep 17 00:00:00 2001 From: DimitrisJim <d.f.hilliard@gmail.com> Date: Mon, 12 Jun 2023 12:30:52 +0300 Subject: [PATCH 6/8] Update chunk from Python 3.11 --- Lib/chunk.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Lib/chunk.py b/Lib/chunk.py index d94dd39807..618781efd1 100644 --- a/Lib/chunk.py +++ b/Lib/chunk.py @@ -48,6 +48,10 @@ default is 1, i.e. aligned. """ +import warnings + +warnings._deprecated(__name__, remove=(3, 13)) + class Chunk: def __init__(self, file, align=True, bigendian=True, inclheader=False): import struct @@ -64,7 +68,7 @@ def __init__(self, file, align=True, bigendian=True, inclheader=False): try: self.chunksize = struct.unpack_from(strflag+'L', file.read(4))[0] except struct.error: - raise EOFError + raise EOFError from None if inclheader: self.chunksize = self.chunksize - 8 # subtract header self.size_read = 0 From 89323dadc22cd8beca5e7df645d3b5de79e08b21 Mon Sep 17 00:00:00 2001 From: DimitrisJim <d.f.hilliard@gmail.com> Date: Mon, 12 Jun 2023 14:42:10 +0300 Subject: [PATCH 7/8] Update codeop from Python 3.11 --- Lib/codeop.py | 95 ++++------- Lib/test/test_opcode.py | 356 +++++++++++++++++++++++++++++++++++++++ Lib/test/test_opcodes.py | 137 --------------- 3 files changed, 388 insertions(+), 200 deletions(-) create mode 100644 Lib/test/test_opcode.py delete mode 100644 Lib/test/test_opcodes.py diff --git a/Lib/codeop.py b/Lib/codeop.py index e29c0b38c0..2213b69f23 100644 --- a/Lib/codeop.py +++ b/Lib/codeop.py @@ -10,30 +10,6 @@ syntax error (OverflowError and ValueError can be produced by malformed literals). -Approach: - -First, check if the source consists entirely of blank lines and -comments; if so, replace it with 'pass', because the built-in -parser doesn't always do the right thing for these. - -Compile three times: as is, with \n, and with \n\n appended. If it -compiles as is, it's complete. If it compiles with one \n appended, -we expect more. If it doesn't compile either way, we compare the -error we get when compiling with \n or \n\n appended. If the errors -are the same, the code is broken. But if the errors are different, we -expect more. Not intuitive; not even guaranteed to hold in future -releases; but this matches the compiler's behavior from Python 1.4 -through 2.2, at least. - -Caveat: - -It is possible (but not likely) that the parser stops parsing with a -successful outcome before reaching the end of the source; in this -case, trailing symbols may be ignored instead of causing an error. -For example, a backslash followed by two newlines may be followed by -arbitrary garbage. This will be fixed once the API for the parser is -better. - The two interfaces are: compile_command(source, filename, symbol): @@ -64,54 +40,50 @@ __all__ = ["compile_command", "Compile", "CommandCompiler"] -PyCF_DONT_IMPLY_DEDENT = 0x200 # Matches pythonrun.h - +# The following flags match the values from Include/cpython/compile.h +# Caveat emptor: These flags are undocumented on purpose and depending +# on their effect outside the standard library is **unsupported**. +PyCF_DONT_IMPLY_DEDENT = 0x200 +PyCF_ALLOW_INCOMPLETE_INPUT = 0x4000 def _maybe_compile(compiler, source, filename, symbol): - # Check for source consisting of only blank lines and comments + # Check for source consisting of only blank lines and comments. for line in source.split("\n"): line = line.strip() if line and line[0] != '#': - break # Leave it alone + break # Leave it alone. else: if symbol != "eval": source = "pass" # Replace it with a 'pass' statement - err = err1 = err2 = None - code = code1 = code2 = None - - try: - code = compiler(source, filename, symbol) - except SyntaxError: - pass - - # Catch syntax warnings after the first compile - # to emit warnings (SyntaxWarning, DeprecationWarning) at most once. + # Disable compiler warnings when checking for incomplete input. with warnings.catch_warnings(): - warnings.simplefilter("error") - - try: - code1 = compiler(source + "\n", filename, symbol) - except SyntaxError as e: - err1 = e - + warnings.simplefilter("ignore", (SyntaxWarning, DeprecationWarning)) try: - code2 = compiler(source + "\n\n", filename, symbol) - except SyntaxError as e: - err2 = e - - try: - if code: - return code - if not code1 and repr(err1) == repr(err2): - raise err1 - finally: - err1 = err2 = None - + compiler(source, filename, symbol) + except SyntaxError: # Let other compile() errors propagate. + try: + compiler(source + "\n", filename, symbol) + return None + except SyntaxError as e: + if "incomplete input" in str(e): + return None + # fallthrough + + return compiler(source, filename, symbol) + + +def _is_syntax_error(err1, err2): + rep1 = repr(err1) + rep2 = repr(err2) + if "was never closed" in rep1 and "was never closed" in rep2: + return False + if rep1 == rep2: + return True + return False def _compile(source, filename, symbol): - return compile(source, filename, symbol, PyCF_DONT_IMPLY_DEDENT) - + return compile(source, filename, symbol, PyCF_DONT_IMPLY_DEDENT | PyCF_ALLOW_INCOMPLETE_INPUT) def compile_command(source, filename="<input>", symbol="single"): r"""Compile a command and determine whether it is incomplete. @@ -134,15 +106,13 @@ def compile_command(source, filename="<input>", symbol="single"): """ return _maybe_compile(_compile, source, filename, symbol) - class Compile: """Instances of this class behave much like the built-in compile function, but if one is used to compile text containing a future statement, it "remembers" and compiles all subsequent program texts with the statement in force.""" - def __init__(self): - self.flags = PyCF_DONT_IMPLY_DEDENT + self.flags = PyCF_DONT_IMPLY_DEDENT | PyCF_ALLOW_INCOMPLETE_INPUT def __call__(self, source, filename, symbol): codeob = compile(source, filename, symbol, self.flags, True) @@ -151,7 +121,6 @@ def __call__(self, source, filename, symbol): self.flags |= feature.compiler_flag return codeob - class CommandCompiler: """Instances of this class have __call__ methods identical in signature to compile_command; the difference is that if the diff --git a/Lib/test/test_opcode.py b/Lib/test/test_opcode.py new file mode 100644 index 0000000000..170eb1cb1d --- /dev/null +++ b/Lib/test/test_opcode.py @@ -0,0 +1,356 @@ +""" + Test cases for codeop.py + Nick Mathewson +""" +import sys +import unittest +import warnings +from test import support +from test.support import warnings_helper + +from codeop import compile_command, PyCF_DONT_IMPLY_DEDENT +import io + +if support.is_jython: + + def unify_callables(d): + for n,v in d.items(): + if hasattr(v, '__call__'): + d[n] = True + return d + +class CodeopTests(unittest.TestCase): + + def assertValid(self, str, symbol='single'): + '''succeed iff str is a valid piece of code''' + if support.is_jython: + code = compile_command(str, "<input>", symbol) + self.assertTrue(code) + if symbol == "single": + d,r = {},{} + saved_stdout = sys.stdout + sys.stdout = io.StringIO() + try: + exec(code, d) + exec(compile(str,"<input>","single"), r) + finally: + sys.stdout = saved_stdout + elif symbol == 'eval': + ctx = {'a': 2} + d = { 'value': eval(code,ctx) } + r = { 'value': eval(str,ctx) } + self.assertEqual(unify_callables(r),unify_callables(d)) + else: + expected = compile(str, "<input>", symbol, PyCF_DONT_IMPLY_DEDENT) + self.assertEqual(compile_command(str, "<input>", symbol), expected) + + def assertIncomplete(self, str, symbol='single'): + '''succeed iff str is the start of a valid piece of code''' + self.assertEqual(compile_command(str, symbol=symbol), None) + + def assertInvalid(self, str, symbol='single', is_syntax=1): + '''succeed iff str is the start of an invalid piece of code''' + try: + compile_command(str,symbol=symbol) + self.fail("No exception raised for invalid code") + except SyntaxError: + self.assertTrue(is_syntax) + except OverflowError: + self.assertTrue(not is_syntax) + + # TODO: RUSTPYTHON + @unittest.expectedFailure + def test_valid(self): + av = self.assertValid + + # special case + if not support.is_jython: + self.assertEqual(compile_command(""), + compile("pass", "<input>", 'single', + PyCF_DONT_IMPLY_DEDENT)) + self.assertEqual(compile_command("\n"), + compile("pass", "<input>", 'single', + PyCF_DONT_IMPLY_DEDENT)) + else: + av("") + av("\n") + + av("a = 1") + av("\na = 1") + av("a = 1\n") + av("a = 1\n\n") + av("\n\na = 1\n\n") + + av("def x():\n pass\n") + av("if 1:\n pass\n") + + av("\n\nif 1: pass\n") + av("\n\nif 1: pass\n\n") + + av("def x():\n\n pass\n") + av("def x():\n pass\n \n") + av("def x():\n pass\n \n") + + av("pass\n") + av("3**3\n") + + av("if 9==3:\n pass\nelse:\n pass\n") + av("if 1:\n pass\n if 1:\n pass\n else:\n pass\n") + + av("#a\n#b\na = 3\n") + av("#a\n\n \na=3\n") + av("a=3\n\n") + av("a = 9+ \\\n3") + + av("3**3","eval") + av("(lambda z: \n z**3)","eval") + + av("9+ \\\n3","eval") + av("9+ \\\n3\n","eval") + + av("\n\na**3","eval") + av("\n \na**3","eval") + av("#a\n#b\na**3","eval") + + av("\n\na = 1\n\n") + av("\n\nif 1: a=1\n\n") + + av("if 1:\n pass\n if 1:\n pass\n else:\n pass\n") + av("#a\n\n \na=3\n\n") + + av("\n\na**3","eval") + av("\n \na**3","eval") + av("#a\n#b\na**3","eval") + + av("def f():\n try: pass\n finally: [x for x in (1,2)]\n") + av("def f():\n pass\n#foo\n") + av("@a.b.c\ndef f():\n pass\n") + + # TODO: RUSTPYTHON + @unittest.expectedFailure + def test_incomplete(self): + ai = self.assertIncomplete + + ai("(a **") + ai("(a,b,") + ai("(a,b,(") + ai("(a,b,(") + ai("a = (") + ai("a = {") + ai("b + {") + + ai("print([1,\n2,") + ai("print({1:1,\n2:3,") + ai("print((1,\n2,") + + ai("if 9==3:\n pass\nelse:") + ai("if 9==3:\n pass\nelse:\n") + ai("if 9==3:\n pass\nelse:\n pass") + ai("if 1:") + ai("if 1:\n") + ai("if 1:\n pass\n if 1:\n pass\n else:") + ai("if 1:\n pass\n if 1:\n pass\n else:\n") + ai("if 1:\n pass\n if 1:\n pass\n else:\n pass") + + ai("def x():") + ai("def x():\n") + ai("def x():\n\n") + + ai("def x():\n pass") + ai("def x():\n pass\n ") + ai("def x():\n pass\n ") + ai("\n\ndef x():\n pass") + + ai("a = 9+ \\") + ai("a = 'a\\") + ai("a = '''xy") + + ai("","eval") + ai("\n","eval") + ai("(","eval") + ai("(9+","eval") + ai("9+ \\","eval") + ai("lambda z: \\","eval") + + ai("if True:\n if True:\n if True: \n") + + ai("@a(") + ai("@a(b") + ai("@a(b,") + ai("@a(b,c") + ai("@a(b,c,") + + ai("from a import (") + ai("from a import (b") + ai("from a import (b,") + ai("from a import (b,c") + ai("from a import (b,c,") + + ai("[") + ai("[a") + ai("[a,") + ai("[a,b") + ai("[a,b,") + + ai("{") + ai("{a") + ai("{a:") + ai("{a:b") + ai("{a:b,") + ai("{a:b,c") + ai("{a:b,c:") + ai("{a:b,c:d") + ai("{a:b,c:d,") + + ai("a(") + ai("a(b") + ai("a(b,") + ai("a(b,c") + ai("a(b,c,") + + ai("a[") + ai("a[b") + ai("a[b,") + ai("a[b:") + ai("a[b:c") + ai("a[b:c:") + ai("a[b:c:d") + + ai("def a(") + ai("def a(b") + ai("def a(b,") + ai("def a(b,c") + ai("def a(b,c,") + + ai("(") + ai("(a") + ai("(a,") + ai("(a,b") + ai("(a,b,") + + ai("if a:\n pass\nelif b:") + ai("if a:\n pass\nelif b:\n pass\nelse:") + + ai("while a:") + ai("while a:\n pass\nelse:") + + ai("for a in b:") + ai("for a in b:\n pass\nelse:") + + ai("try:") + ai("try:\n pass\nexcept:") + ai("try:\n pass\nfinally:") + ai("try:\n pass\nexcept:\n pass\nfinally:") + + ai("with a:") + ai("with a as b:") + + ai("class a:") + ai("class a(") + ai("class a(b") + ai("class a(b,") + ai("class a():") + + ai("[x for") + ai("[x for x in") + ai("[x for x in (") + + ai("(x for") + ai("(x for x in") + ai("(x for x in (") + + def test_invalid(self): + ai = self.assertInvalid + ai("a b") + + ai("a @") + ai("a b @") + ai("a ** @") + + ai("a = ") + ai("a = 9 +") + + ai("def x():\n\npass\n") + + ai("\n\n if 1: pass\n\npass") + + ai("a = 9+ \\\n") + ai("a = 'a\\ ") + ai("a = 'a\\\n") + + ai("a = 1","eval") + ai("]","eval") + ai("())","eval") + ai("[}","eval") + ai("9+","eval") + ai("lambda z:","eval") + ai("a b","eval") + + ai("return 2.3") + ai("if (a == 1 and b = 2): pass") + + ai("del 1") + ai("del (1,)") + ai("del [1]") + ai("del '1'") + + ai("[i for i in range(10)] = (1, 2, 3)") + + def test_invalid_exec(self): + ai = self.assertInvalid + ai("raise = 4", symbol="exec") + ai('def a-b', symbol='exec') + ai('await?', symbol='exec') + ai('=!=', symbol='exec') + ai('a await raise b', symbol='exec') + ai('a await raise b?+1', symbol='exec') + + def test_filename(self): + self.assertEqual(compile_command("a = 1\n", "abc").co_filename, + compile("a = 1\n", "abc", 'single').co_filename) + self.assertNotEqual(compile_command("a = 1\n", "abc").co_filename, + compile("a = 1\n", "def", 'single').co_filename) + + # TODO: RUSTPYTHON + @unittest.expectedFailure + def test_warning(self): + # Test that the warning is only returned once. + with warnings_helper.check_warnings( + (".*literal", SyntaxWarning), + (".*invalid", DeprecationWarning), + ) as w: + compile_command(r"'\e' is 0") + self.assertEqual(len(w.warnings), 2) + + # bpo-41520: check SyntaxWarning treated as an SyntaxError + with warnings.catch_warnings(), self.assertRaises(SyntaxError): + warnings.simplefilter('error', SyntaxWarning) + compile_command('1 is 1', symbol='exec') + + # Check DeprecationWarning treated as an SyntaxError + with warnings.catch_warnings(), self.assertRaises(SyntaxError): + warnings.simplefilter('error', DeprecationWarning) + compile_command(r"'\e'", symbol='exec') + + # TODO: RUSTPYTHON + @unittest.expectedFailure + def test_incomplete_warning(self): + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter('always') + self.assertIncomplete("'\\e' + (") + self.assertEqual(w, []) + + # TODO: RUSTPYTHON + @unittest.expectedFailure + def test_invalid_warning(self): + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter('always') + self.assertInvalid("'\\e' 1") + self.assertEqual(len(w), 1) + self.assertEqual(w[0].category, DeprecationWarning) + self.assertRegex(str(w[0].message), 'invalid escape sequence') + self.assertEqual(w[0].filename, '<input>') + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_opcodes.py b/Lib/test/test_opcodes.py deleted file mode 100644 index e880c3f1ac..0000000000 --- a/Lib/test/test_opcodes.py +++ /dev/null @@ -1,137 +0,0 @@ -# Python test set -- part 2, opcodes - -import unittest -from test import ann_module, support - -class OpcodeTest(unittest.TestCase): - - def test_try_inside_for_loop(self): - n = 0 - for i in range(10): - n = n+i - try: 1/0 - except NameError: pass - except ZeroDivisionError: pass - except TypeError: pass - try: pass - except: pass - try: pass - finally: pass - n = n+i - if n != 90: - self.fail('try inside for') - - def test_setup_annotations_line(self): - # check that SETUP_ANNOTATIONS does not create spurious line numbers - try: - with open(ann_module.__file__, encoding="utf-8") as f: - txt = f.read() - co = compile(txt, ann_module.__file__, 'exec') - self.assertEqual(co.co_firstlineno, 1) - except OSError: - pass - - def test_default_annotations_exist(self): - class C: pass - self.assertEqual(C.__annotations__, {}) - - def test_use_existing_annotations(self): - ns = {'__annotations__': {1: 2}} - exec('x: int', ns) - self.assertEqual(ns['__annotations__'], {'x': int, 1: 2}) - - def test_do_not_recreate_annotations(self): - # Don't rely on the existence of the '__annotations__' global. - with support.swap_item(globals(), '__annotations__', {}): - del globals()['__annotations__'] - class C: - del __annotations__ - with self.assertRaises(NameError): - x: int - - def test_raise_class_exceptions(self): - - class AClass(Exception): pass - class BClass(AClass): pass - class CClass(Exception): pass - class DClass(AClass): - def __init__(self, ignore): - pass - - try: raise AClass() - except: pass - - try: raise AClass() - except AClass: pass - - try: raise BClass() - except AClass: pass - - try: raise BClass() - except CClass: self.fail() - except: pass - - a = AClass() - b = BClass() - - try: - raise b - except AClass as v: - self.assertEqual(v, b) - else: - self.fail("no exception") - - # not enough arguments - ##try: raise BClass, a - ##except TypeError: pass - ##else: self.fail("no exception") - - try: raise DClass(a) - except DClass as v: - self.assertIsInstance(v, DClass) - else: - self.fail("no exception") - - def test_compare_function_objects(self): - - f = eval('lambda: None') - g = eval('lambda: None') - self.assertNotEqual(f, g) - - f = eval('lambda a: a') - g = eval('lambda a: a') - self.assertNotEqual(f, g) - - f = eval('lambda a=1: a') - g = eval('lambda a=1: a') - self.assertNotEqual(f, g) - - f = eval('lambda: 0') - g = eval('lambda: 1') - self.assertNotEqual(f, g) - - f = eval('lambda: None') - g = eval('lambda a: None') - self.assertNotEqual(f, g) - - f = eval('lambda a: None') - g = eval('lambda b: None') - self.assertNotEqual(f, g) - - f = eval('lambda a: None') - g = eval('lambda a=None: None') - self.assertNotEqual(f, g) - - f = eval('lambda a=0: None') - g = eval('lambda a=1: None') - self.assertNotEqual(f, g) - - def test_modulo_of_string_subclasses(self): - class MyString(str): - def __mod__(self, value): - return 42 - self.assertEqual(MyString() % 3, 42) - - -if __name__ == '__main__': - unittest.main() From 436be2b96aef95bac54e9c250c44d395cdf9c627 Mon Sep 17 00:00:00 2001 From: DimitrisJim <d.f.hilliard@gmail.com> Date: Mon, 12 Jun 2023 15:28:55 +0300 Subject: [PATCH 8/8] Upgrade colorsys from Python 3.11 --- Lib/colorsys.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/Lib/colorsys.py b/Lib/colorsys.py index 12b432537b..9bdc83e377 100644 --- a/Lib/colorsys.py +++ b/Lib/colorsys.py @@ -1,10 +1,14 @@ """Conversion functions between RGB and other color systems. + This modules provides two functions for each color system ABC: + rgb_to_abc(r, g, b) --> a, b, c abc_to_rgb(a, b, c) --> r, g, b + All inputs and outputs are triples of floats in the range [0.0...1.0] (with the exception of I and Q, which covers a slightly larger range). Inputs outside the valid range may cause exceptions or invalid outputs. + Supported color systems: RGB: Red, Green, Blue components YIQ: Luminance, Chrominance (used by composite video signals) @@ -71,17 +75,18 @@ def yiq_to_rgb(y, i, q): def rgb_to_hls(r, g, b): maxc = max(r, g, b) minc = min(r, g, b) - # XXX Can optimize (maxc+minc) and (maxc-minc) - l = (minc+maxc)/2.0 + sumc = (maxc+minc) + rangec = (maxc-minc) + l = sumc/2.0 if minc == maxc: return 0.0, l, 0.0 if l <= 0.5: - s = (maxc-minc) / (maxc+minc) + s = rangec / sumc else: - s = (maxc-minc) / (2.0-maxc-minc) - rc = (maxc-r) / (maxc-minc) - gc = (maxc-g) / (maxc-minc) - bc = (maxc-b) / (maxc-minc) + s = rangec / (2.0-sumc) + rc = (maxc-r) / rangec + gc = (maxc-g) / rangec + bc = (maxc-b) / rangec if r == maxc: h = bc-gc elif g == maxc: @@ -120,13 +125,14 @@ def _v(m1, m2, hue): def rgb_to_hsv(r, g, b): maxc = max(r, g, b) minc = min(r, g, b) + rangec = (maxc-minc) v = maxc if minc == maxc: return 0.0, 0.0, v - s = (maxc-minc) / maxc - rc = (maxc-r) / (maxc-minc) - gc = (maxc-g) / (maxc-minc) - bc = (maxc-b) / (maxc-minc) + s = rangec / maxc + rc = (maxc-r) / rangec + gc = (maxc-g) / rangec + bc = (maxc-b) / rangec if r == maxc: h = bc-gc elif g == maxc: