summaryrefslogtreecommitdiff
path: root/Lib
diff options
context:
space:
mode:
authorBenjamin Peterson <benjamin@python.org>2012-10-09 11:16:26 -0400
committerBenjamin Peterson <benjamin@python.org>2012-10-09 11:16:26 -0400
commit455fa0a314b7f7edd0c8554b12a65267ff1e2e5b (patch)
treea4db0395d8674c1e3c4119f0edccf72307b34e49 /Lib
parentb29614e047110f4d9af993a6cdec4e3fb7ef9738 (diff)
parent831893a68ec7114c1fc9c8e36b9159f5c1db50c7 (diff)
downloadcpython-git-455fa0a314b7f7edd0c8554b12a65267ff1e2e5b.tar.gz
merge heads
Diffstat (limited to 'Lib')
-rw-r--r--Lib/bz2.py105
-rw-r--r--Lib/codecs.py2
-rw-r--r--Lib/ctypes/__init__.py2
-rw-r--r--Lib/ctypes/test/test_win32.py22
-rw-r--r--Lib/idlelib/config-extensions.def2
-rw-r--r--Lib/ipaddress.py7
-rw-r--r--Lib/logging/handlers.py10
-rwxr-xr-xLib/platform.py12
-rw-r--r--Lib/pstats.py6
-rw-r--r--Lib/socketserver.py2
-rw-r--r--Lib/subprocess.py9
-rw-r--r--Lib/test/subprocessdata/sigchild_ignore.py11
-rw-r--r--Lib/test/test_capi.py11
-rw-r--r--Lib/test/test_httpservers.py1
-rw-r--r--Lib/test/test_subprocess.py156
-rw-r--r--Lib/test/test_threaded_import.py4
-rw-r--r--Lib/test/test_xml_etree.py44
-rw-r--r--Lib/xml/etree/ElementTree.py4
18 files changed, 318 insertions, 92 deletions
diff --git a/Lib/bz2.py b/Lib/bz2.py
index a50adf79f7..c3075073f0 100644
--- a/Lib/bz2.py
+++ b/Lib/bz2.py
@@ -79,7 +79,8 @@ class BZ2File(io.BufferedIOBase):
mode = "rb"
mode_code = _MODE_READ
self._decompressor = BZ2Decompressor()
- self._buffer = None
+ self._buffer = b""
+ self._buffer_offset = 0
elif mode in ("w", "wb"):
mode = "wb"
mode_code = _MODE_WRITE
@@ -124,7 +125,8 @@ class BZ2File(io.BufferedIOBase):
self._fp = None
self._closefp = False
self._mode = _MODE_CLOSED
- self._buffer = None
+ self._buffer = b""
+ self._buffer_offset = 0
@property
def closed(self):
@@ -157,15 +159,18 @@ class BZ2File(io.BufferedIOBase):
raise ValueError("I/O operation on closed file")
def _check_can_read(self):
- if not self.readable():
+ if self._mode not in (_MODE_READ, _MODE_READ_EOF):
+ self._check_not_closed()
raise io.UnsupportedOperation("File not open for reading")
def _check_can_write(self):
- if not self.writable():
+ if self._mode != _MODE_WRITE:
+ self._check_not_closed()
raise io.UnsupportedOperation("File not open for writing")
def _check_can_seek(self):
- if not self.readable():
+ if self._mode not in (_MODE_READ, _MODE_READ_EOF):
+ self._check_not_closed()
raise io.UnsupportedOperation("Seeking is only supported "
"on files open for reading")
if not self._fp.seekable():
@@ -174,16 +179,13 @@ class BZ2File(io.BufferedIOBase):
# Fill the readahead buffer if it is empty. Returns False on EOF.
def _fill_buffer(self):
+ if self._mode == _MODE_READ_EOF:
+ return False
# Depending on the input data, our call to the decompressor may not
# return any data. In this case, try again after reading another block.
- while True:
- if self._buffer:
- return True
-
- if self._decompressor.unused_data:
- rawblock = self._decompressor.unused_data
- else:
- rawblock = self._fp.read(_BUFFER_SIZE)
+ while self._buffer_offset == len(self._buffer):
+ rawblock = (self._decompressor.unused_data or
+ self._fp.read(_BUFFER_SIZE))
if not rawblock:
if self._decompressor.eof:
@@ -199,30 +201,48 @@ class BZ2File(io.BufferedIOBase):
self._decompressor = BZ2Decompressor()
self._buffer = self._decompressor.decompress(rawblock)
+ self._buffer_offset = 0
+ return True
# Read data until EOF.
# If return_data is false, consume the data without returning it.
def _read_all(self, return_data=True):
+ # The loop assumes that _buffer_offset is 0. Ensure that this is true.
+ self._buffer = self._buffer[self._buffer_offset:]
+ self._buffer_offset = 0
+
blocks = []
while self._fill_buffer():
if return_data:
blocks.append(self._buffer)
self._pos += len(self._buffer)
- self._buffer = None
+ self._buffer = b""
if return_data:
return b"".join(blocks)
# Read a block of up to n bytes.
# If return_data is false, consume the data without returning it.
def _read_block(self, n, return_data=True):
+ # If we have enough data buffered, return immediately.
+ end = self._buffer_offset + n
+ if end <= len(self._buffer):
+ data = self._buffer[self._buffer_offset : end]
+ self._buffer_offset = end
+ self._pos += len(data)
+ return data if return_data else None
+
+ # The loop assumes that _buffer_offset is 0. Ensure that this is true.
+ self._buffer = self._buffer[self._buffer_offset:]
+ self._buffer_offset = 0
+
blocks = []
while n > 0 and self._fill_buffer():
if n < len(self._buffer):
data = self._buffer[:n]
- self._buffer = self._buffer[n:]
+ self._buffer_offset = n
else:
data = self._buffer
- self._buffer = None
+ self._buffer = b""
if return_data:
blocks.append(data)
self._pos += len(data)
@@ -238,9 +258,9 @@ class BZ2File(io.BufferedIOBase):
"""
with self._lock:
self._check_can_read()
- if self._mode == _MODE_READ_EOF or not self._fill_buffer():
+ if not self._fill_buffer():
return b""
- return self._buffer
+ return self._buffer[self._buffer_offset:]
def read(self, size=-1):
"""Read up to size uncompressed bytes from the file.
@@ -250,7 +270,7 @@ class BZ2File(io.BufferedIOBase):
"""
with self._lock:
self._check_can_read()
- if self._mode == _MODE_READ_EOF or size == 0:
+ if size == 0:
return b""
elif size < 0:
return self._read_all()
@@ -268,15 +288,19 @@ class BZ2File(io.BufferedIOBase):
# In this case we make multiple reads, to avoid returning b"".
with self._lock:
self._check_can_read()
- if (size == 0 or self._mode == _MODE_READ_EOF or
- not self._fill_buffer()):
+ if (size == 0 or
+ # Only call _fill_buffer() if the buffer is actually empty.
+ # This gives a significant speedup if *size* is small.
+ (self._buffer_offset == len(self._buffer) and not self._fill_buffer())):
return b""
- if 0 < size < len(self._buffer):
- data = self._buffer[:size]
- self._buffer = self._buffer[size:]
+ if size > 0:
+ data = self._buffer[self._buffer_offset :
+ self._buffer_offset + size]
+ self._buffer_offset += len(data)
else:
- data = self._buffer
- self._buffer = None
+ data = self._buffer[self._buffer_offset:]
+ self._buffer = b""
+ self._buffer_offset = 0
self._pos += len(data)
return data
@@ -295,10 +319,20 @@ class BZ2File(io.BufferedIOBase):
non-negative, no more than size bytes will be read (in which
case the line may be incomplete). Returns b'' if already at EOF.
"""
- if not hasattr(size, "__index__"):
- raise TypeError("Integer argument expected")
- size = size.__index__()
+ if not isinstance(size, int):
+ if not hasattr(size, "__index__"):
+ raise TypeError("Integer argument expected")
+ size = size.__index__()
with self._lock:
+ self._check_can_read()
+ # Shortcut for the common case - the whole line is in the buffer.
+ if size < 0:
+ end = self._buffer.find(b"\n", self._buffer_offset) + 1
+ if end > 0:
+ line = self._buffer[self._buffer_offset : end]
+ self._buffer_offset = end
+ self._pos += len(line)
+ return line
return io.BufferedIOBase.readline(self, size)
def readlines(self, size=-1):
@@ -308,9 +342,10 @@ class BZ2File(io.BufferedIOBase):
further lines will be read once the total size of the lines read
so far equals or exceeds size.
"""
- if not hasattr(size, "__index__"):
- raise TypeError("Integer argument expected")
- size = size.__index__()
+ if not isinstance(size, int):
+ if not hasattr(size, "__index__"):
+ raise TypeError("Integer argument expected")
+ size = size.__index__()
with self._lock:
return io.BufferedIOBase.readlines(self, size)
@@ -345,7 +380,8 @@ class BZ2File(io.BufferedIOBase):
self._mode = _MODE_READ
self._pos = 0
self._decompressor = BZ2Decompressor()
- self._buffer = None
+ self._buffer = b""
+ self._buffer_offset = 0
def seek(self, offset, whence=0):
"""Change the file position.
@@ -385,8 +421,7 @@ class BZ2File(io.BufferedIOBase):
offset -= self._pos
# Read and discard data until we reach the desired position.
- if self._mode != _MODE_READ_EOF:
- self._read_block(offset, return_data=False)
+ self._read_block(offset, return_data=False)
return self._pos
diff --git a/Lib/codecs.py b/Lib/codecs.py
index 9901d5c662..48d4c9c739 100644
--- a/Lib/codecs.py
+++ b/Lib/codecs.py
@@ -461,7 +461,7 @@ class StreamReader(Codec):
# read until we get the required number of characters (if available)
while True:
- # can the request can be satisfied from the character buffer?
+ # can the request be satisfied from the character buffer?
if chars < 0:
if size < 0:
if self.charbuffer:
diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py
index f0bd66a17c..c92e130976 100644
--- a/Lib/ctypes/__init__.py
+++ b/Lib/ctypes/__init__.py
@@ -456,7 +456,7 @@ if _os.name in ("nt", "ce"):
code = GetLastError()
if descr is None:
descr = FormatError(code).strip()
- return WindowsError(code, descr)
+ return WindowsError(None, descr, None, code)
if sizeof(c_uint) == sizeof(c_void_p):
c_size_t = c_uint
diff --git a/Lib/ctypes/test/test_win32.py b/Lib/ctypes/test/test_win32.py
index 2534a748cd..128914ea2d 100644
--- a/Lib/ctypes/test/test_win32.py
+++ b/Lib/ctypes/test/test_win32.py
@@ -67,6 +67,28 @@ if sys.platform == "win32":
self.assertEqual(ex.text, "text")
self.assertEqual(ex.details, ("details",))
+ class TestWinError(unittest.TestCase):
+ def test_winerror(self):
+ # see Issue 16169
+ import errno
+ ERROR_INVALID_PARAMETER = 87
+ msg = FormatError(ERROR_INVALID_PARAMETER).strip()
+ args = (errno.EINVAL, msg, None, ERROR_INVALID_PARAMETER)
+
+ e = WinError(ERROR_INVALID_PARAMETER)
+ self.assertEqual(e.args, args)
+ self.assertEqual(e.errno, errno.EINVAL)
+ self.assertEqual(e.winerror, ERROR_INVALID_PARAMETER)
+
+ windll.kernel32.SetLastError(ERROR_INVALID_PARAMETER)
+ try:
+ raise WinError()
+ except OSError as exc:
+ e = exc
+ self.assertEqual(e.args, args)
+ self.assertEqual(e.errno, errno.EINVAL)
+ self.assertEqual(e.winerror, ERROR_INVALID_PARAMETER)
+
class Structures(unittest.TestCase):
def test_struct_by_value(self):
diff --git a/Lib/idlelib/config-extensions.def b/Lib/idlelib/config-extensions.def
index 78b68f6b56..39e69ce20d 100644
--- a/Lib/idlelib/config-extensions.def
+++ b/Lib/idlelib/config-extensions.def
@@ -46,6 +46,8 @@ zoom-height=<Alt-Key-2>
[ScriptBinding]
enable=1
+enable_shell=0
+enable_editor=1
[ScriptBinding_cfgBindings]
run-module=<Key-F5>
check-module=<Alt-Key-x>
diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py
index 22efb092fb..532e44e3fb 100644
--- a/Lib/ipaddress.py
+++ b/Lib/ipaddress.py
@@ -206,10 +206,11 @@ def summarize_address_range(first, last):
"""Summarize a network range given the first and last IP addresses.
Example:
- >>> summarize_address_range(IPv4Address('192.0.2.0'),
- IPv4Address('192.0.2.130'))
+ >>> list(summarize_address_range(IPv4Address('192.0.2.0'),
+ ... IPv4Address('192.0.2.130')))
+ ... #doctest: +NORMALIZE_WHITESPACE
[IPv4Network('192.0.2.0/25'), IPv4Network('192.0.2.128/31'),
- IPv4Network('192.0.2.130/32')]
+ IPv4Network('192.0.2.130/32')]
Args:
first: the first IPv4Address or IPv6Address in the range.
diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py
index 1bad8560f1..2918360dc1 100644
--- a/Lib/logging/handlers.py
+++ b/Lib/logging/handlers.py
@@ -794,18 +794,12 @@ class SysLogHandler(logging.Handler):
self.formatter = None
def _connect_unixsocket(self, address):
- self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
- # syslog may require either DGRAM or STREAM sockets
+ self.socket = socket.socket(socket.AF_UNIX, self.socktype)
try:
self.socket.connect(address)
except socket.error:
self.socket.close()
- self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
- try:
- self.socket.connect(address)
- except socket.error:
- self.socket.close()
- raise
+ raise
def encodePriority(self, facility, priority):
"""
diff --git a/Lib/platform.py b/Lib/platform.py
index b6538227ea..769fe8846d 100755
--- a/Lib/platform.py
+++ b/Lib/platform.py
@@ -112,7 +112,7 @@ __copyright__ = """
__version__ = '1.0.7'
import collections
-import sys, os, re
+import sys, os, re, subprocess
### Globals & Constants
@@ -922,13 +922,15 @@ def _syscmd_file(target,default=''):
if sys.platform in ('dos','win32','win16','os2'):
# XXX Others too ?
return default
- target = _follow_symlinks(target).replace('"', '\\"')
+ target = _follow_symlinks(target)
try:
- f = os.popen('file -b "%s" 2> %s' % (target, DEV_NULL))
+ proc = subprocess.Popen(['file', target],
+ stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+
except (AttributeError,os.error):
return default
- output = f.read().strip()
- rc = f.close()
+ output = proc.communicate()[0].decode('latin-1')
+ rc = proc.wait()
if not output or rc:
return default
else:
diff --git a/Lib/pstats.py b/Lib/pstats.py
index 13d944c154..6a77605e14 100644
--- a/Lib/pstats.py
+++ b/Lib/pstats.py
@@ -159,15 +159,19 @@ class Stats:
# along with some printable description
sort_arg_dict_default = {
"calls" : (((1,-1), ), "call count"),
+ "ncalls" : (((1,-1), ), "call count"),
+ "cumtime" : (((3,-1), ), "cumulative time"),
"cumulative": (((3,-1), ), "cumulative time"),
"file" : (((4, 1), ), "file name"),
+ "filename" : (((4, 1), ), "file name"),
"line" : (((5, 1), ), "line number"),
"module" : (((4, 1), ), "file name"),
"name" : (((6, 1), ), "function name"),
"nfl" : (((6, 1),(4, 1),(5, 1),), "name/file/line"),
- "pcalls" : (((0,-1), ), "call count"),
+ "pcalls" : (((0,-1), ), "primitive call count"),
"stdname" : (((7, 1), ), "standard name"),
"time" : (((2,-1), ), "internal time"),
+ "tottime" : (((2,-1), ), "internal time"),
}
def get_sort_arg_defs(self):
diff --git a/Lib/socketserver.py b/Lib/socketserver.py
index 261e28e641..a21318d923 100644
--- a/Lib/socketserver.py
+++ b/Lib/socketserver.py
@@ -562,7 +562,7 @@ class ForkingMixIn:
self.collect_children()
def service_actions(self):
- """Collect the zombie child processes regularly in the ForkingMixin.
+ """Collect the zombie child processes regularly in the ForkingMixIn.
service_actions is called in the BaseServer's serve_forver loop.
"""
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
index cec1a24fee..775db501a1 100644
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -1445,9 +1445,16 @@ class Popen(object):
pid, sts = _waitpid(self.pid, _WNOHANG)
if pid == self.pid:
self._handle_exitstatus(sts)
- except _os_error:
+ except _os_error as e:
if _deadstate is not None:
self.returncode = _deadstate
+ elif e.errno == errno.ECHILD:
+ # This happens if SIGCLD is set to be ignored or
+ # waiting for child processes has otherwise been
+ # disabled for our process. This child is dead, we
+ # can't get the status.
+ # http://bugs.python.org/issue15756
+ self.returncode = 0
return self.returncode
diff --git a/Lib/test/subprocessdata/sigchild_ignore.py b/Lib/test/subprocessdata/sigchild_ignore.py
index 6072aece28..86320fb35e 100644
--- a/Lib/test/subprocessdata/sigchild_ignore.py
+++ b/Lib/test/subprocessdata/sigchild_ignore.py
@@ -1,6 +1,15 @@
-import signal, subprocess, sys
+import signal, subprocess, sys, time
# On Linux this causes os.waitpid to fail with OSError as the OS has already
# reaped our child process. The wait() passing the OSError on to the caller
# and causing us to exit with an error is what we are testing against.
signal.signal(signal.SIGCHLD, signal.SIG_IGN)
subprocess.Popen([sys.executable, '-c', 'print("albatross")']).wait()
+# Also ensure poll() handles an errno.ECHILD appropriately.
+p = subprocess.Popen([sys.executable, '-c', 'print("albatross")'])
+num_polls = 0
+while p.poll() is None:
+ # Waiting for the process to finish.
+ time.sleep(0.01) # Avoid being a CPU busy loop.
+ num_polls += 1
+ if num_polls > 3000:
+ raise RuntimeError('poll should have returned 0 within 30 seconds')
diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py
index d3c4a0490a..af15a3d335 100644
--- a/Lib/test/test_capi.py
+++ b/Lib/test/test_capi.py
@@ -316,6 +316,17 @@ class SkipitemTest(unittest.TestCase):
c, i, when_skipped, when_not_skipped))
self.assertIs(when_skipped, when_not_skipped, message)
+ def test_parse_tuple_and_keywords(self):
+ # parse_tuple_and_keywords error handling tests
+ self.assertRaises(TypeError, _testcapi.parse_tuple_and_keywords,
+ (), {}, 42, [])
+ self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords,
+ (), {}, b'', 42)
+ self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords,
+ (), {}, b'', [''] * 42)
+ self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords,
+ (), {}, b'', [42])
+
def test_main():
support.run_unittest(CAPITest, TestPendingCalls,
Test6012, EmbeddingTest, SkipitemTest)
diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py
index 171361ff8d..75133c9379 100644
--- a/Lib/test/test_httpservers.py
+++ b/Lib/test/test_httpservers.py
@@ -62,6 +62,7 @@ class BaseTestCase(unittest.TestCase):
def tearDown(self):
self.thread.stop()
+ self.thread = None
os.environ.__exit__()
support.threading_cleanup(*self._threads)
diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py
index 2420772c36..07e2b4b688 100644
--- a/Lib/test/test_subprocess.py
+++ b/Lib/test/test_subprocess.py
@@ -1,4 +1,5 @@
import unittest
+from test import script_helper
from test import support
import subprocess
import sys
@@ -191,15 +192,134 @@ class ProcessTestCase(BaseTestCase):
p.wait()
self.assertEqual(p.stderr, None)
+ def _assert_python(self, pre_args, **kwargs):
+ # We include sys.exit() to prevent the test runner from hanging
+ # whenever python is found.
+ args = pre_args + ["import sys; sys.exit(47)"]
+ p = subprocess.Popen(args, **kwargs)
+ p.wait()
+ self.assertEqual(47, p.returncode)
+
+ # TODO: make this test work on Linux.
+ # This may be failing on Linux because of issue #7774.
+ @unittest.skipIf(sys.platform not in ('win32', 'darwin'),
+ "possible bug using executable argument on Linux")
+ def test_executable(self):
+ # Check that the executable argument works.
+ self._assert_python(["doesnotexist", "-c"], executable=sys.executable)
+
+ def test_executable_takes_precedence(self):
+ # Check that the executable argument takes precedence over args[0].
+ #
+ # Verify first that the call succeeds without the executable arg.
+ pre_args = [sys.executable, "-c"]
+ self._assert_python(pre_args)
+ self.assertRaises(FileNotFoundError, self._assert_python, pre_args,
+ executable="doesnotexist")
+
+ @unittest.skipIf(mswindows, "executable argument replaces shell")
+ def test_executable_replaces_shell(self):
+ # Check that the executable argument replaces the default shell
+ # when shell=True.
+ self._assert_python([], executable=sys.executable, shell=True)
+
+ # For use in the test_cwd* tests below.
+ def _normalize_cwd(self, cwd):
+ # 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
+
+ # For use in the test_cwd* tests below.
+ def _split_python_path(self):
+ # Return normalized (python_dir, python_base).
+ python_path = os.path.realpath(sys.executable)
+ return os.path.split(python_path)
+
+ # For use in the test_cwd* tests below.
+ def _assert_cwd(self, expected_cwd, python_arg, **kwargs):
+ # Invoke Python via Popen, and assert that (1) the call succeeds,
+ # and that (2) the current working directory of the child process
+ # matches *expected_cwd*.
+ p = subprocess.Popen([python_arg, "-c",
+ "import os, sys; "
+ "sys.stdout.write(os.getcwd()); "
+ "sys.exit(47)"],
+ stdout=subprocess.PIPE,
+ **kwargs)
+ self.addCleanup(p.stdout.close)
+ p.wait()
+ self.assertEqual(47, p.returncode)
+ normcase = os.path.normcase
+ self.assertEqual(normcase(expected_cwd),
+ normcase(p.stdout.read().decode("utf-8")))
+
+ def test_cwd(self):
+ # Check that cwd changes the cwd for the child process.
+ temp_dir = tempfile.gettempdir()
+ temp_dir = self._normalize_cwd(temp_dir)
+ self._assert_cwd(temp_dir, sys.executable, cwd=temp_dir)
+
+ @unittest.skipIf(mswindows, "pending resolution of issue #15533")
+ def test_cwd_with_relative_arg(self):
+ # Check that Popen looks for args[0] relative to cwd if args[0]
+ # is relative.
+ python_dir, python_base = self._split_python_path()
+ rel_python = os.path.join(os.curdir, python_base)
+ with support.temp_cwd() as wrong_dir:
+ # Before calling with the correct cwd, confirm that the call fails
+ # without cwd and with the wrong cwd.
+ self.assertRaises(FileNotFoundError, subprocess.Popen,
+ [rel_python])
+ self.assertRaises(FileNotFoundError, subprocess.Popen,
+ [rel_python], cwd=wrong_dir)
+ python_dir = self._normalize_cwd(python_dir)
+ self._assert_cwd(python_dir, rel_python, cwd=python_dir)
+
+ @unittest.skipIf(mswindows, "pending resolution of issue #15533")
+ def test_cwd_with_relative_executable(self):
+ # Check that Popen looks for executable relative to cwd if executable
+ # is relative (and that executable takes precedence over args[0]).
+ python_dir, python_base = self._split_python_path()
+ rel_python = os.path.join(os.curdir, python_base)
+ doesntexist = "somethingyoudonthave"
+ with support.temp_cwd() as wrong_dir:
+ # Before calling with the correct cwd, confirm that the call fails
+ # without cwd and with the wrong cwd.
+ self.assertRaises(FileNotFoundError, subprocess.Popen,
+ [doesntexist], executable=rel_python)
+ self.assertRaises(FileNotFoundError, subprocess.Popen,
+ [doesntexist], executable=rel_python,
+ cwd=wrong_dir)
+ python_dir = self._normalize_cwd(python_dir)
+ self._assert_cwd(python_dir, doesntexist, executable=rel_python,
+ cwd=python_dir)
+
+ def test_cwd_with_absolute_arg(self):
+ # Check that Popen can find the executable when the cwd is wrong
+ # if args[0] is an absolute path.
+ python_dir, python_base = self._split_python_path()
+ abs_python = os.path.join(python_dir, python_base)
+ rel_python = os.path.join(os.curdir, python_base)
+ with script_helper.temp_dir() as wrong_dir:
+ # Before calling with an absolute path, confirm that using a
+ # relative path fails.
+ self.assertRaises(FileNotFoundError, subprocess.Popen,
+ [rel_python], cwd=wrong_dir)
+ wrong_dir = self._normalize_cwd(wrong_dir)
+ self._assert_cwd(wrong_dir, abs_python, cwd=wrong_dir)
+
@unittest.skipIf(sys.base_prefix != sys.prefix,
'Test is not venv-compatible')
def test_executable_with_cwd(self):
- python_dir = os.path.dirname(os.path.realpath(sys.executable))
- p = subprocess.Popen(["somethingyoudonthave", "-c",
- "import sys; sys.exit(47)"],
- executable=sys.executable, cwd=python_dir)
- p.wait()
- self.assertEqual(p.returncode, 47)
+ python_dir, python_base = self._split_python_path()
+ python_dir = self._normalize_cwd(python_dir)
+ self._assert_cwd(python_dir, "somethingyoudonthave",
+ executable=sys.executable, cwd=python_dir)
@unittest.skipIf(sys.base_prefix != sys.prefix,
'Test is not venv-compatible')
@@ -208,11 +328,7 @@ class ProcessTestCase(BaseTestCase):
def test_executable_without_cwd(self):
# For a normal installation, it should work without 'cwd'
# argument. For test runs in the build directory, see #7774.
- p = subprocess.Popen(["somethingyoudonthave", "-c",
- "import sys; sys.exit(47)"],
- executable=sys.executable)
- p.wait()
- self.assertEqual(p.returncode, 47)
+ self._assert_cwd('', "somethingyoudonthave", executable=sys.executable)
def test_stdin_pipe(self):
# stdin redirection
@@ -369,24 +485,6 @@ class ProcessTestCase(BaseTestCase):
p.wait()
self.assertEqual(p.stdin, None)
- def test_cwd(self):
- tmpdir = tempfile.gettempdir()
- # We cannot use os.path.realpath to canonicalize the path,
- # since it doesn't expand Tru64 {memb} strings. See bug 1063571.
- cwd = os.getcwd()
- os.chdir(tmpdir)
- tmpdir = os.getcwd()
- os.chdir(cwd)
- p = subprocess.Popen([sys.executable, "-c",
- 'import sys,os;'
- 'sys.stdout.write(os.getcwd())'],
- stdout=subprocess.PIPE,
- cwd=tmpdir)
- self.addCleanup(p.stdout.close)
- normcase = os.path.normcase
- self.assertEqual(normcase(p.stdout.read().decode("utf-8")),
- normcase(tmpdir))
-
def test_env(self):
newenv = os.environ.copy()
newenv["FRUIT"] = "orange"
diff --git a/Lib/test/test_threaded_import.py b/Lib/test/test_threaded_import.py
index 4a5d7bee09..0528b139f7 100644
--- a/Lib/test/test_threaded_import.py
+++ b/Lib/test/test_threaded_import.py
@@ -225,11 +225,9 @@ class ThreadedImportTests(unittest.TestCase):
@reap_threads
def test_main():
old_switchinterval = None
- # Issue #15599: FreeBSD/KVM cannot handle gil_interval == 1.
- new_switchinterval = 0.00001 if 'freebsd' in sys.platform else 0.00000001
try:
old_switchinterval = sys.getswitchinterval()
- sys.setswitchinterval(new_switchinterval)
+ sys.setswitchinterval(1e-5)
except AttributeError:
pass
try:
diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py
index 97d64fcf18..9cebc3cd7f 100644
--- a/Lib/test/test_xml_etree.py
+++ b/Lib/test/test_xml_etree.py
@@ -1893,10 +1893,23 @@ class TreeBuilderTest(unittest.TestCase):
sample1 = ('<!DOCTYPE html PUBLIC'
' "-//W3C//DTD XHTML 1.0 Transitional//EN"'
' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
- '<html>text</html>')
+ '<html>text<div>subtext</div>tail</html>')
sample2 = '''<toplevel>sometext</toplevel>'''
+ def _check_sample1_element(self, e):
+ self.assertEqual(e.tag, 'html')
+ self.assertEqual(e.text, 'text')
+ self.assertEqual(e.tail, None)
+ self.assertEqual(e.attrib, {})
+ children = list(e)
+ self.assertEqual(len(children), 1)
+ child = children[0]
+ self.assertEqual(child.tag, 'div')
+ self.assertEqual(child.text, 'subtext')
+ self.assertEqual(child.tail, 'tail')
+ self.assertEqual(child.attrib, {})
+
def test_dummy_builder(self):
class BaseDummyBuilder:
def close(self):
@@ -1929,7 +1942,7 @@ class TreeBuilderTest(unittest.TestCase):
parser.feed(self.sample1)
e = parser.close()
- self.assertEqual(e.tag, 'html')
+ self._check_sample1_element(e)
def test_element_factory(self):
lst = []
@@ -1945,6 +1958,33 @@ class TreeBuilderTest(unittest.TestCase):
self.assertEqual(lst, ['toplevel'])
+ def _check_element_factory_class(self, cls):
+ tb = ET.TreeBuilder(element_factory=cls)
+
+ parser = ET.XMLParser(target=tb)
+ parser.feed(self.sample1)
+ e = parser.close()
+ self.assertIsInstance(e, cls)
+ self._check_sample1_element(e)
+
+ def test_element_factory_subclass(self):
+ class MyElement(ET.Element):
+ pass
+ self._check_element_factory_class(MyElement)
+
+ def test_element_factory_pure_python_subclass(self):
+ # Mimick SimpleTAL's behaviour (issue #16089): both versions of
+ # TreeBuilder should be able to cope with a subclass of the
+ # pure Python Element class.
+ base = ET._Element
+ # Not from a C extension
+ self.assertEqual(base.__module__, 'xml.etree.ElementTree')
+ # Force some multiple inheritance with a C class to make things
+ # more interesting.
+ class MyElement(base, ValueError):
+ pass
+ self._check_element_factory_class(MyElement)
+
def test_doctype(self):
class DoctypeParser:
_doctype = None
diff --git a/Lib/xml/etree/ElementTree.py b/Lib/xml/etree/ElementTree.py
index b9d8df6ab9..9553c51f6c 100644
--- a/Lib/xml/etree/ElementTree.py
+++ b/Lib/xml/etree/ElementTree.py
@@ -303,7 +303,9 @@ class Element:
self._children.insert(index, element)
def _assert_is_element(self, e):
- if not isinstance(e, Element):
+ # Need to refer to the actual Python implementation, not the
+ # shadowing C implementation.
+ if not isinstance(e, _Element):
raise TypeError('expected an Element, not %s' % type(e).__name__)
##