diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fce7978c4..c9940674d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,13 +5,11 @@ repos: hooks: - id: black args: [--safe, --quiet] - language_version: python3 - repo: https://github.com/asottile/blacken-docs rev: v1.0.0 hooks: - id: blacken-docs additional_dependencies: [black==19.3b0] - language_version: python3 - repo: https://github.com/pre-commit/pre-commit-hooks rev: v2.2.3 hooks: diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c4ab6f1bd..811f7475d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,6 @@ -================= -Changelog history -================= +========= +Changelog +========= Versions follow `Semantic Versioning `_ (``..``). @@ -90,6 +90,24 @@ Removals - `#5412 `_: ``ExceptionInfo`` objects (returned by ``pytest.raises``) now have the same ``str`` representation as ``repr``, which avoids some confusion when users use ``print(e)`` to inspect the object. + This means code like: + + .. code-block:: python + + with pytest.raises(SomeException) as e: + ... + assert "some message" in str(e) + + + Needs to be changed to: + + .. code-block:: python + + with pytest.raises(SomeException) as e: + ... + assert "some message" in str(e.value) + + Deprecations @@ -2173,10 +2191,10 @@ Features design. This introduces new ``Node.iter_markers(name)`` and ``Node.get_closest_marker(name)`` APIs. Users are **strongly encouraged** to read the `reasons for the revamp in the docs - `_, + `_, or jump over to details about `updating existing code to use the new APIs - `_. (`#3317 - `_) + `_. + (`#3317 `_) - Now when ``@pytest.fixture`` is applied more than once to the same function a ``ValueError`` is raised. This buggy behavior would cause surprising problems @@ -2582,10 +2600,10 @@ Features `_) - New `pytest_runtest_logfinish - `_ + `_ hook which is called when a test item has finished executing, analogous to `pytest_runtest_logstart - `_. + `_. (`#3101 `_) - Improve performance when collecting tests using many fixtures. (`#3107 @@ -3575,7 +3593,7 @@ Bug Fixes 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`_). - Thanks `@tgoodlet`_ for the report and `@nicoddemus`_ for the PR. + Thanks to `@nicoddemus`_ for the PR. * Fix internal errors when an unprintable ``AssertionError`` is raised inside a test. Thanks `@omerhadari`_ for the PR. @@ -3706,7 +3724,7 @@ Bug Fixes .. _@syre: https://github.com/syre .. _@adler-j: https://github.com/adler-j -.. _@d-b-w: https://bitbucket.org/d-b-w/ +.. _@d-b-w: https://github.com/d-b-w .. _@DuncanBetts: https://github.com/DuncanBetts .. _@dupuy: https://bitbucket.org/dupuy/ .. _@kerrick-lyft: https://github.com/kerrick-lyft @@ -3766,7 +3784,7 @@ Bug Fixes .. _@adborden: https://github.com/adborden .. _@cwitty: https://github.com/cwitty -.. _@d_b_w: https://github.com/d_b_w +.. _@d_b_w: https://github.com/d-b-w .. _@gdyuldin: https://github.com/gdyuldin .. _@matclab: https://github.com/matclab .. _@MSeifert04: https://github.com/MSeifert04 @@ -3801,7 +3819,7 @@ Bug Fixes Thanks `@axil`_ for the PR. * Explain a bad scope value passed to ``@fixture`` declarations or - a ``MetaFunc.parametrize()`` call. Thanks `@tgoodlet`_ for the PR. + a ``MetaFunc.parametrize()`` call. * This version includes ``pluggy-0.4.0``, which correctly handles ``VersionConflict`` errors in plugins (`#704`_). @@ -3811,7 +3829,6 @@ Bug Fixes .. _@philpep: https://github.com/philpep .. _@raquel-ucl: https://github.com/raquel-ucl .. _@axil: https://github.com/axil -.. _@tgoodlet: https://github.com/tgoodlet .. _@vlad-dragos: https://github.com/vlad-dragos .. _#1853: https://github.com/pytest-dev/pytest/issues/1853 @@ -4157,7 +4174,7 @@ time or change existing behaviors in order to make them less surprising/more use * Updated docstrings with a more uniform style. * Add stderr write for ``pytest.exit(msg)`` during startup. Previously the message was never shown. - Thanks `@BeyondEvil`_ for reporting `#1210`_. Thanks to `@JonathonSonesen`_ and + Thanks `@BeyondEvil`_ for reporting `#1210`_. Thanks to `@jgsonesen`_ and `@tomviner`_ for the PR. * No longer display the incorrect test deselection reason (`#1372`_). @@ -4205,7 +4222,7 @@ time or change existing behaviors in order to make them less surprising/more use Thanks to `@Stranger6667`_ for the PR. * Fixed the total tests tally in junit xml output (`#1798`_). - Thanks to `@cryporchild`_ for the PR. + Thanks to `@cboelsen`_ for the PR. * Fixed off-by-one error with lines from ``request.node.warn``. Thanks to `@blueyed`_ for the PR. @@ -4278,7 +4295,7 @@ time or change existing behaviors in order to make them less surprising/more use .. _@BeyondEvil: https://github.com/BeyondEvil .. _@blueyed: https://github.com/blueyed .. _@ceridwen: https://github.com/ceridwen -.. _@cryporchild: https://github.com/cryporchild +.. _@cboelsen: https://github.com/cboelsen .. _@csaftoiu: https://github.com/csaftoiu .. _@d6e: https://github.com/d6e .. _@davehunt: https://github.com/davehunt @@ -4289,7 +4306,7 @@ time or change existing behaviors in order to make them less surprising/more use .. _@gprasad84: https://github.com/gprasad84 .. _@graingert: https://github.com/graingert .. _@hartym: https://github.com/hartym -.. _@JonathonSonesen: https://github.com/JonathonSonesen +.. _@jgsonesen: https://github.com/jgsonesen .. _@kalekundert: https://github.com/kalekundert .. _@kvas-it: https://github.com/kvas-it .. _@marscher: https://github.com/marscher @@ -4426,7 +4443,7 @@ time or change existing behaviors in order to make them less surprising/more use **Changes** -* **Important**: `py.code `_ has been +* **Important**: `py.code `_ has been merged into the ``pytest`` repository as ``pytest._code``. This decision was made because ``py.code`` had very few uses outside ``pytest`` and the fact that it was in a different repository made it difficult to fix bugs on diff --git a/README.rst b/README.rst index 9739a1bda..301e49538 100644 --- a/README.rst +++ b/README.rst @@ -111,13 +111,13 @@ Consult the `Changelog `__ pag Support pytest -------------- -You can support pytest by obtaining a `Tideflift subscription`_. +You can support pytest by obtaining a `Tidelift subscription`_. Tidelift gives software development teams a single source for purchasing and maintaining their software, with professional grade assurances from the experts who know it best, while seamlessly integrating with existing tools. -.. _`Tideflift subscription`: https://tidelift.com/subscription/pkg/pypi-pytest?utm_source=pypi-pytest&utm_medium=referral&utm_campaign=readme +.. _`Tidelift subscription`: https://tidelift.com/subscription/pkg/pypi-pytest?utm_source=pypi-pytest&utm_medium=referral&utm_campaign=readme Security diff --git a/changelog/5606.bugfix.rst b/changelog/5606.bugfix.rst new file mode 100644 index 000000000..82332ba99 --- /dev/null +++ b/changelog/5606.bugfix.rst @@ -0,0 +1,2 @@ +Fixed internal error when test functions were patched with objects that cannot be compared +for truth values against others, like ``numpy`` arrays. diff --git a/changelog/5634.bugfix.rst b/changelog/5634.bugfix.rst new file mode 100644 index 000000000..a2a282f93 --- /dev/null +++ b/changelog/5634.bugfix.rst @@ -0,0 +1,2 @@ +``pytest.exit`` is now correctly handled in ``unittest`` cases. +This makes ``unittest`` cases handle ``quit`` from pytest's pdb correctly. diff --git a/changelog/5650.bugfix.rst b/changelog/5650.bugfix.rst new file mode 100644 index 000000000..db57a40b9 --- /dev/null +++ b/changelog/5650.bugfix.rst @@ -0,0 +1 @@ +Improved output when parsing an ini configuration file fails. diff --git a/doc/en/_templates/globaltoc.html b/doc/en/_templates/globaltoc.html index 39cebb968..50c2239e5 100644 --- a/doc/en/_templates/globaltoc.html +++ b/doc/en/_templates/globaltoc.html @@ -4,7 +4,7 @@
  • Home
  • Install
  • Contents
  • -
  • Reference
  • +
  • API Reference
  • Examples
  • Customize
  • Changelog
  • diff --git a/doc/en/_themes/flask/static/flasky.css_t b/doc/en/_themes/flask/static/flasky.css_t index ad23acf28..108c85401 100644 --- a/doc/en/_themes/flask/static/flasky.css_t +++ b/doc/en/_themes/flask/static/flasky.css_t @@ -424,12 +424,56 @@ a:hover tt { background: #EEE; } +#reference div.section h2 { + /* separate code elements in the reference section */ + border-top: 2px solid #ccc; + padding-top: 0.5em; +} + #reference div.section h3 { /* separate code elements in the reference section */ border-top: 1px solid #ccc; padding-top: 0.5em; } +dl.class, dl.function { + margin-top: 1em; + margin-bottom: 1em; +} + +dl.class > dd { + border-left: 3px solid #ccc; + margin-left: 0px; + padding-left: 30px; +} + +dl.field-list { + flex-direction: column; +} + +dl.field-list dd { + padding-left: 4em; + border-left: 3px solid #ccc; + margin-bottom: 0.5em; +} + +dl.field-list dd > ul { + list-style: none; + padding-left: 0px; +} + +dl.field-list dd > ul > li li :first-child { + text-indent: 0; +} + +dl.field-list dd > ul > li :first-child { + text-indent: -2em; + padding-left: 0px; +} + +dl.field-list dd > p:first-child { + text-indent: -2em; +} @media screen and (max-width: 870px) { diff --git a/doc/en/adopt.rst b/doc/en/adopt.rst index 710f431be..e3c0477bc 100644 --- a/doc/en/adopt.rst +++ b/doc/en/adopt.rst @@ -24,11 +24,9 @@ The ideal pytest helper - feels confident in using pytest (e.g. has explored command line options, knows how to write parametrized tests, has an idea about conftest contents) - does not need to be an expert in every aspect! -`Pytest helpers, sign up here`_! (preferably in February, hard deadline 22 March) +Pytest helpers, sign up here! (preferably in February, hard deadline 22 March) -.. _`Pytest helpers, sign up here`: http://goo.gl/forms/nxqAhqWt1P - The ideal partner project ----------------------------------------- @@ -40,11 +38,9 @@ The ideal partner project - has the support of the core development team, in trying out pytest adoption - has no tests... or 100% test coverage... or somewhere in between! -`Partner projects, sign up here`_! (by 22 March) +Partner projects, sign up here! (by 22 March) -.. _`Partner projects, sign up here`: http://goo.gl/forms/ZGyqlHiwk3 - What does it mean to "adopt pytest"? ----------------------------------------- @@ -68,11 +64,11 @@ Progressive success might look like: It may be after the month is up, the partner project decides that pytest is not right for it. That's okay - hopefully the pytest team will also learn something about its weaknesses or deficiencies. .. _`nose and unittest`: faq.html#how-does-pytest-relate-to-nose-and-unittest -.. _assert: asserts.html +.. _assert: assert.html .. _pycmd: https://bitbucket.org/hpk42/pycmd/overview .. _`setUp/tearDown methods`: xunit_setup.html .. _fixtures: fixture.html -.. _markers: markers.html +.. _markers: mark.html .. _distributed: xdist.html diff --git a/doc/en/announce/release-2.1.0.rst b/doc/en/announce/release-2.1.0.rst index 831548ac2..2a2181d97 100644 --- a/doc/en/announce/release-2.1.0.rst +++ b/doc/en/announce/release-2.1.0.rst @@ -12,7 +12,7 @@ courtesy of Benjamin Peterson. You can now safely use ``assert`` statements in test modules without having to worry about side effects or python optimization ("-OO") options. This is achieved by rewriting assert statements in test modules upon import, using a PEP302 hook. -See http://pytest.org/assert.html#advanced-assertion-introspection for +See https://docs.pytest.org/en/latest/assert.html for detailed information. The work has been partly sponsored by my company, merlinux GmbH. diff --git a/doc/en/announce/release-2.9.0.rst b/doc/en/announce/release-2.9.0.rst index c079fdf6b..05d9a394f 100644 --- a/doc/en/announce/release-2.9.0.rst +++ b/doc/en/announce/release-2.9.0.rst @@ -75,7 +75,7 @@ The py.test Development Team **Changes** -* **Important**: `py.code `_ has been +* **Important**: `py.code `_ has been merged into the ``pytest`` repository as ``pytest._code``. This decision was made because ``py.code`` had very few uses outside ``pytest`` and the fact that it was in a different repository made it difficult to fix bugs on @@ -88,7 +88,7 @@ The py.test Development Team **experimental**, so you definitely should not import it explicitly! Please note that the original ``py.code`` is still available in - `pylib `_. + `pylib `_. * ``pytest_enter_pdb`` now optionally receives the pytest config object. Thanks `@nicoddemus`_ for the PR. diff --git a/doc/en/announce/release-2.9.2.rst b/doc/en/announce/release-2.9.2.rst index 8f274cdf3..b007a6d99 100644 --- a/doc/en/announce/release-2.9.2.rst +++ b/doc/en/announce/release-2.9.2.rst @@ -66,8 +66,8 @@ The py.test Development Team .. _#510: https://github.com/pytest-dev/pytest/issues/510 .. _#1506: https://github.com/pytest-dev/pytest/pull/1506 -.. _#1496: https://github.com/pytest-dev/pytest/issue/1496 -.. _#1524: https://github.com/pytest-dev/pytest/issue/1524 +.. _#1496: https://github.com/pytest-dev/pytest/issues/1496 +.. _#1524: https://github.com/pytest-dev/pytest/pull/1524 .. _@astraw38: https://github.com/astraw38 .. _@hackebrot: https://github.com/hackebrot diff --git a/doc/en/example/parametrize.rst b/doc/en/example/parametrize.rst index 0a5ba8358..3dff2c559 100644 --- a/doc/en/example/parametrize.rst +++ b/doc/en/example/parametrize.rst @@ -552,13 +552,13 @@ Then run ``pytest`` with verbose mode and with only the ``basic`` marker: platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR - collecting ... collected 17 items / 14 deselected / 3 selected + collecting ... collected 18 items / 15 deselected / 3 selected test_pytest_param_example.py::test_eval[1+7-8] PASSED [ 33%] test_pytest_param_example.py::test_eval[basic_2+4] PASSED [ 66%] test_pytest_param_example.py::test_eval[basic_6*9] XFAIL [100%] - ============ 2 passed, 14 deselected, 1 xfailed in 0.12 seconds ============ + ============ 2 passed, 15 deselected, 1 xfailed in 0.12 seconds ============ As the result: diff --git a/doc/en/fixture.rst b/doc/en/fixture.rst index d6c7bfd4d..40493e66f 100644 --- a/doc/en/fixture.rst +++ b/doc/en/fixture.rst @@ -378,6 +378,34 @@ The ``smtp_connection`` connection will be closed after the test finished execution because the ``smtp_connection`` object automatically closes when the ``with`` statement ends. +Using the contextlib.ExitStack context manager finalizers will always be called +regardless if the fixture *setup* code raises an exception. This is handy to properly +close all resources created by a fixture even if one of them fails to be created/acquired: + +.. code-block:: python + + # content of test_yield3.py + + import contextlib + + import pytest + + + @contextlib.contextmanager + def connect(port): + ... # create connection + yield + ... # close connection + + + @pytest.fixture + def equipments(): + with contextlib.ExitStack() as stack: + yield [stack.enter_context(connect(port)) for port in ("C1", "C3", "C28")] + +In the example above, if ``"C28"`` fails with an exception, ``"C1"`` and ``"C3"`` will still +be properly closed. + Note that if an exception happens during the *setup* code (before the ``yield`` keyword), the *teardown* code (after the ``yield``) will not be called. @@ -406,27 +434,39 @@ Here's the ``smtp_connection`` fixture changed to use ``addfinalizer`` for clean return smtp_connection # provide the fixture value +Here's the ``equipments`` fixture changed to use ``addfinalizer`` for cleanup: + +.. code-block:: python + + # content of test_yield3.py + + import contextlib + import functools + + import pytest + + + @contextlib.contextmanager + def connect(port): + ... # create connection + yield + ... # close connection + + + @pytest.fixture + def equipments(request): + r = [] + for port in ("C1", "C3", "C28"): + cm = connect(port) + equip = cm.__enter__() + request.addfinalizer(functools.partial(cm.__exit__, None, None, None)) + r.append(equip) + return r + + Both ``yield`` and ``addfinalizer`` methods work similarly by calling their code after the test -ends, but ``addfinalizer`` has two key differences over ``yield``: - -1. It is possible to register multiple finalizer functions. - -2. Finalizers will always be called regardless if the fixture *setup* code raises an exception. - This is handy to properly close all resources created by a fixture even if one of them - fails to be created/acquired:: - - @pytest.fixture - def equipments(request): - r = [] - for port in ('C1', 'C3', 'C28'): - equip = connect(port) - request.addfinalizer(equip.disconnect) - r.append(equip) - return r - - In the example above, if ``"C28"`` fails with an exception, ``"C1"`` and ``"C3"`` will still - be properly closed. Of course, if an exception happens before the finalize function is - registered then it will not be executed. +ends. Of course, if an exception happens before the finalize function is registered then it +will not be executed. .. _`request-context`: diff --git a/doc/en/flaky.rst b/doc/en/flaky.rst index 8e340316e..0f0eecab0 100644 --- a/doc/en/flaky.rst +++ b/doc/en/flaky.rst @@ -122,4 +122,4 @@ Resources * Google: * `Flaky Tests at Google and How We Mitigate Them `_ by John Micco, 2016 - * `Where do Google's flaky tests come from? `_ by Jeff Listfield, 2017 + * `Where do Google's flaky tests come from? `_ by Jeff Listfield, 2017 diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index 750de086e..c313f3849 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -142,7 +142,7 @@ The first test passed and the second failed. You can easily see the intermediate Request a unique temporary directory for functional tests -------------------------------------------------------------- -``pytest`` provides `Builtin fixtures/function arguments `_ to request arbitrary resources, like a unique temporary directory:: +``pytest`` provides `Builtin fixtures/function arguments `_ to request arbitrary resources, like a unique temporary directory:: # content of test_tmpdir.py def test_needsfiles(tmpdir): diff --git a/doc/en/links.inc b/doc/en/links.inc index 1b7cbd05b..7c5e8d88c 100644 --- a/doc/en/links.inc +++ b/doc/en/links.inc @@ -14,7 +14,7 @@ .. _`distribute docs`: .. _`distribute`: https://pypi.org/project/distribute/ .. _`pip`: https://pypi.org/project/pip/ -.. _`venv`: https://docs.python.org/3/library/venv.html/ +.. _`venv`: https://docs.python.org/3/library/venv.html .. _`virtualenv`: https://pypi.org/project/virtualenv/ .. _hudson: http://hudson-ci.org/ .. _jenkins: http://jenkins-ci.org/ diff --git a/doc/en/projects.rst b/doc/en/projects.rst index 606e9d47c..226358596 100644 --- a/doc/en/projects.rst +++ b/doc/en/projects.rst @@ -28,7 +28,6 @@ Here are some examples of projects using ``pytest`` (please send notes via :ref: * `sentry `_, realtime app-maintenance and exception tracking * `Astropy `_ and `affiliated packages `_ * `tox `_, virtualenv/Hudson integration tool -* `PIDA `_ framework for integrated development * `PyPM `_ ActiveState's package manager * `Fom `_ a fluid object mapper for FluidDB * `applib `_ cross-platform utilities @@ -37,8 +36,7 @@ Here are some examples of projects using ``pytest`` (please send notes via :ref: * `mwlib `_ mediawiki parser and utility library * `The Translate Toolkit `_ for localization and conversion * `execnet `_ rapid multi-Python deployment -* `pylib `_ cross-platform path, IO, dynamic code library -* `Pacha `_ configuration management in five minutes +* `pylib `_ cross-platform path, IO, dynamic code library * `bbfreeze `_ create standalone executables from Python scripts * `pdb++ `_ a fancier version of PDB * `py-s3fuse `_ Amazon S3 FUSE based filesystem @@ -77,7 +75,7 @@ Some organisations using pytest * `Tandberg `_ * `Shootq `_ * `Stups department of Heinrich Heine University Duesseldorf `_ -* `cellzome `_ +* cellzome * `Open End, Gothenborg `_ * `Laboratory of Bioinformatics, Warsaw `_ * `merlinux, Germany `_ diff --git a/doc/en/reference.rst b/doc/en/reference.rst index 5abb01f50..afbef6b1e 100644 --- a/doc/en/reference.rst +++ b/doc/en/reference.rst @@ -1,5 +1,5 @@ -Reference -========= +API Reference +============= This page contains the full reference to pytest's API. diff --git a/doc/en/talks.rst b/doc/en/talks.rst index 20b6e5b09..f66192817 100644 --- a/doc/en/talks.rst +++ b/doc/en/talks.rst @@ -4,9 +4,8 @@ Talks and Tutorials .. sidebar:: Next Open Trainings - - `Training at Europython 2019 `_, 8th July 2019, Basel, Switzerland. - - `Training at Workshoptage 2019 `_ (German), 10th September 2019, Rapperswil, Switzerland. + - `3 day hands-on workshop covering pytest, tox and devpi: "Professional Testing with Python" `_ (English), October 21 - 23, 2019, Leipzig, Germany. .. _`funcargs`: funcargs.html diff --git a/doc/en/warnings.rst b/doc/en/warnings.rst index c2b256bb3..382f15928 100644 --- a/doc/en/warnings.rst +++ b/doc/en/warnings.rst @@ -127,7 +127,7 @@ decorator or to all tests in a module by setting the ``pytestmark`` variable: *Credits go to Florian Schulze for the reference implementation in the* `pytest-warnings`_ *plugin.* -.. _`-W option`: https://docs.python.org/3/using/cmdline.html?highlight=#cmdoption-W +.. _`-W option`: https://docs.python.org/3/using/cmdline.html#cmdoption-w .. _warnings.simplefilter: https://docs.python.org/3/library/warnings.html#warnings.simplefilter .. _`pytest-warnings`: https://github.com/fschulze/pytest-warnings diff --git a/doc/en/writing_plugins.rst b/doc/en/writing_plugins.rst index 4bbc1afce..67a8fcecf 100644 --- a/doc/en/writing_plugins.rst +++ b/doc/en/writing_plugins.rst @@ -164,7 +164,7 @@ If a package is installed this way, ``pytest`` will load .. note:: Make sure to include ``Framework :: Pytest`` in your list of - `PyPI classifiers `_ + `PyPI classifiers `_ to make it easy for users to find your plugin. diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index d238061b4..52ffc36bc 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -64,13 +64,18 @@ def num_mock_patch_args(function): patchings = getattr(function, "patchings", None) if not patchings: return 0 - mock_modules = [sys.modules.get("mock"), sys.modules.get("unittest.mock")] - if any(mock_modules): - sentinels = [m.DEFAULT for m in mock_modules if m is not None] - return len( - [p for p in patchings if not p.attribute_name and p.new in sentinels] - ) - return len(patchings) + + mock_sentinel = getattr(sys.modules.get("mock"), "DEFAULT", object()) + ut_mock_sentinel = getattr(sys.modules.get("unittest.mock"), "DEFAULT", object()) + + return len( + [ + p + for p in patchings + if not p.attribute_name + and (p.new is mock_sentinel or p.new is ut_mock_sentinel) + ] + ) def getfuncargnames(function, is_method=False, cls=None): diff --git a/src/_pytest/config/findpaths.py b/src/_pytest/config/findpaths.py index fa2024470..3f91bbd07 100644 --- a/src/_pytest/config/findpaths.py +++ b/src/_pytest/config/findpaths.py @@ -32,7 +32,11 @@ def getcfg(args, config=None): for inibasename in inibasenames: p = base.join(inibasename) if exists(p): - iniconfig = py.iniconfig.IniConfig(p) + try: + iniconfig = py.iniconfig.IniConfig(p) + except py.iniconfig.ParseError as exc: + raise UsageError(str(exc)) + if ( inibasename == "setup.cfg" and "tool:pytest" in iniconfig.sections diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index 0887d6b9c..2fb6ad851 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -179,9 +179,7 @@ class Mark: @attr.s class MarkDecorator: """ A decorator for test functions and test classes. When applied - it will create :class:`MarkInfo` objects which may be - :ref:`retrieved by hooks as item keywords `. - MarkDecorator instances are often created like this:: + it will create :class:`Mark` objects which are often created like this:: mark1 = pytest.mark.NAME # simple MarkDecorator mark2 = pytest.mark.NAME(name1=value) # parametrized MarkDecorator @@ -193,17 +191,18 @@ class MarkDecorator: pass When a MarkDecorator instance is called it does the following: - 1. If called with a single class as its only positional argument and no - additional keyword arguments, it attaches itself to the class so it - gets applied automatically to all test cases found in that class. - 2. If called with a single function as its only positional argument and - no additional keyword arguments, it attaches a MarkInfo object to the - function, containing all the arguments already stored internally in - the MarkDecorator. - 3. When called in any other case, it performs a 'fake construction' call, - i.e. it returns a new MarkDecorator instance with the original - MarkDecorator's content updated with the arguments passed to this - call. + + 1. If called with a single class as its only positional argument and no + additional keyword arguments, it attaches itself to the class so it + gets applied automatically to all test cases found in that class. + 2. If called with a single function as its only positional argument and + no additional keyword arguments, it attaches a MarkInfo object to the + function, containing all the arguments already stored internally in + the MarkDecorator. + 3. When called in any other case, it performs a 'fake construction' call, + i.e. it returns a new MarkDecorator instance with the original + MarkDecorator's content updated with the arguments passed to this + call. Note: The rules above prevent MarkDecorator objects from storing only a single function or class reference as their positional argument with no diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index cbd833946..bd0f4d59f 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -542,7 +542,7 @@ def raises(expected_exception, *args, **kwargs): string that may contain `special characters`__, the pattern can first be escaped with ``re.escape``. - __ https://docs.python.org/3/library/re.html#regular-expression-syntax + __ https://docs.python.org/3/library/re.html#regular-expression-syntax :kwparam message: **(deprecated since 4.1)** if specified, provides a custom failure message if the exception is not raised. See :ref:`the deprecation docs ` for a workaround. diff --git a/src/_pytest/unittest.py b/src/_pytest/unittest.py index 216266979..337490d13 100644 --- a/src/_pytest/unittest.py +++ b/src/_pytest/unittest.py @@ -6,6 +6,7 @@ import _pytest._code import pytest from _pytest.compat import getimfunc from _pytest.config import hookimpl +from _pytest.outcomes import exit from _pytest.outcomes import fail from _pytest.outcomes import skip from _pytest.outcomes import xfail @@ -153,6 +154,11 @@ class TestCaseFunction(Function): self.__dict__.setdefault("_excinfo", []).append(excinfo) def addError(self, testcase, rawexcinfo): + try: + if isinstance(rawexcinfo[1], exit.Exception): + exit(rawexcinfo[1].msg) + except TypeError: + pass self._addexcinfo(rawexcinfo) def addFailure(self, testcase, rawexcinfo): diff --git a/testing/python/integration.py b/testing/python/integration.py index 0b87fea33..73419eef4 100644 --- a/testing/python/integration.py +++ b/testing/python/integration.py @@ -104,21 +104,15 @@ class TestMockDecoration: values = getfuncargnames(f) assert values == ("x",) - @pytest.mark.xfail( - strict=False, reason="getfuncargnames breaks if mock is imported" - ) - def test_wrapped_getfuncargnames_patching(self): + def test_getfuncargnames_patching(self): from _pytest.compat import getfuncargnames + from unittest.mock import patch - def wrap(f): - def func(): + class T: + def original(self, x, y, z): pass - func.__wrapped__ = f - func.patchings = ["qwe"] - return func - - @wrap + @patch.object(T, "original") def f(x, y, z): pass @@ -126,7 +120,6 @@ class TestMockDecoration: assert values == ("y", "z") def test_unittest_mock(self, testdir): - pytest.importorskip("unittest.mock") testdir.makepyfile( """ import unittest.mock @@ -142,7 +135,6 @@ class TestMockDecoration: reprec.assertoutcome(passed=1) def test_unittest_mock_and_fixture(self, testdir): - pytest.importorskip("unittest.mock") testdir.makepyfile( """ import os.path @@ -164,7 +156,6 @@ class TestMockDecoration: reprec.assertoutcome(passed=1) def test_unittest_mock_and_pypi_mock(self, testdir): - pytest.importorskip("unittest.mock") pytest.importorskip("mock", "1.0.1") testdir.makepyfile( """ @@ -187,6 +178,34 @@ class TestMockDecoration: reprec = testdir.inline_run() reprec.assertoutcome(passed=2) + def test_mock_sentinel_check_against_numpy_like(self, testdir): + """Ensure our function that detects mock arguments compares against sentinels using + identity to circumvent objects which can't be compared with equality against others + in a truth context, like with numpy arrays (#5606). + """ + testdir.makepyfile( + dummy=""" + class NumpyLike: + def __init__(self, value): + self.value = value + def __eq__(self, other): + raise ValueError("like numpy, cannot compare against others for truth") + FOO = NumpyLike(10) + """ + ) + testdir.makepyfile( + """ + from unittest.mock import patch + import dummy + class Test(object): + @patch("dummy.FOO", new=dummy.NumpyLike(50)) + def test_hello(self): + assert dummy.FOO.value == 50 + """ + ) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=1) + def test_mock(self, testdir): pytest.importorskip("mock", "1.0.1") testdir.makepyfile( diff --git a/testing/test_config.py b/testing/test_config.py index ff993e401..c1a58848a 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -122,6 +122,12 @@ class TestParseIni: config = testdir.parseconfigure(sub) assert config.getini("minversion") == "2.0" + def test_ini_parse_error(self, testdir): + testdir.tmpdir.join("pytest.ini").write("addopts = -x") + result = testdir.runpytest() + assert result.ret != 0 + result.stderr.fnmatch_lines(["ERROR: *pytest.ini:1: no section header defined"]) + @pytest.mark.xfail(reason="probably not needed") def test_confcutdir(self, testdir): sub = testdir.mkdir("sub") diff --git a/testing/test_pytester.py b/testing/test_pytester.py index 37b63f31a..ba3135f65 100644 --- a/testing/test_pytester.py +++ b/testing/test_pytester.py @@ -231,8 +231,8 @@ class TestInlineRunModulesCleanup: ): spy_factory = self.spy_factory() monkeypatch.setattr(pytester, "SysModulesSnapshot", spy_factory) - original = dict(sys.modules) testdir.syspathinsert() + original = dict(sys.modules) testdir.makepyfile(import1="# you son of a silly person") testdir.makepyfile(import2="# my hovercraft is full of eels") test_mod = testdir.makepyfile( diff --git a/testing/test_unittest.py b/testing/test_unittest.py index 2467ddd39..ec5f92e18 100644 --- a/testing/test_unittest.py +++ b/testing/test_unittest.py @@ -1050,3 +1050,39 @@ def test_setup_inheritance_skipping(testdir, test_name, expected_outcome): testdir.copy_example("unittest/{}".format(test_name)) result = testdir.runpytest() result.stdout.fnmatch_lines(["* {} in *".format(expected_outcome)]) + + +def test_BdbQuit(testdir): + testdir.makepyfile( + test_foo=""" + import unittest + + class MyTestCase(unittest.TestCase): + def test_bdbquit(self): + import bdb + raise bdb.BdbQuit() + + def test_should_not_run(self): + pass + """ + ) + reprec = testdir.inline_run() + reprec.assertoutcome(failed=1, passed=1) + + +def test_exit_outcome(testdir): + testdir.makepyfile( + test_foo=""" + import pytest + import unittest + + class MyTestCase(unittest.TestCase): + def test_exit_outcome(self): + pytest.exit("pytest_exit called") + + def test_should_not_run(self): + pass + """ + ) + result = testdir.runpytest() + result.stdout.fnmatch_lines(["*Exit: pytest_exit called*", "*= no tests ran in *"]) diff --git a/tox.ini b/tox.ini index 95c49db04..52d400524 100644 --- a/tox.ini +++ b/tox.ini @@ -133,10 +133,8 @@ filterwarnings = ignore::pytest.RemovedInPytest4Warning default:Using or importing the ABCs:DeprecationWarning:unittest2.* ignore:Module already imported so cannot be rewritten:pytest.PytestWarning - # produced by path.local - ignore:bad escape.*:DeprecationWarning:re - # produced by path.readlines - ignore:.*U.*mode is deprecated:DeprecationWarning + # produced by python3.6/site.py itself (3.6.7 on Travis, could not trigger it with 3.6.8). + ignore:.*U.*mode is deprecated:DeprecationWarning:(?!(pytest|_pytest)) # produced by pytest-xdist ignore:.*type argument to addoption.*:DeprecationWarning # produced by python >=3.5 on execnet (pytest-xdist) @@ -144,6 +142,8 @@ filterwarnings = # pytest's own futurewarnings ignore::pytest.PytestExperimentalApiWarning # Do not cause SyntaxError for invalid escape sequences in py37. + # Those are caught/handled by pyupgrade, and not easy to filter with the + # module being the filename (with .py removed). default:invalid escape sequence:DeprecationWarning # ignore use of unregistered marks, because we use many to test the implementation ignore::_pytest.warning_types.PytestUnknownMarkWarning