diff options
author | Benjamin Peterson <benjamin@python.org> | 2012-10-09 11:16:26 -0400 |
---|---|---|
committer | Benjamin Peterson <benjamin@python.org> | 2012-10-09 11:16:26 -0400 |
commit | 455fa0a314b7f7edd0c8554b12a65267ff1e2e5b (patch) | |
tree | a4db0395d8674c1e3c4119f0edccf72307b34e49 /Lib | |
parent | b29614e047110f4d9af993a6cdec4e3fb7ef9738 (diff) | |
parent | 831893a68ec7114c1fc9c8e36b9159f5c1db50c7 (diff) | |
download | cpython-git-455fa0a314b7f7edd0c8554b12a65267ff1e2e5b.tar.gz |
merge heads
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/bz2.py | 105 | ||||
-rw-r--r-- | Lib/codecs.py | 2 | ||||
-rw-r--r-- | Lib/ctypes/__init__.py | 2 | ||||
-rw-r--r-- | Lib/ctypes/test/test_win32.py | 22 | ||||
-rw-r--r-- | Lib/idlelib/config-extensions.def | 2 | ||||
-rw-r--r-- | Lib/ipaddress.py | 7 | ||||
-rw-r--r-- | Lib/logging/handlers.py | 10 | ||||
-rwxr-xr-x | Lib/platform.py | 12 | ||||
-rw-r--r-- | Lib/pstats.py | 6 | ||||
-rw-r--r-- | Lib/socketserver.py | 2 | ||||
-rw-r--r-- | Lib/subprocess.py | 9 | ||||
-rw-r--r-- | Lib/test/subprocessdata/sigchild_ignore.py | 11 | ||||
-rw-r--r-- | Lib/test/test_capi.py | 11 | ||||
-rw-r--r-- | Lib/test/test_httpservers.py | 1 | ||||
-rw-r--r-- | Lib/test/test_subprocess.py | 156 | ||||
-rw-r--r-- | Lib/test/test_threaded_import.py | 4 | ||||
-rw-r--r-- | Lib/test/test_xml_etree.py | 44 | ||||
-rw-r--r-- | Lib/xml/etree/ElementTree.py | 4 |
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__) ## |