doc: Reformat/Modernize some code (#9900)
Found because I was curious what https://pypi.org/project/shed/ does with pytest.
This commit is contained in:
		
							parent
							
								
									eb8b3ad929
								
							
						
					
					
						commit
						e580534df0
					
				| 
						 | 
				
			
			@ -25,7 +25,7 @@ example: specifying and selecting acceptance tests
 | 
			
		|||
            self.tmpdir = request.config.mktemp(request.function.__name__, numbered=True)
 | 
			
		||||
 | 
			
		||||
        def run(self, *cmd):
 | 
			
		||||
            """ called by test code to execute an acceptance test. """
 | 
			
		||||
            """called by test code to execute an acceptance test."""
 | 
			
		||||
            self.tmpdir.chdir()
 | 
			
		||||
            return subprocess.check_output(cmd).decode()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -375,7 +375,7 @@ specifies via named environments:
 | 
			
		|||
        envnames = [mark.args[0] for mark in item.iter_markers(name="env")]
 | 
			
		||||
        if envnames:
 | 
			
		||||
            if item.config.getoption("-E") not in envnames:
 | 
			
		||||
                pytest.skip("test requires env in {!r}".format(envnames))
 | 
			
		||||
                pytest.skip(f"test requires env in {envnames!r}")
 | 
			
		||||
 | 
			
		||||
A test file using this local plugin:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -528,7 +528,7 @@ test function.  From a conftest file we can read it like this:
 | 
			
		|||
 | 
			
		||||
    def pytest_runtest_setup(item):
 | 
			
		||||
        for mark in item.iter_markers(name="glob"):
 | 
			
		||||
            print("glob args={} kwargs={}".format(mark.args, mark.kwargs))
 | 
			
		||||
            print(f"glob args={mark.args} kwargs={mark.kwargs}")
 | 
			
		||||
            sys.stdout.flush()
 | 
			
		||||
 | 
			
		||||
Let's run this without capturing output and see what we get:
 | 
			
		||||
| 
						 | 
				
			
			@ -558,6 +558,7 @@ for your particular platform, you could use the following plugin:
 | 
			
		|||
    # content of conftest.py
 | 
			
		||||
    #
 | 
			
		||||
    import sys
 | 
			
		||||
 | 
			
		||||
    import pytest
 | 
			
		||||
 | 
			
		||||
    ALL = set("darwin linux win32".split())
 | 
			
		||||
| 
						 | 
				
			
			@ -567,7 +568,7 @@ for your particular platform, you could use the following plugin:
 | 
			
		|||
        supported_platforms = ALL.intersection(mark.name for mark in item.iter_markers())
 | 
			
		||||
        plat = sys.platform
 | 
			
		||||
        if supported_platforms and plat not in supported_platforms:
 | 
			
		||||
            pytest.skip("cannot run on platform {}".format(plat))
 | 
			
		||||
            pytest.skip(f"cannot run on platform {plat}")
 | 
			
		||||
 | 
			
		||||
then tests will be skipped if they were specified for a different platform.
 | 
			
		||||
Let's do a little test file to show how this looks like:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -663,6 +663,7 @@ as a complement to ``raises``. For example:
 | 
			
		|||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
    from contextlib import contextmanager
 | 
			
		||||
 | 
			
		||||
    import pytest
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -342,7 +342,7 @@ Example:
 | 
			
		|||
    def checkconfig(x):
 | 
			
		||||
        __tracebackhide__ = True
 | 
			
		||||
        if not hasattr(x, "config"):
 | 
			
		||||
            pytest.fail("not configured: {}".format(x))
 | 
			
		||||
            pytest.fail(f"not configured: {x}")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def test_something():
 | 
			
		||||
| 
						 | 
				
			
			@ -376,6 +376,7 @@ this to make sure unexpected exception types aren't hidden:
 | 
			
		|||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
    import operator
 | 
			
		||||
 | 
			
		||||
    import pytest
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -386,7 +387,7 @@ this to make sure unexpected exception types aren't hidden:
 | 
			
		|||
    def checkconfig(x):
 | 
			
		||||
        __tracebackhide__ = operator.methodcaller("errisinstance", ConfigException)
 | 
			
		||||
        if not hasattr(x, "config"):
 | 
			
		||||
            raise ConfigException("not configured: {}".format(x))
 | 
			
		||||
            raise ConfigException(f"not configured: {x}")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def test_something():
 | 
			
		||||
| 
						 | 
				
			
			@ -565,6 +566,7 @@ an ``incremental`` marker which is to be used on classes:
 | 
			
		|||
    # content of conftest.py
 | 
			
		||||
 | 
			
		||||
    from typing import Dict, Tuple
 | 
			
		||||
 | 
			
		||||
    import pytest
 | 
			
		||||
 | 
			
		||||
    # store history of failures per test class name and per index in parametrize (if parametrize used)
 | 
			
		||||
| 
						 | 
				
			
			@ -608,7 +610,7 @@ an ``incremental`` marker which is to be used on classes:
 | 
			
		|||
                test_name = _test_failed_incremental[cls_name].get(parametrize_index, None)
 | 
			
		||||
                # if name found, test has failed for the combination of class name & test name
 | 
			
		||||
                if test_name is not None:
 | 
			
		||||
                    pytest.xfail("previous test failed ({})".format(test_name))
 | 
			
		||||
                    pytest.xfail(f"previous test failed ({test_name})")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
These two hook implementations work together to abort incremental-marked
 | 
			
		||||
| 
						 | 
				
			
			@ -802,9 +804,10 @@ case we just write some information out to a ``failures`` file:
 | 
			
		|||
 | 
			
		||||
    # content of conftest.py
 | 
			
		||||
 | 
			
		||||
    import pytest
 | 
			
		||||
    import os.path
 | 
			
		||||
 | 
			
		||||
    import pytest
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @pytest.hookimpl(tryfirst=True, hookwrapper=True)
 | 
			
		||||
    def pytest_runtest_makereport(item, call):
 | 
			
		||||
| 
						 | 
				
			
			@ -1066,6 +1069,7 @@ like ``pytest-timeout`` they must be imported explicitly and passed on to pytest
 | 
			
		|||
 | 
			
		||||
    # contents of app_main.py
 | 
			
		||||
    import sys
 | 
			
		||||
 | 
			
		||||
    import pytest_timeout  # Third party plugin
 | 
			
		||||
 | 
			
		||||
    if len(sys.argv) > 1 and sys.argv[1] == "--pytest":
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -238,7 +238,7 @@ file which provides an alternative explanation for ``Foo`` objects:
 | 
			
		|||
       if isinstance(left, Foo) and isinstance(right, Foo) and op == "==":
 | 
			
		||||
           return [
 | 
			
		||||
               "Comparing Foo instances:",
 | 
			
		||||
               "   vals: {} != {}".format(left.val, right.val),
 | 
			
		||||
               f"   vals: {left.val} != {right.val}",
 | 
			
		||||
           ]
 | 
			
		||||
 | 
			
		||||
now, given this test module:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -199,7 +199,6 @@ across pytest invocations:
 | 
			
		|||
 | 
			
		||||
    # content of test_caching.py
 | 
			
		||||
    import pytest
 | 
			
		||||
    import time
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def expensive_computation():
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -253,6 +253,7 @@ which works in a similar manner to :ref:`raises <assertraises>`:
 | 
			
		|||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
    import warnings
 | 
			
		||||
 | 
			
		||||
    import pytest
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -239,7 +239,6 @@ which can then be used in your doctests directly:
 | 
			
		|||
        >>> len(a)
 | 
			
		||||
        10
 | 
			
		||||
        """
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
Note that like the normal ``conftest.py``, the fixtures are discovered in the directory tree conftest is in.
 | 
			
		||||
Meaning that if you put your doctest with your source code, the relevant conftest.py needs to be in the same directory tree.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -398,9 +398,10 @@ access the fixture function:
 | 
			
		|||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
    # content of conftest.py
 | 
			
		||||
    import pytest
 | 
			
		||||
    import smtplib
 | 
			
		||||
 | 
			
		||||
    import pytest
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @pytest.fixture(scope="module")
 | 
			
		||||
    def smtp_connection():
 | 
			
		||||
| 
						 | 
				
			
			@ -609,10 +610,10 @@ Here's what that might look like:
 | 
			
		|||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
    # content of test_emaillib.py
 | 
			
		||||
    import pytest
 | 
			
		||||
 | 
			
		||||
    from emaillib import Email, MailAdminClient
 | 
			
		||||
 | 
			
		||||
    import pytest
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @pytest.fixture
 | 
			
		||||
    def mail_admin():
 | 
			
		||||
| 
						 | 
				
			
			@ -683,10 +684,10 @@ Here's how the previous example would look using the ``addfinalizer`` method:
 | 
			
		|||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
    # content of test_emaillib.py
 | 
			
		||||
    import pytest
 | 
			
		||||
 | 
			
		||||
    from emaillib import Email, MailAdminClient
 | 
			
		||||
 | 
			
		||||
    import pytest
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @pytest.fixture
 | 
			
		||||
    def mail_admin():
 | 
			
		||||
| 
						 | 
				
			
			@ -752,10 +753,10 @@ above):
 | 
			
		|||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
    # content of test_emaillib.py
 | 
			
		||||
    import pytest
 | 
			
		||||
 | 
			
		||||
    from emaillib import Email, MailAdminClient
 | 
			
		||||
 | 
			
		||||
    import pytest
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @pytest.fixture
 | 
			
		||||
    def setup():
 | 
			
		||||
| 
						 | 
				
			
			@ -1030,16 +1031,17 @@ read an optional server URL from the test module which uses our fixture:
 | 
			
		|||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
    # content of conftest.py
 | 
			
		||||
    import pytest
 | 
			
		||||
    import smtplib
 | 
			
		||||
 | 
			
		||||
    import pytest
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @pytest.fixture(scope="module")
 | 
			
		||||
    def smtp_connection(request):
 | 
			
		||||
        server = getattr(request.module, "smtpserver", "smtp.gmail.com")
 | 
			
		||||
        smtp_connection = smtplib.SMTP(server, 587, timeout=5)
 | 
			
		||||
        yield smtp_connection
 | 
			
		||||
        print("finalizing {} ({})".format(smtp_connection, server))
 | 
			
		||||
        print(f"finalizing {smtp_connection} ({server})")
 | 
			
		||||
        smtp_connection.close()
 | 
			
		||||
 | 
			
		||||
We use the ``request.module`` attribute to optionally obtain an
 | 
			
		||||
| 
						 | 
				
			
			@ -1193,15 +1195,16 @@ through the special :py:class:`request <FixtureRequest>` object:
 | 
			
		|||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
    # content of conftest.py
 | 
			
		||||
    import pytest
 | 
			
		||||
    import smtplib
 | 
			
		||||
 | 
			
		||||
    import pytest
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @pytest.fixture(scope="module", params=["smtp.gmail.com", "mail.python.org"])
 | 
			
		||||
    def smtp_connection(request):
 | 
			
		||||
        smtp_connection = smtplib.SMTP(request.param, 587, timeout=5)
 | 
			
		||||
        yield smtp_connection
 | 
			
		||||
        print("finalizing {}".format(smtp_connection))
 | 
			
		||||
        print(f"finalizing {smtp_connection}")
 | 
			
		||||
        smtp_connection.close()
 | 
			
		||||
 | 
			
		||||
The main change is the declaration of ``params`` with
 | 
			
		||||
| 
						 | 
				
			
			@ -1503,7 +1506,7 @@ to show the setup/teardown flow:
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
    def test_2(otherarg, modarg):
 | 
			
		||||
        print("  RUN test2 with otherarg {} and modarg {}".format(otherarg, modarg))
 | 
			
		||||
        print(f"  RUN test2 with otherarg {otherarg} and modarg {modarg}")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Let's run the tests in verbose mode and with looking at the print-output:
 | 
			
		||||
| 
						 | 
				
			
			@ -1604,6 +1607,7 @@ and declare its use in a test module via a ``usefixtures`` marker:
 | 
			
		|||
 | 
			
		||||
    # content of test_setenv.py
 | 
			
		||||
    import os
 | 
			
		||||
 | 
			
		||||
    import pytest
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -73,7 +73,6 @@ messages.  This is supported by the ``caplog`` fixture:
 | 
			
		|||
 | 
			
		||||
    def test_foo(caplog):
 | 
			
		||||
        caplog.set_level(logging.INFO)
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
By default the level is set on the root logger,
 | 
			
		||||
however as a convenience it is also possible to set the log level of any
 | 
			
		||||
| 
						 | 
				
			
			@ -83,7 +82,6 @@ logger:
 | 
			
		|||
 | 
			
		||||
    def test_foo(caplog):
 | 
			
		||||
        caplog.set_level(logging.CRITICAL, logger="root.baz")
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
The log levels set are restored automatically at the end of the test.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -161,9 +159,7 @@ the records for the ``setup`` and ``call`` stages during teardown like so:
 | 
			
		|||
                x.message for x in caplog.get_records(when) if x.levelno == logging.WARNING
 | 
			
		||||
            ]
 | 
			
		||||
            if messages:
 | 
			
		||||
                pytest.fail(
 | 
			
		||||
                    "warning messages encountered during testing: {}".format(messages)
 | 
			
		||||
                )
 | 
			
		||||
                pytest.fail(f"warning messages encountered during testing: {messages}")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -69,6 +69,7 @@ It is also possible to skip the whole module using
 | 
			
		|||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
    import sys
 | 
			
		||||
 | 
			
		||||
    import pytest
 | 
			
		||||
 | 
			
		||||
    if not sys.platform.startswith("win"):
 | 
			
		||||
| 
						 | 
				
			
			@ -409,6 +410,7 @@ test instances when using parametrize:
 | 
			
		|||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
    import sys
 | 
			
		||||
 | 
			
		||||
    import pytest
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -115,6 +115,7 @@ fixture definition:
 | 
			
		|||
    # content of test_unittest_db.py
 | 
			
		||||
 | 
			
		||||
    import unittest
 | 
			
		||||
 | 
			
		||||
    import pytest
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -194,10 +195,10 @@ creation of a per-test temporary directory:
 | 
			
		|||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
    # content of test_unittest_cleandir.py
 | 
			
		||||
    import os
 | 
			
		||||
    import pytest
 | 
			
		||||
    import unittest
 | 
			
		||||
 | 
			
		||||
    import pytest
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    class MyTest(unittest.TestCase):
 | 
			
		||||
        @pytest.fixture(autouse=True)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -183,9 +183,10 @@ You can specify additional plugins to ``pytest.main``:
 | 
			
		|||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
    # content of myinvoke.py
 | 
			
		||||
    import pytest
 | 
			
		||||
    import sys
 | 
			
		||||
 | 
			
		||||
    import pytest
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    class MyPlugin:
 | 
			
		||||
        def pytest_sessionfinish(self):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -194,7 +194,7 @@ class or module can then be passed to the ``pluginmanager`` using the ``pytest_a
 | 
			
		|||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
    def pytest_addhooks(pluginmanager):
 | 
			
		||||
        """ This example assumes the hooks are grouped in the 'sample_hook' module. """
 | 
			
		||||
        """This example assumes the hooks are grouped in the 'sample_hook' module."""
 | 
			
		||||
        from my_app.tests import sample_hook
 | 
			
		||||
 | 
			
		||||
        pluginmanager.add_hookspecs(sample_hook)
 | 
			
		||||
| 
						 | 
				
			
			@ -253,14 +253,14 @@ and use pytest_addoption as follows:
 | 
			
		|||
   # default value
 | 
			
		||||
   @hookspec(firstresult=True)
 | 
			
		||||
   def pytest_config_file_default_value():
 | 
			
		||||
       """ Return the default value for the config file command line option. """
 | 
			
		||||
       """Return the default value for the config file command line option."""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   # contents of myplugin.py
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   def pytest_addhooks(pluginmanager):
 | 
			
		||||
       """ This example assumes the hooks are grouped in the 'hooks' module. """
 | 
			
		||||
       """This example assumes the hooks are grouped in the 'hooks' module."""
 | 
			
		||||
       from . import hooks
 | 
			
		||||
 | 
			
		||||
       pluginmanager.add_hookspecs(hooks)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -367,7 +367,7 @@ string value of ``Hello World!`` if we do not supply a value or ``Hello
 | 
			
		|||
        def _hello(name=None):
 | 
			
		||||
            if not name:
 | 
			
		||||
                name = request.config.getoption("name")
 | 
			
		||||
            return "Hello {name}!".format(name=name)
 | 
			
		||||
            return f"Hello {name}!"
 | 
			
		||||
 | 
			
		||||
        return _hello
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,7 +32,7 @@ which will usually be called once for all the functions:
 | 
			
		|||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
    def setup_module(module):
 | 
			
		||||
        """ setup any state specific to the execution of the given module."""
 | 
			
		||||
        """setup any state specific to the execution of the given module."""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def teardown_module(module):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue