Merge branch 'master' into anydbmfix
This commit is contained in:
		
						commit
						3aac3d0a00
					
				
							
								
								
									
										42
									
								
								.travis.yml
								
								
								
								
							
							
						
						
									
										42
									
								
								.travis.yml
								
								
								
								
							|  | @ -8,34 +8,34 @@ install: "pip install -U tox" | ||||||
| env: | env: | ||||||
|   matrix: |   matrix: | ||||||
|     # coveralls is not listed in tox's envlist, but should run in travis |     # coveralls is not listed in tox's envlist, but should run in travis | ||||||
|     - TESTENV=coveralls |     - TOXENV=coveralls | ||||||
|     # note: please use "tox --listenvs" to populate the build matrix below |     # note: please use "tox --listenvs" to populate the build matrix below | ||||||
|     - TESTENV=linting |     - TOXENV=linting | ||||||
|     - TESTENV=py26 |     - TOXENV=py26 | ||||||
|     - TESTENV=py27 |     - TOXENV=py27 | ||||||
|     - TESTENV=py33 |     - TOXENV=py33 | ||||||
|     - TESTENV=py34 |     - TOXENV=py34 | ||||||
|     - TESTENV=py35 |     - TOXENV=py35 | ||||||
|     - TESTENV=pypy |     - TOXENV=pypy | ||||||
|     - TESTENV=py27-pexpect |     - TOXENV=py27-pexpect | ||||||
|     - TESTENV=py27-xdist |     - TOXENV=py27-xdist | ||||||
|     - TESTENV=py27-trial |     - TOXENV=py27-trial | ||||||
|     - TESTENV=py35-pexpect |     - TOXENV=py35-pexpect | ||||||
|     - TESTENV=py35-xdist |     - TOXENV=py35-xdist | ||||||
|     - TESTENV=py35-trial |     - TOXENV=py35-trial | ||||||
|     - TESTENV=py27-nobyte |     - TOXENV=py27-nobyte | ||||||
|     - TESTENV=doctesting |     - TOXENV=doctesting | ||||||
|     - TESTENV=freeze |     - TOXENV=freeze | ||||||
|     - TESTENV=docs |     - TOXENV=docs | ||||||
| 
 | 
 | ||||||
| matrix: | matrix: | ||||||
|   include: |   include: | ||||||
|     - env: TESTENV=py36 |     - env: TOXENV=py36 | ||||||
|       python: '3.6-dev' |       python: '3.6-dev' | ||||||
|     - env: TESTENV=py37 |     - env: TOXENV=py37 | ||||||
|       python: 'nightly' |       python: 'nightly' | ||||||
| 
 | 
 | ||||||
| script: tox --recreate -e $TESTENV | script: tox --recreate | ||||||
| 
 | 
 | ||||||
| notifications: | notifications: | ||||||
|   irc: |   irc: | ||||||
|  |  | ||||||
|  | @ -1,18 +1,23 @@ | ||||||
| 3.0.7 (unreleased) | 3.0.7 (unreleased) | ||||||
| ================== | ================== | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| * Fix issue in assertion rewriting breaking due to modules silently discarding | * Fix issue in assertion rewriting breaking due to modules silently discarding | ||||||
|   other modules when importing fails |   other modules when importing fails | ||||||
|   Notably, importing the `anydbm` module is fixed. (`#2248`_). |   Notably, importing the `anydbm` module is fixed. (`#2248`_). | ||||||
|   Thanks `@pfhayes`_ for the PR. |   Thanks `@pfhayes`_ for the PR. | ||||||
| 
 | 
 | ||||||
|  | * junitxml: Fix problematic case where system-out tag occured twice per testcase | ||||||
|  |   element in the XML report. Thanks `@kkoukiou`_ for the PR. | ||||||
|  | 
 | ||||||
| * Fix regression, pytest now skips unittest correctly if run with ``--pdb`` | * Fix regression, pytest now skips unittest correctly if run with ``--pdb`` | ||||||
|   (`#2137`_). Thanks to `@gst`_ for the report and `@mbyt`_ for the PR. |   (`#2137`_). Thanks to `@gst`_ for the report and `@mbyt`_ for the PR. | ||||||
| 
 | 
 | ||||||
| * Ignore exceptions raised from descriptors (e.g. properties) during Python test collection (`#2234`_). | * Ignore exceptions raised from descriptors (e.g. properties) during Python test collection (`#2234`_). | ||||||
|   Thanks to `@bluetech`_. |   Thanks to `@bluetech`_. | ||||||
|    |    | ||||||
| *   | * ``--override-ini`` now correctly overrides some fundamental options like ``python_files`` (`#2238`_). | ||||||
|  |   Thanks `@sirex`_ for the report and `@nicoddemus`_ for the PR. | ||||||
| 
 | 
 | ||||||
| * Replace ``raise StopIteration`` usages in the code by simple ``returns`` to finish generators, in accordance to `PEP-479`_ (`#2160`_). | * Replace ``raise StopIteration`` usages in the code by simple ``returns`` to finish generators, in accordance to `PEP-479`_ (`#2160`_). | ||||||
|   Thanks `@tgoodlet`_ for the report and `@nicoddemus`_ for the PR. |   Thanks `@tgoodlet`_ for the report and `@nicoddemus`_ for the PR. | ||||||
|  | @ -34,13 +39,16 @@ | ||||||
| .. _@pfhayes: https://github.com/pfhayes | .. _@pfhayes: https://github.com/pfhayes | ||||||
| .. _@bluetech: https://github.com/bluetech | .. _@bluetech: https://github.com/bluetech | ||||||
| .. _@gst: https://github.com/gst | .. _@gst: https://github.com/gst | ||||||
|  | .. _@sirex: https://github.com/sirex | ||||||
| .. _@vidartf: https://github.com/vidartf | .. _@vidartf: https://github.com/vidartf | ||||||
|  | .. _@kkoukiou: https://github.com/KKoukiou | ||||||
| 
 | 
 | ||||||
| .. _#2248: https://github.com/pytest-dev/pytest/issues/2248 | .. _#2248: https://github.com/pytest-dev/pytest/issues/2248 | ||||||
| .. _#2137: https://github.com/pytest-dev/pytest/issues/2137 | .. _#2137: https://github.com/pytest-dev/pytest/issues/2137 | ||||||
| .. _#2160: https://github.com/pytest-dev/pytest/issues/2160 | .. _#2160: https://github.com/pytest-dev/pytest/issues/2160 | ||||||
| .. _#2231: https://github.com/pytest-dev/pytest/issues/2231 | .. _#2231: https://github.com/pytest-dev/pytest/issues/2231 | ||||||
| .. _#2234: https://github.com/pytest-dev/pytest/issues/2234 | .. _#2234: https://github.com/pytest-dev/pytest/issues/2234 | ||||||
|  | .. _#2238: https://github.com/pytest-dev/pytest/issues/2238 | ||||||
| 
 | 
 | ||||||
| .. _PEP-479: https://www.python.org/dev/peps/pep-0479/ | .. _PEP-479: https://www.python.org/dev/peps/pep-0479/ | ||||||
| 
 | 
 | ||||||
|  | @ -2408,7 +2416,7 @@ Bug fixes: | ||||||
|   teardown function are called earlier. |   teardown function are called earlier. | ||||||
| - add an all-powerful metafunc.parametrize function which allows to | - add an all-powerful metafunc.parametrize function which allows to | ||||||
|   parametrize test function arguments in multiple steps and therefore |   parametrize test function arguments in multiple steps and therefore | ||||||
|   from indepdenent plugins and palces. |   from independent plugins and places. | ||||||
| - add a @pytest.mark.parametrize helper which allows to easily | - add a @pytest.mark.parametrize helper which allows to easily | ||||||
|   call a test function with different argument values |   call a test function with different argument values | ||||||
| - Add examples to the "parametrize" example page, including a quick port | - Add examples to the "parametrize" example page, including a quick port | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| """ | """ | ||||||
| merged implementation of the cache provider | merged implementation of the cache provider | ||||||
| 
 | 
 | ||||||
