summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
Diffstat (limited to 'Lib')
-rw-r--r--Lib/asyncio/base_events.py6
-rw-r--r--Lib/asyncio/selector_events.py15
-rw-r--r--Lib/asyncio/streams.py17
-rw-r--r--Lib/ctypes/test/test_pep3118.py14
-rw-r--r--Lib/distutils/tests/support.py2
-rw-r--r--Lib/email/generator.py9
-rw-r--r--Lib/ensurepip/__init__.py2
-rw-r--r--Lib/ensurepip/_bundled/pip-1.5.6-py2.py3-none-any.whl (renamed from Lib/ensurepip/_bundled/pip-1.5.4-py2.py3-none-any.whl)bin1169272 -> 1002021 bytes
-rw-r--r--Lib/fileinput.py5
-rw-r--r--Lib/idlelib/EditorWindow.py16
-rw-r--r--Lib/idlelib/aboutDialog.py11
-rw-r--r--Lib/idlelib/configSectionNameDialog.py28
-rw-r--r--Lib/idlelib/idle_test/htest.py93
-rw-r--r--Lib/importlib/_bootstrap.py23
-rwxr-xr-xLib/pydoc.py3
-rw-r--r--Lib/random.py4
-rw-r--r--Lib/test/script_helper.py12
-rw-r--r--Lib/test/test_asyncio/test_selector_events.py5
-rw-r--r--Lib/test/test_asyncio/test_streams.py39
-rw-r--r--Lib/test/test_cmd_line_script.py49
-rw-r--r--Lib/test/test_code_module.py2
-rw-r--r--Lib/test/test_faulthandler.py25
-rw-r--r--Lib/test/test_fileinput.py10
-rw-r--r--Lib/test/test_gc.py32
-rw-r--r--Lib/test/test_importlib/test_api.py15
-rw-r--r--Lib/test/test_io.py32
-rw-r--r--Lib/test/test_long.py7
-rw-r--r--Lib/test/test_re.py5
-rw-r--r--Lib/test/test_signal.py2
-rw-r--r--Lib/test/test_subprocess.py1
-rw-r--r--Lib/urllib/robotparser.py11
31 files changed, 421 insertions, 74 deletions
diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py
index d2bdc07..3d4a87a 100644
--- a/Lib/asyncio/base_events.py
+++ b/Lib/asyncio/base_events.py
@@ -775,11 +775,7 @@ class BaseEventLoop(events.AbstractEventLoop):
elif self._scheduled:
# Compute the desired timeout.
when = self._scheduled[0]._when
- deadline = max(0, when - self.time())
- if timeout is None:
- timeout = deadline
- else:
- timeout = min(timeout, deadline)
+ timeout = max(0, when - self.time())
# TODO: Instrumentation only in debug mode?
if logger.isEnabledFor(logging.INFO):
diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py
index 367c5fb..c7df8d8 100644
--- a/Lib/asyncio/selector_events.py
+++ b/Lib/asyncio/selector_events.py
@@ -87,10 +87,17 @@ class BaseSelectorEventLoop(base_events.BaseEventLoop):
pass
def _write_to_self(self):
- try:
- self._csock.send(b'x')
- except (BlockingIOError, InterruptedError):
- pass
+ # This may be called from a different thread, possibly after
+ # _close_self_pipe() has been called or even while it is
+ # running. Guard for self._csock being None or closed. When
+ # a socket is closed, send() raises OSError (with errno set to
+ # EBADF, but let's not rely on the exact error code).
+ csock = self._csock
+ if csock is not None:
+ try:
+ csock.send(b'x')
+ except OSError:
+ pass
def _start_serving(self, protocol_factory, sock,
sslcontext=None, server=None):
diff --git a/Lib/asyncio/streams.py b/Lib/asyncio/streams.py
index 27d595f..e239248 100644
--- a/Lib/asyncio/streams.py
+++ b/Lib/asyncio/streams.py
@@ -419,12 +419,17 @@ class StreamReader:
return b''
if n < 0:
- while not self._eof:
- self._waiter = self._create_waiter('read')
- try:
- yield from self._waiter
- finally:
- self._waiter = None
+ # This used to just loop creating a new waiter hoping to
+ # collect everything in self._buffer, but that would
+ # deadlock if the subprocess sends more than self.limit
+ # bytes. So just call self.read(self._limit) until EOF.
+ blocks = []
+ while True:
+ block = yield from self.read(self._limit)
+ if not block:
+ break
+ blocks.append(block)
+ return b''.join(blocks)
else:
if not self._buffer and not self._eof:
self._waiter = self._create_waiter('read')
diff --git a/Lib/ctypes/test/test_pep3118.py b/Lib/ctypes/test/test_pep3118.py
index ad13b01..32f802c 100644
--- a/Lib/ctypes/test/test_pep3118.py
+++ b/Lib/ctypes/test/test_pep3118.py
@@ -96,6 +96,9 @@ class EmptyStruct(Structure):
class aUnion(Union):
_fields_ = [("a", c_int)]
+class StructWithArrays(Structure):
+ _fields_ = [("x", c_long * 3 * 2), ("y", Point * 4)]
+
class Incomplete(Structure):
pass
@@ -145,10 +148,10 @@ native_types = [
## arrays and pointers
- (c_double * 4, "(4)<d", (4,), c_double),
- (c_float * 4 * 3 * 2, "(2,3,4)<f", (2,3,4), c_float),
- (POINTER(c_short) * 2, "(2)&<h", (2,), POINTER(c_short)),
- (POINTER(c_short) * 2 * 3, "(3,2)&<h", (3,2,), POINTER(c_short)),
+ (c_double * 4, "<d", (4,), c_double),
+ (c_float * 4 * 3 * 2, "<f", (2,3,4), c_float),
+ (POINTER(c_short) * 2, "&<h", (2,), POINTER(c_short)),
+ (POINTER(c_short) * 2 * 3, "&<h", (3,2,), POINTER(c_short)),
(POINTER(c_short * 2), "&(2)<h", (), POINTER(c_short)),
## structures and unions
@@ -160,6 +163,9 @@ native_types = [
(EmptyStruct, "T{}", (), EmptyStruct),
# the pep does't support unions
(aUnion, "B", (), aUnion),
+ # structure with sub-arrays
+ (StructWithArrays, "T{(2,3)<l:x:(4)T{<l:x:<l:y:}:y:}", (), StructWithArrays),
+ (StructWithArrays * 3, "T{(2,3)<l:x:(4)T{<l:x:<l:y:}:y:}", (3,), StructWithArrays),
## pointer to incomplete structure
(Incomplete, "B", (), Incomplete),
diff --git a/Lib/distutils/tests/support.py b/Lib/distutils/tests/support.py
index 71ad4f4..7385c6b 100644
--- a/Lib/distutils/tests/support.py
+++ b/Lib/distutils/tests/support.py
@@ -207,4 +207,4 @@ def fixup_build_ext(cmd):
cmd.library_dirs = []
else:
name, equals, value = runshared.partition('=')
- cmd.library_dirs = value.split(os.pathsep)
+ cmd.library_dirs = [d for d in value.split(os.pathsep) if d]
diff --git a/Lib/email/generator.py b/Lib/email/generator.py
index 1535210..4735721 100644
--- a/Lib/email/generator.py
+++ b/Lib/email/generator.py
@@ -51,8 +51,9 @@ class Generator:
by RFC 2822.
The policy keyword specifies a policy object that controls a number of
- aspects of the generator's operation. The default policy maintains
- backward compatibility.
+ aspects of the generator's operation. If no policy is specified,
+ the policy associated with the Message object passed to the
+ flatten method is used.
"""
self._fp = outfp
@@ -76,7 +77,9 @@ class Generator:
Note that for subobjects, no From_ line is printed.
linesep specifies the characters used to indicate a new line in
- the output. The default value is determined by the policy.
+ the output. The default value is determined by the policy specified
+ when the Generator instance was created or, if none was specified,
+ from the policy associated with the msg.
"""
# We use the _XXX constants for operating on data that comes directly
diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py
index 7cf6a4b..84c2125 100644
--- a/Lib/ensurepip/__init__.py
+++ b/Lib/ensurepip/__init__.py
@@ -10,7 +10,7 @@ __all__ = ["version", "bootstrap"]
_SETUPTOOLS_VERSION = "2.1"
-_PIP_VERSION = "1.5.4"
+_PIP_VERSION = "1.5.6"
# pip currently requires ssl support, so we try to provide a nicer
# error message when that is missing (http://bugs.python.org/issue19744)
diff --git a/Lib/ensurepip/_bundled/pip-1.5.4-py2.py3-none-any.whl b/Lib/ensurepip/_bundled/pip-1.5.6-py2.py3-none-any.whl
index e07d476..097ab43 100644
--- a/Lib/ensurepip/_bundled/pip-1.5.4-py2.py3-none-any.whl
+++ b/Lib/ensurepip/_bundled/pip-1.5.6-py2.py3-none-any.whl
Binary files differ
diff --git a/Lib/fileinput.py b/Lib/fileinput.py
index de29518..87758ad 100644
--- a/Lib/fileinput.py
+++ b/Lib/fileinput.py
@@ -320,7 +320,10 @@ class FileInput:
self._backupfilename = 0
if self._filename == '-':
self._filename = '<stdin>'
- self._file = sys.stdin
+ if 'b' in self._mode:
+ self._file = sys.stdin.buffer
+ else:
+ self._file = sys.stdin
self._isstdin = True
else:
if self._inplace:
diff --git a/Lib/idlelib/EditorWindow.py b/Lib/idlelib/EditorWindow.py
index cdb6775..06fb137 100644
--- a/Lib/idlelib/EditorWindow.py
+++ b/Lib/idlelib/EditorWindow.py
@@ -79,6 +79,8 @@ class HelpDialog(object):
self.parent = None
helpDialog = HelpDialog() # singleton instance
+def _Help_dialog(parent): # wrapper for htest
+ helpDialog.show_dialog(parent)
class EditorWindow(object):
@@ -1064,7 +1066,7 @@ class EditorWindow(object):
try:
try:
mod = importlib.import_module('.' + name, package=__package__)
- except ImportError:
+ except (ImportError, TypeError):
mod = importlib.import_module(name)
except ImportError:
print("\nFailed to import extension: ", name)
@@ -1700,19 +1702,21 @@ def fixwordbreaks(root):
tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]')
-def test():
- root = Tk()
+def _Editor_window(parent):
+ root = parent
fixwordbreaks(root)
root.withdraw()
if sys.argv[1:]:
filename = sys.argv[1]
else:
filename = None
+ macosxSupport.setupApp(root, None)
edit = EditorWindow(root=root, filename=filename)
edit.set_close_hook(root.quit)
edit.text.bind("<<close-all-windows>>", edit.close_event)
- root.mainloop()
- root.destroy()
if __name__ == '__main__':
- test()
+ from idlelib.idle_test.htest import run
+ if len(sys.argv) <= 1:
+ run(_Help_dialog)
+ run(_Editor_window)
diff --git a/Lib/idlelib/aboutDialog.py b/Lib/idlelib/aboutDialog.py
index 7fe1ab8..2b58013 100644
--- a/Lib/idlelib/aboutDialog.py
+++ b/Lib/idlelib/aboutDialog.py
@@ -12,7 +12,7 @@ class AboutDialog(Toplevel):
"""Modal about dialog for idle
"""
- def __init__(self,parent,title):
+ def __init__(self, parent, title):
Toplevel.__init__(self, parent)
self.configure(borderwidth=5)
self.geometry("+%d+%d" % (parent.winfo_rootx()+30,
@@ -136,10 +136,5 @@ class AboutDialog(Toplevel):
self.destroy()
if __name__ == '__main__':
- # test the dialog
- root = Tk()
- def run():
- from idlelib import aboutDialog
- aboutDialog.AboutDialog(root, 'About')
- Button(root, text='Dialog', command=run).pack()
- root.mainloop()
+ from idlelib.idle_test.htest import run
+ run(AboutDialog)
diff --git a/Lib/idlelib/configSectionNameDialog.py b/Lib/idlelib/configSectionNameDialog.py
index b05e38e..5137836 100644
--- a/Lib/idlelib/configSectionNameDialog.py
+++ b/Lib/idlelib/configSectionNameDialog.py
@@ -8,10 +8,11 @@ from tkinter import *
import tkinter.messagebox as tkMessageBox
class GetCfgSectionNameDialog(Toplevel):
- def __init__(self, parent, title, message, used_names):
+ def __init__(self, parent, title, message, used_names, _htest=False):
"""
message - string, informational message to display
used_names - string collection, names already in use for validity check
+ _htest - bool, change box location when running htest
"""
Toplevel.__init__(self, parent)
self.configure(borderwidth=5)
@@ -30,11 +31,12 @@ class GetCfgSectionNameDialog(Toplevel):
self.messageInfo.config(width=self.frameMain.winfo_reqwidth())
self.geometry(
"+%d+%d" % (
- parent.winfo_rootx() +
- (parent.winfo_width()/2 - self.winfo_reqwidth()/2),
- parent.winfo_rooty() +
- (parent.winfo_height()/2 - self.winfo_reqheight()/2)
- ) ) #centre dialog over parent
+ parent.winfo_rootx() +
+ (parent.winfo_width()/2 - self.winfo_reqwidth()/2),
+ parent.winfo_rooty() +
+ ((parent.winfo_height()/2 - self.winfo_reqheight()/2)
+ if not _htest else 100)
+ ) ) #centre dialog over parent (or below htest box)
self.deiconify() #geometry set, unhide
self.wait_window()
@@ -92,15 +94,5 @@ if __name__ == '__main__':
import unittest
unittest.main('idlelib.idle_test.test_config_name', verbosity=2, exit=False)
- # also human test the dialog
- root = Tk()
- def run():
- dlg=GetCfgSectionNameDialog(root,'Get Name',
- "After the text entered with [Ok] is stripped, <nothing>, "
- "'abc', or more that 30 chars are errors. "
- "Close with a valid entry (printed), [Cancel], or [X]",
- {'abc'})
- print(dlg.result)
- Message(root, text='').pack() # will be needed for oher dialog tests
- Button(root, text='Click to begin dialog test', command=run).pack()
- root.mainloop()
+ from idlelib.idle_test.htest import run
+ run(GetCfgSectionNameDialog)
diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py
new file mode 100644
index 0000000..001f7ee
--- /dev/null
+++ b/Lib/idlelib/idle_test/htest.py
@@ -0,0 +1,93 @@
+'''Run human tests of Idle's window, dialog, and popup widgets.
+
+run(test): run *test*, a callable that causes a widget to be displayed.
+runall(): run all tests defined in this file.
+
+Let X be a global name bound to a widget callable. End the module with
+
+if __name__ == '__main__':
+ <unittest, if there is one>
+ from idlelib.idle_test.htest import run
+ run(X)
+
+The X object must have a .__name__ attribute and a 'parent' parameter.
+X will often be a widget class, but a callable instance with .__name__
+or a wrapper function also work. The name of wrapper functions, like
+'_Editor_Window', should start with '_'.
+
+This file must contain a matching instance of the folling template,
+with X.__name__ prepended, as in '_Editor_window_spec ...'.
+
+_spec = {
+ 'file': '',
+ 'kwds': {'title': ''},
+ 'msg': ""
+ }
+
+file (no .py): used in runall() to import the file and get X.
+kwds: passed to X (**kwds), after 'parent' is added, to initialize X.
+title: an example; used for some widgets, delete if not.
+msg: displayed in a master window. Hints as to how the user might
+ test the widget. Close the window to skip or end the test.
+'''
+from importlib import import_module
+import tkinter as tk
+
+
+_Editor_window_spec = {
+ 'file': 'EditorWindow',
+ 'kwds': {},
+ 'msg': "Test editor functions of interest"
+ }
+
+_Help_dialog_spec = {
+ 'file': 'EditorWindow',
+ 'kwds': {},
+ 'msg': "If the help text displays, this works"
+ }
+
+AboutDialog_spec = {
+ 'file': 'aboutDialog',
+ 'kwds': {'title': 'About test'},
+ 'msg': "Try each button"
+ }
+
+
+GetCfgSectionNameDialog_spec = {
+ 'file': 'configSectionNameDialog',
+ 'kwds': {'title':'Get Name',
+ 'message':'Enter something',
+ 'used_names': {'abc'},
+ '_htest': True},
+ 'msg': "After the text entered with [Ok] is stripped, <nothing>, "
+ "'abc', or more that 30 chars are errors.\n"
+ "Close 'Get Name' with a valid entry (printed to Shell), [Cancel], or [X]",
+ }
+
+def run(test):
+ "Display a widget with callable *test* using a _spec dict"
+ root = tk.Tk()
+ test_spec = globals()[test.__name__ + '_spec']
+ test_kwds = test_spec['kwds']
+ test_kwds['parent'] = root
+
+ def run_test():
+ widget = test(**test_kwds)
+ try:
+ print(widget.result)
+ except AttributeError:
+ pass
+ tk.Label(root, text=test_spec['msg'], justify='left').pack()
+ tk.Button(root, text='Test ' + test.__name__, command=run_test).pack()
+ root.mainloop()
+
+def runall():
+ "Run all tests. Quick and dirty version."
+ for k, d in globals().items():
+ if k.endswith('_spec'):
+ mod = import_module('idlelib.' + d['file'])
+ test = getattr(mod, k[:-5])
+ run(test)
+
+if __name__ == '__main__':
+ runall()
diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py
index beaa9b3..b8836c1 100644
--- a/Lib/importlib/_bootstrap.py
+++ b/Lib/importlib/_bootstrap.py
@@ -1220,6 +1220,29 @@ class _SpecMethods:
return self._load_unlocked()
+def _fix_up_module(ns, name, pathname, cpathname=None):
+ # This function is used by PyImport_ExecCodeModuleObject().
+ loader = ns.get('__loader__')
+ spec = ns.get('__spec__')
+ if not loader:
+ if spec:
+ loader = spec.loader
+ elif pathname == cpathname:
+ loader = SourcelessFileLoader(name, pathname)
+ else:
+ loader = SourceFileLoader(name, pathname)
+ if not spec:
+ spec = spec_from_file_location(name, pathname, loader=loader)
+ try:
+ ns['__spec__'] = spec
+ ns['__loader__'] = loader
+ ns['__file__'] = pathname
+ ns['__cached__'] = cpathname
+ except Exception:
+ # Not important enough to report.
+ pass
+
+
# Loaders #####################################################################
class BuiltinImporter:
diff --git a/Lib/pydoc.py b/Lib/pydoc.py
index 5f12832..42f48e1 100755
--- a/Lib/pydoc.py
+++ b/Lib/pydoc.py
@@ -1404,6 +1404,9 @@ class _PlainTextDoc(TextDoc):
def pager(text):
"""The first time this is called, determine what kind of pager to use."""
global pager
+ # Escape non-encodable characters to avoid encoding errors later
+ encoding = sys.getfilesystemencoding()
+ text = text.encode(encoding, 'backslashreplace').decode(encoding)
pager = getpager()
pager(text)
diff --git a/Lib/random.py b/Lib/random.py
index 808175a..174e755a 100644
--- a/Lib/random.py
+++ b/Lib/random.py
@@ -105,7 +105,9 @@ class Random(_random.Random):
if a is None:
try:
- a = int.from_bytes(_urandom(32), 'big')
+ # Seed with enough bytes to span the 19937 bit
+ # state space for the Mersenne Twister
+ a = int.from_bytes(_urandom(2500), 'big')
except NotImplementedError:
import time
a = int(time.time() * 256) # use fractional seconds
diff --git a/Lib/test/script_helper.py b/Lib/test/script_helper.py
index af0545b..5559349 100644
--- a/Lib/test/script_helper.py
+++ b/Lib/test/script_helper.py
@@ -78,7 +78,7 @@ def assert_python_failure(*args, **env_vars):
"""
return _assert_python(False, *args, **env_vars)
-def spawn_python(*args, **kw):
+def spawn_python(*args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kw):
"""Run a Python subprocess with the given arguments.
kw is extra keyword args to pass to subprocess.Popen. Returns a Popen
@@ -86,8 +86,16 @@ def spawn_python(*args, **kw):
"""
cmd_line = [sys.executable, '-E']
cmd_line.extend(args)
+ # Under Fedora (?), GNU readline can output junk on stderr when initialized,
+ # depending on the TERM setting. Setting TERM=vt100 is supposed to disable
+ # that. References:
+ # - http://reinout.vanrees.org/weblog/2009/08/14/readline-invisible-character-hack.html
+ # - http://stackoverflow.com/questions/15760712/python-readline-module-prints-escape-character-during-import
+ # - http://lists.gnu.org/archive/html/bug-readline/2007-08/msg00004.html
+ env = kw.setdefault('env', dict(os.environ))
+ env['TERM'] = 'vt100'
return subprocess.Popen(cmd_line, stdin=subprocess.PIPE,
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
+ stdout=stdout, stderr=stderr,
**kw)
def kill_python(p):
diff --git a/Lib/test/test_asyncio/test_selector_events.py b/Lib/test/test_asyncio/test_selector_events.py
index 964b2e8..0735237 100644
--- a/Lib/test/test_asyncio/test_selector_events.py
+++ b/Lib/test/test_asyncio/test_selector_events.py
@@ -121,8 +121,9 @@ class BaseSelectorEventLoopTests(unittest.TestCase):
self.assertIsNone(self.loop._write_to_self())
def test_write_to_self_exception(self):
- self.loop._csock.send.side_effect = OSError()
- self.assertRaises(OSError, self.loop._write_to_self)
+ # _write_to_self() swallows OSError
+ self.loop._csock.send.side_effect = RuntimeError()
+ self.assertRaises(RuntimeError, self.loop._write_to_self)
def test_sock_recv(self):
sock = mock.Mock()
diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py
index 031499e..1ecc8eb 100644
--- a/Lib/test/test_asyncio/test_streams.py
+++ b/Lib/test/test_asyncio/test_streams.py
@@ -1,7 +1,9 @@
"""Tests for streams.py."""
import gc
+import os
import socket
+import sys
import unittest
from unittest import mock
try:
@@ -583,6 +585,43 @@ class StreamReaderTests(unittest.TestCase):
server.stop()
self.assertEqual(msg, b"hello world!\n")
+ @unittest.skipIf(sys.platform == 'win32', "Don't have pipes")
+ def test_read_all_from_pipe_reader(self):
+ # See Tulip issue 168. This test is derived from the example
+ # subprocess_attach_read_pipe.py, but we configure the
+ # StreamReader's limit so that twice it is less than the size
+ # of the data writter. Also we must explicitly attach a child
+ # watcher to the event loop.
+
+ code = """\
+import os, sys
+fd = int(sys.argv[1])
+os.write(fd, b'data')
+os.close(fd)
+"""
+ rfd, wfd = os.pipe()
+ args = [sys.executable, '-c', code, str(wfd)]
+
+ pipe = open(rfd, 'rb', 0)
+ reader = asyncio.StreamReader(loop=self.loop, limit=1)
+ protocol = asyncio.StreamReaderProtocol(reader, loop=self.loop)
+ transport, _ = self.loop.run_until_complete(
+ self.loop.connect_read_pipe(lambda: protocol, pipe))
+
+ watcher = asyncio.SafeChildWatcher()
+ watcher.attach_loop(self.loop)
+ try:
+ asyncio.set_child_watcher(watcher)
+ proc = self.loop.run_until_complete(
+ asyncio.create_subprocess_exec(*args, pass_fds={wfd}, loop=self.loop))
+ self.loop.run_until_complete(proc.wait())
+ finally:
+ asyncio.set_child_watcher(None)
+
+ os.close(wfd)
+ data = self.loop.run_until_complete(reader.read(-1))
+ self.assertEqual(data, b'data')
+
if __name__ == '__main__':
unittest.main()
diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py
index 1e6746d..88a9e2b 100644
--- a/Lib/test/test_cmd_line_script.py
+++ b/Lib/test/test_cmd_line_script.py
@@ -1,5 +1,6 @@
# tests command line execution of scripts
+import contextlib
import importlib
import importlib.machinery
import zipimport
@@ -8,6 +9,7 @@ import sys
import os
import os.path
import py_compile
+import subprocess
import textwrap
from test import support
@@ -173,6 +175,53 @@ class CmdLineTest(unittest.TestCase):
expected = repr(importlib.machinery.BuiltinImporter).encode("utf-8")
self.assertIn(expected, out)
+ @contextlib.contextmanager
+ def interactive_python(self, separate_stderr=False):
+ if separate_stderr:
+ p = spawn_python('-i', bufsize=1, stderr=subprocess.PIPE)
+ stderr = p.stderr
+ else:
+ p = spawn_python('-i', bufsize=1, stderr=subprocess.STDOUT)
+ stderr = p.stdout
+ try:
+ # Drain stderr until prompt
+ while True:
+ data = stderr.read(4)
+ if data == b">>> ":
+ break
+ stderr.readline()
+ yield p
+ finally:
+ kill_python(p)
+ stderr.close()
+
+ def check_repl_stdout_flush(self, separate_stderr=False):
+ with self.interactive_python(separate_stderr) as p:
+ p.stdin.write(b"print('foo')\n")
+ p.stdin.flush()
+ self.assertEqual(b'foo', p.stdout.readline().strip())
+
+ def check_repl_stderr_flush(self, separate_stderr=False):
+ with self.interactive_python(separate_stderr) as p:
+ p.stdin.write(b"1/0\n")
+ p.stdin.flush()
+ stderr = p.stderr if separate_stderr else p.stdout
+ self.assertIn(b'Traceback ', stderr.readline())
+ self.assertIn(b'File "<stdin>"', stderr.readline())
+ self.assertIn(b'ZeroDivisionError', stderr.readline())
+
+ def test_repl_stdout_flush(self):
+ self.check_repl_stdout_flush()
+
+ def test_repl_stdout_flush_separate_stderr(self):
+ self.check_repl_stdout_flush(True)
+
+ def test_repl_stderr_flush(self):
+ self.check_repl_stderr_flush()
+
+ def test_repl_stderr_flush_separate_stderr(self):
+ self.check_repl_stderr_flush(True)
+
def test_basic_script(self):
with temp_dir() as script_dir:
script_name = _make_test_script(script_dir, 'script')
diff --git a/Lib/test/test_code_module.py b/Lib/test/test_code_module.py
index 5fd21dc..7a80a80 100644
--- a/Lib/test/test_code_module.py
+++ b/Lib/test/test_code_module.py
@@ -51,7 +51,7 @@ class TestInteractiveConsole(unittest.TestCase):
self.infunc.side_effect = ["undefined", EOFError('Finished')]
self.console.interact()
for call in self.stderr.method_calls:
- if 'NameError:' in ''.join(call[1]):
+ if 'NameError' in ''.join(call[1]):
break
else:
raise AssertionError("No syntax error from console")
diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py
index ebd99ed..b0fc279 100644
--- a/Lib/test/test_faulthandler.py
+++ b/Lib/test/test_faulthandler.py
@@ -591,6 +591,31 @@ sys.exit(exitcode)
def test_register_chain(self):
self.check_register(chain=True)
+ @contextmanager
+ def check_stderr_none(self):
+ stderr = sys.stderr
+ try:
+ sys.stderr = None
+ with self.assertRaises(RuntimeError) as cm:
+ yield
+ self.assertEqual(str(cm.exception), "sys.stderr is None")
+ finally:
+ sys.stderr = stderr
+
+ def test_stderr_None(self):
+ # Issue #21497: provide an helpful error if sys.stderr is None,
+ # instead of just an attribute error: "None has no attribute fileno".
+ with self.check_stderr_none():
+ faulthandler.enable()
+ with self.check_stderr_none():
+ faulthandler.dump_traceback()
+ if hasattr(faulthandler, 'dump_traceback_later'):
+ with self.check_stderr_none():
+ faulthandler.dump_traceback_later(1e-3)
+ if hasattr(faulthandler, "register"):
+ with self.check_stderr_none():
+ faulthandler.register(signal.SIGUSR1)
+
if __name__ == "__main__":
unittest.main()
diff --git a/Lib/test/test_fileinput.py b/Lib/test/test_fileinput.py
index eba55c9..1d089f5 100644
--- a/Lib/test/test_fileinput.py
+++ b/Lib/test/test_fileinput.py
@@ -19,11 +19,12 @@ try:
except ImportError:
gzip = None
-from io import StringIO
+from io import BytesIO, StringIO
from fileinput import FileInput, hook_encoded
from test.support import verbose, TESTFN, run_unittest, check_warnings
from test.support import unlink as safe_unlink
+from unittest import mock
# The fileinput module has 2 interfaces: the FileInput class which does
@@ -232,6 +233,13 @@ class FileInputTests(unittest.TestCase):
finally:
remove_tempfiles(t1)
+ def test_stdin_binary_mode(self):
+ with mock.patch('sys.stdin') as m_stdin:
+ m_stdin.buffer = BytesIO(b'spam, bacon, sausage, and spam')
+ fi = FileInput(files=['-'], mode='rb')
+ lines = list(fi)
+ self.assertEqual(lines, [b'spam, bacon, sausage, and spam'])
+
def test_file_opening_hook(self):
try:
# cannot use openhook and inplace mode
diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py
index 7eb104a..c0be537 100644
--- a/Lib/test/test_gc.py
+++ b/Lib/test/test_gc.py
@@ -580,6 +580,38 @@ class GCTests(unittest.TestCase):
# would be damaged, with an empty __dict__.
self.assertEqual(x, None)
+ def test_bug21435(self):
+ # This is a poor test - its only virtue is that it happened to
+ # segfault on Tim's Windows box before the patch for 21435 was
+ # applied. That's a nasty bug relying on specific pieces of cyclic
+ # trash appearing in exactly the right order in finalize_garbage()'s
+ # input list.
+ # But there's no reliable way to force that order from Python code,
+ # so over time chances are good this test won't really be testing much
+ # of anything anymore. Still, if it blows up, there's _some_
+ # problem ;-)
+ gc.collect()
+
+ class A:
+ pass
+
+ class B:
+ def __init__(self, x):
+ self.x = x
+
+ def __del__(self):
+ self.attr = None
+
+ def do_work():
+ a = A()
+ b = B(A())
+
+ a.attr = b
+ b.attr = a
+
+ do_work()
+ gc.collect() # this blows up (bad C pointer) when it fails
+
@cpython_only
def test_garbage_at_shutdown(self):
import subprocess
diff --git a/Lib/test/test_importlib/test_api.py b/Lib/test/test_importlib/test_api.py
index 744001b..2a2d42b 100644
--- a/Lib/test/test_importlib/test_api.py
+++ b/Lib/test/test_importlib/test_api.py
@@ -241,13 +241,13 @@ class ReloadTests:
'__file__': path,
'__cached__': cached,
'__doc__': None,
- '__builtins__': __builtins__,
}
support.create_empty_file(path)
module = self.init.import_module(name)
- ns = vars(module)
+ ns = vars(module).copy()
loader = ns.pop('__loader__')
spec = ns.pop('__spec__')
+ ns.pop('__builtins__', None) # An implementation detail.
self.assertEqual(spec.name, name)
self.assertEqual(spec.loader, loader)
self.assertEqual(loader.path, path)
@@ -263,14 +263,14 @@ class ReloadTests:
'__cached__': cached,
'__path__': [os.path.dirname(init_path)],
'__doc__': None,
- '__builtins__': __builtins__,
}
os.mkdir(name)
os.rename(path, init_path)
reloaded = self.init.reload(module)
- ns = vars(reloaded)
+ ns = vars(reloaded).copy()
loader = ns.pop('__loader__')
spec = ns.pop('__spec__')
+ ns.pop('__builtins__', None) # An implementation detail.
self.assertEqual(spec.name, name)
self.assertEqual(spec.loader, loader)
self.assertIs(reloaded, module)
@@ -295,10 +295,11 @@ class ReloadTests:
with open(bad_path, 'w') as init_file:
init_file.write('eggs = None')
module = self.init.import_module(name)
- ns = vars(module)
+ ns = vars(module).copy()
loader = ns.pop('__loader__')
path = ns.pop('__path__')
spec = ns.pop('__spec__')
+ ns.pop('__builtins__', None) # An implementation detail.
self.assertEqual(spec.name, name)
self.assertIs(spec.loader, None)
self.assertIsNot(loader, None)
@@ -319,14 +320,14 @@ class ReloadTests:
'__cached__': cached,
'__path__': [os.path.dirname(init_path)],
'__doc__': None,
- '__builtins__': __builtins__,
'eggs': None,
}
os.rename(bad_path, init_path)
reloaded = self.init.reload(module)
- ns = vars(reloaded)
+ ns = vars(reloaded).copy()
loader = ns.pop('__loader__')
spec = ns.pop('__spec__')
+ ns.pop('__builtins__', None) # An implementation detail.
self.assertEqual(spec.name, name)
self.assertEqual(spec.loader, loader)
self.assertIs(reloaded, module)
diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py
index 267537f..ef1e056 100644
--- a/Lib/test/test_io.py
+++ b/Lib/test/test_io.py
@@ -2615,6 +2615,38 @@ class TextIOWrapperTest(unittest.TestCase):
txt.write('5')
self.assertEqual(b''.join(raw._write_stack), b'123\n45')
+ def test_bufio_write_through(self):
+ # Issue #21396: write_through=True doesn't force a flush()
+ # on the underlying binary buffered object.
+ flush_called, write_called = [], []
+ class BufferedWriter(self.BufferedWriter):
+ def flush(self, *args, **kwargs):
+ flush_called.append(True)
+ return super().flush(*args, **kwargs)
+ def write(self, *args, **kwargs):
+ write_called.append(True)
+ return super().write(*args, **kwargs)
+
+ rawio = self.BytesIO()
+ data = b"a"
+ bufio = BufferedWriter(rawio, len(data)*2)
+ textio = self.TextIOWrapper(bufio, encoding='ascii',
+ write_through=True)
+ # write to the buffered io but don't overflow the buffer
+ text = data.decode('ascii')
+ textio.write(text)
+
+ # buffer.flush is not called with write_through=True
+ self.assertFalse(flush_called)
+ # buffer.write *is* called with write_through=True
+ self.assertTrue(write_called)
+ self.assertEqual(rawio.getvalue(), b"") # no flush
+
+ write_called = [] # reset
+ textio.write(text * 10) # total content is larger than bufio buffer
+ self.assertTrue(write_called)
+ self.assertEqual(rawio.getvalue(), data * 11) # all flushed
+
def test_read_nonbytes(self):
# Issue #17106
# Crash when underlying read() returns non-bytes
diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py
index 13152ec..5f14795 100644
--- a/Lib/test/test_long.py
+++ b/Lib/test/test_long.py
@@ -1235,6 +1235,13 @@ class LongTest(unittest.TestCase):
for n in map(int, integers):
self.assertEqual(n, 0)
+ def test_shift_bool(self):
+ # Issue #21422: ensure that bool << int and bool >> int return int
+ for value in (True, False):
+ for shift in (0, 2):
+ self.assertEqual(type(value << shift), int)
+ self.assertEqual(type(value >> shift), int)
+
def test_main():
support.run_unittest(LongTest)
diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py
index 33ccd15..0c8a52f 100644
--- a/Lib/test/test_re.py
+++ b/Lib/test/test_re.py
@@ -1223,6 +1223,11 @@ class ReTests(unittest.TestCase):
pat.scanner(string='abracadabra', pos=3, endpos=10).search().span(),
(7, 9))
+ def test_bug_20998(self):
+ # Issue #20998: Fullmatch of repeated single character pattern
+ # with ignore case.
+ self.assertEqual(re.fullmatch('[a-c]+', 'ABC', re.I).span(), (0, 3))
+
class PatternReprTests(unittest.TestCase):
def check(self, pattern, expected):
diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py
index a6f2c64..74f74af 100644
--- a/Lib/test/test_signal.py
+++ b/Lib/test/test_signal.py
@@ -454,7 +454,7 @@ class SiginterruptTest(unittest.TestCase):
stdout = first_line + stdout
exitcode = process.wait()
if exitcode not in (2, 3):
- raise Exception("Child error (exit code %s): %s"
+ raise Exception("Child error (exit code %s): %r"
% (exitcode, stdout))
return (exitcode == 3)
diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py
index 4c8d493..32ffb5f 100644
--- a/Lib/test/test_subprocess.py
+++ b/Lib/test/test_subprocess.py
@@ -786,6 +786,7 @@ class ProcessTestCase(BaseTestCase):
stdout=subprocess.PIPE,
universal_newlines=1)
p.stdin.write("line1\n")
+ p.stdin.flush()
self.assertEqual(p.stdout.readline(), "line1\n")
p.stdin.write("line3\n")
p.stdin.close()
diff --git a/Lib/urllib/robotparser.py b/Lib/urllib/robotparser.py
index 978ba58..1d7b751 100644
--- a/Lib/urllib/robotparser.py
+++ b/Lib/urllib/robotparser.py
@@ -7,7 +7,7 @@
2) PSF license for Python 2.2
The robots.txt Exclusion Protocol is implemented as specified in
- http://info.webcrawler.com/mak/projects/robots/norobots-rfc.html
+ http://www.robotstxt.org/norobots-rfc.txt
"""
import urllib.parse, urllib.request
@@ -57,7 +57,7 @@ class RobotFileParser:
except urllib.error.HTTPError as err:
if err.code in (401, 403):
self.disallow_all = True
- elif err.code >= 400:
+ elif err.code >= 400 and err.code < 500:
self.allow_all = True
else:
raw = f.read()
@@ -85,6 +85,7 @@ class RobotFileParser:
state = 0
entry = Entry()
+ self.modified()
for line in lines:
if not line:
if state == 1:
@@ -129,6 +130,12 @@ class RobotFileParser:
return False
if self.allow_all:
return True
+ # Until the robots.txt file has been read or found not
+ # to exist, we must assume that no url is allowable.
+ # This prevents false positives when a user erronenously
+ # calls can_fetch() before calling read().
+ if not self.last_checked:
+ return False
# search for given user agent matches
# the first match counts
parsed_url = urllib.parse.urlparse(urllib.parse.unquote(url))