merge heads

This commit is contained in:
Benjamin Peterson 2012-10-10 14:10:44 -04:00
commit b36a05094d
6 changed files with 143 additions and 66 deletions

View File

@ -1,3 +1,5 @@
:keepdoctest:
:mod:`doctest` --- Test interactive Python examples :mod:`doctest` --- Test interactive Python examples
=================================================== ===================================================
@ -318,7 +320,8 @@ The fine print:
Tabs in output generated by the tested code are not modified. Because any Tabs in output generated by the tested code are not modified. Because any
hard tabs in the sample output *are* expanded, this means that if the code hard tabs in the sample output *are* expanded, this means that if the code
output includes hard tabs, the only way the doctest can pass is if the output includes hard tabs, the only way the doctest can pass is if the
:const:`NORMALIZE_WHITESPACE` option or directive is in effect. :const:`NORMALIZE_WHITESPACE` option or :ref:`directive <doctest-directives>`
is in effect.
Alternatively, the test can be rewritten to capture the output and compare it Alternatively, the test can be rewritten to capture the output and compare it
to an expected value as part of the test. This handling of tabs in the to an expected value as part of the test. This handling of tabs in the
source was arrived at through trial and error, and has proven to be the least source was arrived at through trial and error, and has proven to be the least
@ -483,15 +486,16 @@ Some details you should read once, but won't need to remember:
SyntaxError: invalid syntax SyntaxError: invalid syntax
.. _option-flags-and-directives:
.. _doctest-options: .. _doctest-options:
Option Flags and Directives Option Flags
^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^
A number of option flags control various aspects of doctest's behavior. A number of option flags control various aspects of doctest's behavior.
Symbolic names for the flags are supplied as module constants, which can be Symbolic names for the flags are supplied as module constants, which can be
or'ed together and passed to various functions. The names can also be used in or'ed together and passed to various functions. The names can also be used in
doctest directives (see below). :ref:`doctest directives <doctest-directives>`.
The first group of options define test semantics, controlling aspects of how The first group of options define test semantics, controlling aspects of how
doctest decides whether actual output matches an example's expected output: doctest decides whether actual output matches an example's expected output:
@ -545,14 +549,14 @@ doctest decides whether actual output matches an example's expected output:
:exc:`TypeError` is raised. :exc:`TypeError` is raised.
It will also ignore the module name used in Python 3 doctest reports. Hence It will also ignore the module name used in Python 3 doctest reports. Hence
both these variations will work regardless of whether the test is run under both of these variations will work with the flag specified, regardless of
Python 2.7 or Python 3.2 (or later versions): whether the test is run under Python 2.7 or Python 3.2 (or later versions)::
>>> raise CustomError('message') #doctest: +IGNORE_EXCEPTION_DETAIL >>> raise CustomError('message')
Traceback (most recent call last): Traceback (most recent call last):
CustomError: message CustomError: message
>>> raise CustomError('message') #doctest: +IGNORE_EXCEPTION_DETAIL >>> raise CustomError('message')
Traceback (most recent call last): Traceback (most recent call last):
my_module.CustomError: message my_module.CustomError: message
@ -562,15 +566,16 @@ doctest decides whether actual output matches an example's expected output:
exception name. Using :const:`IGNORE_EXCEPTION_DETAIL` and the details exception name. Using :const:`IGNORE_EXCEPTION_DETAIL` and the details
from Python 2.3 is also the only clear way to write a doctest that doesn't from Python 2.3 is also the only clear way to write a doctest that doesn't
care about the exception detail yet continues to pass under Python 2.3 or care about the exception detail yet continues to pass under Python 2.3 or
earlier (those releases do not support doctest directives and ignore them earlier (those releases do not support :ref:`doctest directives
as irrelevant comments). For example, :: <doctest-directives>` and ignore them as irrelevant comments). For example::
>>> (1, 2)[3] = 'moo' #doctest: +IGNORE_EXCEPTION_DETAIL >>> (1, 2)[3] = 'moo'
Traceback (most recent call last): Traceback (most recent call last):
File "<stdin>", line 1, in ? File "<stdin>", line 1, in ?
TypeError: object doesn't support item assignment TypeError: object doesn't support item assignment
passes under Python 2.3 and later Python versions, even though the detail passes under Python 2.3 and later Python versions with the flag specified,
even though the detail
changed in Python 2.4 to say "does not" instead of "doesn't". changed in Python 2.4 to say "does not" instead of "doesn't".
.. versionchanged:: 3.2 .. versionchanged:: 3.2
@ -632,9 +637,30 @@ The second group of options controls how test failures are reported:
A bitmask or'ing together all the reporting flags above. A bitmask or'ing together all the reporting flags above.
"Doctest directives" may be used to modify the option flags for individual
examples. Doctest directives are expressed as a special Python comment There is also a way to register new option flag names, though this isn't
following an example's source code: useful unless you intend to extend :mod:`doctest` internals via subclassing:
.. function:: register_optionflag(name)
Create a new option flag with a given name, and return the new flag's integer
value. :func:`register_optionflag` can be used when subclassing
:class:`OutputChecker` or :class:`DocTestRunner` to create new options that are
supported by your subclasses. :func:`register_optionflag` should always be
called using the following idiom::
MY_FLAG = register_optionflag('MY_FLAG')
.. _doctest-directives:
Directives
^^^^^^^^^^
Doctest directives may be used to modify the :ref:`option flags
<doctest-options>` for an individual example. Doctest directives are
special Python comments following an example's source code:
.. productionlist:: doctest .. productionlist:: doctest
directive: "#" "doctest:" `directive_options` directive: "#" "doctest:" `directive_options`
@ -650,16 +676,7 @@ above.
An example's doctest directives modify doctest's behavior for that single An example's doctest directives modify doctest's behavior for that single
example. Use ``+`` to enable the named behavior, or ``-`` to disable it. example. Use ``+`` to enable the named behavior, or ``-`` to disable it.
.. note:: For example, this test passes::
Due to an `unfortunate limitation`_ of our current documentation
publishing process, syntax highlighting has been disabled in the examples
below in order to ensure the doctest directives are correctly displayed.
.. _unfortunate limitation: http://bugs.python.org/issue12947
For example, this test passes:
.. code-block:: text
>>> print(list(range(20))) # doctest: +NORMALIZE_WHITESPACE >>> print(list(range(20))) # doctest: +NORMALIZE_WHITESPACE
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
@ -668,25 +685,19 @@ For example, this test passes:
Without the directive it would fail, both because the actual output doesn't have Without the directive it would fail, both because the actual output doesn't have
two blanks before the single-digit list elements, and because the actual output two blanks before the single-digit list elements, and because the actual output
is on a single line. This test also passes, and also requires a directive to do is on a single line. This test also passes, and also requires a directive to do
so: so::
.. code-block:: text
>>> print(list(range(20))) # doctest: +ELLIPSIS >>> print(list(range(20))) # doctest: +ELLIPSIS
[0, 1, ..., 18, 19] [0, 1, ..., 18, 19]
Multiple directives can be used on a single physical line, separated by Multiple directives can be used on a single physical line, separated by
commas: commas::
.. code-block:: text
>>> print(list(range(20))) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE >>> print(list(range(20))) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
[0, 1, ..., 18, 19] [0, 1, ..., 18, 19]
If multiple directive comments are used for a single example, then they are If multiple directive comments are used for a single example, then they are
combined: combined::
.. code-block:: text
>>> print(list(range(20))) # doctest: +ELLIPSIS >>> print(list(range(20))) # doctest: +ELLIPSIS
... # doctest: +NORMALIZE_WHITESPACE ... # doctest: +NORMALIZE_WHITESPACE
@ -694,9 +705,7 @@ combined:
As the previous example shows, you can add ``...`` lines to your example As the previous example shows, you can add ``...`` lines to your example
containing only directives. This can be useful when an example is too long for containing only directives. This can be useful when an example is too long for
a directive to comfortably fit on the same line: a directive to comfortably fit on the same line::
.. code-block:: text
>>> print(list(range(5)) + list(range(10, 20)) + list(range(30, 40))) >>> print(list(range(5)) + list(range(10, 20)) + list(range(30, 40)))
... # doctest: +ELLIPSIS ... # doctest: +ELLIPSIS
@ -708,20 +717,6 @@ usually the only meaningful choice. However, option flags can also be passed to
functions that run doctests, establishing different defaults. In such cases, functions that run doctests, establishing different defaults. In such cases,
disabling an option via ``-`` in a directive can be useful. disabling an option via ``-`` in a directive can be useful.
There's also a way to register new option flag names, although this isn't useful
unless you intend to extend :mod:`doctest` internals via subclassing:
.. function:: register_optionflag(name)
Create a new option flag with a given name, and return the new flag's integer
value. :func:`register_optionflag` can be used when subclassing
:class:`OutputChecker` or :class:`DocTestRunner` to create new options that are
supported by your subclasses. :func:`register_optionflag` should always be
called using the following idiom::
MY_FLAG = register_optionflag('MY_FLAG')
.. _doctest-warnings: .. _doctest-warnings:

View File

@ -33,9 +33,38 @@ def new_visit_versionmodified(self, node):
self.body.append('<span class="versionmodified">%s</span> ' % text) self.body.append('<span class="versionmodified">%s</span> ' % text)
from sphinx.writers.html import HTMLTranslator from sphinx.writers.html import HTMLTranslator
from sphinx.writers.latex import LaTeXTranslator
from sphinx.locale import versionlabels from sphinx.locale import versionlabels
HTMLTranslator.visit_versionmodified = new_visit_versionmodified HTMLTranslator.visit_versionmodified = new_visit_versionmodified
HTMLTranslator.visit_versionmodified = new_visit_versionmodified
# monkey-patch HTML and LaTeX translators to keep doctest blocks in the
# doctest docs themselves
orig_visit_literal_block = HTMLTranslator.visit_literal_block
def new_visit_literal_block(self, node):
meta = self.builder.env.metadata[self.builder.current_docname]
old_trim_doctest_flags = self.highlighter.trim_doctest_flags
if 'keepdoctest' in meta:
self.highlighter.trim_doctest_flags = False
try:
orig_visit_literal_block(self, node)
finally:
self.highlighter.trim_doctest_flags = old_trim_doctest_flags
HTMLTranslator.visit_literal_block = new_visit_literal_block
orig_depart_literal_block = LaTeXTranslator.depart_literal_block
def new_depart_literal_block(self, node):
meta = self.builder.env.metadata[self.curfilestack[-1]]
old_trim_doctest_flags = self.highlighter.trim_doctest_flags
if 'keepdoctest' in meta:
self.highlighter.trim_doctest_flags = False
try:
orig_depart_literal_block(self, node)
finally:
self.highlighter.trim_doctest_flags = old_trim_doctest_flags
LaTeXTranslator.depart_literal_block = new_depart_literal_block
# Support for marking up and linking to bugs.python.org issues # Support for marking up and linking to bugs.python.org issues

View File

@ -1327,6 +1327,7 @@ class Popen(object):
if executable is None: if executable is None:
executable = args[0] executable = args[0]
orig_executable = executable
# For transferring possible exec failure from child to parent. # For transferring possible exec failure from child to parent.
# Data format: "exception name:hex errno:description" # Data format: "exception name:hex errno:description"
@ -1409,10 +1410,17 @@ class Popen(object):
err_msg = err_msg.decode(errors="surrogatepass") err_msg = err_msg.decode(errors="surrogatepass")
if issubclass(child_exception_type, OSError) and hex_errno: if issubclass(child_exception_type, OSError) and hex_errno:
errno_num = int(hex_errno, 16) errno_num = int(hex_errno, 16)
child_exec_never_called = (err_msg == "noexec")
if child_exec_never_called:
err_msg = ""
if errno_num != 0: if errno_num != 0:
err_msg = os.strerror(errno_num) err_msg = os.strerror(errno_num)
if errno_num == errno.ENOENT: if errno_num == errno.ENOENT:
err_msg += ': ' + repr(args[0]) if child_exec_never_called:
# The error must be from chdir(cwd).
err_msg += ': ' + repr(cwd)
else:
err_msg += ': ' + repr(orig_executable)
raise child_exception_type(errno_num, err_msg) raise child_exception_type(errno_num, err_msg)
raise child_exception_type(err_msg) raise child_exception_type(err_msg)

View File

@ -200,13 +200,16 @@ class ProcessTestCase(BaseTestCase):
p.wait() p.wait()
self.assertEqual(47, p.returncode) 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): def test_executable(self):
# Check that the executable argument works. # Check that the executable argument works.
self._assert_python(["doesnotexist", "-c"], executable=sys.executable) #
# On Unix (non-Mac and non-Windows), Python looks at args[0] to
# determine where its standard library is, so we need the directory
# of args[0] to be valid for the Popen() call to Python to succeed.
# See also issue #16170 and issue #7774.
doesnotexist = os.path.join(os.path.dirname(sys.executable),
"doesnotexist")
self._assert_python([doesnotexist, "-c"], executable=sys.executable)
def test_executable_takes_precedence(self): def test_executable_takes_precedence(self):
# Check that the executable argument takes precedence over args[0]. # Check that the executable argument takes precedence over args[0].
@ -1035,24 +1038,30 @@ class _SuppressCoreFiles(object):
@unittest.skipIf(mswindows, "POSIX specific tests") @unittest.skipIf(mswindows, "POSIX specific tests")
class POSIXProcessTestCase(BaseTestCase): class POSIXProcessTestCase(BaseTestCase):
def test_exceptions(self): def setUp(self):
nonexistent_dir = "/_this/pa.th/does/not/exist" super().setUp()
self._nonexistent_dir = "/_this/pa.th/does/not/exist"
def _get_chdir_exception(self):
try: try:
os.chdir(nonexistent_dir) os.chdir(self._nonexistent_dir)
except OSError as e: except OSError as e:
# This avoids hard coding the errno value or the OS perror() # This avoids hard coding the errno value or the OS perror()
# string and instead capture the exception that we want to see # string and instead capture the exception that we want to see
# below for comparison. # below for comparison.
desired_exception = e desired_exception = e
desired_exception.strerror += ': ' + repr(sys.executable) desired_exception.strerror += ': ' + repr(self._nonexistent_dir)
else: else:
self.fail("chdir to nonexistant directory %s succeeded." % self.fail("chdir to nonexistant directory %s succeeded." %
nonexistent_dir) self._nonexistent_dir)
return desired_exception
# Error in the child re-raised in the parent. def test_exception_cwd(self):
"""Test error in the child raised in the parent for a bad cwd."""
desired_exception = self._get_chdir_exception()
try: try:
p = subprocess.Popen([sys.executable, "-c", ""], p = subprocess.Popen([sys.executable, "-c", ""],
cwd=nonexistent_dir) cwd=self._nonexistent_dir)
except OSError as e: except OSError as e:
# Test that the child process chdir failure actually makes # Test that the child process chdir failure actually makes
# it up to the parent process as the correct exception. # it up to the parent process as the correct exception.
@ -1061,6 +1070,33 @@ class POSIXProcessTestCase(BaseTestCase):
else: else:
self.fail("Expected OSError: %s" % desired_exception) self.fail("Expected OSError: %s" % desired_exception)
def test_exception_bad_executable(self):
"""Test error in the child raised in the parent for a bad executable."""
desired_exception = self._get_chdir_exception()
try:
p = subprocess.Popen([sys.executable, "-c", ""],
executable=self._nonexistent_dir)
except OSError as e:
# Test that the child process exec failure actually makes
# it up to the parent process as the correct exception.
self.assertEqual(desired_exception.errno, e.errno)
self.assertEqual(desired_exception.strerror, e.strerror)
else:
self.fail("Expected OSError: %s" % desired_exception)
def test_exception_bad_args_0(self):
"""Test error in the child raised in the parent for a bad args[0]."""
desired_exception = self._get_chdir_exception()
try:
p = subprocess.Popen([self._nonexistent_dir, "-c", ""])
except OSError as e:
# Test that the child process exec failure actually makes
# it up to the parent process as the correct exception.
self.assertEqual(desired_exception.errno, e.errno)
self.assertEqual(desired_exception.strerror, e.strerror)
else:
self.fail("Expected OSError: %s" % desired_exception)
def test_restore_signals(self): def test_restore_signals(self):
# Code coverage for both values of restore_signals to make sure it # Code coverage for both values of restore_signals to make sure it
# at least does not blow up. # at least does not blow up.

View File

@ -42,6 +42,10 @@ Core and Builtins
Library Library
------- -------
- Issue #16114: The subprocess module no longer provides a misleading error
message stating that args[0] did not exist when either the cwd or executable
keyword arguments specified a path that did not exist.
- Issue #16169: Fix ctypes.WinError()'s confusion between errno and winerror. - Issue #16169: Fix ctypes.WinError()'s confusion between errno and winerror.
- Issue #1492704: shutil.copyfile() raises a distinct SameFileError now if - Issue #1492704: shutil.copyfile() raises a distinct SameFileError now if

View File

@ -356,7 +356,7 @@ child_exec(char *const exec_array[],
PyObject *preexec_fn, PyObject *preexec_fn,
PyObject *preexec_fn_args_tuple) PyObject *preexec_fn_args_tuple)
{ {
int i, saved_errno, unused; int i, saved_errno, unused, reached_preexec = 0;
PyObject *result; PyObject *result;
const char* err_msg = ""; const char* err_msg = "";
/* Buffer large enough to hold a hex integer. We can't malloc. */ /* Buffer large enough to hold a hex integer. We can't malloc. */
@ -440,6 +440,7 @@ child_exec(char *const exec_array[],
POSIX_CALL(setsid()); POSIX_CALL(setsid());
#endif #endif
reached_preexec = 1;
if (preexec_fn != Py_None && preexec_fn_args_tuple) { if (preexec_fn != Py_None && preexec_fn_args_tuple) {
/* This is where the user has asked us to deadlock their program. */ /* This is where the user has asked us to deadlock their program. */
result = PyObject_Call(preexec_fn, preexec_fn_args_tuple, NULL); result = PyObject_Call(preexec_fn, preexec_fn_args_tuple, NULL);
@ -489,6 +490,10 @@ error:
} }
unused = write(errpipe_write, cur, hex_errno + sizeof(hex_errno) - cur); unused = write(errpipe_write, cur, hex_errno + sizeof(hex_errno) - cur);
unused = write(errpipe_write, ":", 1); unused = write(errpipe_write, ":", 1);
if (!reached_preexec) {
/* Indicate to the parent that the error happened before exec(). */
unused = write(errpipe_write, "noexec", 6);
}
/* We can't call strerror(saved_errno). It is not async signal safe. /* We can't call strerror(saved_errno). It is not async signal safe.
* The parent process will look the error message up. */ * The parent process will look the error message up. */
} else { } else {