From ff3d13ed0efab6692a07059b1d61c53eec6e0412 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 26 Mar 2018 11:20:33 -0300 Subject: [PATCH 01/23] Update typo in fixture.rst --- doc/en/fixture.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/fixture.rst b/doc/en/fixture.rst index 5bb877c90..493d0b30f 100644 --- a/doc/en/fixture.rst +++ b/doc/en/fixture.rst @@ -294,7 +294,7 @@ The fixtures requested by ``test_foo`` will be instantiated in the following ord 1. ``s1``: is the highest-scoped fixture (``session``). 2. ``m1``: is the second highest-scoped fixture (``module``). -3. ``tempdir``: is a ``function``-scoped fixture, required by ``f1``: it needs to be instantiated at this point +3. ``tmpdir``: is a ``function``-scoped fixture, required by ``f1``: it needs to be instantiated at this point because it is a dependency of ``f1``. 4. ``f1``: is the first ``function``-scoped fixture in ``test_foo`` parameter list. 5. ``f2``: is the last ``function``-scoped fixture in ``test_foo`` parameter list. From 34afded06dbcc4a2615043c3deaea64511d4287d Mon Sep 17 00:00:00 2001 From: Jeffrey Rackauckas Date: Tue, 27 Mar 2018 19:57:15 -0700 Subject: [PATCH 02/23] Update pytest.raises to raise a TypeError when an invalid keyword argument is passed. --- _pytest/python_api.py | 4 ++++ changelog/3348.bugfix.rst | 1 + testing/python/raises.py | 5 +++++ 3 files changed, 10 insertions(+) create mode 100644 changelog/3348.bugfix.rst diff --git a/_pytest/python_api.py b/_pytest/python_api.py index 69ae6ed0e..5e4776ce3 100644 --- a/_pytest/python_api.py +++ b/_pytest/python_api.py @@ -597,6 +597,10 @@ def raises(expected_exception, *args, **kwargs): message = kwargs.pop("message") if "match" in kwargs: match_expr = kwargs.pop("match") + if len(kwargs.keys()) > 0: + msg = 'Unexpected keyword arguments passed to pytest.raises: ' + msg += ', '.join(kwargs.keys()) + raise TypeError(msg) return RaisesContext(expected_exception, message, match_expr) elif isinstance(args[0], str): code, = args diff --git a/changelog/3348.bugfix.rst b/changelog/3348.bugfix.rst new file mode 100644 index 000000000..7b56ebf98 --- /dev/null +++ b/changelog/3348.bugfix.rst @@ -0,0 +1 @@ +Updated `pytest.raises` to raise a TypeError when an invalid keyword argument is passed. \ No newline at end of file diff --git a/testing/python/raises.py b/testing/python/raises.py index 183259f6b..156319816 100644 --- a/testing/python/raises.py +++ b/testing/python/raises.py @@ -61,6 +61,11 @@ class TestRaises(object): with pytest.raises(TypeError): pytest.raises('wrong', lambda: None) + def test_invalid_arguments_to_raises(self): + with pytest.raises(TypeError, match='unknown'): + with pytest.raises(TypeError, unknown='bogus'): + raise ValueError() + def test_tuple(self): with pytest.raises((KeyError, ValueError)): raise KeyError('oops') From a5d9fbe2b0d14a84325e87629a785eda72f7d9f4 Mon Sep 17 00:00:00 2001 From: Jeffrey Rackauckas Date: Tue, 27 Mar 2018 20:24:15 -0700 Subject: [PATCH 03/23] Change pytest.raises to use match instead of matches --- testing/test_recwarn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/test_recwarn.py b/testing/test_recwarn.py index f1cf542e9..1d99a7656 100644 --- a/testing/test_recwarn.py +++ b/testing/test_recwarn.py @@ -113,7 +113,7 @@ class TestDeprecatedCall(object): pass msg = 'Did not produce DeprecationWarning or PendingDeprecationWarning' - with pytest.raises(AssertionError, matches=msg): + with pytest.raises(AssertionError, match=msg): if mode == 'call': pytest.deprecated_call(f) else: From bfe773bfc8a37981faec2c27ea3ba65aa04f5c5d Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 28 Mar 2018 07:30:14 -0300 Subject: [PATCH 04/23] Use shorter 'if kwargs' check as requested during review --- _pytest/python_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_pytest/python_api.py b/_pytest/python_api.py index 5e4776ce3..0b30b7ac8 100644 --- a/_pytest/python_api.py +++ b/_pytest/python_api.py @@ -597,7 +597,7 @@ def raises(expected_exception, *args, **kwargs): message = kwargs.pop("message") if "match" in kwargs: match_expr = kwargs.pop("match") - if len(kwargs.keys()) > 0: + if kwargs: msg = 'Unexpected keyword arguments passed to pytest.raises: ' msg += ', '.join(kwargs.keys()) raise TypeError(msg) From 765658130235e586542df81213f666f547377353 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 28 Mar 2018 07:31:26 -0300 Subject: [PATCH 05/23] Update changelog formatting --- changelog/3348.bugfix.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/3348.bugfix.rst b/changelog/3348.bugfix.rst index 7b56ebf98..7cf13ab2c 100644 --- a/changelog/3348.bugfix.rst +++ b/changelog/3348.bugfix.rst @@ -1 +1 @@ -Updated `pytest.raises` to raise a TypeError when an invalid keyword argument is passed. \ No newline at end of file +``pytest.raises`` now raises ``TypeError`` when receiving an unknown keyword argument. From 0a3cd881f64deb906585e8cb54ee30865bc7e1c1 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 28 Mar 2018 19:15:12 -0300 Subject: [PATCH 06/23] Add docs about using pytest.param in parametrized fixtures --- _pytest/mark/__init__.py | 3 ++- doc/en/example/reportingdemo.rst | 2 +- doc/en/fixture.rst | 34 ++++++++++++++++++++++++++++++++ doc/en/warnings.rst | 18 ++++++++--------- 4 files changed, 46 insertions(+), 11 deletions(-) diff --git a/_pytest/mark/__init__.py b/_pytest/mark/__init__.py index 51540dbd7..7c96116d1 100644 --- a/_pytest/mark/__init__.py +++ b/_pytest/mark/__init__.py @@ -20,7 +20,8 @@ class MarkerError(Exception): def param(*values, **kw): - """Specify a parameter in a `pytest.mark.parametrize`_ call. + """Specify a parameter in `pytest.mark.parametrize`_ calls or + :ref:`parametrized fixtures `. .. code-block:: python diff --git a/doc/en/example/reportingdemo.rst b/doc/en/example/reportingdemo.rst index 6c8c14723..256fe9a16 100644 --- a/doc/en/example/reportingdemo.rst +++ b/doc/en/example/reportingdemo.rst @@ -358,7 +358,7 @@ get on the terminal - we are working on that):: > int(s) E ValueError: invalid literal for int() with base 10: 'qwe' - <0-codegen $PYTHON_PREFIX/lib/python3.5/site-packages/_pytest/python_api.py:609>:1: ValueError + <0-codegen $PYTHON_PREFIX/lib/python3.5/site-packages/_pytest/python_api.py:613>:1: ValueError ______________________ TestRaises.test_raises_doesnt _______________________ self = diff --git a/doc/en/fixture.rst b/doc/en/fixture.rst index 493d0b30f..6f3debd80 100644 --- a/doc/en/fixture.rst +++ b/doc/en/fixture.rst @@ -623,6 +623,40 @@ Running the above tests results in the following test IDs being used:: ======================= no tests ran in 0.12 seconds ======================= +.. _`fixture-parametrize-marks`: + +Using marks with parametrized fixtures +-------------------------------------- + +:func:`pytest.param` can be used to apply marks in values sets of parametrized fixtures in the same way +that they can be used with :ref:`@pytest.mark.parametrize <@pytest.mark.parametrize>`. + +Example:: + + # content of test_fixture_marks.py + import pytest + @pytest.fixture(params=[0, 1, pytest.param(2, marks=pytest.mark.skip)]) + def data_set(request): + return request.param + + def test_data(data_set): + pass + +Running this test will *skip* the invocation of ``data_set`` with value ``2``:: + + $ pytest test_fixture_marks.py -v + =========================== test session starts ============================ + platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.5 + cachedir: .pytest_cache + rootdir: $REGENDOC_TMPDIR, inifile: + collecting ... collected 3 items + + test_fixture_marks.py::test_data[0] PASSED [ 33%] + test_fixture_marks.py::test_data[1] PASSED [ 66%] + test_fixture_marks.py::test_data[2] SKIPPED [100%] + + =================== 2 passed, 1 skipped in 0.12 seconds ==================== + .. _`interdependent fixtures`: Modularity: using fixtures from a fixture function diff --git a/doc/en/warnings.rst b/doc/en/warnings.rst index e78a6afc0..f7b67f5f2 100644 --- a/doc/en/warnings.rst +++ b/doc/en/warnings.rst @@ -25,14 +25,14 @@ Running pytest now produces this output:: platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 1 item - + test_show_warnings.py . [100%] - + ============================= warnings summary ============================= test_show_warnings.py::test_one $REGENDOC_TMPDIR/test_show_warnings.py:4: UserWarning: api v1, should use functions from v2 warnings.warn(UserWarning("api v1, should use functions from v2")) - + -- Docs: http://doc.pytest.org/en/latest/warnings.html =================== 1 passed, 1 warnings in 0.12 seconds =================== @@ -45,17 +45,17 @@ them into errors:: F [100%] ================================= FAILURES ================================= _________________________________ test_one _________________________________ - + def test_one(): > assert api_v1() == 1 - - test_show_warnings.py:8: - _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ - + + test_show_warnings.py:8: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + def api_v1(): > warnings.warn(UserWarning("api v1, should use functions from v2")) E UserWarning: api v1, should use functions from v2 - + test_show_warnings.py:4: UserWarning 1 failed in 0.12 seconds From faded25ee8821fa16b2a31ea65dee49569d158f8 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Thu, 29 Mar 2018 20:02:00 +0200 Subject: [PATCH 07/23] chage collections.abc import to fix deprecation warnings on python 3.7 --- _pytest/assertion/util.py | 6 +++++- _pytest/mark/structures.py | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/_pytest/assertion/util.py b/_pytest/assertion/util.py index 5a380ae09..71ad48251 100644 --- a/_pytest/assertion/util.py +++ b/_pytest/assertion/util.py @@ -5,7 +5,11 @@ import pprint import _pytest._code import py import six -from collections import Sequence +import sys +if sys.version_info >= (3, 4): + from collections.abc import Sequence +else: + from collections import Sequence u = six.text_type diff --git a/_pytest/mark/structures.py b/_pytest/mark/structures.py index c56972980..c52fbd277 100644 --- a/_pytest/mark/structures.py +++ b/_pytest/mark/structures.py @@ -1,4 +1,9 @@ -from collections import namedtuple, MutableMapping as MappingMixin +import sys +if sys.version_info >= (3, 4): + from collections.abc import MutableMapping as MappingMixin +else: + from collections import MutableMapping as MappingMixin +from collections import namedtuple import warnings from operator import attrgetter import inspect From 8ca93219404c92f3f9d080a88e9b6d7642086025 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 29 Mar 2018 18:54:30 -0300 Subject: [PATCH 08/23] Move compatibility imports to compat.py and fix linting --- _pytest/assertion/util.py | 6 +----- _pytest/compat.py | 8 ++++++++ _pytest/mark/structures.py | 15 +++++---------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/_pytest/assertion/util.py b/_pytest/assertion/util.py index 71ad48251..06d60a6fc 100644 --- a/_pytest/assertion/util.py +++ b/_pytest/assertion/util.py @@ -5,11 +5,7 @@ import pprint import _pytest._code import py import six -import sys -if sys.version_info >= (3, 4): - from collections.abc import Sequence -else: - from collections import Sequence +from ..compat import Sequence u = six.text_type diff --git a/_pytest/compat.py b/_pytest/compat.py index 92df65656..a4ec25212 100644 --- a/_pytest/compat.py +++ b/_pytest/compat.py @@ -38,6 +38,14 @@ PY35 = sys.version_info[:2] >= (3, 5) PY36 = sys.version_info[:2] >= (3, 6) MODULE_NOT_FOUND_ERROR = 'ModuleNotFoundError' if PY36 else 'ImportError' +if _PY2: + # those raise DeprecationWarnings in Python >=3.7 + from collections.abc import MutableMapping as MappingMixin # noqa + from collections.abc import Sequence # noqa +else: + from collections import MutableMapping as MappingMixin # noqa + from collections import Sequence # noqa + def _format_args(func): return str(signature(func)) diff --git a/_pytest/mark/structures.py b/_pytest/mark/structures.py index c52fbd277..a0d4a5ce7 100644 --- a/_pytest/mark/structures.py +++ b/_pytest/mark/structures.py @@ -1,18 +1,13 @@ -import sys -if sys.version_info >= (3, 4): - from collections.abc import MutableMapping as MappingMixin -else: - from collections import MutableMapping as MappingMixin -from collections import namedtuple -import warnings -from operator import attrgetter import inspect +import warnings +from collections import namedtuple +from operator import attrgetter import attr -from ..deprecated import MARK_PARAMETERSET_UNPACKING -from ..compat import NOTSET, getfslineno from six.moves import map +from ..compat import NOTSET, getfslineno, MappingMixin +from ..deprecated import MARK_PARAMETERSET_UNPACKING EMPTY_PARAMETERSET_OPTION = "empty_parameter_set_mark" From 6a81aae4f27d53a0e6006d049bda3c32a255c792 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 29 Mar 2018 19:04:46 -0300 Subject: [PATCH 09/23] Move compatibility imports to compat.py and fix linting --- changelog/3339.trivial | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/3339.trivial diff --git a/changelog/3339.trivial b/changelog/3339.trivial new file mode 100644 index 000000000..20196e144 --- /dev/null +++ b/changelog/3339.trivial @@ -0,0 +1 @@ +Import some modules from ``collections`` instead of ``collections.abc`` as the former modules trigger ``DeprecationWarning`` in Python 3.7. From e5eba8419accaedcd4585b499d838ef3d93a1763 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 29 Mar 2018 19:21:59 -0300 Subject: [PATCH 10/23] Remove stale CHANGELOG entries Those were not removed in the last release because of a bug in towncrier; I was about to fallback to don't use the .rst extension for entries but just noticed that this was fixed in towncrier 17.8.0 so it should no longer be a problem. --- changelog/1478.feature | 1 - changelog/1642.feature.rst | 1 - changelog/1713.doc.rst | 1 - changelog/2370.bugfix.rst | 1 - changelog/2405.feature.rst | 1 - changelog/2770.feature | 2 -- changelog/2770.removal.rst | 1 - changelog/3034.feature | 1 - changelog/3084.removal | 1 - changelog/3139.feature | 1 - changelog/3149.feature | 1 - changelog/3156.feature | 1 - changelog/3189.feature | 1 - changelog/3190.feature | 1 - changelog/3198.feature.rst | 1 - changelog/3204.feature | 1 - changelog/3213.feature | 1 - changelog/3228.trivial.rst | 1 - changelog/3236.feature.rst | 1 - changelog/3245.trivial.rst | 1 - changelog/3246.trival.rst | 1 - changelog/3250.trivial.rst | 1 - changelog/3255.feature.rst | 1 - changelog/3265.trivial.rst | 1 - changelog/3268.trivial | 1 - changelog/3291.trivial.rst | 1 - changelog/3292.trivial.rst | 1 - changelog/3296.feature | 1 - changelog/3296.trivial | 1 - changelog/3297.bugfix.rst | 2 -- changelog/3304.trivial | 1 - changelog/3308.trivial.rst | 1 - changelog/3312.feature | 1 - changelog/3314.bugfix.rst | 3 --- 34 files changed, 38 deletions(-) delete mode 100644 changelog/1478.feature delete mode 100644 changelog/1642.feature.rst delete mode 100644 changelog/1713.doc.rst delete mode 100644 changelog/2370.bugfix.rst delete mode 100644 changelog/2405.feature.rst delete mode 100644 changelog/2770.feature delete mode 100644 changelog/2770.removal.rst delete mode 100644 changelog/3034.feature delete mode 100644 changelog/3084.removal delete mode 100644 changelog/3139.feature delete mode 100644 changelog/3149.feature delete mode 100644 changelog/3156.feature delete mode 100644 changelog/3189.feature delete mode 100644 changelog/3190.feature delete mode 100644 changelog/3198.feature.rst delete mode 100644 changelog/3204.feature delete mode 100644 changelog/3213.feature delete mode 100644 changelog/3228.trivial.rst delete mode 100644 changelog/3236.feature.rst delete mode 100644 changelog/3245.trivial.rst delete mode 100644 changelog/3246.trival.rst delete mode 100644 changelog/3250.trivial.rst delete mode 100644 changelog/3255.feature.rst delete mode 100644 changelog/3265.trivial.rst delete mode 100644 changelog/3268.trivial delete mode 100644 changelog/3291.trivial.rst delete mode 100644 changelog/3292.trivial.rst delete mode 100644 changelog/3296.feature delete mode 100644 changelog/3296.trivial delete mode 100644 changelog/3297.bugfix.rst delete mode 100644 changelog/3304.trivial delete mode 100644 changelog/3308.trivial.rst delete mode 100644 changelog/3312.feature delete mode 100644 changelog/3314.bugfix.rst diff --git a/changelog/1478.feature b/changelog/1478.feature deleted file mode 100644 index defc79b9b..000000000 --- a/changelog/1478.feature +++ /dev/null @@ -1 +0,0 @@ -New ``--show-capture`` command-line option that allows to specify how to display captured output when tests fail: ``no``, ``stdout``, ``stderr``, ``log`` or ``all`` (the default). diff --git a/changelog/1642.feature.rst b/changelog/1642.feature.rst deleted file mode 100644 index cb40bee75..000000000 --- a/changelog/1642.feature.rst +++ /dev/null @@ -1 +0,0 @@ -New ``--rootdir`` command-line option to override the rules for discovering the root directory. See `customize `_ in the documentation for details. diff --git a/changelog/1713.doc.rst b/changelog/1713.doc.rst deleted file mode 100644 index 291ee3c74..000000000 --- a/changelog/1713.doc.rst +++ /dev/null @@ -1 +0,0 @@ -Added a `reference `_ page to the docs. diff --git a/changelog/2370.bugfix.rst b/changelog/2370.bugfix.rst deleted file mode 100644 index 937acb471..000000000 --- a/changelog/2370.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Suppress ``IOError`` when closing the temporary file used for capturing streams in Python 2.7. diff --git a/changelog/2405.feature.rst b/changelog/2405.feature.rst deleted file mode 100644 index b041c1328..000000000 --- a/changelog/2405.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Fixtures are now instantiated based on their scopes, with higher-scoped fixtures (such as ``session``) being instantiated first than lower-scoped fixtures (such as ``function``). The relative order of fixtures of the same scope is kept unchanged, based in their declaration order and their dependencies. diff --git a/changelog/2770.feature b/changelog/2770.feature deleted file mode 100644 index 248f2893d..000000000 --- a/changelog/2770.feature +++ /dev/null @@ -1,2 +0,0 @@ -``record_xml_property`` renamed to ``record_property`` and is now compatible with xdist, markers and any reporter. -``record_xml_property`` name is now deprecated. diff --git a/changelog/2770.removal.rst b/changelog/2770.removal.rst deleted file mode 100644 index 0e38009ab..000000000 --- a/changelog/2770.removal.rst +++ /dev/null @@ -1 +0,0 @@ -``record_xml_property`` fixture is now deprecated in favor of the more generic ``record_property``. \ No newline at end of file diff --git a/changelog/3034.feature b/changelog/3034.feature deleted file mode 100644 index 12330cdd6..000000000 --- a/changelog/3034.feature +++ /dev/null @@ -1 +0,0 @@ -New ``--nf``, ``--new-first`` options: run new tests first followed by the rest of the tests, in both cases tests are also sorted by the file modified time, with more recent files coming first. diff --git a/changelog/3084.removal b/changelog/3084.removal deleted file mode 100644 index 52bf7ed91..000000000 --- a/changelog/3084.removal +++ /dev/null @@ -1 +0,0 @@ -Defining ``pytest_plugins`` is now deprecated in non-top-level conftest.py files, because they "leak" to the entire directory tree. diff --git a/changelog/3139.feature b/changelog/3139.feature deleted file mode 100644 index 39ac0bb75..000000000 --- a/changelog/3139.feature +++ /dev/null @@ -1 +0,0 @@ -New ``--last-failed-no-failures`` command-line option that allows to specify the behavior of the cache plugin's ```--last-failed`` feature when no tests failed in the last run (or no cache was found): ``none`` or ``all`` (the default). diff --git a/changelog/3149.feature b/changelog/3149.feature deleted file mode 100644 index 0431f76ce..000000000 --- a/changelog/3149.feature +++ /dev/null @@ -1 +0,0 @@ -New ``--doctest-continue-on-failure`` command-line option to enable doctests to show multiple failures for each snippet, instead of stopping at the first failure. diff --git a/changelog/3156.feature b/changelog/3156.feature deleted file mode 100644 index 125605b38..000000000 --- a/changelog/3156.feature +++ /dev/null @@ -1 +0,0 @@ -Captured log messages are added to the ```` tag in the generated junit xml file if the ``junit_logging`` ini option is set to ``system-out``. If the value of this ini option is ``system-err`, the logs are written to ````. The default value for ``junit_logging`` is ``no``, meaning captured logs are not written to the output file. diff --git a/changelog/3189.feature b/changelog/3189.feature deleted file mode 100644 index d35789b1f..000000000 --- a/changelog/3189.feature +++ /dev/null @@ -1 +0,0 @@ -Allow the logging plugin to handle ``pytest_runtest_logstart`` and ``pytest_runtest_logfinish`` hooks when live logs are enabled. diff --git a/changelog/3190.feature b/changelog/3190.feature deleted file mode 100644 index 95bb5e39b..000000000 --- a/changelog/3190.feature +++ /dev/null @@ -1 +0,0 @@ -Passing `--log-cli-level` in the command-line now automatically activates live logging. diff --git a/changelog/3198.feature.rst b/changelog/3198.feature.rst deleted file mode 100644 index 3c7838302..000000000 --- a/changelog/3198.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Add command line option ``--deselect`` to allow deselection of individual tests at collection time. diff --git a/changelog/3204.feature b/changelog/3204.feature deleted file mode 100644 index 8ab129a12..000000000 --- a/changelog/3204.feature +++ /dev/null @@ -1 +0,0 @@ -Captured logs are printed before entering pdb. diff --git a/changelog/3213.feature b/changelog/3213.feature deleted file mode 100644 index 1b25793a7..000000000 --- a/changelog/3213.feature +++ /dev/null @@ -1 +0,0 @@ -Deselected item count is now shown before tests are run, e.g. ``collected X items / Y deselected``. diff --git a/changelog/3228.trivial.rst b/changelog/3228.trivial.rst deleted file mode 100644 index 8b69e25b4..000000000 --- a/changelog/3228.trivial.rst +++ /dev/null @@ -1 +0,0 @@ -Change minimum requirement of ``attrs`` to ``17.4.0``. diff --git a/changelog/3236.feature.rst b/changelog/3236.feature.rst deleted file mode 100644 index 0fd9c9b58..000000000 --- a/changelog/3236.feature.rst +++ /dev/null @@ -1 +0,0 @@ -The builtin module ``platform`` is now available for use in expressions in ``pytest.mark``. diff --git a/changelog/3245.trivial.rst b/changelog/3245.trivial.rst deleted file mode 100644 index 0b93dc675..000000000 --- a/changelog/3245.trivial.rst +++ /dev/null @@ -1 +0,0 @@ -Renamed example directories so all tests pass when ran from the base directory. \ No newline at end of file diff --git a/changelog/3246.trival.rst b/changelog/3246.trival.rst deleted file mode 100644 index 58e13a1dd..000000000 --- a/changelog/3246.trival.rst +++ /dev/null @@ -1 +0,0 @@ -Remove usage of deprecated ``metafunc.addcall`` in our own tests. diff --git a/changelog/3250.trivial.rst b/changelog/3250.trivial.rst deleted file mode 100644 index a80bac513..000000000 --- a/changelog/3250.trivial.rst +++ /dev/null @@ -1 +0,0 @@ -Internal ``mark.py`` module has been turned into a package. diff --git a/changelog/3255.feature.rst b/changelog/3255.feature.rst deleted file mode 100644 index d4994740d..000000000 --- a/changelog/3255.feature.rst +++ /dev/null @@ -1 +0,0 @@ -The *short test summary info* section now is displayed after tracebacks and warnings in the terminal. diff --git a/changelog/3265.trivial.rst b/changelog/3265.trivial.rst deleted file mode 100644 index b4ad22ecf..000000000 --- a/changelog/3265.trivial.rst +++ /dev/null @@ -1 +0,0 @@ -``pytest`` now depends on the `more_itertools `_ package. diff --git a/changelog/3268.trivial b/changelog/3268.trivial deleted file mode 100644 index 1cfb3ff10..000000000 --- a/changelog/3268.trivial +++ /dev/null @@ -1 +0,0 @@ -Added warning when ``[pytest]`` section is used in a ``.cfg`` file passed with ``-c`` diff --git a/changelog/3291.trivial.rst b/changelog/3291.trivial.rst deleted file mode 100644 index a2e65c2d7..000000000 --- a/changelog/3291.trivial.rst +++ /dev/null @@ -1 +0,0 @@ -``nodeids`` can now be passed explicitly to ``FSCollector`` and ``Node`` constructors. diff --git a/changelog/3292.trivial.rst b/changelog/3292.trivial.rst deleted file mode 100644 index 0e60e3431..000000000 --- a/changelog/3292.trivial.rst +++ /dev/null @@ -1 +0,0 @@ -Internal refactoring of ``FormattedExcinfo`` to use ``attrs`` facilities and remove old support code for legacy Python versions. diff --git a/changelog/3296.feature b/changelog/3296.feature deleted file mode 100644 index dde6b78ea..000000000 --- a/changelog/3296.feature +++ /dev/null @@ -1 +0,0 @@ -New ``--verbosity`` flag to set verbosity level explicitly. \ No newline at end of file diff --git a/changelog/3296.trivial b/changelog/3296.trivial deleted file mode 100644 index 7b5b4e1b4..000000000 --- a/changelog/3296.trivial +++ /dev/null @@ -1 +0,0 @@ -Refactoring to unify how verbosity is handled internally. \ No newline at end of file diff --git a/changelog/3297.bugfix.rst b/changelog/3297.bugfix.rst deleted file mode 100644 index f3cbc2c9c..000000000 --- a/changelog/3297.bugfix.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixed ``clear()`` method on ``caplog`` fixture which cleared ``records``, -but not the ``text`` property. \ No newline at end of file diff --git a/changelog/3304.trivial b/changelog/3304.trivial deleted file mode 100644 index 6e66a1e13..000000000 --- a/changelog/3304.trivial +++ /dev/null @@ -1 +0,0 @@ -Internal refactoring to better integrate with argparse. \ No newline at end of file diff --git a/changelog/3308.trivial.rst b/changelog/3308.trivial.rst deleted file mode 100644 index 693384836..000000000 --- a/changelog/3308.trivial.rst +++ /dev/null @@ -1 +0,0 @@ -Fix a python example when calling a fixture in doc/en/usage.rst diff --git a/changelog/3312.feature b/changelog/3312.feature deleted file mode 100644 index ffb4df8e9..000000000 --- a/changelog/3312.feature +++ /dev/null @@ -1 +0,0 @@ -``pytest.approx`` now accepts comparing a numpy array with a scalar. diff --git a/changelog/3314.bugfix.rst b/changelog/3314.bugfix.rst deleted file mode 100644 index 4b671ec21..000000000 --- a/changelog/3314.bugfix.rst +++ /dev/null @@ -1,3 +0,0 @@ -During test collection, when stdin is not allowed to be read, the -``DontReadFromStdin`` object still allow itself to be iterable and -resolved to an iterator without crashing. From daf1de0fedeeb320d4f4597aa0991acde4efb3f2 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 29 Mar 2018 19:48:48 -0300 Subject: [PATCH 11/23] Add reference docs for pytest_sessionstart/finish and update their docstrings --- _pytest/hookspec.py | 5 +++-- doc/en/reference.rst | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/_pytest/hookspec.py b/_pytest/hookspec.py index 70349416e..f5bdfabc5 100644 --- a/_pytest/hookspec.py +++ b/_pytest/hookspec.py @@ -413,14 +413,15 @@ def pytest_fixture_post_finalizer(fixturedef, request): def pytest_sessionstart(session): - """ before session.main() is called. + """ called after the ``Session`` object has been created and before performing collection + and entering the run test loop. :param _pytest.main.Session session: the pytest session object """ def pytest_sessionfinish(session, exitstatus): - """ whole test run finishes. + """ called after whole test run finished, right before returning the exit status to the system. :param _pytest.main.Session session: the pytest session object :param int exitstatus: the status which pytest will return to the system diff --git a/doc/en/reference.rst b/doc/en/reference.rst index d87ec28f3..ef7f8f51f 100644 --- a/doc/en/reference.rst +++ b/doc/en/reference.rst @@ -527,6 +527,8 @@ Initialization hooks called for plugins and ``conftest.py`` files. .. autofunction:: pytest_addhooks .. autofunction:: pytest_configure .. autofunction:: pytest_unconfigure +.. autofunction:: pytest_sessionstart +.. autofunction:: pytest_sessionfinish Test running hooks ~~~~~~~~~~~~~~~~~~ From 3fc2c94b5e105421c3d554d92ac07eb289d56289 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 29 Mar 2018 23:15:03 -0300 Subject: [PATCH 12/23] Switch imports based on python versions, my bad --- _pytest/compat.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_pytest/compat.py b/_pytest/compat.py index a4ec25212..bcb31cb88 100644 --- a/_pytest/compat.py +++ b/_pytest/compat.py @@ -38,11 +38,11 @@ PY35 = sys.version_info[:2] >= (3, 5) PY36 = sys.version_info[:2] >= (3, 6) MODULE_NOT_FOUND_ERROR = 'ModuleNotFoundError' if PY36 else 'ImportError' -if _PY2: - # those raise DeprecationWarnings in Python >=3.7 +if _PY3: from collections.abc import MutableMapping as MappingMixin # noqa from collections.abc import Sequence # noqa else: + # those raise DeprecationWarnings in Python >=3.7 from collections import MutableMapping as MappingMixin # noqa from collections import Sequence # noqa From 211f3c47b5f71c4f1c243591fe7601db95939455 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sun, 1 Apr 2018 15:31:22 +0200 Subject: [PATCH 13/23] record_property is no longer experimental --- _pytest/junitxml.py | 5 ----- changelog/3360.trivial | 2 ++ 2 files changed, 2 insertions(+), 5 deletions(-) create mode 100644 changelog/3360.trivial diff --git a/_pytest/junitxml.py b/_pytest/junitxml.py index 5207c2514..3a0e4a071 100644 --- a/_pytest/junitxml.py +++ b/_pytest/junitxml.py @@ -245,11 +245,6 @@ def record_property(request): def test_function(record_property): record_property("example_key", 1) """ - request.node.warn( - code='C3', - message='record_property is an experimental feature', - ) - def append_property(name, value): request.node.user_properties.append((name, value)) return append_property diff --git a/changelog/3360.trivial b/changelog/3360.trivial new file mode 100644 index 000000000..3b0e89e1f --- /dev/null +++ b/changelog/3360.trivial @@ -0,0 +1,2 @@ +record_property is no longer experimental, removing the warnings was forgotten. + From 7c0c91a7a291b27f1555d47d899a1ff61dee1d48 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Tue, 3 Apr 2018 08:00:57 +0200 Subject: [PATCH 14/23] fix record_property test --- testing/test_junitxml.py | 1 - 1 file changed, 1 deletion(-) diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index b8bbd888f..3241cf6c1 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -877,7 +877,6 @@ def test_record_property(testdir): pnodes[1].assert_attr(name="foo", value="<1") result.stdout.fnmatch_lines([ 'test_record_property.py::test_record', - '*record_property*experimental*', ]) From 9aa2a837850ef668ea6387ad7dc9ce4ce8d8a31d Mon Sep 17 00:00:00 2001 From: Stefan Scherfke Date: Tue, 3 Apr 2018 16:48:32 +0200 Subject: [PATCH 15/23] Use correct spelling for "more-itertools" --- CHANGELOG.rst | 2 +- setup.py | 2 +- tox.ini | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9d8f96d1b..1896f550b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -136,7 +136,7 @@ Trivial/Internal Changes - Internal ``mark.py`` module has been turned into a package. (`#3250 `_) -- ``pytest`` now depends on the `more_itertools +- ``pytest`` now depends on the `more-itertools `_ package. (`#3265 `_) diff --git a/setup.py b/setup.py index 1cbabd72e..984994a7f 100644 --- a/setup.py +++ b/setup.py @@ -60,7 +60,7 @@ def main(): 'six>=1.10.0', 'setuptools', 'attrs>=17.4.0', - 'more_itertools>=4.0.0', + 'more-itertools>=4.0.0', ] # if _PYTEST_SETUP_SKIP_PLUGGY_DEP is set, skip installing pluggy; # used by tox.ini to test with pluggy master diff --git a/tox.ini b/tox.ini index ac0e04ca0..75f28ca92 100644 --- a/tox.ini +++ b/tox.ini @@ -128,7 +128,7 @@ usedevelop = True changedir = doc/en deps = attrs - more_itertools + more-itertools PyYAML sphinx sphinxcontrib-trio From 2018cf12b1579b648461d32fe624c06b51a6b696 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Thu, 5 Apr 2018 14:05:26 +0200 Subject: [PATCH 16/23] fix the record_property test --- testing/test_junitxml.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index 3241cf6c1..a8f5b9fec 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -868,16 +868,13 @@ def test_record_property(testdir): def test_record(record_property, other): record_property("foo", "<1"); """) - result, dom = runandparse(testdir, '-rw') + result, dom = runandparse(testdir, '-rwv') node = dom.find_first_by_tag("testsuite") tnode = node.find_first_by_tag("testcase") psnode = tnode.find_first_by_tag('properties') pnodes = psnode.find_by_tag('property') pnodes[0].assert_attr(name="bar", value="1") pnodes[1].assert_attr(name="foo", value="<1") - result.stdout.fnmatch_lines([ - 'test_record_property.py::test_record', - ]) def test_record_property_same_name(testdir): From db24723b6194a0c32107b8fb4d9cb6df9983aef8 Mon Sep 17 00:00:00 2001 From: David Chudzicki Date: Thu, 5 Apr 2018 20:34:26 +0200 Subject: [PATCH 17/23] no-capture in sample code demo'ing plugin --- doc/en/writing_plugins.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/en/writing_plugins.rst b/doc/en/writing_plugins.rst index 140fca481..7da09dbbb 100644 --- a/doc/en/writing_plugins.rst +++ b/doc/en/writing_plugins.rst @@ -85,8 +85,8 @@ sub directory but not for other directories:: Here is how you might run it:: - pytest test_flat.py # will not show "setting up" - pytest a/test_sub.py # will show "setting up" +     pytest test_flat.py --capture=no # will not show "setting up" + pytest a/test_sub.py --capture=no # will show "setting up" .. note:: If you have ``conftest.py`` files which do not reside in a From f17dfa4292746ed7f6e13b856c3b4a1d5e119bb9 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 5 Apr 2018 20:55:08 -0300 Subject: [PATCH 18/23] Remove label descriptions from development guide The descriptions have been moved directly to GitHub's new label description slot. --- doc/en/development_guide.rst | 71 +++++------------------------------- 1 file changed, 9 insertions(+), 62 deletions(-) diff --git a/doc/en/development_guide.rst b/doc/en/development_guide.rst index 465e97de0..2dac82880 100644 --- a/doc/en/development_guide.rst +++ b/doc/en/development_guide.rst @@ -2,7 +2,7 @@ Development Guide ================= -Some general guidelines regarding development in pytest for core maintainers and general contributors. Nothing here +Some general guidelines regarding development in pytest for maintainers and contributors. Nothing here is set in stone and can't be changed, feel free to suggest improvements or changes in the workflow. @@ -37,72 +37,19 @@ Any question, feature, bug or proposal is welcome as an issue. Users are encoura GitHub issues should use labels to categorize them. Labels should be created sporadically, to fill a niche; we should avoid creating labels just for the sake of creating them. -Here is a list of labels and a brief description mentioning their intent. +Each label should include a description in the GitHub's interface stating its purpose. +Temporary labels +~~~~~~~~~~~~~~~~ -**Type** +To classify issues for a special event it is encouraged to create a temporary label. This helps those involved to find +the relevant issues to work on. Examples of that are sprints in Python events or global hacking events. -* ``type: backward compatibility``: issue that will cause problems with old pytest versions. -* ``type: bug``: problem that needs to be addressed. -* ``type: deprecation``: feature that will be deprecated in the future. -* ``type: docs``: documentation missing or needing clarification. -* ``type: enhancement``: new feature or API change, should be merged into ``features``. -* ``type: feature-branch``: new feature or API change, should be merged into ``features``. -* ``type: infrastructure``: improvement to development/releases/CI structure. -* ``type: performance``: performance or memory problem/improvement. -* ``type: proposal``: proposal for a new feature, often to gather opinions or design the API around the new feature. -* ``type: question``: question regarding usage, installation, internals or how to test something. -* ``type: refactoring``: internal improvements to the code. -* ``type: regression``: indicates a problem that was introduced in a release which was working previously. +* ``temporary: EP2017 sprint``: candidate issues or PRs tackled during the EuroPython 2017 -**Status** +Issues created at those events should have other relevant labels added as well. -* ``status: critical``: grave problem or usability issue that affects lots of users. -* ``status: easy``: easy issue that is friendly to new contributors. -* ``status: help wanted``: core developers need help from experts on this topic. -* ``status: needs information``: reporter needs to provide more information; can be closed after 2 or more weeks of inactivity. - -**Topic** - -* ``topic: collection`` -* ``topic: fixtures`` -* ``topic: parametrize`` -* ``topic: reporting`` -* ``topic: selection`` -* ``topic: tracebacks`` - -**Plugin (internal or external)** - -* ``plugin: cache`` -* ``plugin: capture`` -* ``plugin: doctests`` -* ``plugin: junitxml`` -* ``plugin: monkeypatch`` -* ``plugin: nose`` -* ``plugin: pastebin`` -* ``plugin: pytester`` -* ``plugin: tmpdir`` -* ``plugin: unittest`` -* ``plugin: warnings`` -* ``plugin: xdist`` - - -**OS** - -Issues specific to a single operating system. Do not use as a means to indicate where an issue originated from, only -for problems that happen **only** in that system. - -* ``os: linux`` -* ``os: mac`` -* ``os: windows`` - -**Temporary** - -Used to classify issues for limited time, to help find issues related in events for example. -They should be removed after they are no longer relevant. - -* ``temporary: EP2017 sprint``: -* ``temporary: sprint-candidate``: +Those labels should be removed after they are no longer relevant. .. include:: ../../HOWTORELEASE.rst From ec2d8223cff2fe83119d142c3b4b365786b3182d Mon Sep 17 00:00:00 2001 From: Tim Strazny Date: Fri, 6 Apr 2018 14:16:12 +0200 Subject: [PATCH 19/23] Fix issue #3372 --- AUTHORS | 1 + _pytest/python_api.py | 9 +++++---- changelog/3372.bugfix.rst | 1 + testing/python/raises.py | 18 ++++++++++++++++++ 4 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 changelog/3372.bugfix.rst diff --git a/AUTHORS b/AUTHORS index ad9541423..eb3c016a5 100644 --- a/AUTHORS +++ b/AUTHORS @@ -188,6 +188,7 @@ Tareq Alayan Ted Xiao Thomas Grainger Thomas Hisch +Tim Strazny Tom Dalton Tom Viner Trevor Bekolay diff --git a/_pytest/python_api.py b/_pytest/python_api.py index 0b30b7ac8..1d1617d72 100644 --- a/_pytest/python_api.py +++ b/_pytest/python_api.py @@ -584,10 +584,11 @@ def raises(expected_exception, *args, **kwargs): """ __tracebackhide__ = True - for exc in filterfalse(isclass, always_iterable(expected_exception)): - msg = ("exceptions must be old-style classes or" - " derived from BaseException, not %s") - raise TypeError(msg % type(exc)) + if not issubclass(expected_exception, BaseException): + for exc in filterfalse(isclass, always_iterable(expected_exception)): + msg = ("exceptions must be old-style classes or" + " derived from BaseException, not %s") + raise TypeError(msg % type(exc)) message = "DID NOT RAISE {0}".format(expected_exception) match_expr = None diff --git a/changelog/3372.bugfix.rst b/changelog/3372.bugfix.rst new file mode 100644 index 000000000..722bdab1e --- /dev/null +++ b/changelog/3372.bugfix.rst @@ -0,0 +1 @@ +``pytest.raises`` now works with exception classes that look like iterables. diff --git a/testing/python/raises.py b/testing/python/raises.py index 156319816..053426395 100644 --- a/testing/python/raises.py +++ b/testing/python/raises.py @@ -1,3 +1,4 @@ +from _pytest.outcomes import Failed import pytest import sys @@ -147,3 +148,20 @@ class TestRaises(object): with pytest.raises(ValueError): with pytest.raises(IndexError, match='nomatch'): int('asdf') + + def test_raises_exception_looks_iterable(self): + from six import add_metaclass + + class Meta(type(object)): + def __getitem__(self, item): + return 1/0 + + def __len__(self): + return 1 + + @add_metaclass(Meta) + class ClassLooksIterableException(Exception): + pass + + with pytest.raises(Failed, match="DID NOT RAISE "): + pytest.raises(ClassLooksIterableException, lambda: None) From 0cd74dc3243877dcb5e0d266e6a1e9862c60f207 Mon Sep 17 00:00:00 2001 From: Tim Strazny Date: Fri, 6 Apr 2018 14:40:30 +0200 Subject: [PATCH 20/23] Ensure object is class before calling issubclass. --- _pytest/python_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_pytest/python_api.py b/_pytest/python_api.py index 1d1617d72..92eaa5bd5 100644 --- a/_pytest/python_api.py +++ b/_pytest/python_api.py @@ -584,7 +584,7 @@ def raises(expected_exception, *args, **kwargs): """ __tracebackhide__ = True - if not issubclass(expected_exception, BaseException): + if not isclass(expected_exception) or not issubclass(expected_exception, BaseException): for exc in filterfalse(isclass, always_iterable(expected_exception)): msg = ("exceptions must be old-style classes or" " derived from BaseException, not %s") From 846d91fb95cc27c3a8b4ca50317a7de0bc0c510e Mon Sep 17 00:00:00 2001 From: Tim Strazny Date: Fri, 6 Apr 2018 16:23:04 +0200 Subject: [PATCH 21/23] Follow Ronny's advice and use ``type`` in ``base_type``. --- _pytest/python_api.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/_pytest/python_api.py b/_pytest/python_api.py index 92eaa5bd5..521c3bc48 100644 --- a/_pytest/python_api.py +++ b/_pytest/python_api.py @@ -2,6 +2,7 @@ import math import sys import py +from six import binary_type, text_type from six.moves import zip, filterfalse from more_itertools.more import always_iterable @@ -584,11 +585,11 @@ def raises(expected_exception, *args, **kwargs): """ __tracebackhide__ = True - if not isclass(expected_exception) or not issubclass(expected_exception, BaseException): - for exc in filterfalse(isclass, always_iterable(expected_exception)): - msg = ("exceptions must be old-style classes or" - " derived from BaseException, not %s") - raise TypeError(msg % type(exc)) + base_type = (type, text_type, binary_type) + for exc in filterfalse(isclass, always_iterable(expected_exception, base_type)): + msg = ("exceptions must be old-style classes or" + " derived from BaseException, not %s") + raise TypeError(msg % type(exc)) message = "DID NOT RAISE {0}".format(expected_exception) match_expr = None From 5bd85610163df47dfe5070532c09268032dc4b42 Mon Sep 17 00:00:00 2001 From: Tim Strazny Date: Fri, 6 Apr 2018 17:36:35 +0200 Subject: [PATCH 22/23] linting: unfortunate dedent be gone. --- _pytest/python_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_pytest/python_api.py b/_pytest/python_api.py index 521c3bc48..8e09a4a6f 100644 --- a/_pytest/python_api.py +++ b/_pytest/python_api.py @@ -588,7 +588,7 @@ def raises(expected_exception, *args, **kwargs): base_type = (type, text_type, binary_type) for exc in filterfalse(isclass, always_iterable(expected_exception, base_type)): msg = ("exceptions must be old-style classes or" - " derived from BaseException, not %s") + " derived from BaseException, not %s") raise TypeError(msg % type(exc)) message = "DID NOT RAISE {0}".format(expected_exception) From aa95a425d7708d80fc887b1825662daf3a624ba7 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 11 Apr 2018 19:41:10 -0300 Subject: [PATCH 23/23] Attempt to solve race-condition which corrupts .pyc files on Windows This uses of the `atomicwrites` library. This is very hard to create a reliable test for. Fix #3008 --- _pytest/assertion/rewrite.py | 41 ++++++++++++----------------------- changelog/3008.bugfix.rst | 1 + changelog/3008.trivial.rst | 1 + setup.py | 1 + testing/test_assertrewrite.py | 12 +++++----- 5 files changed, 23 insertions(+), 33 deletions(-) create mode 100644 changelog/3008.bugfix.rst create mode 100644 changelog/3008.trivial.rst diff --git a/_pytest/assertion/rewrite.py b/_pytest/assertion/rewrite.py index db3674930..0499a792f 100644 --- a/_pytest/assertion/rewrite.py +++ b/_pytest/assertion/rewrite.py @@ -12,7 +12,9 @@ import struct import sys import types +import atomicwrites import py + from _pytest.assertion import util @@ -140,7 +142,7 @@ class AssertionRewritingHook(object): # Probably a SyntaxError in the test. return None if write: - _make_rewritten_pyc(state, source_stat, pyc, co) + _write_pyc(state, co, source_stat, pyc) else: state.trace("found cached rewritten pyc for %r" % (fn,)) self.modules[name] = co, pyc @@ -258,22 +260,21 @@ def _write_pyc(state, co, source_stat, pyc): # sometime to be able to use imp.load_compiled to load them. (See # the comment in load_module above.) try: - fp = open(pyc, "wb") - except IOError: - err = sys.exc_info()[1].errno - state.trace("error writing pyc file at %s: errno=%s" % (pyc, err)) + with atomicwrites.atomic_write(pyc, mode="wb", overwrite=True) as fp: + fp.write(imp.get_magic()) + mtime = int(source_stat.mtime) + size = source_stat.size & 0xFFFFFFFF + fp.write(struct.pack("`_ library. diff --git a/setup.py b/setup.py index 984994a7f..d6defba6b 100644 --- a/setup.py +++ b/setup.py @@ -61,6 +61,7 @@ def main(): 'setuptools', 'attrs>=17.4.0', 'more-itertools>=4.0.0', + 'atomicwrites>=1.0', ] # if _PYTEST_SETUP_SKIP_PLUGGY_DEP is set, skip installing pluggy; # used by tox.ini to test with pluggy master diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index 2a3dbc2ec..4f7c95600 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -839,22 +839,22 @@ class TestAssertionRewriteHookDetails(object): def test_write_pyc(self, testdir, tmpdir, monkeypatch): from _pytest.assertion.rewrite import _write_pyc from _pytest.assertion import AssertionState - try: - import __builtin__ as b - except ImportError: - import builtins as b + import atomicwrites + from contextlib import contextmanager config = testdir.parseconfig([]) state = AssertionState(config, "rewrite") source_path = tmpdir.ensure("source.py") pycpath = tmpdir.join("pyc").strpath assert _write_pyc(state, [1], source_path.stat(), pycpath) - def open(*args): + @contextmanager + def atomic_write_failed(fn, mode='r', overwrite=False): e = IOError() e.errno = 10 raise e + yield # noqa - monkeypatch.setattr(b, "open", open) + monkeypatch.setattr(atomicwrites, "atomic_write", atomic_write_failed) assert not _write_pyc(state, [1], source_path.stat(), pycpath) def test_resources_provider_for_loader(self, testdir):