| the name cache was not choosen to ensure pluggy automatically | the name cache was not chosen to ensure pluggy automatically | ||||||
| ignores the external pytest-cache | ignores the external pytest-cache | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -877,6 +877,7 @@ class Config(object): | ||||||
|         self.trace = self.pluginmanager.trace.root.get("config") |         self.trace = self.pluginmanager.trace.root.get("config") | ||||||
|         self.hook = self.pluginmanager.hook |         self.hook = self.pluginmanager.hook | ||||||
|         self._inicache = {} |         self._inicache = {} | ||||||
|  |         self._override_ini = () | ||||||
|         self._opt2dest = {} |         self._opt2dest = {} | ||||||
|         self._cleanup = [] |         self._cleanup = [] | ||||||
|         self._warn = self.pluginmanager._warn |         self._warn = self.pluginmanager._warn | ||||||
|  | @ -977,6 +978,7 @@ class Config(object): | ||||||
|         self.invocation_dir = py.path.local() |         self.invocation_dir = py.path.local() | ||||||
|         self._parser.addini('addopts', 'extra command line options', 'args') |         self._parser.addini('addopts', 'extra command line options', 'args') | ||||||
|         self._parser.addini('minversion', 'minimally required pytest version') |         self._parser.addini('minversion', 'minimally required pytest version') | ||||||
|  |         self._override_ini = ns.override_ini or () | ||||||
| 
 | 
 | ||||||
|     def _consider_importhook(self, args, entrypoint_name): |     def _consider_importhook(self, args, entrypoint_name): | ||||||
|         """Install the PEP 302 import hook if using assertion re-writing. |         """Install the PEP 302 import hook if using assertion re-writing. | ||||||
|  | @ -1159,8 +1161,7 @@ class Config(object): | ||||||
|         # and -o foo1=bar1 -o foo2=bar2 options |         # and -o foo1=bar1 -o foo2=bar2 options | ||||||
|         # always use the last item if multiple value set for same ini-name, |         # always use the last item if multiple value set for same ini-name, | ||||||
|         # e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2 |         # e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2 | ||||||
|         if self.getoption("override_ini", None): |         for ini_config_list in self._override_ini: | ||||||
|             for ini_config_list in self.option.override_ini: |  | ||||||
|             for ini_config in ini_config_list: |             for ini_config in ini_config_list: | ||||||
|                 try: |                 try: | ||||||
|                     (key, user_ini_value) = ini_config.split("=", 1) |                     (key, user_ini_value) = ini_config.split("=", 1) | ||||||
|  |  | ||||||
|  | @ -246,7 +246,7 @@ def pytest_unconfigure(config): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # ------------------------------------------------------------------------- | # ------------------------------------------------------------------------- | ||||||
| # hooks for customising the assert methods | # hooks for customizing the assert methods | ||||||
| # ------------------------------------------------------------------------- | # ------------------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
| def pytest_assertrepr_compare(config, op, left, right): | def pytest_assertrepr_compare(config, op, left, right): | ||||||
|  | @ -255,7 +255,7 @@ def pytest_assertrepr_compare(config, op, left, right): | ||||||
|     Return None for no custom explanation, otherwise return a list |     Return None for no custom explanation, otherwise return a list | ||||||
|     of strings.  The strings will be joined by newlines but any newlines |     of strings.  The strings will be joined by newlines but any newlines | ||||||
|     *in* a string will be escaped.  Note that all but the first line will |     *in* a string will be escaped.  Note that all but the first line will | ||||||
|     be indented sligthly, the intention is for the first line to be a summary. |     be indented slightly, the intention is for the first line to be a summary. | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
| # ------------------------------------------------------------------------- | # ------------------------------------------------------------------------- | ||||||
|  | @ -263,7 +263,14 @@ def pytest_assertrepr_compare(config, op, left, right): | ||||||
| # ------------------------------------------------------------------------- | # ------------------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
| def pytest_report_header(config, startdir): | def pytest_report_header(config, startdir): | ||||||
|     """ return a string to be displayed as header info for terminal reporting.""" |     """ return a string to be displayed as header info for terminal reporting. | ||||||
|  | 
 | ||||||
|  |     .. note:: | ||||||
|  | 
 | ||||||
|  |         This function should be implemented only in plugins or ``conftest.py`` | ||||||
|  |         files situated at the tests root directory due to how pytest | ||||||
|  |         :ref:`discovers plugins during startup <pluginorder>`. | ||||||
|  |     """ | ||||||
| 
 | 
 | ||||||
| @hookspec(firstresult=True) | @hookspec(firstresult=True) | ||||||
| def pytest_report_teststatus(report): | def pytest_report_teststatus(report): | ||||||
|  |  | ||||||
|  | @ -119,7 +119,7 @@ class _NodeReporter(object): | ||||||
|         node = kind(data, message=message) |         node = kind(data, message=message) | ||||||
|         self.append(node) |         self.append(node) | ||||||
| 
 | 
 | ||||||
|     def _write_captured_output(self, report): |     def write_captured_output(self, report): | ||||||
|         for capname in ('out', 'err'): |         for capname in ('out', 'err'): | ||||||
|             content = getattr(report, 'capstd' + capname) |             content = getattr(report, 'capstd' + capname) | ||||||
|             if content: |             if content: | ||||||
|  | @ -128,7 +128,6 @@ class _NodeReporter(object): | ||||||
| 
 | 
 | ||||||
|     def append_pass(self, report): |     def append_pass(self, report): | ||||||
|         self.add_stats('passed') |         self.add_stats('passed') | ||||||
|         self._write_captured_output(report) |  | ||||||
| 
 | 
 | ||||||
|     def append_failure(self, report): |     def append_failure(self, report): | ||||||
|         # msg = str(report.longrepr.reprtraceback.extraline) |         # msg = str(report.longrepr.reprtraceback.extraline) | ||||||
|  | @ -147,7 +146,6 @@ class _NodeReporter(object): | ||||||
|             fail = Junit.failure(message=message) |             fail = Junit.failure(message=message) | ||||||
|             fail.append(bin_xml_escape(report.longrepr)) |             fail.append(bin_xml_escape(report.longrepr)) | ||||||
|             self.append(fail) |             self.append(fail) | ||||||
|         self._write_captured_output(report) |  | ||||||
| 
 | 
 | ||||||
|     def append_collect_error(self, report): |     def append_collect_error(self, report): | ||||||
|         # msg = str(report.longrepr.reprtraceback.extraline) |         # msg = str(report.longrepr.reprtraceback.extraline) | ||||||
|  | @ -165,7 +163,6 @@ class _NodeReporter(object): | ||||||
|             msg = "test setup failure" |             msg = "test setup failure" | ||||||
|         self._add_simple( |         self._add_simple( | ||||||
|             Junit.error, msg, report.longrepr) |             Junit.error, msg, report.longrepr) | ||||||
|         self._write_captured_output(report) |  | ||||||
| 
 | 
 | ||||||
|     def append_skipped(self, report): |     def append_skipped(self, report): | ||||||
|         if hasattr(report, "wasxfail"): |         if hasattr(report, "wasxfail"): | ||||||
|  | @ -180,7 +177,7 @@ class _NodeReporter(object): | ||||||
|                 Junit.skipped("%s:%s: %s" % (filename, lineno, skipreason), |                 Junit.skipped("%s:%s: %s" % (filename, lineno, skipreason), | ||||||
|                               type="pytest.skip", |                               type="pytest.skip", | ||||||
|                               message=skipreason)) |                               message=skipreason)) | ||||||
|         self._write_captured_output(report) |         self.write_captured_output(report) | ||||||
| 
 | 
 | ||||||
|     def finalize(self): |     def finalize(self): | ||||||
|         data = self.to_xml().unicode(indent=0) |         data = self.to_xml().unicode(indent=0) | ||||||
|  | @ -345,6 +342,8 @@ class LogXML(object): | ||||||
|             reporter.append_skipped(report) |             reporter.append_skipped(report) | ||||||
|         self.update_testcase_duration(report) |         self.update_testcase_duration(report) | ||||||
|         if report.when == "teardown": |         if report.when == "teardown": | ||||||
|  |             reporter = self._opentestcase(report) | ||||||
|  |             reporter.write_captured_output(report) | ||||||
|             self.finalize(report) |             self.finalize(report) | ||||||
| 
 | 
 | ||||||
