summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
Diffstat (limited to 'Lib')
-rw-r--r--Lib/_pyio.py3
-rwxr-xr-xLib/cgi.py5
-rw-r--r--Lib/collections/__init__.py16
-rw-r--r--Lib/configparser.py29
-rw-r--r--Lib/distutils/_msvccompiler.py4
-rw-r--r--Lib/distutils/ccompiler.py2
-rw-r--r--Lib/distutils/tests/test_msvccompiler.py2
-rw-r--r--Lib/encodings/quopri_codec.py2
-rw-r--r--Lib/functools.py2
-rw-r--r--Lib/html/parser.py10
-rw-r--r--Lib/http/server.py3
-rw-r--r--Lib/idlelib/NEWS.txt13
-rwxr-xr-xLib/idlelib/PyShell.py1
-rw-r--r--Lib/idlelib/ScriptBinding.py2
-rw-r--r--Lib/idlelib/StackViewer.py11
-rw-r--r--Lib/idlelib/configDialog.py85
-rw-r--r--Lib/idlelib/idle_test/test_warning.py9
-rw-r--r--Lib/idlelib/idlever.py12
-rwxr-xr-xLib/pdb.py3
-rw-r--r--Lib/shutil.py9
-rw-r--r--Lib/test/test_binascii.py3
-rw-r--r--Lib/test/test_cgi.py18
-rw-r--r--Lib/test/test_codecs.py8
-rw-r--r--Lib/test/test_collections.py12
-rw-r--r--Lib/test/test_configparser.py7
-rw-r--r--Lib/test/test_functools.py18
-rw-r--r--Lib/test/test_gdb.py52
-rw-r--r--Lib/test/test_glob.py6
-rw-r--r--Lib/test/test_htmlparser.py15
-rw-r--r--Lib/test/test_itertools.py10
-rw-r--r--Lib/test/test_mmap.py5
-rw-r--r--Lib/test/test_os.py27
-rw-r--r--Lib/test/test_pdb.py12
-rw-r--r--Lib/test/test_pep277.py8
-rw-r--r--Lib/test/test_popen.py4
-rw-r--r--Lib/test/test_posix.py8
-rw-r--r--Lib/test/test_posixpath.py37
-rw-r--r--Lib/test/test_py_compile.py8
-rw-r--r--Lib/test/test_pyexpat.py59
-rw-r--r--Lib/test/test_shutil.py247
-rw-r--r--Lib/test/test_subprocess.py7
-rw-r--r--Lib/test/test_sysconfig.py8
-rw-r--r--Lib/test/test_tarfile.py12
-rw-r--r--Lib/test/test_unicode_file.py8
-rw-r--r--Lib/test/test_warnings/__init__.py6
-rw-r--r--Lib/test/test_wsgiref.py5
-rw-r--r--Lib/test/test_zlib.py8
-rw-r--r--Lib/unittest/case.py25
-rw-r--r--Lib/unittest/main.py2
-rw-r--r--Lib/unittest/mock.py5
-rw-r--r--Lib/unittest/test/test_assertions.py15
-rw-r--r--Lib/unittest/test/test_skipping.py33
-rw-r--r--Lib/unittest/test/testmock/testmock.py3
53 files changed, 590 insertions, 334 deletions
diff --git a/Lib/_pyio.py b/Lib/_pyio.py
index 50ad9ff..f47df91 100644
--- a/Lib/_pyio.py
+++ b/Lib/_pyio.py
@@ -8,12 +8,13 @@ import codecs
import errno
import array
import stat
+import sys
# Import _thread instead of threading to reduce startup cost
try:
from _thread import allocate_lock as Lock
except ImportError:
from _dummy_thread import allocate_lock as Lock
-if os.name == 'win32':
+if sys.platform in {'win32', 'cygwin'}:
from msvcrt import setmode as _setmode
else:
_setmode = None
diff --git a/Lib/cgi.py b/Lib/cgi.py
index 4be28ba..26d2544 100755
--- a/Lib/cgi.py
+++ b/Lib/cgi.py
@@ -720,6 +720,11 @@ class FieldStorage:
self.bytes_read += len(hdr_text)
parser.feed(hdr_text.decode(self.encoding, self.errors))
headers = parser.close()
+
+ # Some clients add Content-Length for part headers, ignore them
+ if 'content-length' in headers:
+ del headers['content-length']
+
part = klass(self.fp, headers, ib, environ, keep_blank_values,
strict_parsing,self.limit-self.bytes_read,
self.encoding, self.errors)
diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py
index 80dc4f6..943cbca 100644
--- a/Lib/collections/__init__.py
+++ b/Lib/collections/__init__.py
@@ -320,23 +320,14 @@ class {typename}(tuple):
'Return a nicely formatted representation string'
return self.__class__.__name__ + '({repr_fmt})' % self
- @property
- def __dict__(self):
- 'A new OrderedDict mapping field names to their values'
- return OrderedDict(zip(self._fields, self))
-
def _asdict(self):
'Return a new OrderedDict which maps field names to their values.'
- return self.__dict__
+ return OrderedDict(zip(self._fields, self))
def __getnewargs__(self):
'Return self as a plain tuple. Used by copy and pickle.'
return tuple(self)
- def __getstate__(self):
- 'Exclude the OrderedDict from pickling'
- return None
-
{field_defs}
"""
@@ -901,9 +892,8 @@ class ChainMap(MutableMapping):
__copy__ = copy
def new_child(self, m=None): # like Django's Context.push()
- '''
- New ChainMap with a new map followed by all previous maps. If no
- map is provided, an empty dict is used.
+ '''New ChainMap with a new map followed by all previous maps.
+ If no map is provided, an empty dict is used.
'''
if m is None:
m = {}
diff --git a/Lib/configparser.py b/Lib/configparser.py
index ecd0660..3a9fb56 100644
--- a/Lib/configparser.py
+++ b/Lib/configparser.py
@@ -263,12 +263,9 @@ class InterpolationMissingOptionError(InterpolationError):
"""A string substitution required a setting which was not available."""
def __init__(self, option, section, rawval, reference):
- msg = ("Bad value substitution:\n"
- "\tsection: [%s]\n"
- "\toption : %s\n"
- "\tkey : %s\n"
- "\trawval : %s\n"
- % (section, option, reference, rawval))
+ msg = ("Bad value substitution: option {!r} in section {!r} contains "
+ "an interpolation key {!r} which is not a valid option name. "
+ "Raw value: {!r}".format(option, section, reference, rawval))
InterpolationError.__init__(self, option, section, msg)
self.reference = reference
self.args = (option, section, rawval, reference)
@@ -286,11 +283,11 @@ class InterpolationDepthError(InterpolationError):
"""Raised when substitutions are nested too deeply."""
def __init__(self, option, section, rawval):
- msg = ("Value interpolation too deeply recursive:\n"
- "\tsection: [%s]\n"
- "\toption : %s\n"
- "\trawval : %s\n"
- % (section, option, rawval))
+ msg = ("Recursion limit exceeded in value substitution: option {!r} "
+ "in section {!r} contains an interpolation key which "
+ "cannot be substituted in {} steps. Raw value: {!r}"
+ "".format(option, section, MAX_INTERPOLATION_DEPTH,
+ rawval))
InterpolationError.__init__(self, option, section, msg)
self.args = (option, section, rawval)
@@ -406,8 +403,9 @@ class BasicInterpolation(Interpolation):
def _interpolate_some(self, parser, option, accum, rest, section, map,
depth):
+ rawval = parser.get(section, option, raw=True, fallback=rest)
if depth > MAX_INTERPOLATION_DEPTH:
- raise InterpolationDepthError(option, section, rest)
+ raise InterpolationDepthError(option, section, rawval)
while rest:
p = rest.find("%")
if p < 0:
@@ -432,7 +430,7 @@ class BasicInterpolation(Interpolation):
v = map[var]
except KeyError:
raise InterpolationMissingOptionError(
- option, section, rest, var) from None
+ option, section, rawval, var) from None
if "%" in v:
self._interpolate_some(parser, option, accum, v,
section, map, depth + 1)
@@ -466,8 +464,9 @@ class ExtendedInterpolation(Interpolation):
def _interpolate_some(self, parser, option, accum, rest, section, map,
depth):
+ rawval = parser.get(section, option, raw=True, fallback=rest)
if depth > MAX_INTERPOLATION_DEPTH:
- raise InterpolationDepthError(option, section, rest)
+ raise InterpolationDepthError(option, section, rawval)
while rest:
p = rest.find("$")
if p < 0:
@@ -504,7 +503,7 @@ class ExtendedInterpolation(Interpolation):
"More than one ':' found: %r" % (rest,))
except (KeyError, NoSectionError, NoOptionError):
raise InterpolationMissingOptionError(
- option, section, rest, ":".join(path)) from None
+ option, section, rawval, ":".join(path)) from None
if "$" in v:
self._interpolate_some(parser, opt, accum, v, sect,
dict(parser.items(sect, raw=True)),
diff --git a/Lib/distutils/_msvccompiler.py b/Lib/distutils/_msvccompiler.py
index 82b78a0..03a5f10 100644
--- a/Lib/distutils/_msvccompiler.py
+++ b/Lib/distutils/_msvccompiler.py
@@ -100,7 +100,7 @@ def _get_vc_env(plat_spec):
(line.partition('=') for line in out.splitlines())
if key and value
}
-
+
if vcruntime:
env['py_vcruntime_redist'] = vcruntime
return env
@@ -236,7 +236,7 @@ class MSVCCompiler(CCompiler) :
'/nologo', '/Ox', '/W3', '/GL', '/DNDEBUG'
]
self.compile_options.append('/MD' if self._vcruntime_redist else '/MT')
-
+
self.compile_options_debug = [
'/nologo', '/Od', '/MDd', '/Zi', '/W3', '/D_DEBUG'
]
diff --git a/Lib/distutils/ccompiler.py b/Lib/distutils/ccompiler.py
index b7df394..82fd7d5 100644
--- a/Lib/distutils/ccompiler.py
+++ b/Lib/distutils/ccompiler.py
@@ -752,7 +752,7 @@ class CCompiler:
raise NotImplementedError
def library_option(self, lib):
- """Return the compiler option to add 'dir' to the list of libraries
+ """Return the compiler option to add 'lib' to the list of libraries
linked into the shared library or executable.
"""
raise NotImplementedError
diff --git a/Lib/distutils/tests/test_msvccompiler.py b/Lib/distutils/tests/test_msvccompiler.py
index 874d603..c4d911f 100644
--- a/Lib/distutils/tests/test_msvccompiler.py
+++ b/Lib/distutils/tests/test_msvccompiler.py
@@ -77,7 +77,7 @@ class msvccompilerTestCase(support.TempdirManager,
compiler.initialize()
dll = compiler._vcruntime_redist
self.assertTrue(os.path.isfile(dll))
-
+
compiler._copy_vcruntime(tempdir)
self.assertFalse(os.path.isfile(os.path.join(
diff --git a/Lib/encodings/quopri_codec.py b/Lib/encodings/quopri_codec.py
index 0533dbe..496cb76 100644
--- a/Lib/encodings/quopri_codec.py
+++ b/Lib/encodings/quopri_codec.py
@@ -11,7 +11,7 @@ def quopri_encode(input, errors='strict'):
assert errors == 'strict'
f = BytesIO(input)
g = BytesIO()
- quopri.encode(f, g, 1)
+ quopri.encode(f, g, quotetabs=True)
return (g.getvalue(), len(input))
def quopri_decode(input, errors='strict'):
diff --git a/Lib/functools.py b/Lib/functools.py
index 09df068..06a4ff1 100644
--- a/Lib/functools.py
+++ b/Lib/functools.py
@@ -567,7 +567,7 @@ def _c3_merge(sequences):
break # reject the current head, it appears later
else:
break
- if not candidate:
+ if candidate is None:
raise RuntimeError("Inconsistent hierarchy")
result.append(candidate)
# remove the chosen candidate
diff --git a/Lib/html/parser.py b/Lib/html/parser.py
index 390d4cc..43e6411 100644
--- a/Lib/html/parser.py
+++ b/Lib/html/parser.py
@@ -139,7 +139,15 @@ class HTMLParser(_markupbase.ParserBase):
if self.convert_charrefs and not self.cdata_elem:
j = rawdata.find('<', i)
if j < 0:
- if not end:
+ # if we can't find the next <, either we are at the end
+ # or there's more text incoming. If the latter is True,
+ # we can't pass the text to handle_data in case we have
+ # a charref cut in half at end. Try to determine if
+ # this is the case before proceding by looking for an
+ # & near the end and see if it's followed by a space or ;.
+ amppos = rawdata.rfind('&', max(i, n-34))
+ if (amppos >= 0 and
+ not re.compile(r'[\s;]').search(rawdata, amppos)):
break # wait till we get all the text
j = n
else:
diff --git a/Lib/http/server.py b/Lib/http/server.py
index fd13be3..6a0e6fe 100644
--- a/Lib/http/server.py
+++ b/Lib/http/server.py
@@ -1167,8 +1167,7 @@ def test(HandlerClass=BaseHTTPRequestHandler,
ServerClass=HTTPServer, protocol="HTTP/1.0", port=8000, bind=""):
"""Test the HTTP request handler class.
- This runs an HTTP server on port 8000 (or the first command line
- argument).
+ This runs an HTTP server on port 8000 (or the port argument).
"""
server_address = (bind, port)
diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt
index 2d8ce54..be063c4 100644
--- a/Lib/idlelib/NEWS.txt
+++ b/Lib/idlelib/NEWS.txt
@@ -2,6 +2,19 @@ What's New in IDLE 3.5.0?
=========================
*Release date: 2015-09-13* ??
+- Issue #23672: Allow Idle to edit and run files with astral chars in name.
+ Patch by Mohd Sanad Zaki Rizvi.
+
+- Issue 24745: Idle editor default font. Switch from Courier to
+ platform-sensitive TkFixedFont. This should not affect current customized
+ font selections. If there is a problem, edit $HOME/.idlerc/config-main.cfg
+ and remove 'fontxxx' entries from [Editor Window]. Patch by Mark Roseman.
+
+- Issue #21192: Idle editor. When a file is run, put its name in the restart bar.
+ Do not print false prompts. Original patch by Adnan Umer.
+
+- Issue #13884: Idle menus. Remove tearoff lines. Patch by Roger Serwy.
+
- Issue #23184: remove unused names and imports in idlelib.
Initial patch by Al Sweigart.
diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py
index 5854cf9..90fc689 100755
--- a/Lib/idlelib/PyShell.py
+++ b/Lib/idlelib/PyShell.py
@@ -1043,6 +1043,7 @@ class PyShell(OutputWindow):
self.write("Python %s on %s\n%s\n%s" %
(sys.version, sys.platform, self.COPYRIGHT, nosub))
+ self.text.focus_force()
self.showprompt()
import tkinter
tkinter._default_root = None # 03Jan04 KBK What's this?
diff --git a/Lib/idlelib/ScriptBinding.py b/Lib/idlelib/ScriptBinding.py
index e8cb2fc..e5636df 100644
--- a/Lib/idlelib/ScriptBinding.py
+++ b/Lib/idlelib/ScriptBinding.py
@@ -69,7 +69,7 @@ class ScriptBinding:
try:
tabnanny.process_tokens(tokenize.generate_tokens(f.readline))
except tokenize.TokenError as msg:
- msgtxt, (lineno, start) = msg
+ msgtxt, (lineno, start) = msg.args
self.editwin.gotoline(lineno)
self.errorbox("Tabnanny Tokenizing Error",
"Token Error: %s" % msgtxt)
diff --git a/Lib/idlelib/StackViewer.py b/Lib/idlelib/StackViewer.py
index b1e5e26..ccc755c 100644
--- a/Lib/idlelib/StackViewer.py
+++ b/Lib/idlelib/StackViewer.py
@@ -10,8 +10,7 @@ from idlelib.PyShell import PyShellFileList
def StackBrowser(root, flist=None, tb=None, top=None):
if top is None:
- from tkinter import Toplevel
- top = Toplevel(root)
+ top = tk.Toplevel(root)
sc = ScrolledCanvas(top, bg="white", highlightthickness=0)
sc.frame.pack(expand=1, fill="both")
item = StackTreeItem(flist, tb)
@@ -108,12 +107,9 @@ class VariablesTreeItem(ObjectTreeItem):
def IsExpandable(self):
return len(self.object) > 0
- def keys(self):
- return list(self.object.keys())
-
def GetSubList(self):
sublist = []
- for key in self.keys():
+ for key in self.object.keys():
try:
value = self.object[key]
except KeyError:
@@ -124,6 +120,9 @@ class VariablesTreeItem(ObjectTreeItem):
sublist.append(item)
return sublist
+ def keys(self): # unused, left for possible 3rd party use
+ return list(self.object.keys())
+
def _stack_viewer(parent):
root = tk.Tk()
root.title("Test StackViewer")
diff --git a/Lib/idlelib/configDialog.py b/Lib/idlelib/configDialog.py
index 9ed6336..b70cb60 100644
--- a/Lib/idlelib/configDialog.py
+++ b/Lib/idlelib/configDialog.py
@@ -1201,9 +1201,6 @@ class VerticalScrolledFrame(Frame):
# update the scrollbars to match the size of the inner frame
size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
canvas.config(scrollregion="0 0 %s %s" % size)
- if interior.winfo_reqwidth() != canvas.winfo_width():
- # update the canvas's width to fit the inner frame
- canvas.config(width=interior.winfo_reqwidth())
interior.bind('<Configure>', _configure_interior)
def _configure_canvas(event):
@@ -1323,38 +1320,56 @@ class ConfigExtensionsDialog(Toplevel):
def create_widgets(self):
"""Create the dialog's widgets."""
+ self.extension_names = StringVar(self)
self.rowconfigure(0, weight=1)
- self.rowconfigure(1, weight=0)
- self.columnconfigure(0, weight=1)
-
- # create the tabbed pages
- self.tabbed_page_set = TabbedPageSet(
- self, page_names=self.extensions.keys(),
- n_rows=None, max_tabs_per_row=5,
- page_class=TabbedPageSet.PageRemove)
- self.tabbed_page_set.grid(row=0, column=0, sticky=NSEW)
- for ext_name in self.extensions:
- self.create_tab_page(ext_name)
-
- self.create_action_buttons().grid(row=1)
+ self.columnconfigure(2, weight=1)
+ self.extension_list = Listbox(self, listvariable=self.extension_names,
+ selectmode='browse')
+ self.extension_list.bind('<<ListboxSelect>>', self.extension_selected)
+ scroll = Scrollbar(self, command=self.extension_list.yview)
+ self.extension_list.yscrollcommand=scroll.set
+ self.details_frame = LabelFrame(self, width=250, height=250)
+ self.extension_list.grid(column=0, row=0, sticky='nws')
+ scroll.grid(column=1, row=0, sticky='ns')
+ self.details_frame.grid(column=2, row=0, sticky='nsew', padx=[10, 0])
+ self.configure(padx=10, pady=10)
+ self.config_frame = {}
+ self.current_extension = None
+
+ self.outerframe = self # TEMPORARY
+ self.tabbed_page_set = self.extension_list # TEMPORARY
+
+ # create the individual pages
+ ext_names = ''
+ for ext_name in sorted(self.extensions):
+ self.create_extension_frame(ext_name)
+ ext_names = ext_names + '{' + ext_name + '} '
+ self.extension_names.set(ext_names)
+ self.extension_list.selection_set(0)
+ self.extension_selected(None)
+ self.create_action_buttons().grid(row=1, columnspan=3)
+
+ def extension_selected(self, event):
+ newsel = self.extension_list.curselection()
+ if newsel:
+ newsel = self.extension_list.get(newsel)
+ if newsel is None or newsel != self.current_extension:
+ if self.current_extension:
+ self.details_frame.config(text='')
+ self.config_frame[self.current_extension].grid_forget()
+ self.current_extension = None
+ if newsel:
+ self.details_frame.config(text=newsel)
+ self.config_frame[newsel].grid(column=0, row=0, sticky='nsew')
+ self.current_extension = newsel
create_action_buttons = ConfigDialog.create_action_buttons
- def create_tab_page(self, ext_name):
- """Create the page for an extension."""
-
- page = LabelFrame(self.tabbed_page_set.pages[ext_name].frame,
- border=2, padx=2, relief=GROOVE,
- text=' %s ' % ext_name)
- page.pack(fill=BOTH, expand=True, padx=12, pady=2)
-
- # create the scrollable frame which will contain the entries
- scrolled_frame = VerticalScrolledFrame(page, pady=2, height=250)
- scrolled_frame.pack(side=BOTTOM, fill=BOTH, expand=TRUE)
- entry_area = scrolled_frame.interior
- entry_area.columnconfigure(0, weight=0)
- entry_area.columnconfigure(1, weight=1)
-
+ def create_extension_frame(self, ext_name):
+ """Create a frame holding the widgets to configure one extension"""
+ f = VerticalScrolledFrame(self.details_frame, height=250, width=250)
+ self.config_frame[ext_name] = f
+ entry_area = f.interior
# create an entry for each configuration option
for row, opt in enumerate(self.extensions[ext_name]):
# create a row with a label and entry/checkbutton
@@ -1365,15 +1380,15 @@ class ConfigExtensionsDialog(Toplevel):
Checkbutton(entry_area, textvariable=var, variable=var,
onvalue='True', offvalue='False',
indicatoron=FALSE, selectcolor='', width=8
- ).grid(row=row, column=1, sticky=W, padx=7)
+ ).grid(row=row, column=1, sticky=W, padx=7)
elif opt['type'] == 'int':
Entry(entry_area, textvariable=var, validate='key',
- validatecommand=(self.is_int, '%P')
- ).grid(row=row, column=1, sticky=NSEW, padx=7)
+ validatecommand=(self.is_int, '%P')
+ ).grid(row=row, column=1, sticky=NSEW, padx=7)
else:
Entry(entry_area, textvariable=var
- ).grid(row=row, column=1, sticky=NSEW, padx=7)
+ ).grid(row=row, column=1, sticky=NSEW, padx=7)
return
diff --git a/Lib/idlelib/idle_test/test_warning.py b/Lib/idlelib/idle_test/test_warning.py
index 18627dd..54ac993 100644
--- a/Lib/idlelib/idle_test/test_warning.py
+++ b/Lib/idlelib/idle_test/test_warning.py
@@ -68,6 +68,15 @@ class ShellWarnTest(unittest.TestCase):
'Test', UserWarning, 'test_warning.py', 99, f, 'Line of code')
self.assertEqual(shellmsg.splitlines(), f.getvalue().splitlines())
+class ImportWarnTest(unittest.TestCase):
+ def test_idlever(self):
+ with warnings.catch_warnings(record=True) as w:
+ warnings.simplefilter("always")
+ import idlelib.idlever
+ self.assertEqual(len(w), 1)
+ self.assertTrue(issubclass(w[-1].category, DeprecationWarning))
+ self.assertIn("version", str(w[-1].message))
+
if __name__ == '__main__':
unittest.main(verbosity=2, exit=False)
diff --git a/Lib/idlelib/idlever.py b/Lib/idlelib/idlever.py
index 563d933..13c68b8 100644
--- a/Lib/idlelib/idlever.py
+++ b/Lib/idlelib/idlever.py
@@ -1,4 +1,12 @@
-"""Unused by Idle: there is no separate Idle version anymore.
-Kept only for possible existing extension use."""
+"""
+The separate Idle version was eliminated years ago;
+idlelib.idlever is no longer used by Idle
+and will be removed in 3.6 or later. Use
+ from sys import version
+ IDLE_VERSION = version[:version.index(' ')]
+"""
+# Kept for now only for possible existing extension use
+import warnings as w
+w.warn(__doc__, DeprecationWarning)
from sys import version
IDLE_VERSION = version[:version.index(' ')]
diff --git a/Lib/pdb.py b/Lib/pdb.py
index cf2edbf..4cba8a0 100755
--- a/Lib/pdb.py
+++ b/Lib/pdb.py
@@ -1669,6 +1669,9 @@ def main():
# In most cases SystemExit does not warrant a post-mortem session.
print("The program exited via sys.exit(). Exit status:", end=' ')
print(sys.exc_info()[1])
+ except SyntaxError:
+ traceback.print_exc()
+ sys.exit(1)
except:
traceback.print_exc()
print("Uncaught exception. Entering post mortem debugging")
diff --git a/Lib/shutil.py b/Lib/shutil.py
index a5da587..3f4b6bf 100644
--- a/Lib/shutil.py
+++ b/Lib/shutil.py
@@ -679,7 +679,16 @@ def _make_zipfile(base_name, base_dir, verbose=0, dry_run=0, logger=None):
if not dry_run:
with zipfile.ZipFile(zip_filename, "w",
compression=zipfile.ZIP_DEFLATED) as zf:
+ path = os.path.normpath(base_dir)
+ zf.write(path, path)
+ if logger is not None:
+ logger.info("adding '%s'", path)
for dirpath, dirnames, filenames in os.walk(base_dir):
+ for name in sorted(dirnames):
+ path = os.path.normpath(os.path.join(dirpath, name))
+ zf.write(path, path)
+ if logger is not None:
+ logger.info("adding '%s'", path)
for name in filenames:
path = os.path.normpath(os.path.join(dirpath, name))
if os.path.isfile(path):
diff --git a/Lib/test/test_binascii.py b/Lib/test/test_binascii.py
index 4e67887..8367afe 100644
--- a/Lib/test/test_binascii.py
+++ b/Lib/test/test_binascii.py
@@ -178,6 +178,8 @@ class BinASCIITest(unittest.TestCase):
self.assertEqual(binascii.unhexlify(self.type2test(t)), u)
def test_qp(self):
+ binascii.a2b_qp(data=b"", header=False) # Keyword arguments allowed
+
# A test for SF bug 534347 (segfaults without the proper fix)
try:
binascii.a2b_qp(b"", **{1:1})
@@ -185,6 +187,7 @@ class BinASCIITest(unittest.TestCase):
pass
else:
self.fail("binascii.a2b_qp(**{1:1}) didn't raise TypeError")
+
self.assertEqual(binascii.a2b_qp(b"= "), b"= ")
self.assertEqual(binascii.a2b_qp(b"=="), b"=")
self.assertEqual(binascii.a2b_qp(b"=AX"), b"=AX")
diff --git a/Lib/test/test_cgi.py b/Lib/test/test_cgi.py
index a7a9d02..ab9f6ab 100644
--- a/Lib/test/test_cgi.py
+++ b/Lib/test/test_cgi.py
@@ -326,6 +326,24 @@ Content-Type: text/plain
got = getattr(files[x], k)
self.assertEqual(got, exp)
+ def test_fieldstorage_part_content_length(self):
+ BOUNDARY = "JfISa01"
+ POSTDATA = """--JfISa01
+Content-Disposition: form-data; name="submit-name"
+Content-Length: 5
+
+Larry
+--JfISa01"""
+ env = {
+ 'REQUEST_METHOD': 'POST',
+ 'CONTENT_TYPE': 'multipart/form-data; boundary={}'.format(BOUNDARY),
+ 'CONTENT_LENGTH': str(len(POSTDATA))}
+ fp = BytesIO(POSTDATA.encode('latin-1'))
+ fs = cgi.FieldStorage(fp, environ=env, encoding="latin-1")
+ self.assertEqual(len(fs.list), 1)
+ self.assertEqual(fs.list[0].name, 'submit-name')
+ self.assertEqual(fs.list[0].value, 'Larry')
+
def test_fieldstorage_as_context_manager(self):
fp = BytesIO(b'x' * 10)
env = {'REQUEST_METHOD': 'PUT'}
diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py
index eab07c9..a4a6f95 100644
--- a/Lib/test/test_codecs.py
+++ b/Lib/test/test_codecs.py
@@ -2684,6 +2684,14 @@ class TransformCodecTest(unittest.TestCase):
info = codecs.lookup(alias)
self.assertEqual(info.name, expected_name)
+ def test_quopri_stateless(self):
+ # Should encode with quotetabs=True
+ encoded = codecs.encode(b"space tab\teol \n", "quopri-codec")
+ self.assertEqual(encoded, b"space=20tab=09eol=20\n")
+ # But should still support unescaped tabs and spaces
+ unescaped = b"space tab eol\n"
+ self.assertEqual(codecs.decode(unescaped, "quopri-codec"), unescaped)
+
def test_uu_invalid(self):
# Missing "begin" line
self.assertRaises(ValueError, codecs.decode, b"", "uu-codec")
diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py
index c2d03ee..4124f91 100644
--- a/Lib/test/test_collections.py
+++ b/Lib/test/test_collections.py
@@ -257,7 +257,6 @@ class TestNamedTuple(unittest.TestCase):
self.assertEqual(p._fields, ('x', 'y')) # test _fields attribute
self.assertEqual(p._replace(x=1), (1, 22)) # test _replace method
self.assertEqual(p._asdict(), dict(x=11, y=22)) # test _asdict method
- self.assertEqual(vars(p), p._asdict()) # verify that vars() works
try:
p._replace(x=1, error=2)
@@ -412,6 +411,17 @@ class TestNamedTuple(unittest.TestCase):
globals().pop('NTColor', None) # clean-up after this test
+ def test_namedtuple_subclass_issue_24931(self):
+ class Point(namedtuple('_Point', ['x', 'y'])):
+ pass
+
+ a = Point(3, 4)
+ self.assertEqual(a._asdict(), OrderedDict([('x', 3), ('y', 4)]))
+
+ a.w = 5
+ self.assertEqual(a.__dict__, {'w': 5})
+
+
################################################################################
### Abstract Base Classes
################################################################################
diff --git a/Lib/test/test_configparser.py b/Lib/test/test_configparser.py
index 470d2cd..71a8f3f 100644
--- a/Lib/test/test_configparser.py
+++ b/Lib/test/test_configparser.py
@@ -847,7 +847,8 @@ class ConfigParserTestCase(BasicTestCase, unittest.TestCase):
"something with lots of interpolation (10 steps)")
e = self.get_error(cf, configparser.InterpolationDepthError, "Foo", "bar11")
if self.interpolation == configparser._UNSET:
- self.assertEqual(e.args, ("bar11", "Foo", "%(with1)s"))
+ self.assertEqual(e.args, ("bar11", "Foo",
+ "something %(with11)s lots of interpolation (11 steps)"))
elif isinstance(self.interpolation, configparser.LegacyInterpolation):
self.assertEqual(e.args, ("bar11", "Foo",
"something %(with11)s lots of interpolation (11 steps)"))
@@ -861,7 +862,7 @@ class ConfigParserTestCase(BasicTestCase, unittest.TestCase):
self.assertEqual(e.option, "name")
if self.interpolation == configparser._UNSET:
self.assertEqual(e.args, ('name', 'Interpolation Error',
- '', 'reference'))
+ '%(reference)s', 'reference'))
elif isinstance(self.interpolation, configparser.LegacyInterpolation):
self.assertEqual(e.args, ('name', 'Interpolation Error',
'%(reference)s', 'reference'))
@@ -1177,7 +1178,7 @@ class ConfigParserTestCaseExtendedInterpolation(BasicTestCase, unittest.TestCase
with self.assertRaises(exception_class) as cm:
cf['interpolated']['$trying']
self.assertEqual(cm.exception.reference, 'dollars:${sick')
- self.assertEqual(cm.exception.args[2], '}') #rawval
+ self.assertEqual(cm.exception.args[2], '${dollars:${sick}}') #rawval
def test_case_sensitivity_basic(self):
ini = textwrap.dedent("""
diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py
index ac211c4..ae929ec 100644
--- a/Lib/test/test_functools.py
+++ b/Lib/test/test_functools.py
@@ -1491,6 +1491,24 @@ class TestSingleDispatch(unittest.TestCase):
many_abcs = [c.Mapping, c.Sized, c.Callable, c.Container, c.Iterable]
self.assertEqual(mro(X, abcs=many_abcs), expected)
+ def test_false_meta(self):
+ # see issue23572
+ class MetaA(type):
+ def __len__(self):
+ return 0
+ class A(metaclass=MetaA):
+ pass
+ class AA(A):
+ pass
+ @functools.singledispatch
+ def fun(a):
+ return 'base A'
+ @fun.register(A)
+ def _(a):
+ return 'fun A'
+ aa = AA()
+ self.assertEqual(fun(aa), 'fun A')
+
def test_mro_conflicts(self):
c = collections
@functools.singledispatch
diff --git a/Lib/test/test_gdb.py b/Lib/test/test_gdb.py
index 0322677..db777be 100644
--- a/Lib/test/test_gdb.py
+++ b/Lib/test/test_gdb.py
@@ -21,19 +21,34 @@ except ImportError:
from test import support
from test.support import run_unittest, findfile, python_is_optimized
-try:
- gdb_version, _ = subprocess.Popen(["gdb", "-nx", "--version"],
- stdout=subprocess.PIPE).communicate()
-except OSError:
- # This is what "no gdb" looks like. There may, however, be other
- # errors that manifest this way too.
- raise unittest.SkipTest("Couldn't find gdb on the path")
-gdb_version_number = re.search(b"^GNU gdb [^\d]*(\d+)\.(\d)", gdb_version)
-gdb_major_version = int(gdb_version_number.group(1))
-gdb_minor_version = int(gdb_version_number.group(2))
+def get_gdb_version():
+ try:
+ proc = subprocess.Popen(["gdb", "-nx", "--version"],
+ stdout=subprocess.PIPE,
+ universal_newlines=True)
+ with proc:
+ version = proc.communicate()[0]
+ except OSError:
+ # This is what "no gdb" looks like. There may, however, be other
+ # errors that manifest this way too.
+ raise unittest.SkipTest("Couldn't find gdb on the path")
+
+ # Regex to parse:
+ # 'GNU gdb (GDB; SUSE Linux Enterprise 12) 7.7\n' -> 7.7
+ # 'GNU gdb (GDB) Fedora 7.9.1-17.fc22\n' -> 7.9
+ # 'GNU gdb 6.1.1 [FreeBSD]\n' -> 6.1
+ # 'GNU gdb (GDB) Fedora (7.5.1-37.fc18)\n' -> 7.5
+ match = re.search(r"^GNU gdb.*?\b(\d+)\.(\d)", version)
+ if match is None:
+ raise Exception("unable to parse GDB version: %r" % version)
+ return (version, int(match.group(1)), int(match.group(2)))
+
+gdb_version, gdb_major_version, gdb_minor_version = get_gdb_version()
if gdb_major_version < 7:
- raise unittest.SkipTest("gdb versions before 7.0 didn't support python embedding"
- " Saw:\n" + gdb_version.decode('ascii', 'replace'))
+ raise unittest.SkipTest("gdb versions before 7.0 didn't support python "
+ "embedding. Saw %s.%s:\n%s"
+ % (gdb_major_version, gdb_minor_version,
+ gdb_version))
if not sysconfig.is_python_build():
raise unittest.SkipTest("test_gdb only works on source builds at the moment.")
@@ -59,9 +74,12 @@ def run_gdb(*args, **env_vars):
base_cmd = ('gdb', '--batch', '-nx')
if (gdb_major_version, gdb_minor_version) >= (7, 4):
base_cmd += ('-iex', 'add-auto-load-safe-path ' + checkout_hook_path)
- out, err = subprocess.Popen(base_cmd + args,
- stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env,
- ).communicate()
+ proc = subprocess.Popen(base_cmd + args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ env=env)
+ with proc:
+ out, err = proc.communicate()
return out.decode('utf-8', 'replace'), err.decode('utf-8', 'replace')
# Verify that "gdb" was built with the embedded python support enabled:
@@ -880,8 +898,8 @@ class PyLocalsTests(DebuggerTests):
def test_main():
if support.verbose:
- print("GDB version:")
- for line in os.fsdecode(gdb_version).splitlines():
+ print("GDB version %s.%s:" % (gdb_major_version, gdb_minor_version))
+ for line in gdb_version.splitlines():
print(" " * 4 + line)
run_unittest(PrettyPrintTests,
PyListTests,
diff --git a/Lib/test/test_glob.py b/Lib/test/test_glob.py
index 21b0153..926588e 100644
--- a/Lib/test/test_glob.py
+++ b/Lib/test/test_glob.py
@@ -242,9 +242,7 @@ class GlobTests(unittest.TestCase):
('a', 'bcd', 'EF'), ('a', 'bcd', 'efg')))
eq(self.rglob('a', '**', 'bcd'), self.joins(('a', 'bcd')))
- predir = os.path.abspath(os.curdir)
- try:
- os.chdir(self.tempdir)
+ with change_cwd(self.tempdir):
join = os.path.join
eq(glob.glob('**', recursive=True), [join(*i) for i in full])
eq(glob.glob(join('**', ''), recursive=True),
@@ -256,8 +254,6 @@ class GlobTests(unittest.TestCase):
if can_symlink():
expect += [join('sym3', 'EF')]
eq(glob.glob(join('**', 'EF'), recursive=True), expect)
- finally:
- os.chdir(predir)
@skip_unless_symlink
diff --git a/Lib/test/test_htmlparser.py b/Lib/test/test_htmlparser.py
index de8f3e8..11420b2c 100644
--- a/Lib/test/test_htmlparser.py
+++ b/Lib/test/test_htmlparser.py
@@ -72,9 +72,6 @@ class EventCollectorExtra(EventCollector):
class EventCollectorCharrefs(EventCollector):
- def get_events(self):
- return self.events
-
def handle_charref(self, data):
self.fail('This should never be called with convert_charrefs=True')
@@ -633,6 +630,18 @@ text
]
self._run_check(html, expected)
+ def test_convert_charrefs_dropped_text(self):
+ # #23144: make sure that all the events are triggered when
+ # convert_charrefs is True, even if we don't call .close()
+ parser = EventCollector(convert_charrefs=True)
+ # before the fix, bar & baz was missing
+ parser.feed("foo <a>link</a> bar &amp; baz")
+ self.assertEqual(
+ parser.get_events(),
+ [('data', 'foo '), ('starttag', 'a', []), ('data', 'link'),
+ ('endtag', 'a'), ('data', ' bar & baz')]
+ )
+
class AttributesTestCase(TestCaseBase):
diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py
index fcd8869..5b3ba7e 100644
--- a/Lib/test/test_itertools.py
+++ b/Lib/test/test_itertools.py
@@ -985,6 +985,16 @@ class TestBasicOps(unittest.TestCase):
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
self.pickletest(proto, product(*args))
+ def test_product_issue_25021(self):
+ # test that indices are properly clamped to the length of the tuples
+ p = product((1, 2),(3,))
+ p.__setstate__((0, 0x1000)) # will access tuple element 1 if not clamped
+ self.assertEqual(next(p), (2, 3))
+ # test that empty tuple in the list will result in an immediate StopIteration
+ p = product((1, 2), (), (3,))
+ p.__setstate__((0, 0, 0x1000)) # will access tuple element 1 if not clamped
+ self.assertRaises(StopIteration, next, p)
+
def test_repeat(self):
self.assertEqual(list(repeat(object='a', times=3)), ['a', 'a', 'a'])
self.assertEqual(lzip(range(3),repeat('a')),
diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py
index 3de84e8..0f25742 100644
--- a/Lib/test/test_mmap.py
+++ b/Lib/test/test_mmap.py
@@ -731,7 +731,10 @@ class LargeMmapTests(unittest.TestCase):
f.write(tail)
f.flush()
except (OSError, OverflowError):
- f.close()
+ try:
+ f.close()
+ except (OSError, OverflowError):
+ pass
raise unittest.SkipTest("filesystem does not have largefile support")
return f
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index d91f58c..245309e 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -85,7 +85,7 @@ HAVE_WHEEL_GROUP = sys.platform.startswith('freebsd') and os.getgid() == 0
# Tests creating TESTFN
class FileTests(unittest.TestCase):
def setUp(self):
- if os.path.exists(support.TESTFN):
+ if os.path.lexists(support.TESTFN):
os.unlink(support.TESTFN)
tearDown = setUp
@@ -209,6 +209,19 @@ class FileTests(unittest.TestCase):
with open(TESTFN2, 'r') as f:
self.assertEqual(f.read(), "1")
+ def test_open_keywords(self):
+ f = os.open(path=__file__, flags=os.O_RDONLY, mode=0o777,
+ dir_fd=None)
+ os.close(f)
+
+ def test_symlink_keywords(self):
+ symlink = support.get_attribute(os, "symlink")
+ try:
+ symlink(src='target', dst=support.TESTFN,
+ target_is_directory=False, dir_fd=None)
+ except (NotImplementedError, OSError):
+ pass # No OS support or unprivileged user
+
# Test attributes on return values from os.*stat* family.
class StatAttributeTests(unittest.TestCase):
@@ -1773,7 +1786,7 @@ class Win32KillTests(unittest.TestCase):
os.kill(proc.pid, signal.SIGINT)
self.fail("subprocess did not stop on {}".format(name))
- @unittest.skip("subprocesses aren't inheriting CTRL+C property")
+ @unittest.skip("subprocesses aren't inheriting Ctrl+C property")
def test_CTRL_C_EVENT(self):
from ctypes import wintypes
import ctypes
@@ -1786,7 +1799,7 @@ class Win32KillTests(unittest.TestCase):
SetConsoleCtrlHandler.restype = wintypes.BOOL
# Calling this with NULL and FALSE causes the calling process to
- # handle CTRL+C, rather than ignore it. This property is inherited
+ # handle Ctrl+C, rather than ignore it. This property is inherited
# by subprocesses.
SetConsoleCtrlHandler(NULL, 0)
@@ -2313,6 +2326,14 @@ class TestSendfile(unittest.TestCase):
os.sendfile(self.sockno, self.fileno, -1, 4096)
self.assertEqual(cm.exception.errno, errno.EINVAL)
+ def test_keywords(self):
+ # Keyword arguments should be supported
+ os.sendfile(out=self.sockno, offset=0, count=4096,
+ **{'in': self.fileno})
+ if self.SUPPORT_HEADERS_TRAILERS:
+ os.sendfile(self.sockno, self.fileno, offset=0, count=4096,
+ headers=(), trailers=(), flags=0)
+
# --- headers / trailers tests
@requires_headers_trailers
diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py
index ec8346c..35044ad 100644
--- a/Lib/test/test_pdb.py
+++ b/Lib/test/test_pdb.py
@@ -1043,6 +1043,18 @@ class PdbTestCase(unittest.TestCase):
self.assertNotIn('Error', stdout.decode(),
"Got an error running test script under PDB")
+ def test_issue16180(self):
+ # A syntax error in the debuggee.
+ script = "def f: pass\n"
+ commands = ''
+ expected = "SyntaxError:"
+ stdout, stderr = self.run_pdb(script, commands)
+ self.assertIn(expected, stdout,
+ '\n\nExpected:\n{}\nGot:\n{}\n'
+ 'Fail to handle a syntax error in the debuggee.'
+ .format(expected, stdout))
+
+
def tearDown(self):
support.unlink(support.TESTFN)
diff --git a/Lib/test/test_pep277.py b/Lib/test/test_pep277.py
index 6c833a8..98c716b 100644
--- a/Lib/test/test_pep277.py
+++ b/Lib/test/test_pep277.py
@@ -158,17 +158,11 @@ class UnicodeFileTests(unittest.TestCase):
def test_directory(self):
dirname = os.path.join(support.TESTFN, 'Gr\xfc\xdf-\u66e8\u66e9\u66eb')
filename = '\xdf-\u66e8\u66e9\u66eb'
- oldwd = os.getcwd()
- os.mkdir(dirname)
- os.chdir(dirname)
- try:
+ with support.temp_cwd(dirname):
with open(filename, 'wb') as f:
f.write((filename + '\n').encode("utf-8"))
os.access(filename,os.R_OK)
os.remove(filename)
- finally:
- os.chdir(oldwd)
- os.rmdir(dirname)
class UnicodeNFCFileTests(UnicodeFileTests):
diff --git a/Lib/test/test_popen.py b/Lib/test/test_popen.py
index 8958db0..da01a87 100644
--- a/Lib/test/test_popen.py
+++ b/Lib/test/test_popen.py
@@ -57,5 +57,9 @@ class PopenTest(unittest.TestCase):
with os.popen("echo hello") as f:
self.assertEqual(list(f), ["hello\n"])
+ def test_keywords(self):
+ with os.popen(cmd="exit 0", mode="w", buffering=-1):
+ pass
+
if __name__ == "__main__":
unittest.main()
diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py
index 77e5b0c4..2a59c38 100644
--- a/Lib/test/test_posix.py
+++ b/Lib/test/test_posix.py
@@ -442,6 +442,14 @@ class PosixTester(unittest.TestCase):
else:
self.assertTrue(stat.S_ISFIFO(posix.stat(support.TESTFN).st_mode))
+ # Keyword arguments are also supported
+ support.unlink(support.TESTFN)
+ try:
+ posix.mknod(path=support.TESTFN, mode=mode, device=0,
+ dir_fd=None)
+ except OSError as e:
+ self.assertIn(e.errno, (errno.EPERM, errno.EINVAL))
+
@unittest.skipUnless(hasattr(posix, 'stat'), 'test needs posix.stat()')
@unittest.skipUnless(hasattr(posix, 'makedev'), 'test needs posix.makedev()')
def test_makedev(self):
diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py
index ece3555..9d20471 100644
--- a/Lib/test/test_posixpath.py
+++ b/Lib/test/test_posixpath.py
@@ -316,7 +316,6 @@ class PosixPathTest(unittest.TestCase):
# Bug #930024, return the path unchanged if we get into an infinite
# symlink loop.
try:
- old_path = abspath('.')
os.symlink(ABSTFN, ABSTFN)
self.assertEqual(realpath(ABSTFN), ABSTFN)
@@ -342,10 +341,9 @@ class PosixPathTest(unittest.TestCase):
self.assertEqual(realpath(ABSTFN+"c"), ABSTFN+"c")
# Test using relative path as well.
- os.chdir(dirname(ABSTFN))
- self.assertEqual(realpath(basename(ABSTFN)), ABSTFN)
+ with support.change_cwd(dirname(ABSTFN)):
+ self.assertEqual(realpath(basename(ABSTFN)), ABSTFN)
finally:
- os.chdir(old_path)
support.unlink(ABSTFN)
support.unlink(ABSTFN+"1")
support.unlink(ABSTFN+"2")
@@ -373,7 +371,6 @@ class PosixPathTest(unittest.TestCase):
@skip_if_ABSTFN_contains_backslash
def test_realpath_deep_recursion(self):
depth = 10
- old_path = abspath('.')
try:
os.mkdir(ABSTFN)
for i in range(depth):
@@ -382,10 +379,9 @@ class PosixPathTest(unittest.TestCase):
self.assertEqual(realpath(ABSTFN + '/%d' % depth), ABSTFN)
# Test using relative path as well.
- os.chdir(ABSTFN)
- self.assertEqual(realpath('%d' % depth), ABSTFN)
+ with support.change_cwd(ABSTFN):
+ self.assertEqual(realpath('%d' % depth), ABSTFN)
finally:
- os.chdir(old_path)
for i in range(depth + 1):
support.unlink(ABSTFN + '/%d' % i)
safe_rmdir(ABSTFN)
@@ -399,15 +395,13 @@ class PosixPathTest(unittest.TestCase):
# /usr/doc with 'doc' being a symlink to /usr/share/doc. We call
# realpath("a"). This should return /usr/share/doc/a/.
try:
- old_path = abspath('.')
os.mkdir(ABSTFN)
os.mkdir(ABSTFN + "/y")
os.symlink(ABSTFN + "/y", ABSTFN + "/k")
- os.chdir(ABSTFN + "/k")
- self.assertEqual(realpath("a"), ABSTFN + "/y/a")
+ with support.change_cwd(ABSTFN + "/k"):
+ self.assertEqual(realpath("a"), ABSTFN + "/y/a")
finally:
- os.chdir(old_path)
support.unlink(ABSTFN + "/k")
safe_rmdir(ABSTFN + "/y")
safe_rmdir(ABSTFN)
@@ -424,7 +418,6 @@ class PosixPathTest(unittest.TestCase):
# and a symbolic link 'link-y' pointing to 'y' in directory 'a',
# then realpath("link-y/..") should return 'k', not 'a'.
try:
- old_path = abspath('.')
os.mkdir(ABSTFN)
os.mkdir(ABSTFN + "/k")
os.mkdir(ABSTFN + "/k/y")
@@ -433,11 +426,10 @@ class PosixPathTest(unittest.TestCase):
# Absolute path.
self.assertEqual(realpath(ABSTFN + "/link-y/.."), ABSTFN + "/k")
# Relative path.
- os.chdir(dirname(ABSTFN))
- self.assertEqual(realpath(basename(ABSTFN) + "/link-y/.."),
- ABSTFN + "/k")
+ with support.change_cwd(dirname(ABSTFN)):
+ self.assertEqual(realpath(basename(ABSTFN) + "/link-y/.."),
+ ABSTFN + "/k")
finally:
- os.chdir(old_path)
support.unlink(ABSTFN + "/link-y")
safe_rmdir(ABSTFN + "/k/y")
safe_rmdir(ABSTFN + "/k")
@@ -451,17 +443,14 @@ class PosixPathTest(unittest.TestCase):
# must be resolved too.
try:
- old_path = abspath('.')
os.mkdir(ABSTFN)
os.mkdir(ABSTFN + "/k")
os.symlink(ABSTFN, ABSTFN + "link")
- os.chdir(dirname(ABSTFN))
-
- base = basename(ABSTFN)
- self.assertEqual(realpath(base + "link"), ABSTFN)
- self.assertEqual(realpath(base + "link/k"), ABSTFN + "/k")
+ with support.change_cwd(dirname(ABSTFN)):
+ base = basename(ABSTFN)
+ self.assertEqual(realpath(base + "link"), ABSTFN)
+ self.assertEqual(realpath(base + "link/k"), ABSTFN + "/k")
finally:
- os.chdir(old_path)
support.unlink(ABSTFN + "link")
safe_rmdir(ABSTFN + "/k")
safe_rmdir(ABSTFN)
diff --git a/Lib/test/test_py_compile.py b/Lib/test/test_py_compile.py
index 03cca5d..4a6caa5 100644
--- a/Lib/test/test_py_compile.py
+++ b/Lib/test/test_py_compile.py
@@ -63,11 +63,9 @@ class PyCompileTests(unittest.TestCase):
self.assertTrue(os.path.exists(self.cache_path))
def test_cwd(self):
- cwd = os.getcwd()
- os.chdir(self.directory)
- py_compile.compile(os.path.basename(self.source_path),
- os.path.basename(self.pyc_path))
- os.chdir(cwd)
+ with support.change_cwd(self.directory):
+ py_compile.compile(os.path.basename(self.source_path),
+ os.path.basename(self.pyc_path))
self.assertTrue(os.path.exists(self.pyc_path))
self.assertFalse(os.path.exists(self.cache_path))
diff --git a/Lib/test/test_pyexpat.py b/Lib/test/test_pyexpat.py
index 08e95c6..550aebf 100644
--- a/Lib/test/test_pyexpat.py
+++ b/Lib/test/test_pyexpat.py
@@ -3,6 +3,7 @@
from io import BytesIO
import os
+import sys
import sysconfig
import unittest
import traceback
@@ -16,22 +17,47 @@ from test.support import sortdict
class SetAttributeTest(unittest.TestCase):
def setUp(self):
self.parser = expat.ParserCreate(namespace_separator='!')
- self.set_get_pairs = [
- [0, 0],
- [1, 1],
- [2, 1],
- [0, 0],
- ]
+
+ def test_buffer_text(self):
+ self.assertIs(self.parser.buffer_text, False)
+ for x in 0, 1, 2, 0:
+ self.parser.buffer_text = x
+ self.assertIs(self.parser.buffer_text, bool(x))
+
+ def test_namespace_prefixes(self):
+ self.assertIs(self.parser.namespace_prefixes, False)
+ for x in 0, 1, 2, 0:
+ self.parser.namespace_prefixes = x
+ self.assertIs(self.parser.namespace_prefixes, bool(x))
def test_ordered_attributes(self):
- for x, y in self.set_get_pairs:
+ self.assertIs(self.parser.ordered_attributes, False)
+ for x in 0, 1, 2, 0:
self.parser.ordered_attributes = x
- self.assertEqual(self.parser.ordered_attributes, y)
+ self.assertIs(self.parser.ordered_attributes, bool(x))
+
+ def test_specified_attributes(self):
+ self.assertIs(self.parser.specified_attributes, False)
+ for x in 0, 1, 2, 0:
+ self.parser.specified_attributes = x
+ self.assertIs(self.parser.specified_attributes, bool(x))
def test_specified_attributes(self):
- for x, y in self.set_get_pairs:
+ self.assertIs(self.parser.specified_attributes, False)
+ for x in 0, 1, 2, 0:
self.parser.specified_attributes = x
- self.assertEqual(self.parser.specified_attributes, y)
+ self.assertIs(self.parser.specified_attributes, bool(x))
+
+ def test_invalid_attributes(self):
+ with self.assertRaises(AttributeError):
+ self.parser.returns_unicode = 1
+ with self.assertRaises(AttributeError):
+ self.parser.returns_unicode
+
+ # Issue #25019
+ self.assertRaises(TypeError, setattr, self.parser, range(0xF), 0)
+ self.assertRaises(TypeError, self.parser.__setattr__, range(0xF), 0)
+ self.assertRaises(TypeError, getattr, self.parser, range(0xF))
data = b'''\
@@ -514,11 +540,14 @@ class ChardataBufferTest(unittest.TestCase):
def test_wrong_size(self):
parser = expat.ParserCreate()
parser.buffer_text = 1
- def f(size):
- parser.buffer_size = size
-
- self.assertRaises(ValueError, f, -1)
- self.assertRaises(ValueError, f, 0)
+ with self.assertRaises(ValueError):
+ parser.buffer_size = -1
+ with self.assertRaises(ValueError):
+ parser.buffer_size = 0
+ with self.assertRaises((ValueError, OverflowError)):
+ parser.buffer_size = sys.maxsize + 1
+ with self.assertRaises(TypeError):
+ parser.buffer_size = 512.0
def test_unchanged_size(self):
xml1 = b"<?xml version='1.0' encoding='iso8859'?><s>" + b'a' * 512
diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py
index 5183c3c..522959a 100644
--- a/Lib/test/test_shutil.py
+++ b/Lib/test/test_shutil.py
@@ -12,11 +12,9 @@ import errno
import functools
import subprocess
from contextlib import ExitStack
-from test import support
-from test.support import TESTFN
from os.path import splitdrive
from distutils.spawn import find_executable, spawn
-from shutil import (_make_tarball, _make_zipfile, make_archive,
+from shutil import (make_archive,
register_archive_format, unregister_archive_format,
get_archive_formats, Error, unpack_archive,
register_unpack_format, RegistryError,
@@ -94,6 +92,18 @@ def read_file(path, binary=False):
with open(path, 'rb' if binary else 'r') as fp:
return fp.read()
+def rlistdir(path):
+ res = []
+ for name in sorted(os.listdir(path)):
+ p = os.path.join(path, name)
+ if os.path.isdir(p) and not os.path.islink(p):
+ res.append(name + '/')
+ for n in rlistdir(p):
+ res.append(name + '/' + n)
+ else:
+ res.append(name)
+ return res
+
class TestShutil(unittest.TestCase):
@@ -959,139 +969,143 @@ class TestShutil(unittest.TestCase):
@requires_zlib
def test_make_tarball(self):
# creating something to tar
- tmpdir = self.mkdtemp()
- write_file((tmpdir, 'file1'), 'xxx')
- write_file((tmpdir, 'file2'), 'xxx')
- os.mkdir(os.path.join(tmpdir, 'sub'))
- write_file((tmpdir, 'sub', 'file3'), 'xxx')
+ root_dir, base_dir = self._create_files('')
tmpdir2 = self.mkdtemp()
# force shutil to create the directory
os.rmdir(tmpdir2)
- unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0],
- "source and target should be on same drive")
+ # working with relative paths
+ work_dir = os.path.dirname(tmpdir2)
+ rel_base_name = os.path.join(os.path.basename(tmpdir2), 'archive')
- base_name = os.path.join(tmpdir2, 'archive')
-
- # working with relative paths to avoid tar warnings
- old_dir = os.getcwd()
- os.chdir(tmpdir)
- try:
- _make_tarball(splitdrive(base_name)[1], '.')
- finally:
- os.chdir(old_dir)
+ with support.change_cwd(work_dir):
+ base_name = os.path.abspath(rel_base_name)
+ tarball = make_archive(rel_base_name, 'gztar', root_dir, '.')
# check if the compressed tarball was created
- tarball = base_name + '.tar.gz'
- self.assertTrue(os.path.exists(tarball))
+ self.assertEqual(tarball, base_name + '.tar.gz')
+ self.assertTrue(os.path.isfile(tarball))
+ self.assertTrue(tarfile.is_tarfile(tarball))
+ with tarfile.open(tarball, 'r:gz') as tf:
+ self.assertCountEqual(tf.getnames(),
+ ['.', './sub', './sub2',
+ './file1', './file2', './sub/file3'])
# trying an uncompressed one
- base_name = os.path.join(tmpdir2, 'archive')
- old_dir = os.getcwd()
- os.chdir(tmpdir)
- try:
- _make_tarball(splitdrive(base_name)[1], '.', compress=None)
- finally:
- os.chdir(old_dir)
- tarball = base_name + '.tar'
- self.assertTrue(os.path.exists(tarball))
+ with support.change_cwd(work_dir):
+ tarball = make_archive(rel_base_name, 'tar', root_dir, '.')
+ self.assertEqual(tarball, base_name + '.tar')
+ self.assertTrue(os.path.isfile(tarball))
+ self.assertTrue(tarfile.is_tarfile(tarball))
+ with tarfile.open(tarball, 'r') as tf:
+ self.assertCountEqual(tf.getnames(),
+ ['.', './sub', './sub2',
+ './file1', './file2', './sub/file3'])
def _tarinfo(self, path):
- tar = tarfile.open(path)
- try:
+ with tarfile.open(path) as tar:
names = tar.getnames()
names.sort()
return tuple(names)
- finally:
- tar.close()
- def _create_files(self):
+ def _create_files(self, base_dir='dist'):
# creating something to tar
- tmpdir = self.mkdtemp()
- dist = os.path.join(tmpdir, 'dist')
- os.mkdir(dist)
+ root_dir = self.mkdtemp()
+ dist = os.path.join(root_dir, base_dir)
+ os.makedirs(dist, exist_ok=True)
write_file((dist, 'file1'), 'xxx')
write_file((dist, 'file2'), 'xxx')
os.mkdir(os.path.join(dist, 'sub'))
write_file((dist, 'sub', 'file3'), 'xxx')
os.mkdir(os.path.join(dist, 'sub2'))
- tmpdir2 = self.mkdtemp()
- base_name = os.path.join(tmpdir2, 'archive')
- return tmpdir, tmpdir2, base_name
+ if base_dir:
+ write_file((root_dir, 'outer'), 'xxx')
+ return root_dir, base_dir
@requires_zlib
- @unittest.skipUnless(find_executable('tar') and find_executable('gzip'),
+ @unittest.skipUnless(find_executable('tar'),
'Need the tar command to run')
def test_tarfile_vs_tar(self):
- tmpdir, tmpdir2, base_name = self._create_files()
- old_dir = os.getcwd()
- os.chdir(tmpdir)
- try:
- _make_tarball(base_name, 'dist')
- finally:
- os.chdir(old_dir)
+ root_dir, base_dir = self._create_files()
+ base_name = os.path.join(self.mkdtemp(), 'archive')
+ tarball = make_archive(base_name, 'gztar', root_dir, base_dir)
# check if the compressed tarball was created
- tarball = base_name + '.tar.gz'
- self.assertTrue(os.path.exists(tarball))
+ self.assertEqual(tarball, base_name + '.tar.gz')
+ self.assertTrue(os.path.isfile(tarball))
# now create another tarball using `tar`
- tarball2 = os.path.join(tmpdir, 'archive2.tar.gz')
- tar_cmd = ['tar', '-cf', 'archive2.tar', 'dist']
- gzip_cmd = ['gzip', '-f9', 'archive2.tar']
- old_dir = os.getcwd()
- os.chdir(tmpdir)
- try:
- with captured_stdout() as s:
- spawn(tar_cmd)
- spawn(gzip_cmd)
- finally:
- os.chdir(old_dir)
+ tarball2 = os.path.join(root_dir, 'archive2.tar')
+ tar_cmd = ['tar', '-cf', 'archive2.tar', base_dir]
+ with support.change_cwd(root_dir), captured_stdout():
+ spawn(tar_cmd)
- self.assertTrue(os.path.exists(tarball2))
+ self.assertTrue(os.path.isfile(tarball2))
# let's compare both tarballs
self.assertEqual(self._tarinfo(tarball), self._tarinfo(tarball2))
# trying an uncompressed one
- base_name = os.path.join(tmpdir2, 'archive')
- old_dir = os.getcwd()
- os.chdir(tmpdir)
- try:
- _make_tarball(base_name, 'dist', compress=None)
- finally:
- os.chdir(old_dir)
- tarball = base_name + '.tar'
- self.assertTrue(os.path.exists(tarball))
+ tarball = make_archive(base_name, 'tar', root_dir, base_dir)
+ self.assertEqual(tarball, base_name + '.tar')
+ self.assertTrue(os.path.isfile(tarball))
# now for a dry_run
- base_name = os.path.join(tmpdir2, 'archive')
- old_dir = os.getcwd()
- os.chdir(tmpdir)
- try:
- _make_tarball(base_name, 'dist', compress=None, dry_run=True)
- finally:
- os.chdir(old_dir)
- tarball = base_name + '.tar'
- self.assertTrue(os.path.exists(tarball))
+ tarball = make_archive(base_name, 'tar', root_dir, base_dir,
+ dry_run=True)
+ self.assertEqual(tarball, base_name + '.tar')
+ self.assertTrue(os.path.isfile(tarball))
@requires_zlib
@unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run')
def test_make_zipfile(self):
- # creating something to tar
- tmpdir = self.mkdtemp()
- write_file((tmpdir, 'file1'), 'xxx')
- write_file((tmpdir, 'file2'), 'xxx')
+ # creating something to zip
+ root_dir, base_dir = self._create_files()
tmpdir2 = self.mkdtemp()
# force shutil to create the directory
os.rmdir(tmpdir2)
- base_name = os.path.join(tmpdir2, 'archive')
- _make_zipfile(base_name, tmpdir)
+ # working with relative paths
+ work_dir = os.path.dirname(tmpdir2)
+ rel_base_name = os.path.join(os.path.basename(tmpdir2), 'archive')
+
+ with support.change_cwd(work_dir):
+ base_name = os.path.abspath(rel_base_name)
+ res = make_archive(rel_base_name, 'zip', root_dir, base_dir)
+
+ self.assertEqual(res, base_name + '.zip')
+ self.assertTrue(os.path.isfile(res))
+ self.assertTrue(zipfile.is_zipfile(res))
+ with zipfile.ZipFile(res) as zf:
+ self.assertCountEqual(zf.namelist(),
+ ['dist/', 'dist/sub/', 'dist/sub2/',
+ 'dist/file1', 'dist/file2', 'dist/sub/file3'])
- # check if the compressed tarball was created
- tarball = base_name + '.zip'
- self.assertTrue(os.path.exists(tarball))
+ @requires_zlib
+ @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run')
+ @unittest.skipUnless(find_executable('zip'),
+ 'Need the zip command to run')
+ def test_zipfile_vs_zip(self):
+ root_dir, base_dir = self._create_files()
+ base_name = os.path.join(self.mkdtemp(), 'archive')
+ archive = make_archive(base_name, 'zip', root_dir, base_dir)
+
+ # check if ZIP file was created
+ self.assertEqual(archive, base_name + '.zip')
+ self.assertTrue(os.path.isfile(archive))
+
+ # now create another ZIP file using `zip`
+ archive2 = os.path.join(root_dir, 'archive2.zip')
+ zip_cmd = ['zip', '-q', '-r', 'archive2.zip', base_dir]
+ with support.change_cwd(root_dir):
+ spawn(zip_cmd)
+ self.assertTrue(os.path.isfile(archive2))
+ # let's compare both ZIP files
+ with zipfile.ZipFile(archive) as zf:
+ names = zf.namelist()
+ with zipfile.ZipFile(archive2) as zf:
+ names2 = zf.namelist()
+ self.assertEqual(sorted(names), sorted(names2))
def test_make_archive(self):
tmpdir = self.mkdtemp()
@@ -1108,40 +1122,37 @@ class TestShutil(unittest.TestCase):
else:
group = owner = 'root'
- base_dir, root_dir, base_name = self._create_files()
- base_name = os.path.join(self.mkdtemp() , 'archive')
+ root_dir, base_dir = self._create_files()
+ base_name = os.path.join(self.mkdtemp(), 'archive')
res = make_archive(base_name, 'zip', root_dir, base_dir, owner=owner,
group=group)
- self.assertTrue(os.path.exists(res))
+ self.assertTrue(os.path.isfile(res))
res = make_archive(base_name, 'zip', root_dir, base_dir)
- self.assertTrue(os.path.exists(res))
+ self.assertTrue(os.path.isfile(res))
res = make_archive(base_name, 'tar', root_dir, base_dir,
owner=owner, group=group)
- self.assertTrue(os.path.exists(res))
+ self.assertTrue(os.path.isfile(res))
res = make_archive(base_name, 'tar', root_dir, base_dir,
owner='kjhkjhkjg', group='oihohoh')
- self.assertTrue(os.path.exists(res))
+ self.assertTrue(os.path.isfile(res))
@requires_zlib
@unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support")
def test_tarfile_root_owner(self):
- tmpdir, tmpdir2, base_name = self._create_files()
- old_dir = os.getcwd()
- os.chdir(tmpdir)
+ root_dir, base_dir = self._create_files()
+ base_name = os.path.join(self.mkdtemp(), 'archive')
group = grp.getgrgid(0)[0]
owner = pwd.getpwuid(0)[0]
- try:
- archive_name = _make_tarball(base_name, 'dist', compress=None,
- owner=owner, group=group)
- finally:
- os.chdir(old_dir)
+ with support.change_cwd(root_dir):
+ archive_name = make_archive(base_name, 'gztar', root_dir, 'dist',
+ owner=owner, group=group)
# check if the compressed tarball was created
- self.assertTrue(os.path.exists(archive_name))
+ self.assertTrue(os.path.isfile(archive_name))
# now checks the rights
archive = tarfile.open(archive_name)
@@ -1198,18 +1209,6 @@ class TestShutil(unittest.TestCase):
formats = [name for name, params in get_archive_formats()]
self.assertNotIn('xxx', formats)
- def _compare_dirs(self, dir1, dir2):
- # check that dir1 and dir2 are equivalent,
- # return the diff
- diff = []
- for root, dirs, files in os.walk(dir1):
- for file_ in files:
- path = os.path.join(root, file_)
- target_path = os.path.join(dir2, os.path.split(path)[-1])
- if not os.path.exists(target_path):
- diff.append(file_)
- return diff
-
@requires_zlib
def test_unpack_archive(self):
formats = ['tar', 'gztar', 'zip']
@@ -1218,22 +1217,22 @@ class TestShutil(unittest.TestCase):
if LZMA_SUPPORTED:
formats.append('xztar')
+ root_dir, base_dir = self._create_files()
+ expected = rlistdir(root_dir)
+ expected.remove('outer')
for format in formats:
- tmpdir = self.mkdtemp()
- base_dir, root_dir, base_name = self._create_files()
- tmpdir2 = self.mkdtemp()
+ base_name = os.path.join(self.mkdtemp(), 'archive')
filename = make_archive(base_name, format, root_dir, base_dir)
# let's try to unpack it now
+ tmpdir2 = self.mkdtemp()
unpack_archive(filename, tmpdir2)
- diff = self._compare_dirs(tmpdir, tmpdir2)
- self.assertEqual(diff, [])
+ self.assertEqual(rlistdir(tmpdir2), expected)
# and again, this time with the format specified
tmpdir3 = self.mkdtemp()
unpack_archive(filename, tmpdir3, format=format)
- diff = self._compare_dirs(tmpdir, tmpdir3)
- self.assertEqual(diff, [])
+ self.assertEqual(rlistdir(tmpdir3), expected)
self.assertRaises(shutil.ReadError, unpack_archive, TESTFN)
self.assertRaises(ValueError, unpack_archive, TESTFN, format='xxx')
diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py
index 9c0229a..ff2010d 100644
--- a/Lib/test/test_subprocess.py
+++ b/Lib/test/test_subprocess.py
@@ -317,11 +317,8 @@ class ProcessTestCase(BaseTestCase):
# Normalize an expected cwd (for Tru64 support).
# We can't use os.path.realpath since it doesn't expand Tru64 {memb}
# strings. See bug #1063571.
- original_cwd = os.getcwd()
- os.chdir(cwd)
- cwd = os.getcwd()
- os.chdir(original_cwd)
- return cwd
+ with support.change_cwd(cwd):
+ return os.getcwd()
# For use in the test_cwd* tests below.
def _split_python_path(self):
diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py
index c0f27a6..0917c3e 100644
--- a/Lib/test/test_sysconfig.py
+++ b/Lib/test/test_sysconfig.py
@@ -6,7 +6,7 @@ import shutil
from copy import copy
from test.support import (run_unittest, TESTFN, unlink, check_warnings,
- captured_stdout, skip_unless_symlink)
+ captured_stdout, skip_unless_symlink, change_cwd)
import sysconfig
from sysconfig import (get_paths, get_platform, get_config_vars,
@@ -361,12 +361,8 @@ class TestSysConfig(unittest.TestCase):
# srcdir should be independent of the current working directory
# See Issues #15322, #15364.
srcdir = sysconfig.get_config_var('srcdir')
- cwd = os.getcwd()
- try:
- os.chdir('..')
+ with change_cwd(os.pardir):
srcdir2 = sysconfig.get_config_var('srcdir')
- finally:
- os.chdir(cwd)
self.assertEqual(srcdir, srcdir2)
@unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None,
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
index 57cddb2..1412cae 100644
--- a/Lib/test/test_tarfile.py
+++ b/Lib/test/test_tarfile.py
@@ -1132,10 +1132,8 @@ class WriteTest(WriteTestBase, unittest.TestCase):
self.assertEqual(tar.getnames(), [],
"added the archive to itself")
- cwd = os.getcwd()
- os.chdir(TEMPDIR)
- tar.add(dstname)
- os.chdir(cwd)
+ with support.change_cwd(TEMPDIR):
+ tar.add(dstname)
self.assertEqual(tar.getnames(), [],
"added the archive to itself")
finally:
@@ -1292,9 +1290,7 @@ class WriteTest(WriteTestBase, unittest.TestCase):
def test_cwd(self):
# Test adding the current working directory.
- cwd = os.getcwd()
- os.chdir(TEMPDIR)
- try:
+ with support.change_cwd(TEMPDIR):
tar = tarfile.open(tmpname, self.mode)
try:
tar.add(".")
@@ -1308,8 +1304,6 @@ class WriteTest(WriteTestBase, unittest.TestCase):
self.assertTrue(t.name.startswith("./"), t.name)
finally:
tar.close()
- finally:
- os.chdir(cwd)
def test_open_nonwritable_fileobj(self):
for exctype in OSError, EOFError, RuntimeError:
diff --git a/Lib/test/test_unicode_file.py b/Lib/test/test_unicode_file.py
index faa8da3..e4709a1 100644
--- a/Lib/test/test_unicode_file.py
+++ b/Lib/test/test_unicode_file.py
@@ -5,7 +5,7 @@ import os, glob, time, shutil
import unicodedata
import unittest
-from test.support import (run_unittest, rmtree,
+from test.support import (run_unittest, rmtree, change_cwd,
TESTFN_ENCODING, TESTFN_UNICODE, TESTFN_UNENCODABLE, create_empty_file)
if not os.path.supports_unicode_filenames:
@@ -82,13 +82,11 @@ class TestUnicodeFiles(unittest.TestCase):
self.assertFalse(os.path.exists(filename2 + '.new'))
def _do_directory(self, make_name, chdir_name):
- cwd = os.getcwd()
if os.path.isdir(make_name):
rmtree(make_name)
os.mkdir(make_name)
try:
- os.chdir(chdir_name)
- try:
+ with change_cwd(chdir_name):
cwd_result = os.getcwd()
name_result = make_name
@@ -96,8 +94,6 @@ class TestUnicodeFiles(unittest.TestCase):
name_result = unicodedata.normalize("NFD", name_result)
self.assertEqual(os.path.basename(cwd_result),name_result)
- finally:
- os.chdir(cwd)
finally:
os.rmdir(make_name)
diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py
index 991a249..cea9c57 100644
--- a/Lib/test/test_warnings/__init__.py
+++ b/Lib/test/test_warnings/__init__.py
@@ -44,6 +44,7 @@ class BaseTest:
"""Basic bookkeeping required for testing."""
def setUp(self):
+ self.old_unittest_module = unittest.case.warnings
# The __warningregistry__ needs to be in a pristine state for tests
# to work properly.
if '__warningregistry__' in globals():
@@ -55,10 +56,15 @@ class BaseTest:
# The 'warnings' module must be explicitly set so that the proper
# interaction between _warnings and 'warnings' can be controlled.
sys.modules['warnings'] = self.module
+ # Ensure that unittest.TestCase.assertWarns() uses the same warnings
+ # module than warnings.catch_warnings(). Otherwise,
+ # warnings.catch_warnings() will be unable to remove the added filter.
+ unittest.case.warnings = self.module
super(BaseTest, self).setUp()
def tearDown(self):
sys.modules['warnings'] = original_warnings
+ unittest.case.warnings = self.old_unittest_module
super(BaseTest, self).tearDown()
class PublicAPITests(BaseTest):
diff --git a/Lib/test/test_wsgiref.py b/Lib/test/test_wsgiref.py
index 112a1b9..8cca595 100644
--- a/Lib/test/test_wsgiref.py
+++ b/Lib/test/test_wsgiref.py
@@ -1,11 +1,10 @@
-from __future__ import nested_scopes # Backward compat for 2.1
from unittest import TestCase
from wsgiref.util import setup_testing_defaults
from wsgiref.headers import Headers
from wsgiref.handlers import BaseHandler, BaseCGIHandler
from wsgiref import util
from wsgiref.validate import validator
-from wsgiref.simple_server import WSGIServer, WSGIRequestHandler, demo_app
+from wsgiref.simple_server import WSGIServer, WSGIRequestHandler
from wsgiref.simple_server import make_server
from io import StringIO, BytesIO, BufferedReader
from socketserver import BaseServer
@@ -14,8 +13,8 @@ from platform import python_implementation
import os
import re
import sys
+import unittest
-from test import support
class MockServer(WSGIServer):
"""Non-socket HTTP server"""
diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py
index bd935ed..7cd1d7c 100644
--- a/Lib/test/test_zlib.py
+++ b/Lib/test/test_zlib.py
@@ -222,9 +222,9 @@ class CompressObjectTestCase(BaseCompressTestCase, unittest.TestCase):
level = 2
method = zlib.DEFLATED
wbits = -12
- memlevel = 9
+ memLevel = 9
strategy = zlib.Z_FILTERED
- co = zlib.compressobj(level, method, wbits, memlevel, strategy)
+ co = zlib.compressobj(level, method, wbits, memLevel, strategy)
x1 = co.compress(HAMLET_SCENE)
x2 = co.flush()
dco = zlib.decompressobj(wbits)
@@ -232,6 +232,10 @@ class CompressObjectTestCase(BaseCompressTestCase, unittest.TestCase):
y2 = dco.flush()
self.assertEqual(HAMLET_SCENE, y1 + y2)
+ # keyword arguments should also be supported
+ zlib.compressobj(level=level, method=method, wbits=wbits,
+ memLevel=memLevel, strategy=strategy, zdict=b"")
+
def test_compressincremental(self):
# compress object in steps, decompress object as one-shot
data = HAMLET_SCENE * 128
diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py
index 7701ad3..ac8d67d 100644
--- a/Lib/unittest/case.py
+++ b/Lib/unittest/case.py
@@ -583,8 +583,11 @@ class TestCase(object):
finally:
result.stopTest(self)
return
- expecting_failure = getattr(testMethod,
- "__unittest_expecting_failure__", False)
+ expecting_failure_method = getattr(testMethod,
+ "__unittest_expecting_failure__", False)
+ expecting_failure_class = getattr(self,
+ "__unittest_expecting_failure__", False)
+ expecting_failure = expecting_failure_class or expecting_failure_method
outcome = _Outcome(result)
try:
self._outcome = outcome
@@ -1279,8 +1282,10 @@ class TestCase(object):
assert expected_regex, "expected_regex must not be empty."
expected_regex = re.compile(expected_regex)
if not expected_regex.search(text):
- msg = msg or "Regex didn't match"
- msg = '%s: %r not found in %r' % (msg, expected_regex.pattern, text)
+ standardMsg = "Regex didn't match: %r not found in %r" % (
+ expected_regex.pattern, text)
+ # _formatMessage ensures the longMessage option is respected
+ msg = self._formatMessage(msg, standardMsg)
raise self.failureException(msg)
def assertNotRegex(self, text, unexpected_regex, msg=None):
@@ -1289,11 +1294,12 @@ class TestCase(object):
unexpected_regex = re.compile(unexpected_regex)
match = unexpected_regex.search(text)
if match:
- msg = msg or "Regex matched"
- msg = '%s: %r matches %r in %r' % (msg,
- text[match.start():match.end()],
- unexpected_regex.pattern,
- text)
+ standardMsg = 'Regex matched: %r matches %r in %r' % (
+ text[match.start() : match.end()],
+ unexpected_regex.pattern,
+ text)
+ # _formatMessage ensures the longMessage option is respected
+ msg = self._formatMessage(msg, standardMsg)
raise self.failureException(msg)
@@ -1315,6 +1321,7 @@ class TestCase(object):
failIf = _deprecate(assertFalse)
assertRaisesRegexp = _deprecate(assertRaisesRegex)
assertRegexpMatches = _deprecate(assertRegex)
+ assertNotRegexpMatches = _deprecate(assertNotRegex)
diff --git a/Lib/unittest/main.py b/Lib/unittest/main.py
index b209a3a..09fefe1 100644
--- a/Lib/unittest/main.py
+++ b/Lib/unittest/main.py
@@ -171,7 +171,7 @@ class TestProgram(object):
if self.catchbreak is None:
parser.add_argument('-c', '--catch', dest='catchbreak',
action='store_true',
- help='Catch ctrl-C and display results so far')
+ help='Catch Ctrl-C and display results so far')
self.catchbreak = False
if self.buffer is None:
parser.add_argument('-b', '--buffer', dest='buffer',
diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py
index 4c58639..99ce1e2 100644
--- a/Lib/unittest/mock.py
+++ b/Lib/unittest/mock.py
@@ -2005,8 +2005,7 @@ class _Call(tuple):
else:
other_args = ()
other_kwargs = value
- else:
- # len 2
+ elif len_other == 2:
# could be (name, args) or (name, kwargs) or (args, kwargs)
first, second = other
if isinstance(first, str):
@@ -2017,6 +2016,8 @@ class _Call(tuple):
other_args, other_kwargs = (), second
else:
other_args, other_kwargs = first, second
+ else:
+ return False
if self_name and other_name != self_name:
return False
diff --git a/Lib/unittest/test/test_assertions.py b/Lib/unittest/test/test_assertions.py
index c349a95..e6e2bc2 100644
--- a/Lib/unittest/test/test_assertions.py
+++ b/Lib/unittest/test/test_assertions.py
@@ -133,7 +133,6 @@ class Test_Assertions(unittest.TestCase):
try:
self.assertNotRegex('Ala ma kota', r'k.t', 'Message')
except self.failureException as e:
- self.assertIn("'kot'", e.args[0])
self.assertIn('Message', e.args[0])
else:
self.fail('assertNotRegex should have failed.')
@@ -329,6 +328,20 @@ class TestLongMessage(unittest.TestCase):
"^unexpectedly identical: None$",
"^unexpectedly identical: None : oops$"])
+ def testAssertRegex(self):
+ self.assertMessages('assertRegex', ('foo', 'bar'),
+ ["^Regex didn't match:",
+ "^oops$",
+ "^Regex didn't match:",
+ "^Regex didn't match: (.*) : oops$"])
+
+ def testAssertNotRegex(self):
+ self.assertMessages('assertNotRegex', ('foo', 'foo'),
+ ["^Regex matched:",
+ "^oops$",
+ "^Regex matched:",
+ "^Regex matched: (.*) : oops$"])
+
def assertMessagesCM(self, methodName, args, func, errors):
"""
diff --git a/Lib/unittest/test/test_skipping.py b/Lib/unittest/test/test_skipping.py
index 807510f..71f7b70 100644
--- a/Lib/unittest/test/test_skipping.py
+++ b/Lib/unittest/test/test_skipping.py
@@ -120,6 +120,39 @@ class Test_TestSkipping(unittest.TestCase):
self.assertEqual(result.expectedFailures[0][0], test)
self.assertTrue(result.wasSuccessful())
+ def test_expected_failure_with_wrapped_class(self):
+ @unittest.expectedFailure
+ class Foo(unittest.TestCase):
+ def test_1(self):
+ self.assertTrue(False)
+
+ events = []
+ result = LoggingResult(events)
+ test = Foo("test_1")
+ test.run(result)
+ self.assertEqual(events,
+ ['startTest', 'addExpectedFailure', 'stopTest'])
+ self.assertEqual(result.expectedFailures[0][0], test)
+ self.assertTrue(result.wasSuccessful())
+
+ def test_expected_failure_with_wrapped_subclass(self):
+ class Foo(unittest.TestCase):
+ def test_1(self):
+ self.assertTrue(False)
+
+ @unittest.expectedFailure
+ class Bar(Foo):
+ pass
+
+ events = []
+ result = LoggingResult(events)
+ test = Bar("test_1")
+ test.run(result)
+ self.assertEqual(events,
+ ['startTest', 'addExpectedFailure', 'stopTest'])
+ self.assertEqual(result.expectedFailures[0][0], test)
+ self.assertTrue(result.wasSuccessful())
+
def test_expected_failure_subtests(self):
# A failure in any subtest counts as the expected failure of the
# whole test.
diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py
index 97d844f..2a6069f 100644
--- a/Lib/unittest/test/testmock/testmock.py
+++ b/Lib/unittest/test/testmock/testmock.py
@@ -300,6 +300,9 @@ class MockTest(unittest.TestCase):
self.assertEqual(mock.call_args,
((sentinel.Arg,), {"kw": sentinel.Kwarg}))
+ # Comparing call_args to a long sequence should not raise
+ # an exception. See issue 24857.
+ self.assertFalse(mock.call_args == "a long sequence")
def test_assert_called_with(self):
mock = Mock()