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 4ccb0305e..98d2ce041 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 ac00c772a..a39c94c13 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -221,12 +221,14 @@ class _NodeReporter(object): 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 29d3f7f6e..079b01f32 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -1222,3 +1222,19 @@ def test_set_suite_name(testdir, suite_name): 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 000000000..89f136458 --- /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 | 205 +++++++++++++++++++++----------------------- 1 file changed, 96 insertions(+), 109 deletions(-) diff --git a/doc/en/warnings.rst b/doc/en/warnings.rst index 060057d91..f83377cf6 100644 --- a/doc/en/warnings.rst +++ b/doc/en/warnings.rst @@ -75,60 +75,6 @@ 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 -------------------------- - -Although not recommended, you can use the ``--disable-warnings`` command-line option to suppress the -warning summary entirely from the test run output. - -Disabling warning capture entirely ----------------------------------- - -This plugin is enabled by default but can be disabled entirely in your ``pytest.ini`` file with: - - .. code-block:: ini - - [pytest] - addopts = -p no:warnings - -Or passing ``-p no:warnings`` in the command-line. This might be useful if your test suites handles warnings -using an external system. - - -.. _`deprecation-warnings`: - -DeprecationWarning and PendingDeprecationWarning ------------------------------------------------- - -.. versionadded:: 3.8 -.. versionchanged:: 3.9 - -By default pytest will display ``DeprecationWarning`` and ``PendingDeprecationWarning``. - -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). -For example: - -.. code-block:: ini - - [pytest] - filterwarnings = - ignore:.*U.*mode is deprecated:DeprecationWarning - - -.. 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 - by calling ``warnings.simplefilter`` (see issue `#2430 `_ - for an example of that). - - .. _`filterwarnings`: ``@pytest.mark.filterwarnings`` @@ -167,24 +113,6 @@ decorator or to all tests in a module by setting the ``pytestmark`` variable: pytestmark = pytest.mark.filterwarnings("error") -.. note:: - - 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. - -.. note:: - - ``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``: - - .. code-block:: ini - - [pytest] - filterwarnings = - once::DeprecationWarning - once::PendingDeprecationWarning - *Credits go to Florian Schulze for the reference implementation in the* `pytest-warnings`_ *plugin.* @@ -193,6 +121,102 @@ decorator or to all tests in a module by setting the ``pytestmark`` variable: .. _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. + +Disabling warning capture entirely +---------------------------------- + +This plugin is enabled by default but can be disabled entirely in your ``pytest.ini`` file with: + + .. code-block:: ini + + [pytest] + addopts = -p no:warnings + +Or passing ``-p no:warnings`` in the command-line. This might be useful if your test suites handles warnings +using an external system. + + +.. _`deprecation-warnings`: + +DeprecationWarning and PendingDeprecationWarning +------------------------------------------------ + +.. versionadded:: 3.8 +.. versionchanged:: 3.9 + +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 warning filters options (ini or marks) to ignore +those warnings. + +For example: + +.. code-block:: ini + + [pytest] + filterwarnings = + 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. + + 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). + + +.. _`ensuring a function triggers a deprecation warning`: + +.. _ensuring_function_triggers: + +Ensuring code 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() + + .. _`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 015f9dbd3..14881e373 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 30746d035..2fb451d7e 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 fe6711ac7..ee6795250 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -279,7 +279,7 @@ class LogCaptureFixture(object): 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 30746d035..3a06c7d5f 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 000000000..b26ad4bad --- /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 c83bd853d..d17b936c1 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 6bab5312d..ac0739455 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ if "_PYTEST_SETUP_SKIP_PLUGGY_DEP" not in os.environ: 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 000000000..6f7467f50 --- /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 cda5e9947..081fce904 100644 --- a/src/_pytest/pathlib.py +++ b/src/_pytest/pathlib.py @@ -100,6 +100,26 @@ else: _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 9f4158eb7..441714528 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -196,6 +196,10 @@ class TestNumberedDir(object): 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 441714528..f1b6fe424 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -248,7 +248,7 @@ class TestNumberedDir(object): 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 f1b6fe424..3c413e7c2 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -197,8 +197,10 @@ class TestNumberedDir(object): 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 5c479feed..00abca0b2 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: