Commit 859c068e authored by Stéphane Wirtel's avatar Stéphane Wirtel Committed by Julien Palard

bpo-34962: make doctest in Doc/ now passes, and is enforced in CI (GH-9806)

parent 53ebf4b0
...@@ -53,8 +53,8 @@ matrix: ...@@ -53,8 +53,8 @@ matrix:
- cd Doc - cd Doc
# Sphinx is pinned so that new versions that introduce new warnings won't suddenly cause build failures. # Sphinx is pinned so that new versions that introduce new warnings won't suddenly cause build failures.
# (Updating the version is fine as long as no warnings are raised by doing so.) # (Updating the version is fine as long as no warnings are raised by doing so.)
# The theme used by the docs is stored seperately, so we need to install that as well. # The theme used by the docs is stored separately, so we need to install that as well.
- python -m pip install sphinx~=1.6.1 blurb python-docs-theme - python -m pip install sphinx blurb python-docs-theme
script: script:
- make check suspicious html SPHINXOPTS="-q -W -j4" - make check suspicious html SPHINXOPTS="-q -W -j4"
- os: osx - os: osx
...@@ -155,8 +155,14 @@ script: ...@@ -155,8 +155,14 @@ script:
# Check that all symbols exported by libpython start with "Py" or "_Py" # Check that all symbols exported by libpython start with "Py" or "_Py"
- make smelly - make smelly
# `-r -w` implicitly provided through `make buildbottest`. # `-r -w` implicitly provided through `make buildbottest`.
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then XVFB_RUN=xvfb-run; fi; $XVFB_RUN make buildbottest TESTOPTS="-j4 -uall,-cpu" - |
if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
XVFB_RUN=xvfb-run;
fi
$XVFB_RUN make buildbottest TESTOPTS="-j4 -uall,-cpu"
if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
$XVFB_RUN make PYTHON=../python SPHINXOPTS="-q -W -j4" -C Doc/ venv doctest
fi
notifications: notifications:
email: false email: false
webhooks: webhooks:
......
...@@ -16,6 +16,13 @@ sys.path.append(os.path.abspath('includes')) ...@@ -16,6 +16,13 @@ sys.path.append(os.path.abspath('includes'))
extensions = ['sphinx.ext.coverage', 'sphinx.ext.doctest', extensions = ['sphinx.ext.coverage', 'sphinx.ext.doctest',
'pyspecific', 'c_annotations', 'escape4chm'] 'pyspecific', 'c_annotations', 'escape4chm']
doctest_global_setup = '''
try:
import _tkinter
except ImportError:
_tkinter = None
'''
# General substitutions. # General substitutions.
project = 'Python' project = 'Python'
copyright = '2001-%s, Python Software Foundation' % time.strftime('%Y') copyright = '2001-%s, Python Software Foundation' % time.strftime('%Y')
......
.. _examples: .. _distutils_examples:
******** ********
Examples Examples
......
...@@ -621,18 +621,19 @@ The :mod:`multiprocessing` package mostly replicates the API of the ...@@ -621,18 +621,19 @@ The :mod:`multiprocessing` package mostly replicates the API of the
Example usage of some of the methods of :class:`Process`: Example usage of some of the methods of :class:`Process`:
.. doctest:: .. doctest::
:options: +ELLIPSIS
>>> import multiprocessing, time, signal >>> import multiprocessing, time, signal
>>> p = multiprocessing.Process(target=time.sleep, args=(1000,)) >>> p = multiprocessing.Process(target=time.sleep, args=(1000,))
>>> print(p, p.is_alive()) >>> print(p, p.is_alive())
<Process(Process-1, initial)> False <Process(..., initial)> False
>>> p.start() >>> p.start()
>>> print(p, p.is_alive()) >>> print(p, p.is_alive())
<Process(Process-1, started)> True <Process(..., started)> True
>>> p.terminate() >>> p.terminate()
>>> time.sleep(0.1) >>> time.sleep(0.1)
>>> print(p, p.is_alive()) >>> print(p, p.is_alive())
<Process(Process-1, stopped[SIGTERM])> False <Process(..., stopped[SIGTERM])> False
>>> p.exitcode == -signal.SIGTERM >>> p.exitcode == -signal.SIGTERM
True True
......
...@@ -1234,9 +1234,7 @@ Checking for a Pair ...@@ -1234,9 +1234,7 @@ Checking for a Pair
^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
In this example, we'll use the following helper function to display match In this example, we'll use the following helper function to display match
objects a little more gracefully: objects a little more gracefully::
.. testcode::
def displaymatch(match): def displaymatch(match):
if match is None: if match is None:
...@@ -1269,10 +1267,9 @@ To match this with a regular expression, one could use backreferences as such:: ...@@ -1269,10 +1267,9 @@ To match this with a regular expression, one could use backreferences as such::
"<Match: '354aa', groups=('a',)>" "<Match: '354aa', groups=('a',)>"
To find out what card the pair consists of, one could use the To find out what card the pair consists of, one could use the
:meth:`~Match.group` method of the match object in the following manner: :meth:`~Match.group` method of the match object in the following manner::
.. doctest::
>>> pair = re.compile(r".*(.).*\1")
>>> pair.match("717ak").group(1) >>> pair.match("717ak").group(1)
'7' '7'
...@@ -1377,7 +1374,9 @@ easily read and modified by Python as demonstrated in the following example that ...@@ -1377,7 +1374,9 @@ easily read and modified by Python as demonstrated in the following example that
creates a phonebook. creates a phonebook.
First, here is the input. Normally it may come from a file, here we are using First, here is the input. Normally it may come from a file, here we are using
triple-quoted string syntax:: triple-quoted string syntax
.. doctest::
>>> text = """Ross McFluff: 834.345.1254 155 Elm Street >>> text = """Ross McFluff: 834.345.1254 155 Elm Street
... ...
......
This diff is collapsed.
...@@ -9,6 +9,19 @@ ...@@ -9,6 +9,19 @@
.. _getting-started: .. _getting-started:
.. testsetup::
import unittest
from unittest.mock import Mock, MagicMock, patch, call, sentinel
class SomeClass:
attribute = 'this is a doctest'
@staticmethod
def static_method():
pass
Using Mock Using Mock
---------- ----------
...@@ -99,7 +112,7 @@ by looking at the return value of the mocked class. ...@@ -99,7 +112,7 @@ by looking at the return value of the mocked class.
In the example below we have a function ``some_function`` that instantiates ``Foo`` In the example below we have a function ``some_function`` that instantiates ``Foo``
and calls a method on it. The call to :func:`patch` replaces the class ``Foo`` with a and calls a method on it. The call to :func:`patch` replaces the class ``Foo`` with a
mock. The ``Foo`` instance is the result of calling the mock, so it is configured mock. The ``Foo`` instance is the result of calling the mock, so it is configured
by modifying the mock :attr:`~Mock.return_value`. by modifying the mock :attr:`~Mock.return_value`. ::
>>> def some_function(): >>> def some_function():
... instance = module.Foo() ... instance = module.Foo()
...@@ -321,7 +334,7 @@ whatever) to be replaced with. 'patch.object' takes an object and the name of ...@@ -321,7 +334,7 @@ whatever) to be replaced with. 'patch.object' takes an object and the name of
the attribute you would like patched, plus optionally the value to patch it the attribute you would like patched, plus optionally the value to patch it
with. with.
``patch.object``: ``patch.object``::
>>> original = SomeClass.attribute >>> original = SomeClass.attribute
>>> @patch.object(SomeClass, 'attribute', sentinel.attribute) >>> @patch.object(SomeClass, 'attribute', sentinel.attribute)
...@@ -348,7 +361,7 @@ instead of :func:`patch.object`: ...@@ -348,7 +361,7 @@ instead of :func:`patch.object`:
>>> mock.assert_called_with('filename', 'r') >>> mock.assert_called_with('filename', 'r')
>>> assert handle == sentinel.file_handle, "incorrect file handle returned" >>> assert handle == sentinel.file_handle, "incorrect file handle returned"
The module name can be 'dotted', in the form ``package.module`` if needed: The module name can be 'dotted', in the form ``package.module`` if needed::
>>> @patch('package.module.ClassName.attribute', sentinel.attribute) >>> @patch('package.module.ClassName.attribute', sentinel.attribute)
... def test(): ... def test():
...@@ -380,7 +393,7 @@ passed into the test function / method: ...@@ -380,7 +393,7 @@ passed into the test function / method:
... ...
>>> MyTest('test_something').test_something() >>> MyTest('test_something').test_something()
You can stack up multiple patch decorators using this pattern: You can stack up multiple patch decorators using this pattern::
>>> class MyTest(unittest.TestCase): >>> class MyTest(unittest.TestCase):
... @patch('package.module.ClassName1') ... @patch('package.module.ClassName1')
...@@ -485,7 +498,7 @@ response object for it. To set the response as the return value for that final ...@@ -485,7 +498,7 @@ response object for it. To set the response as the return value for that final
mock_backend.get_endpoint.return_value.create_call.return_value.start_call.return_value = mock_response mock_backend.get_endpoint.return_value.create_call.return_value.start_call.return_value = mock_response
We can do that in a slightly nicer way using the :meth:`~Mock.configure_mock` We can do that in a slightly nicer way using the :meth:`~Mock.configure_mock`
method to directly set the return value for us: method to directly set the return value for us::
>>> something = Something() >>> something = Something()
>>> mock_response = Mock(spec=open) >>> mock_response = Mock(spec=open)
...@@ -494,7 +507,7 @@ method to directly set the return value for us: ...@@ -494,7 +507,7 @@ method to directly set the return value for us:
>>> mock_backend.configure_mock(**config) >>> mock_backend.configure_mock(**config)
With these we monkey patch the "mock backend" in place and can make the real With these we monkey patch the "mock backend" in place and can make the real
call: call::
>>> something.backend = mock_backend >>> something.backend = mock_backend
>>> something.method() >>> something.method()
...@@ -502,7 +515,7 @@ call: ...@@ -502,7 +515,7 @@ call:
Using :attr:`~Mock.mock_calls` we can check the chained call with a single Using :attr:`~Mock.mock_calls` we can check the chained call with a single
assert. A chained call is several calls in one line of code, so there will be assert. A chained call is several calls in one line of code, so there will be
several entries in ``mock_calls``. We can use :meth:`call.call_list` to create several entries in ``mock_calls``. We can use :meth:`call.call_list` to create
this list of calls for us: this list of calls for us::
>>> chained = call.get_endpoint('foobar').create_call('spam', 'eggs').start_call() >>> chained = call.get_endpoint('foobar').create_call('spam', 'eggs').start_call()
>>> call_list = chained.call_list() >>> call_list = chained.call_list()
...@@ -525,7 +538,7 @@ The :func:`patch decorator <patch>` is used here to ...@@ -525,7 +538,7 @@ The :func:`patch decorator <patch>` is used here to
mock out the ``date`` class in the module under test. The :attr:`side_effect` mock out the ``date`` class in the module under test. The :attr:`side_effect`
attribute on the mock date class is then set to a lambda function that returns attribute on the mock date class is then set to a lambda function that returns
a real date. When the mock date class is called a real date will be a real date. When the mock date class is called a real date will be
constructed and returned by ``side_effect``. constructed and returned by ``side_effect``. ::
>>> from datetime import date >>> from datetime import date
>>> with patch('mymodule.date') as mock_date: >>> with patch('mymodule.date') as mock_date:
...@@ -534,7 +547,6 @@ constructed and returned by ``side_effect``. ...@@ -534,7 +547,6 @@ constructed and returned by ``side_effect``.
... ...
... assert mymodule.date.today() == date(2010, 10, 8) ... assert mymodule.date.today() == date(2010, 10, 8)
... assert mymodule.date(2009, 6, 8) == date(2009, 6, 8) ... assert mymodule.date(2009, 6, 8) == date(2009, 6, 8)
...
Note that we don't patch :class:`datetime.date` globally, we patch ``date`` in the Note that we don't patch :class:`datetime.date` globally, we patch ``date`` in the
module that *uses* it. See :ref:`where to patch <where-to-patch>`. module that *uses* it. See :ref:`where to patch <where-to-patch>`.
...@@ -600,10 +612,10 @@ is to apply the patch decorators to every method. This can feel like unnecessary ...@@ -600,10 +612,10 @@ is to apply the patch decorators to every method. This can feel like unnecessary
repetition. For Python 2.6 or more recent you can use :func:`patch` (in all its repetition. For Python 2.6 or more recent you can use :func:`patch` (in all its
various forms) as a class decorator. This applies the patches to all test various forms) as a class decorator. This applies the patches to all test
methods on the class. A test method is identified by methods whose names start methods on the class. A test method is identified by methods whose names start
with ``test``: with ``test``::
>>> @patch('mymodule.SomeClass') >>> @patch('mymodule.SomeClass')
... class MyTest(TestCase): ... class MyTest(unittest.TestCase):
... ...
... def test_one(self, MockSomeClass): ... def test_one(self, MockSomeClass):
... self.assertIs(mymodule.SomeClass, MockSomeClass) ... self.assertIs(mymodule.SomeClass, MockSomeClass)
...@@ -621,8 +633,9 @@ with ``test``: ...@@ -621,8 +633,9 @@ with ``test``:
An alternative way of managing patches is to use the :ref:`start-and-stop`. An alternative way of managing patches is to use the :ref:`start-and-stop`.
These allow you to move the patching into your ``setUp`` and ``tearDown`` methods. These allow you to move the patching into your ``setUp`` and ``tearDown`` methods.
::
>>> class MyTest(TestCase): >>> class MyTest(unittest.TestCase):
... def setUp(self): ... def setUp(self):
... self.patcher = patch('mymodule.foo') ... self.patcher = patch('mymodule.foo')
... self.mock_foo = self.patcher.start() ... self.mock_foo = self.patcher.start()
...@@ -638,9 +651,9 @@ These allow you to move the patching into your ``setUp`` and ``tearDown`` method ...@@ -638,9 +651,9 @@ These allow you to move the patching into your ``setUp`` and ``tearDown`` method
If you use this technique you must ensure that the patching is "undone" by If you use this technique you must ensure that the patching is "undone" by
calling ``stop``. This can be fiddlier than you might think, because if an calling ``stop``. This can be fiddlier than you might think, because if an
exception is raised in the setUp then tearDown is not called. exception is raised in the setUp then tearDown is not called.
:meth:`unittest.TestCase.addCleanup` makes this easier: :meth:`unittest.TestCase.addCleanup` makes this easier::
>>> class MyTest(TestCase): >>> class MyTest(unittest.TestCase):
... def setUp(self): ... def setUp(self):
... patcher = patch('mymodule.foo') ... patcher = patch('mymodule.foo')
... self.addCleanup(patcher.stop) ... self.addCleanup(patcher.stop)
...@@ -753,7 +766,7 @@ defined in 'mymodule':: ...@@ -753,7 +766,7 @@ defined in 'mymodule'::
val.clear() val.clear()
When we try to test that ``grob`` calls ``frob`` with the correct argument look When we try to test that ``grob`` calls ``frob`` with the correct argument look
what happens: what happens::
>>> with patch('mymodule.frob') as mock_frob: >>> with patch('mymodule.frob') as mock_frob:
... val = {6} ... val = {6}
...@@ -777,7 +790,7 @@ functionality. If you provide a ``side_effect`` function for a mock then ...@@ -777,7 +790,7 @@ functionality. If you provide a ``side_effect`` function for a mock then
opportunity to copy the arguments and store them for later assertions. In this opportunity to copy the arguments and store them for later assertions. In this
example I'm using *another* mock to store the arguments so that I can use the example I'm using *another* mock to store the arguments so that I can use the
mock methods for doing the assertion. Again a helper function sets this up for mock methods for doing the assertion. Again a helper function sets this up for
me. me. ::
>>> from copy import deepcopy >>> from copy import deepcopy
>>> from unittest.mock import Mock, patch, DEFAULT >>> from unittest.mock import Mock, patch, DEFAULT
...@@ -854,9 +867,9 @@ Nesting Patches ...@@ -854,9 +867,9 @@ Nesting Patches
Using patch as a context manager is nice, but if you do multiple patches you Using patch as a context manager is nice, but if you do multiple patches you
can end up with nested with statements indenting further and further to the can end up with nested with statements indenting further and further to the
right: right::
>>> class MyTest(TestCase): >>> class MyTest(unittest.TestCase):
... ...
... def test_foo(self): ... def test_foo(self):
... with patch('mymodule.Foo') as mock_foo: ... with patch('mymodule.Foo') as mock_foo:
...@@ -873,9 +886,9 @@ right: ...@@ -873,9 +886,9 @@ right:
With unittest ``cleanup`` functions and the :ref:`start-and-stop` we can With unittest ``cleanup`` functions and the :ref:`start-and-stop` we can
achieve the same effect without the nested indentation. A simple helper achieve the same effect without the nested indentation. A simple helper
method, ``create_patch``, puts the patch in place and returns the created mock method, ``create_patch``, puts the patch in place and returns the created mock
for us: for us::
>>> class MyTest(TestCase): >>> class MyTest(unittest.TestCase):
... ...
... def create_patch(self, name): ... def create_patch(self, name):
... patcher = patch(name) ... patcher = patch(name)
...@@ -969,7 +982,7 @@ mock methods and attributes: ...@@ -969,7 +982,7 @@ mock methods and attributes:
>>> mock.__setitem__.call_args_list >>> mock.__setitem__.call_args_list
[call('b', 'fish'), call('d', 'eggs')] [call('b', 'fish'), call('d', 'eggs')]
>>> my_dict >>> my_dict
{'a': 1, 'c': 3, 'b': 'fish', 'd': 'eggs'} {'a': 1, 'b': 'fish', 'c': 3, 'd': 'eggs'}
Mock subclasses and their attributes Mock subclasses and their attributes
...@@ -1064,6 +1077,7 @@ previously will be restored safely. ...@@ -1064,6 +1077,7 @@ previously will be restored safely.
Here's an example that mocks out the 'fooble' module. Here's an example that mocks out the 'fooble' module.
>>> import sys
>>> mock = Mock() >>> mock = Mock()
>>> with patch.dict('sys.modules', {'fooble': mock}): >>> with patch.dict('sys.modules', {'fooble': mock}):
... import fooble ... import fooble
...@@ -1132,7 +1146,7 @@ the ``mock_calls`` attribute on the manager mock: ...@@ -1132,7 +1146,7 @@ the ``mock_calls`` attribute on the manager mock:
If ``patch`` is creating, and putting in place, your mocks then you can attach If ``patch`` is creating, and putting in place, your mocks then you can attach
them to a manager mock using the :meth:`~Mock.attach_mock` method. After them to a manager mock using the :meth:`~Mock.attach_mock` method. After
attaching calls will be recorded in ``mock_calls`` of the manager. attaching calls will be recorded in ``mock_calls`` of the manager. ::
>>> manager = MagicMock() >>> manager = MagicMock()
>>> with patch('mymodule.Class1') as MockClass1: >>> with patch('mymodule.Class1') as MockClass1:
...@@ -1141,14 +1155,13 @@ attaching calls will be recorded in ``mock_calls`` of the manager. ...@@ -1141,14 +1155,13 @@ attaching calls will be recorded in ``mock_calls`` of the manager.
... manager.attach_mock(MockClass2, 'MockClass2') ... manager.attach_mock(MockClass2, 'MockClass2')
... MockClass1().foo() ... MockClass1().foo()
... MockClass2().bar() ... MockClass2().bar()
...
<MagicMock name='mock.MockClass1().foo()' id='...'> <MagicMock name='mock.MockClass1().foo()' id='...'>
<MagicMock name='mock.MockClass2().bar()' id='...'> <MagicMock name='mock.MockClass2().bar()' id='...'>
>>> manager.mock_calls >>> manager.mock_calls
[call.MockClass1(), [call.MockClass1(),
call.MockClass1().foo(), call.MockClass1().foo(),
call.MockClass2(), call.MockClass2(),
call.MockClass2().bar()] call.MockClass2().bar()]
If many calls have been made, but you're only interested in a particular If many calls have been made, but you're only interested in a particular
sequence of them then an alternative is to use the sequence of them then an alternative is to use the
......
This diff is collapsed.
make docstest in Doc now passes., and is enforced in CI
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment