diff --git a/Lib/aifc.py b/Lib/aifc.py index 1916e7ef8..5254987e2 100644 --- a/Lib/aifc.py +++ b/Lib/aifc.py @@ -138,7 +138,11 @@ import struct 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 @@ class Aifc_read: # 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 @@ class Aifc_read: 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 @@ class Aifc_write: 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 @@ class Aifc_write: 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 @@ class Aifc_write: # 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 @@ class Aifc_write: 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 diff --git a/Lib/cgi.py b/Lib/cgi.py index c22c71b38..8787567be 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 @@ from email.message import Message 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 @@ log = initlog # The current logging function # 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 @@ class FieldStorage: 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 @@ class FieldStorage: 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 @@ class FieldStorage: 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 @@ class FieldStorage: 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 @@ class FieldStorage: 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/cgitb.py b/Lib/cgitb.py index 4f81271be..8ce0e833a 100644 --- a/Lib/cgitb.py +++ b/Lib/cgitb.py @@ -31,6 +31,11 @@ import tempfile 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 = '
' + pydoc.html.heading( - '%s' % - strong(pydoc.html.escape(str(etype))), - '#ffffff', '#6622aa', pyver + '| + +{html_escape(str(etype))} |
+
+{pyver} {date} |
+
A problem occurred in a Python script. Here is the sequence of function calls leading up to the error, in the order they occurred.
''' @@ -181,8 +192,8 @@ function calls leading up to the error, in the order they occurred.''' diff --git a/Lib/chunk.py b/Lib/chunk.py index d94dd3980..618781efd 100644 --- a/Lib/chunk.py +++ b/Lib/chunk.py @@ -48,6 +48,10 @@ specifies whether or not chunks are aligned on 2-byte boundaries. The 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 @@ class Chunk: 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 diff --git a/Lib/cmd.py b/Lib/cmd.py index 859e91096..88ee7d3dd 100644 --- a/Lib/cmd.py +++ b/Lib/cmd.py @@ -310,10 +310,10 @@ class Cmd: 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 @@ class Cmd: 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/codeop.py b/Lib/codeop.py index e29c0b38c..2213b69f2 100644 --- a/Lib/codeop.py +++ b/Lib/codeop.py @@ -10,30 +10,6 @@ and: 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 @@ _features = [getattr(__future__, fname) __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") - + warnings.simplefilter("ignore", (SyntaxWarning, DeprecationWarning)) try: - code1 = compiler(source + "\n", filename, symbol) - except SyntaxError as e: - err1 = e + 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 - try: - code2 = compiler(source + "\n\n", filename, symbol) - except SyntaxError as e: - err2 = e + return compiler(source, filename, symbol) - try: - if code: - return code - if not code1 and repr(err1) == repr(err2): - raise err1 - finally: - err1 = err2 = None +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="", symbol="single"): r"""Compile a command and determine whether it is incomplete. @@ -134,15 +106,13 @@ def compile_command(source, filename="", 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 @@ class Compile: 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/colorsys.py b/Lib/colorsys.py index 12b432537..9bdc83e37 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: diff --git a/Lib/test/test_cgi.py b/Lib/test/test_cgi.py index cc736572b..43164cff3 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 @@ import unittest 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 @@ class CgiTests(unittest.TestCase): '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 @@ Content-Length: 3 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 @@ Content-Length: 3 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 @@ Content-Length: 3 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 @@ this is the content of the fake file ("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) diff --git a/Lib/test/test_cgitb.py b/Lib/test/test_cgitb.py index 5cbb054a3..501c7fcce 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 @@ class TestCgitb(unittest.TestCase): 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 @@ class TestCgitb(unittest.TestCase): 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("<module>", out) @@ -56,8 +58,9 @@ class TestCgitb(unittest.TestCase): 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('', out)
diff --git a/Lib/test/test_cmd.py b/Lib/test/test_cmd.py
index c9ffad485..319801c71 100644
--- a/Lib/test/test_cmd.py
+++ b/Lib/test/test_cmd.py
@@ -6,10 +6,10 @@ Original by Michael Schneider
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("")