|     def update_testcase_duration(self, report): |     def update_testcase_duration(self, report): | ||||||
|  |  | ||||||
|  | @ -81,7 +81,7 @@ def pytest_namespace(): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def pytest_configure(config): | def pytest_configure(config): | ||||||
|     pytest.config = config # compatibiltiy |     pytest.config = config # compatibility | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def wrap_session(config, doit): | def wrap_session(config, doit): | ||||||
|  |  | ||||||
|  | @ -66,7 +66,7 @@ def pytest_collection_modifyitems(items, config): | ||||||
|         return |         return | ||||||
|     # pytest used to allow "-" for negating |     # pytest used to allow "-" for negating | ||||||
|     # but today we just allow "-" at the beginning, use "not" instead |     # but today we just allow "-" at the beginning, use "not" instead | ||||||
|     # we probably remove "-" alltogether soon |     # we probably remove "-" altogether soon | ||||||
|     if keywordexpr.startswith("-"): |     if keywordexpr.startswith("-"): | ||||||
|         keywordexpr = "not " + keywordexpr[1:] |         keywordexpr = "not " + keywordexpr[1:] | ||||||
|     selectuntil = False |     selectuntil = False | ||||||
|  |  | ||||||
|  | @ -332,7 +332,7 @@ def testdir(request, tmpdir_factory): | ||||||
|     return Testdir(request, tmpdir_factory) |     return Testdir(request, tmpdir_factory) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| rex_outcome = re.compile("(\d+) ([\w-]+)") | rex_outcome = re.compile(r"(\d+) ([\w-]+)") | ||||||
| class RunResult: | class RunResult: | ||||||
|     """The result of running a command. |     """The result of running a command. | ||||||
| 
 | 
 | ||||||
|  | @ -566,7 +566,7 @@ class Testdir: | ||||||
|     def mkpydir(self, name): |     def mkpydir(self, name): | ||||||
|         """Create a new python package. |         """Create a new python package. | ||||||
| 
 | 
 | ||||||
|         This creates a (sub)direcotry with an empty ``__init__.py`` |         This creates a (sub)directory with an empty ``__init__.py`` | ||||||
|         file so that is recognised as a python package. |         file so that is recognised as a python package. | ||||||
| 
 | 
 | ||||||
|         """ |         """ | ||||||
|  | @ -661,7 +661,7 @@ class Testdir: | ||||||
|     def inline_genitems(self, *args): |     def inline_genitems(self, *args): | ||||||
|         """Run ``pytest.main(['--collectonly'])`` in-process. |         """Run ``pytest.main(['--collectonly'])`` in-process. | ||||||
| 
 | 
 | ||||||
|         Retuns a tuple of the collected items and a |         Returns a tuple of the collected items and a | ||||||
|         :py:class:`HookRecorder` instance. |         :py:class:`HookRecorder` instance. | ||||||
| 
 | 
 | ||||||
|         This runs the :py:func:`pytest.main` function to run all of |         This runs the :py:func:`pytest.main` function to run all of | ||||||
|  | @ -857,7 +857,7 @@ class Testdir: | ||||||
|            :py:meth:`parseconfigure`. |            :py:meth:`parseconfigure`. | ||||||
| 
 | 
 | ||||||
|         :param withinit: Whether to also write a ``__init__.py`` file |         :param withinit: Whether to also write a ``__init__.py`` file | ||||||
|            to the temporarly directory to ensure it is a package. |            to the temporary directory to ensure it is a package. | ||||||
| 
 | 
 | ||||||
|         """ |         """ | ||||||
|         kw = {self.request.function.__name__: Source(source).strip()} |         kw = {self.request.function.__name__: Source(source).strip()} | ||||||
|  |  | ||||||
|  | @ -629,7 +629,7 @@ class Generator(FunctionMixin, PyCollector): | ||||||
|     def getcallargs(self, obj): |     def getcallargs(self, obj): | ||||||
|         if not isinstance(obj, (tuple, list)): |         if not isinstance(obj, (tuple, list)): | ||||||
|             obj = (obj,) |             obj = (obj,) | ||||||
|         # explict naming |         # explicit naming | ||||||
|         if isinstance(obj[0], py.builtin._basestring): |         if isinstance(obj[0], py.builtin._basestring): | ||||||
|             name = obj[0] |             name = obj[0] | ||||||
|             obj = obj[1:] |             obj = obj[1:] | ||||||
|  |  | ||||||
|  | @ -116,7 +116,7 @@ def tmpdir(request, tmpdir_factory): | ||||||
|     path object. |     path object. | ||||||
|     """ |     """ | ||||||
|     name = request.node.name |     name = request.node.name | ||||||
|     name = re.sub("[\W]", "_", name) |     name = re.sub(r"[\W]", "_", name) | ||||||
|     MAXVAL = 30 |     MAXVAL = 30 | ||||||
|     if len(name) > MAXVAL: |     if len(name) > MAXVAL: | ||||||
|         name = name[:MAXVAL] |         name = name[:MAXVAL] | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ import sys | ||||||
| import traceback | import traceback | ||||||
| 
 | 
 | ||||||
| import pytest | import pytest | ||||||
| # for transfering markers | # for transferring markers | ||||||
| import _pytest._code | import _pytest._code | ||||||
| from _pytest.python import transfer_markers | from _pytest.python import transfer_markers | ||||||
| from _pytest.skipping import MarkEvaluator | from _pytest.skipping import MarkEvaluator | ||||||
|  |  | ||||||
|  | @ -243,7 +243,9 @@ Fixture finalization / executing teardown code | ||||||
| 
 | 
 | ||||||
| pytest supports execution of fixture specific finalization code | pytest supports execution of fixture specific finalization code | ||||||
| when the fixture goes out of scope.  By using a ``yield`` statement instead of ``return``, all | when the fixture goes out of scope.  By using a ``yield`` statement instead of ``return``, all | ||||||
| the code after the *yield* statement serves as the teardown code.:: | the code after the *yield* statement serves as the teardown code: | ||||||
|  | 
 | ||||||
|  | .. code-block:: python | ||||||
| 
 | 
 | ||||||
|     # content of conftest.py |     # content of conftest.py | ||||||
| 
 | 
 | ||||||
|  | @ -275,22 +277,23 @@ occur around each single test.  In either case the test | ||||||
| module itself does not need to change or know about these details | module itself does not need to change or know about these details | ||||||
| of fixture setup. | of fixture setup. | ||||||
| 
 | 
 | ||||||
| Note that we can also seamlessly use the ``yield`` syntax with ``with`` statements:: | Note that we can also seamlessly use the ``yield`` syntax with ``with`` statements: | ||||||
|  | 
 | ||||||
|  | .. code-block:: python | ||||||
| 
 | 
 | ||||||
|     # content of test_yield2.py |     # content of test_yield2.py | ||||||
| 
 | 
 | ||||||
|  |     import smtplib | ||||||
|     import pytest |     import pytest | ||||||
| 
 | 
 | ||||||
|     @pytest.fixture |     @pytest.fixture(scope="module") | ||||||
|     def passwd(): |     def smtp(request): | ||||||
|         with open("/etc/passwd") as f: |         with smtplib.SMTP("smtp.gmail.com") as smtp: | ||||||
|             yield f.readlines() |             yield smtp  # provide the fixture value | ||||||
| 
 | 
 | ||||||
|     def test_has_lines(passwd): |  | ||||||
|         assert len(passwd) >= 1 |  | ||||||
| 
 | 
 | ||||||
| The file ``f`` will be closed after the test finished execution | The ``smtp`` connection will be closed after the test finished execution | ||||||
| because the Python ``file`` object supports finalization when | because the ``smtp`` object automatically closes when | ||||||
| the ``with`` statement ends. | the ``with`` statement ends. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -236,20 +236,31 @@ import ``helper.py`` normally.  The contents of | ||||||
| Requiring/Loading plugins in a test module or conftest file | Requiring/Loading plugins in a test module or conftest file | ||||||
| ----------------------------------------------------------- | ----------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
| You can require plugins in a test module or a conftest file like this:: | You can require plugins in a test module or a ``conftest.py`` file like this: | ||||||
| 
 | 
 | ||||||
|     pytest_plugins = "name1", "name2", | .. code-block:: python | ||||||
|  | 
 | ||||||
|  |     pytest_plugins = ["name1", "name2"] | ||||||
| 
 | 
 | ||||||
