From 12edc4e7b83fdf712f0c9f266a3beba1650b6bef Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 15 Oct 2018 20:06:59 -0300 Subject: [PATCH 01/11] Pass TRAVIS env var to test environments [skip appveyor] xdist has an workaround inplace for Travis so "-n auto" works. Fix #4162 --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 4ccb0305ea8..98d2ce0414e 100644 --- a/tox.ini +++ b/tox.ini @@ -21,7 +21,7 @@ commands = {env:_PYTEST_TOX_COVERAGE_RUN:} pytest --lsof coverage: coverage combine coverage: coverage report -passenv = USER USERNAME COVERAGE_* +passenv = USER USERNAME COVERAGE_* TRAVIS setenv = # configuration if a user runs tox with a "coverage" factor, for example "tox -e py36-coverage" coverage: _PYTEST_TOX_COVERAGE_RUN=coverage run -m From f55ded20a951988391d7f2877bc94e648a014948 Mon Sep 17 00:00:00 2001 From: Vincent Barbaresi Date: Tue, 16 Oct 2018 14:46:38 +0200 Subject: [PATCH 02/11] fix #3533: properly escape raw XML object Using string formatting with the raw escaped object lead to string evaluation "" Format the unescaped string first, then use the XML escape method as a last step. --- src/_pytest/junitxml.py | 8 +++++--- testing/test_junitxml.py | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/_pytest/junitxml.py b/src/_pytest/junitxml.py index ac00c772a1a..a39c94c1394 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -221,12 +221,14 @@ def append_skipped(self, report): else: filename, lineno, skipreason = report.longrepr if skipreason.startswith("Skipped: "): - skipreason = bin_xml_escape(skipreason[9:]) + skipreason = skipreason[9:] + details = "%s:%s: %s" % (filename, lineno, skipreason) + self.append( Junit.skipped( - "%s:%s: %s" % (filename, lineno, skipreason), + bin_xml_escape(details), type="pytest.skip", - message=skipreason, + message=bin_xml_escape(skipreason), ) ) self.write_captured_output(report) diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index 29d3f7f6ec1..079b01f3201 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -1222,3 +1222,19 @@ def test_func(): assert result.ret == 0 node = dom.find_first_by_tag("testsuite") node.assert_attr(name=expected) + + +def test_escaped_skipreason_issue3533(testdir): + testdir.makepyfile( + """ + import pytest + @pytest.mark.skip(reason='1 <> 2') + def test_skip(): + pass + """ + ) + _, dom = runandparse(testdir) + node = dom.find_first_by_tag("testcase") + snode = node.find_first_by_tag("skipped") + assert "1 <> 2" in snode.text + snode.assert_attr(message="1 <> 2") From d72154acda1e3f14ce6d531b5e4f54aff9e9a5f4 Mon Sep 17 00:00:00 2001 From: Vincent Barbaresi Date: Tue, 16 Oct 2018 15:01:07 +0200 Subject: [PATCH 03/11] add changelog for #3533 --- changelog/3533.bugfix.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/3533.bugfix.rst diff --git a/changelog/3533.bugfix.rst b/changelog/3533.bugfix.rst new file mode 100644 index 00000000000..89f13645860 --- /dev/null +++ b/changelog/3533.bugfix.rst @@ -0,0 +1 @@ +Fix unescaped XML raw objects in JUnit report for skipped tests From 215a2ed3dec9d762a2fadb08c5393eb36ddf224f Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 16 Oct 2018 21:00:57 -0300 Subject: [PATCH 04/11] Update warnings docs Some points on the document work different in 3.9, plus changed the order of the sections a bit to make more sense for users reading it for the first time. --- doc/en/warnings.rst | 181 ++++++++++++++++++++------------------------ 1 file changed, 84 insertions(+), 97 deletions(-) diff --git a/doc/en/warnings.rst b/doc/en/warnings.rst index 060057d9114..f83377cf68f 100644 --- a/doc/en/warnings.rst +++ b/doc/en/warnings.rst @@ -75,8 +75,54 @@ Both ``-W`` command-line option and ``filterwarnings`` ini option are based on P `-W option`_ and `warnings.simplefilter`_, so please refer to those sections in the Python documentation for other examples and advanced usage. -Disabling warning summary -------------------------- +.. _`filterwarnings`: + +``@pytest.mark.filterwarnings`` +------------------------------- + +.. versionadded:: 3.2 + +You can use the ``@pytest.mark.filterwarnings`` to add warning filters to specific test items, +allowing you to have finer control of which warnings should be captured at test, class or +even module level: + +.. code-block:: python + + import warnings + + + def api_v1(): + warnings.warn(UserWarning("api v1, should use functions from v2")) + return 1 + + + @pytest.mark.filterwarnings("ignore:api v1") + def test_one(): + assert api_v1() == 1 + + +Filters applied using a mark take precedence over filters passed on the command line or configured +by the ``filterwarnings`` ini option. + +You may apply a filter to all tests of a class by using the ``filterwarnings`` mark as a class +decorator or to all tests in a module by setting the ``pytestmark`` variable: + +.. code-block:: python + + # turns all warnings into errors for this module + pytestmark = pytest.mark.filterwarnings("error") + + + +*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 +.. _warnings.simplefilter: https://docs.python.org/3/library/warnings.html#warnings.simplefilter +.. _`pytest-warnings`: https://github.com/fschulze/pytest-warnings + +Disabling warnings summary +-------------------------- Although not recommended, you can use the ``--disable-warnings`` command-line option to suppress the warning summary entirely from the test run output. @@ -103,10 +149,14 @@ DeprecationWarning and PendingDeprecationWarning .. versionadded:: 3.8 .. versionchanged:: 3.9 -By default pytest will display ``DeprecationWarning`` and ``PendingDeprecationWarning``. +By default pytest will display ``DeprecationWarning`` and ``PendingDeprecationWarning`` warnings from +user code and third-party libraries, as recommended by `PEP-0506 `_. +This helps users keep their code modern and avoid breakages when deprecated warnings are effectively removed. Sometimes it is useful to hide some specific deprecation warnings that happen in code that you have no control over -(such as third-party libraries), in which case you might use the standard warning filters options (ini or marks). +(such as third-party libraries), in which case you might use the warning filters options (ini or marks) to ignore +those warnings. + For example: .. code-block:: ini @@ -116,83 +166,57 @@ For example: ignore:.*U.*mode is deprecated:DeprecationWarning +This will ignore all warnings of type ``DeprecationWarning`` where the start of the message matches +the regular expression ``".*U.*mode is deprecated"``. + .. note:: If warnings are configured at the interpreter level, using the `PYTHONWARNINGS `_ environment variable or the ``-W`` command-line option, pytest will not configure any filters by default. -.. note:: - This feature makes pytest more compliant with `PEP-0506 `_ which suggests that those warnings should - be shown by default by test runners, but pytest doesn't follow ``PEP-0506`` completely because resetting all - warning filters like suggested in the PEP will break existing test suites that configure warning filters themselves + Also pytest doesn't follow ``PEP-0506`` suggestion of resetting all warning filters because + it might break test suites that configure warning filters themselves by calling ``warnings.simplefilter`` (see issue `#2430 `_ for an example of that). -.. _`filterwarnings`: - -``@pytest.mark.filterwarnings`` -------------------------------- - -.. versionadded:: 3.2 - -You can use the ``@pytest.mark.filterwarnings`` to add warning filters to specific test items, -allowing you to have finer control of which warnings should be captured at test, class or -even module level: - -.. code-block:: python - - import warnings - - - def api_v1(): - warnings.warn(UserWarning("api v1, should use functions from v2")) - return 1 - - - @pytest.mark.filterwarnings("ignore:api v1") - def test_one(): - assert api_v1() == 1 - - -Filters applied using a mark take precedence over filters passed on the command line or configured -by the ``filterwarnings`` ini option. +.. _`ensuring a function triggers a deprecation warning`: -You may apply a filter to all tests of a class by using the ``filterwarnings`` mark as a class -decorator or to all tests in a module by setting the ``pytestmark`` variable: +.. _ensuring_function_triggers: -.. code-block:: python +Ensuring code triggers a deprecation warning +-------------------------------------------- - # turns all warnings into errors for this module - pytestmark = pytest.mark.filterwarnings("error") +You can also call a global helper for checking +that a certain function call triggers a ``DeprecationWarning`` or +``PendingDeprecationWarning``:: + import pytest -.. note:: + def test_global(): + pytest.deprecated_call(myfunction, 17) - Except for these features, pytest does not change the python warning filter; it only captures - and displays the warnings which are issued with respect to the currently configured filter, - including changes to the filter made by test functions or by the system under test. +By default, ``DeprecationWarning`` and ``PendingDeprecationWarning`` will not be +caught when using ``pytest.warns`` or ``recwarn`` because default Python warnings filters hide +them. If you wish to record them in your own code, use the +command ``warnings.simplefilter('always')``:: -.. note:: + import warnings + import pytest - ``DeprecationWarning`` and ``PendingDeprecationWarning`` are hidden by the standard library - by default so you have to explicitly configure them to be displayed in your ``pytest.ini``: + def test_deprecation(recwarn): + warnings.simplefilter('always') + warnings.warn("deprecated", DeprecationWarning) + assert len(recwarn) == 1 + assert recwarn.pop(DeprecationWarning) - .. code-block:: ini +You can also use it as a contextmanager:: - [pytest] - filterwarnings = - once::DeprecationWarning - once::PendingDeprecationWarning + def test_global(): + with pytest.deprecated_call(): + myobject.deprecated_method() -*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 -.. _warnings.simplefilter: https://docs.python.org/3/library/warnings.html#warnings.simplefilter -.. _`pytest-warnings`: https://github.com/fschulze/pytest-warnings - .. _`asserting warnings`: @@ -299,43 +323,6 @@ warnings, or index into it to get a particular recorded warning. Full API: :class:`WarningsRecorder`. -.. _`ensuring a function triggers a deprecation warning`: - -.. _ensuring_function_triggers: - -Ensuring a function triggers a deprecation warning -------------------------------------------------------- - -You can also call a global helper for checking -that a certain function call triggers a ``DeprecationWarning`` or -``PendingDeprecationWarning``:: - - import pytest - - def test_global(): - pytest.deprecated_call(myfunction, 17) - -By default, ``DeprecationWarning`` and ``PendingDeprecationWarning`` will not be -caught when using ``pytest.warns`` or ``recwarn`` because default Python warnings filters hide -them. If you wish to record them in your own code, use the -command ``warnings.simplefilter('always')``:: - - import warnings - import pytest - - def test_deprecation(recwarn): - warnings.simplefilter('always') - warnings.warn("deprecated", DeprecationWarning) - assert len(recwarn) == 1 - assert recwarn.pop(DeprecationWarning) - -You can also use it as a contextmanager:: - - def test_global(): - with pytest.deprecated_call(): - myobject.deprecated_method() - - .. _internal-warnings: From ab8907f6f5b7a0d9df645651dbb34732b775b9cb Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Tue, 16 Oct 2018 17:23:29 -0700 Subject: [PATCH 05/11] s/comparision/comparison/g [ci skip] --- CHANGELOG.rst | 2 +- doc/en/deprecations.rst | 2 +- src/_pytest/logging.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 015f9dbd309..14881e373da 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -45,7 +45,7 @@ Deprecations Users should just ``import pytest`` and access those objects using the ``pytest`` module. * ``request.cached_setup``, this was the precursor of the setup/teardown mechanism available to fixtures. You can - consult `funcarg comparision section in the docs `_. + consult `funcarg comparison section in the docs `_. * Using objects named ``"Class"`` as a way to customize the type of nodes that are collected in ``Collector`` subclasses has been deprecated. Users instead should use ``pytest_collect_make_item`` to customize node types during diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index 30746d03508..2fb451d7e47 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -56,7 +56,7 @@ This should be updated to make use of standard fixture mechanisms: session.close() -You can consult `funcarg comparision section in the docs `_ for +You can consult `funcarg comparison section in the docs `_ for more information. This has been documented as deprecated for years, but only now we are actually emitting deprecation warnings. diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index fe6711ac7f4..ee679525043 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -279,7 +279,7 @@ def messages(self): Unlike 'records', which contains the format string and parameters for interpolation, log messages in this list are all interpolated. Unlike 'text', which contains the output from the handler, log messages in this list are unadorned with - levels, timestamps, etc, making exact comparisions more reliable. + levels, timestamps, etc, making exact comparisons more reliable. Note that traceback or stack info (from :func:`logging.exception` or the `exc_info` or `stack_info` arguments to the logging functions) is not included, as this is added by the formatter in the handler. From ba5a2955443313dd4b49ce539508ef5be93966b2 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 17 Oct 2018 14:53:41 -0300 Subject: [PATCH 06/11] Fix hook name in deprecations.rst As mentioned in https://github.com/pytest-dev/pytest/issues/4128#issuecomment-430690498 --- doc/en/deprecations.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index 30746d03508..3a06c7d5fa6 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -68,7 +68,7 @@ Using ``Class`` in custom Collectors .. deprecated:: 3.9 Using objects named ``"Class"`` as a way to customize the type of nodes that are collected in ``Collector`` -subclasses has been deprecated. Users instead should use ``pytest_collect_make_item`` to customize node types during +subclasses has been deprecated. Users instead should use ``pytest_pycollect_makeitem`` to customize node types during collection. This issue should affect only advanced plugins who create new collection types, so if you see this warning From 4f4c91caf5a95d6aa9f0207f9ec880f1be75dd99 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 17 Oct 2018 20:08:07 +0200 Subject: [PATCH 07/11] fix #4177 - pin setuptools>=40.0 --- changelog/4177.bugfix.rst | 1 + pyproject.toml | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 changelog/4177.bugfix.rst diff --git a/changelog/4177.bugfix.rst b/changelog/4177.bugfix.rst new file mode 100644 index 00000000000..b26ad4bad29 --- /dev/null +++ b/changelog/4177.bugfix.rst @@ -0,0 +1 @@ +Pin ``setuptools>=40.0`` to support ``py_modules`` in ``setup.cfg`` diff --git a/pyproject.toml b/pyproject.toml index c83bd853dec..d17b936c157 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [build-system] requires = [ # sync with setup.py until we discard non-pep-517/518 - "setuptools>=30.3", + "setuptools>=40.0", "setuptools-scm", "wheel", ] diff --git a/setup.py b/setup.py index 6bab5312d49..ac07394558e 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ def main(): setup( use_scm_version={"write_to": "src/_pytest/_version.py"}, - setup_requires=["setuptools-scm", "setuptools>=30.3"], + setup_requires=["setuptools-scm", "setuptools>=40.0"], package_dir={"": "src"}, install_requires=INSTALL_REQUIRES, ) From cc335d44a09e7d3e204b9304f4c0361ebce4c726 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 17 Oct 2018 20:43:27 +0200 Subject: [PATCH 08/11] fix #4179 - bring back the current testrun symlink --- changelog/4179.bugfix.rst | 1 + src/_pytest/pathlib.py | 21 +++++++++++++++++++++ testing/test_tmpdir.py | 4 ++++ 3 files changed, 26 insertions(+) create mode 100644 changelog/4179.bugfix.rst diff --git a/changelog/4179.bugfix.rst b/changelog/4179.bugfix.rst new file mode 100644 index 00000000000..6f7467f50b4 --- /dev/null +++ b/changelog/4179.bugfix.rst @@ -0,0 +1 @@ +Restore the tmpdir behaviour of symlinking the current test run. diff --git a/src/_pytest/pathlib.py b/src/_pytest/pathlib.py index cda5e9947f8..081fce90465 100644 --- a/src/_pytest/pathlib.py +++ b/src/_pytest/pathlib.py @@ -100,6 +100,26 @@ def _max(iterable, default): _max = max +def _force_symlink(root, target, link_to): + """helper to create the current symlink + + its full of race conditions that are reasonably ok to ignore + for the contex of best effort linking to the latest testrun + + the presumption being thatin case of much parallelism + the inaccuracy is going to be acceptable + """ + current_symlink = root.joinpath(target) + try: + current_symlink.unlink() + except OSError: + pass + try: + current_symlink.symlink_to(link_to) + except Exception: + pass + + def make_numbered_dir(root, prefix): """create a directory with a increased number as suffix for the given prefix""" for i in range(10): @@ -112,6 +132,7 @@ def make_numbered_dir(root, prefix): except Exception: pass else: + _force_symlink(root, prefix + "current", new_path) return new_path else: raise EnvironmentError( diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index 9f4158eb76a..4417145282b 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -196,6 +196,10 @@ def test_make(self, tmp_path): assert d.name.startswith(self.PREFIX) assert d.name.endswith(str(i)) + symlink = tmp_path.joinpath(self.PREFIX + "current") + assert symlink.is_symlink() + assert symlink.resolve() == d.resolve() + def test_cleanup_lock_create(self, tmp_path): d = tmp_path.joinpath("test") d.mkdir() From 8dca8f3c9f3ca09bfa11fb1b322a4707c098bab6 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 17 Oct 2018 21:16:44 +0200 Subject: [PATCH 09/11] fix test_cleanup_keep for expecting symlinks --- testing/test_tmpdir.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index 4417145282b..f1b6fe42438 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -248,7 +248,7 @@ def _do_cleanup(self, tmp_path): def test_cleanup_keep(self, tmp_path): self._do_cleanup(tmp_path) - a, b = tmp_path.iterdir() + a, b = (x for x in tmp_path.iterdir() if not x.is_symlink()) print(a, b) def test_cleanup_locked(self, tmp_path): From 56dd7bc551f739a00795ab866578ce3789fa572e Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 17 Oct 2018 21:39:23 +0200 Subject: [PATCH 10/11] TestNumberedDir: ignore that symlinks arent created on windows --- testing/test_tmpdir.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index f1b6fe42438..3c413e7c271 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -197,8 +197,10 @@ def test_make(self, tmp_path): assert d.name.endswith(str(i)) symlink = tmp_path.joinpath(self.PREFIX + "current") - assert symlink.is_symlink() - assert symlink.resolve() == d.resolve() + if symlink.exists(): + # unix + assert symlink.is_symlink() + assert symlink.resolve() == d.resolve() def test_cleanup_lock_create(self, tmp_path): d = tmp_path.joinpath("test") From 12f94b81c0b76dfcdf32290615d6755458b6d626 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 17 Oct 2018 18:18:08 -0300 Subject: [PATCH 11/11] No longer upload code coverage to coveralls We have since moved to codecov and uploading to coveralls is breaking OS-X builds for py37 [1], so we might as well take this opportunity to drop it. [1] https://travis-ci.org/pytest-dev/pytest/jobs/442858038 --- .travis.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5c479feed9b..00abca0b2f0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -93,12 +93,6 @@ after_success: coverage xml --ignore-errors coverage report -m --ignore-errors bash <(curl -s https://codecov.io/bash) -Z -X gcov -X coveragepy -X search -X xcode -X gcovout -X fix -f coverage.xml -F "${TOXENV//-/,},linux" - - # Coveralls does not support merged reports. - if [[ "$TOXENV" = py37 ]]; then - pip install coveralls - coveralls - fi fi notifications: