diff options
author | Larry Hastings <larry@hastings.org> | 2014-09-22 14:21:08 (GMT) |
---|---|---|
committer | Larry Hastings <larry@hastings.org> | 2014-09-22 14:21:08 (GMT) |
commit | 2887f76d4512283cf332ca22bef19bc86f4a3147 (patch) | |
tree | e3cbf66f90a18678e6ce19b9b5c72f1e3d2e9ec1 | |
parent | f26c2e72d8306b733fe9dad539522a3c04d69798 (diff) | |
parent | ca2e02cfe68b6d5ddf6cd3f143fe29bd748d0f12 (diff) | |
download | cpython-2887f76d4512283cf332ca22bef19bc86f4a3147.zip cpython-2887f76d4512283cf332ca22bef19bc86f4a3147.tar.gz cpython-2887f76d4512283cf332ca22bef19bc86f4a3147.tar.bz2 |
Merge.
-rw-r--r-- | Doc/library/email.parser.rst | 2 | ||||
-rw-r--r-- | Doc/library/subprocess.rst | 18 | ||||
-rw-r--r-- | Lib/sre_parse.py | 44 | ||||
-rw-r--r-- | Lib/subprocess.py | 3 | ||||
-rw-r--r-- | Lib/test/test_re.py | 29 | ||||
-rw-r--r-- | Lib/test/test_subprocess.py | 33 | ||||
-rw-r--r-- | Lib/test/test_threading.py | 85 | ||||
-rw-r--r-- | Lib/threading.py | 16 | ||||
-rw-r--r-- | Misc/NEWS | 9 |
9 files changed, 199 insertions, 40 deletions
diff --git a/Doc/library/email.parser.rst b/Doc/library/email.parser.rst index 9f137cd..ec74fe0 100644 --- a/Doc/library/email.parser.rst +++ b/Doc/library/email.parser.rst @@ -181,7 +181,7 @@ have the same API as the :class:`Parser` and :class:`BytesParser` classes. .. versionchanged:: 3.3 Removed the *strict* argument. Added the *policy* keyword. - .. method:: parse(fp, headeronly=False) + .. method:: parse(fp, headersonly=False) Read all the data from the binary file-like object *fp*, parse the resulting bytes, and return the message object. *fp* must support diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index 854993c..b2238f0 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -406,12 +406,18 @@ functions. Read the `Security Considerations`_ section before using ``shell=True``. - *bufsize* will be supplied as the corresponding argument to the :func:`open` - function when creating the stdin/stdout/stderr pipe file objects: :const:`0` - means unbuffered (read and write are one system call and can return short), - :const:`1` means line buffered, any other positive value means use a buffer - of approximately that size. A negative bufsize (the default) means the - system default of io.DEFAULT_BUFFER_SIZE will be used. + *bufsize* will be supplied as the corresponding argument to the + :func:`open` function when creating the stdin/stdout/stderr pipe + file objects: + + - :const:`0` means unbuffered (read and write are one + system call and can return short) + - :const:`1` means line buffered + (only usable if ``universal_newlines=True`` i.e., in a text mode) + - any other positive value means use a buffer of approximately that + size + - negative bufsize (the default) means the system default of + io.DEFAULT_BUFFER_SIZE will be used. .. versionchanged:: 3.3.1 *bufsize* now defaults to -1 to enable buffering by default to match the diff --git a/Lib/sre_parse.py b/Lib/sre_parse.py index 3209ce0..b56d437 100644 --- a/Lib/sre_parse.py +++ b/Lib/sre_parse.py @@ -94,33 +94,45 @@ class SubPattern: self.data = data self.width = None def dump(self, level=0): - nl = 1 + nl = True seqtypes = (tuple, list) for op, av in self.data: - print(level*" " + op, end=' '); nl = 0 - if op == "in": + print(level*" " + op, end='') + if op == IN: # member sublanguage - print(); nl = 1 + print() for op, a in av: print((level+1)*" " + op, a) - elif op == "branch": - print(); nl = 1 - i = 0 - for a in av[1]: - if i > 0: + elif op == BRANCH: + print() + for i, a in enumerate(av[1]): + if i: print(level*" " + "or") - a.dump(level+1); nl = 1 - i = i + 1 + a.dump(level+1) + elif op == GROUPREF_EXISTS: + condgroup, item_yes, item_no = av + print('', condgroup) + item_yes.dump(level+1) + if item_no: + print(level*" " + "else") + item_no.dump(level+1) elif isinstance(av, seqtypes): + nl = False for a in av: if isinstance(a, SubPattern): - if not nl: print() - a.dump(level+1); nl = 1 + if not nl: + print() + a.dump(level+1) + nl = True else: - print(a, end=' ') ; nl = 0 + if not nl: + print(' ', end='') + print(a, end='') + nl = False + if not nl: + print() else: - print(av, end=' ') ; nl = 0 - if not nl: print() + print('', av) def __repr__(self): return repr(self.data) def __len__(self): diff --git a/Lib/subprocess.py b/Lib/subprocess.py index ddc033a..bc0ef0b 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -837,7 +837,8 @@ class Popen(object): if p2cwrite != -1: self.stdin = io.open(p2cwrite, 'wb', bufsize) if universal_newlines: - self.stdin = io.TextIOWrapper(self.stdin, write_through=True) + self.stdin = io.TextIOWrapper(self.stdin, write_through=True, + line_buffering=(bufsize == 1)) if c2pread != -1: self.stdout = io.open(c2pread, 'rb', bufsize) if universal_newlines: diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index 011fba9..e937c85 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -1203,16 +1203,33 @@ class ReTests(unittest.TestCase): self.assertEqual(m.group(2), "y") def test_debug_flag(self): + pat = r'(\.)(?:[ch]|py)(?(1)$|: )' with captured_stdout() as out: - re.compile('foo', re.DEBUG) - self.assertEqual(out.getvalue().splitlines(), - ['literal 102 ', 'literal 111 ', 'literal 111 ']) + re.compile(pat, re.DEBUG) + dump = '''\ +subpattern 1 + literal 46 +subpattern None + branch + in + literal 99 + literal 104 + or + literal 112 + literal 121 +subpattern None + groupref_exists 1 + at at_end + else + literal 58 + literal 32 +''' + self.assertEqual(out.getvalue(), dump) # Debug output is output again even a second time (bypassing # the cache -- issue #20426). with captured_stdout() as out: - re.compile('foo', re.DEBUG) - self.assertEqual(out.getvalue().splitlines(), - ['literal 102 ', 'literal 111 ', 'literal 111 ']) + re.compile(pat, re.DEBUG) + self.assertEqual(out.getvalue(), dump) def test_keyword_parameters(self): # Issue #20283: Accepting the string keyword parameter. diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index d0ab718..9616da0 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1008,6 +1008,39 @@ class ProcessTestCase(BaseTestCase): p = subprocess.Popen([sys.executable, "-c", "pass"], bufsize=None) self.assertEqual(p.wait(), 0) + def _test_bufsize_equal_one(self, line, expected, universal_newlines): + # subprocess may deadlock with bufsize=1, see issue #21332 + with subprocess.Popen([sys.executable, "-c", "import sys;" + "sys.stdout.write(sys.stdin.readline());" + "sys.stdout.flush()"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + bufsize=1, + universal_newlines=universal_newlines) as p: + p.stdin.write(line) # expect that it flushes the line in text mode + os.close(p.stdin.fileno()) # close it without flushing the buffer + read_line = p.stdout.readline() + try: + p.stdin.close() + except OSError: + pass + p.stdin = None + self.assertEqual(p.returncode, 0) + self.assertEqual(read_line, expected) + + def test_bufsize_equal_one_text_mode(self): + # line is flushed in text mode with bufsize=1. + # we should get the full line in return + line = "line\n" + self._test_bufsize_equal_one(line, line, universal_newlines=True) + + def test_bufsize_equal_one_binary_mode(self): + # line is not flushed in binary mode with bufsize=1. + # we should get empty response + line = b'line' + os.linesep.encode() # assume ascii-based locale + self._test_bufsize_equal_one(line, b'', universal_newlines=False) + def test_leaking_fds_on_error(self): # see bug #5179: Popen leaks file descriptors to PIPEs if # the child fails to execute; this will eventually exhaust diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 7170f60..98f01ee 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -4,7 +4,7 @@ Tests for the threading module. import test.support from test.support import verbose, strip_python_stderr, import_module, cpython_only -from test.script_helper import assert_python_ok +from test.script_helper import assert_python_ok, assert_python_failure import random import re @@ -15,7 +15,6 @@ import time import unittest import weakref import os -from test.script_helper import assert_python_ok, assert_python_failure import subprocess from test import lock_tests @@ -962,6 +961,88 @@ class ThreadingExceptionTests(BaseTestCase): self.assertEqual(p.returncode, 0, "Unexpected error: " + stderr.decode()) self.assertEqual(data, expected_output) + def test_print_exception(self): + script = r"""if True: + import threading + import time + + running = False + def run(): + global running + running = True + while running: + time.sleep(0.01) + 1/0 + t = threading.Thread(target=run) + t.start() + while not running: + time.sleep(0.01) + running = False + t.join() + """ + rc, out, err = assert_python_ok("-c", script) + self.assertEqual(out, b'') + err = err.decode() + self.assertIn("Exception in thread", err) + self.assertIn("Traceback (most recent call last):", err) + self.assertIn("ZeroDivisionError", err) + self.assertNotIn("Unhandled exception", err) + + def test_print_exception_stderr_is_none_1(self): + script = r"""if True: + import sys + import threading + import time + + running = False + def run(): + global running + running = True + while running: + time.sleep(0.01) + 1/0 + t = threading.Thread(target=run) + t.start() + while not running: + time.sleep(0.01) + sys.stderr = None + running = False + t.join() + """ + rc, out, err = assert_python_ok("-c", script) + self.assertEqual(out, b'') + err = err.decode() + self.assertIn("Exception in thread", err) + self.assertIn("Traceback (most recent call last):", err) + self.assertIn("ZeroDivisionError", err) + self.assertNotIn("Unhandled exception", err) + + def test_print_exception_stderr_is_none_2(self): + script = r"""if True: + import sys + import threading + import time + + running = False + def run(): + global running + running = True + while running: + time.sleep(0.01) + 1/0 + sys.stderr = None + t = threading.Thread(target=run) + t.start() + while not running: + time.sleep(0.01) + running = False + t.join() + """ + rc, out, err = assert_python_ok("-c", script) + self.assertEqual(out, b'') + self.assertNotIn("Unhandled exception", err.decode()) + + class TimerTests(BaseTestCase): def setUp(self): diff --git a/Lib/threading.py b/Lib/threading.py index dfe9d41..7a5a440 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -248,7 +248,7 @@ class Condition: def _is_owned(self): # Return True if lock is owned by current_thread. - # This method is called only if __lock doesn't have _is_owned(). + # This method is called only if _lock doesn't have _is_owned(). if self._lock.acquire(0): self._lock.release() return False @@ -749,12 +749,12 @@ class Thread: """ - __initialized = False + _initialized = False # Need to store a reference to sys.exc_info for printing # out exceptions when a thread tries to use a global var. during interp. # shutdown and thus raises an exception about trying to perform some # operation on/with a NoneType - __exc_info = _sys.exc_info + _exc_info = _sys.exc_info # Keep sys.exc_clear too to clear the exception just before # allowing .join() to return. #XXX __exc_clear = _sys.exc_clear @@ -926,10 +926,10 @@ class Thread: # shutdown) use self._stderr. Otherwise still use sys (as in # _sys) in case sys.stderr was redefined since the creation of # self. - if _sys: - _sys.stderr.write("Exception in thread %s:\n%s\n" % - (self.name, _format_exc())) - else: + if _sys and _sys.stderr is not None: + print("Exception in thread %s:\n%s" % + (self.name, _format_exc()), file=self._stderr) + elif self._stderr is not None: # Do the best job possible w/o a huge amt. of code to # approximate a traceback (code ideas from # Lib/traceback.py) @@ -957,7 +957,7 @@ class Thread: # test_threading.test_no_refcycle_through_target when # the exception keeps the target alive past when we # assert that it's dead. - #XXX self.__exc_clear() + #XXX self._exc_clear() pass finally: with _active_limbo_lock: @@ -13,6 +13,15 @@ Core and Builtins Library ------- +- Issue #22415: Fixed debugging output of the GROUPREF_EXISTS opcode in the re + module. Removed trailing spaces in debugging output. + +- Issue #22423: Unhandled exception in thread no longer causes unhandled + AttributeError when sys.stderr is None. + +- Issue #21332: Ensure that ``bufsize=1`` in subprocess.Popen() selects + line buffering, rather than block buffering. Patch by Akira Li. + What's New in Python 3.4.2rc1? ============================== |