| When the test module or conftest plugin is loaded the specified plugins | When the test module or conftest plugin is loaded the specified plugins | ||||||
| will be loaded as well.  You can also use dotted path like this:: | will be loaded as well. Any module can be blessed as a plugin, including internal | ||||||
|  | application modules: | ||||||
|  | 
 | ||||||
|  | .. code-block:: python | ||||||
| 
 | 
 | ||||||
|     pytest_plugins = "myapp.testsupport.myplugin" |     pytest_plugins = "myapp.testsupport.myplugin" | ||||||
| 
 | 
 | ||||||
| which will import the specified module as a ``pytest`` plugin. | ``pytest_plugins`` variables are processed recursively, so note that in the example above | ||||||
|  | if ``myapp.testsupport.myplugin`` also declares ``pytest_plugins``, the contents | ||||||
|  | of the variable will also be loaded as plugins, and so on. | ||||||
| 
 | 
 | ||||||
| Plugins imported like this will automatically be marked to require | This mechanism makes it easy to share fixtures within applications or even | ||||||
| assertion rewriting using the :func:`pytest.register_assert_rewrite` | external applications without the need to create external plugins using  | ||||||
| mechanism.  However for this to have any effect the module must not be | the ``setuptools``'s entry point technique. | ||||||
|  | 
 | ||||||
|  | Plugins imported by ``pytest_plugins`` will also automatically be marked | ||||||
|  | for assertion rewriting (see :func:`pytest.register_assert_rewrite`).  | ||||||
|  | However for this to have any effect the module must not be | ||||||
| imported already; if it was already imported at the time the | imported already; if it was already imported at the time the | ||||||
| ``pytest_plugins`` statement is processed, a warning will result and | ``pytest_plugins`` statement is processed, a warning will result and | ||||||
| assertions inside the plugin will not be re-written.  To fix this you | assertions inside the plugin will not be re-written.  To fix this you | ||||||
|  |  | ||||||
|  | @ -364,7 +364,7 @@ class TestAssert_reprcompare: | ||||||
|         expl = '\n'.join(callequal(left, right, verbose=True)) |         expl = '\n'.join(callequal(left, right, verbose=True)) | ||||||
|         assert expl.endswith(textwrap.dedent(expected).strip()) |         assert expl.endswith(textwrap.dedent(expected).strip()) | ||||||
| 
 | 
 | ||||||
|     def test_list_different_lenghts(self): |     def test_list_different_lengths(self): | ||||||
|         expl = callequal([0, 1], [0, 1, 2]) |         expl = callequal([0, 1], [0, 1, 2]) | ||||||
|         assert len(expl) > 1 |         assert len(expl) > 1 | ||||||
|         expl = callequal([0, 1, 2], [0, 1]) |         expl = callequal([0, 1, 2], [0, 1]) | ||||||
|  |  | ||||||
|  | @ -271,7 +271,7 @@ class TestAssertionRewrite: | ||||||
| 
 | 
 | ||||||
|         getmsg(f, must_pass=True) |         getmsg(f, must_pass=True) | ||||||
| 
 | 
 | ||||||
|     def test_short_circut_evaluation(self): |     def test_short_circuit_evaluation(self): | ||||||
|         def f(): |         def f(): | ||||||
|             assert True or explode  # noqa |             assert True or explode  # noqa | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -604,7 +604,7 @@ def test_capture_binary_output(testdir): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def test_error_during_readouterr(testdir): | def test_error_during_readouterr(testdir): | ||||||
|     """Make sure we suspend capturing if errors occurr during readouterr""" |     """Make sure we suspend capturing if errors occur during readouterr""" | ||||||
|     testdir.makepyfile(pytest_xyz=""" |     testdir.makepyfile(pytest_xyz=""" | ||||||
|         from _pytest.capture import FDCapture |         from _pytest.capture import FDCapture | ||||||
|         def bad_snap(self): |         def bad_snap(self): | ||||||
|  |  | ||||||
|  | @ -778,6 +778,21 @@ class TestOverrideIniArgs: | ||||||
|         result = testdir.runpytest("--override-ini", 'xdist_strict True', "-s") |         result = testdir.runpytest("--override-ini", 'xdist_strict True', "-s") | ||||||
|         result.stderr.fnmatch_lines(["*ERROR* *expects option=value*"]) |         result.stderr.fnmatch_lines(["*ERROR* *expects option=value*"]) | ||||||
| 
 | 
 | ||||||
|  |     @pytest.mark.parametrize('with_ini', [True, False]) | ||||||
|  |     def test_override_ini_handled_asap(self, testdir, with_ini): | ||||||
|  |         """-o should be handled as soon as possible and always override what's in ini files (#2238)""" | ||||||
|  |         if with_ini: | ||||||
|  |             testdir.makeini(""" | ||||||
|  |                 [pytest] | ||||||
|  |                 python_files=test_*.py | ||||||
|  |             """) | ||||||
|  |         testdir.makepyfile(unittest_ini_handle=""" | ||||||
|  |             def test(): | ||||||
|  |                 pass | ||||||
|  |         """) | ||||||
|  |         result = testdir.runpytest("--override-ini", 'python_files=unittest_*.py') | ||||||
|  |         result.stdout.fnmatch_lines(["*1 passed in*"]) | ||||||
|  | 
 | ||||||
|     def test_with_arg_outside_cwd_without_inifile(self, tmpdir, monkeypatch): |     def test_with_arg_outside_cwd_without_inifile(self, tmpdir, monkeypatch): | ||||||
|         monkeypatch.chdir(str(tmpdir)) |         monkeypatch.chdir(str(tmpdir)) | ||||||
|         a = tmpdir.mkdir("a") |         a = tmpdir.mkdir("a") | ||||||
|  |  | ||||||
|  | @ -557,6 +557,25 @@ class TestPython: | ||||||
|         systemout = pnode.find_first_by_tag("system-err") |         systemout = pnode.find_first_by_tag("system-err") | ||||||
|         assert "hello-stderr" in systemout.toxml() |         assert "hello-stderr" in systemout.toxml() | ||||||
| 
 | 
 | ||||||
|  |     def test_avoid_double_stdout(self, testdir): | ||||||
|  |         testdir.makepyfile(""" | ||||||
|  |             import sys | ||||||
|  |             import pytest | ||||||
|  | 
 | ||||||
|  |             @pytest.fixture | ||||||
|  |             def arg(request): | ||||||
|  |                 yield | ||||||
|  |                 sys.stdout.write('hello-stdout teardown') | ||||||
|  |                 raise ValueError() | ||||||
|  |             def test_function(arg): | ||||||
|  |                 sys.stdout.write('hello-stdout call') | ||||||
|  |         """) | ||||||
|  |         result, dom = runandparse(testdir) | ||||||
|  |         node = dom.find_first_by_tag("testsuite") | ||||||
|  |         pnode = node.find_first_by_tag("testcase") | ||||||
|  |         systemout = pnode.find_first_by_tag("system-out") | ||||||
|  |         assert "hello-stdout call" in systemout.toxml() | ||||||
|  |         assert "hello-stdout teardown" in systemout.toxml() | ||||||
| 
 | 
 | ||||||
| def test_mangle_test_address(): | def test_mangle_test_address(): | ||||||
|     from _pytest.junitxml import mangle_test_address |     from _pytest.junitxml import mangle_test_address | ||||||
|  |  | ||||||
|  | @ -197,7 +197,7 @@ class TestNewSession(SessionTests): | ||||||
|         colfail = [x for x in finished if x.failed] |         colfail = [x for x in finished if x.failed] | ||||||
|         assert len(colfail) == 1 |         assert len(colfail) == 1 | ||||||
| 
 | 
 | ||||||
|     def test_minus_x_overriden_by_maxfail(self, testdir): |     def test_minus_x_overridden_by_maxfail(self, testdir): | ||||||
|         testdir.makepyfile(__init__="") |         testdir.makepyfile(__init__="") | ||||||
|         testdir.makepyfile(test_one="xxxx", test_two="yyyy", test_third="zzz") |         testdir.makepyfile(test_one="xxxx", test_two="yyyy", test_third="zzz") | ||||||
|         reprec = testdir.inline_run("-x", "--maxfail=2", testdir.tmpdir) |         reprec = testdir.inline_run("-x", "--maxfail=2", testdir.tmpdir) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue