summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLarry Hastings <larry@hastings.org>2014-09-22 14:21:08 (GMT)
committerLarry Hastings <larry@hastings.org>2014-09-22 14:21:08 (GMT)
commit2887f76d4512283cf332ca22bef19bc86f4a3147 (patch)
treee3cbf66f90a18678e6ce19b9b5c72f1e3d2e9ec1
parentf26c2e72d8306b733fe9dad539522a3c04d69798 (diff)
parentca2e02cfe68b6d5ddf6cd3f143fe29bd748d0f12 (diff)
downloadcpython-2887f76d4512283cf332ca22bef19bc86f4a3147.zip
cpython-2887f76d4512283cf332ca22bef19bc86f4a3147.tar.gz
cpython-2887f76d4512283cf332ca22bef19bc86f4a3147.tar.bz2
Merge.
-rw-r--r--Doc/library/email.parser.rst2
-rw-r--r--Doc/library/subprocess.rst18
-rw-r--r--Lib/sre_parse.py44
-rw-r--r--Lib/subprocess.py3
-rw-r--r--Lib/test/test_re.py29
-rw-r--r--Lib/test/test_subprocess.py33
-rw-r--r--Lib/test/test_threading.py85
-rw-r--r--Lib/threading.py16
-rw-r--r--Misc/NEWS9
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:
diff --git a/Misc/NEWS b/Misc/NEWS
index c6107d5..5cdb5bd 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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?
==============================