From 2f11a8569806933a0e585a576e6e6af652ae6bfb Mon Sep 17 00:00:00 2001 From: Raquel Alegre Date: Tue, 23 Aug 2016 16:41:11 +0100 Subject: [PATCH 01/29] Import pkg_resources only when necessary. --- _pytest/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_pytest/config.py b/_pytest/config.py index 2a1215811..a401bb068 100644 --- a/_pytest/config.py +++ b/_pytest/config.py @@ -5,7 +5,6 @@ import traceback import types import warnings -import pkg_resources import py # DON't import pytest here because it causes import cycle troubles import sys, os @@ -942,6 +941,7 @@ class Config(object): and find all the installed plugins to mark them for re-writing by the importhook. """ + import pkg_resources ns, unknown_args = self._parser.parse_known_and_unknown_args(args) mode = ns.assertmode if mode == 'rewrite': From 7d498fdc8247ed06c79a0407ddd5fb0f781ce20f Mon Sep 17 00:00:00 2001 From: Raquel Alegre Date: Tue, 23 Aug 2016 16:47:15 +0100 Subject: [PATCH 02/29] Added myself as AUTHOR as requested on PR. --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 2e487e772..895310caa 100644 --- a/AUTHORS +++ b/AUTHORS @@ -103,6 +103,7 @@ Punyashloka Biswal Quentin Pradet Ralf Schmitt Raphael Pierzina +Raquel Alegre Roberto Polli Romain Dorgueil Roman Bolshakov From a1597aca89aa05e35499ce775f1135ab4be5fed4 Mon Sep 17 00:00:00 2001 From: Raquel Alegre Date: Tue, 23 Aug 2016 16:48:24 +0100 Subject: [PATCH 03/29] Added #1853 to changelog as requested on PR. --- CHANGELOG.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d571a2bf3..f6f233314 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -9,7 +9,7 @@ * Fix loader error when running ``pytest`` embedded in a zipfile. Thanks `@mbachry`_ for the PR. -* +* Fix pkg_resources import error in Jython projects (`#1853`). * From d3f4b3d14ae385526e26b7c74e469584d769f486 Mon Sep 17 00:00:00 2001 From: Raquel Alegre Date: Tue, 23 Aug 2016 16:50:00 +0100 Subject: [PATCH 04/29] mend --- CHANGELOG.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f6f233314..6c9aaaffa 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -8,8 +8,9 @@ * Fix loader error when running ``pytest`` embedded in a zipfile. Thanks `@mbachry`_ for the PR. - + * Fix pkg_resources import error in Jython projects (`#1853`). + Thanks `@raquel-ucl`_ for the PR. * From 8f516d27fa2c32b9f90e410c0dbacd26117fd873 Mon Sep 17 00:00:00 2001 From: Raquel Alegre Date: Wed, 24 Aug 2016 10:25:01 +0100 Subject: [PATCH 05/29] Moved import pkg_resources to else clause. --- _pytest/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_pytest/config.py b/_pytest/config.py index a401bb068..8d115ea65 100644 --- a/_pytest/config.py +++ b/_pytest/config.py @@ -941,7 +941,6 @@ class Config(object): and find all the installed plugins to mark them for re-writing by the importhook. """ - import pkg_resources ns, unknown_args = self._parser.parse_known_and_unknown_args(args) mode = ns.assertmode if mode == 'rewrite': @@ -950,6 +949,7 @@ class Config(object): except SystemError: mode = 'plain' else: + import pkg_resources self.pluginmanager.rewrite_hook = hook for entrypoint in pkg_resources.iter_entry_points('pytest11'): for entry in entrypoint.dist._get_metadata('RECORD'): From 7a4fe7582c806498c981e336028eb6d3ee42ddb4 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 2 Sep 2016 12:27:06 -0300 Subject: [PATCH 06/29] Send only minor/major release announces to TIP mailing list As discussed recently on the testing-in-python mailing list, people have asked to restrict the amount of announcements there only to major/minor pytest releases --- HOWTORELEASE.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HOWTORELEASE.rst b/HOWTORELEASE.rst index 7da047503..372ecf7f1 100644 --- a/HOWTORELEASE.rst +++ b/HOWTORELEASE.rst @@ -54,8 +54,8 @@ Note: this assumes you have already registered on pypi. 11. Send release announcement to mailing lists: - pytest-dev@python.org - - testing-in-python@lists.idyll.org - python-announce-list@python.org + - testing-in-python@lists.idyll.org (only for minor/major releases) And announce the release on Twitter, making sure to add the hashtag ``#pytest``. From 1e10de574dc9c08c9a2af8b74380feb28da8c28b Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 2 Sep 2016 18:25:26 -0300 Subject: [PATCH 07/29] The "ids" argument to "parametrize" again accepts unicode strings in Python 2 Fixes #1905 --- CHANGELOG.rst | 9 ++++++++- _pytest/python.py | 2 +- testing/python/metafunc.py | 8 ++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b7ca19734..b3b1dda8d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,7 +1,9 @@ 3.0.3.dev0 ========== -* +* The ``ids`` argument to ``parametrize`` again accepts ``unicode`` strings + in Python 2 (`#1905`_). + Thanks `@philpep`_ for the report and `@nicoddemus`_ for the PR. * @@ -12,6 +14,11 @@ * +.. _@philpep: https://github.com/philpep + +.. _#1905: https://github.com/pytest-dev/pytest/issues/1905 + + 3.0.2 ===== diff --git a/_pytest/python.py b/_pytest/python.py index 2ab1de6b0..33e7cff66 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -832,7 +832,7 @@ class Metafunc(fixtures.FuncargnamesCompatAttr): raise ValueError('%d tests specified with %d ids' %( len(argvalues), len(ids))) for id_value in ids: - if id_value is not None and not isinstance(id_value, str): + if id_value is not None and not isinstance(id_value, py.builtin._basestring): msg = 'ids must be list of strings, found: %s (type: %s)' raise ValueError(msg % (saferepr(id_value), type(id_value).__name__)) ids = idmaker(argnames, argvalues, idfn, ids, self.config) diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 58f566973..b9bf589c3 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -105,6 +105,14 @@ class TestMetafunc: ids = [x.id for x in metafunc._calls] assert ids == ["basic-abc", "basic-def", "advanced-abc", "advanced-def"] + def test_parametrize_and_id_unicode(self): + """Allow unicode strings for "ids" parameter in Python 2 (##1905)""" + def func(x): pass + metafunc = self.Metafunc(func) + metafunc.parametrize("x", [1, 2], ids=[u'basic', u'advanced']) + ids = [x.id for x in metafunc._calls] + assert ids == [u"basic", u"advanced"] + def test_parametrize_with_wrong_number_of_ids(self, testdir): def func(x, y): pass metafunc = self.Metafunc(func) From 183f3737d489cd0e19bd6a05a48b5af214aeadff Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 3 Sep 2016 15:47:54 -0300 Subject: [PATCH 08/29] Fix reference docs for monkeypatch Fix #1909 --- doc/en/monkeypatch.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/en/monkeypatch.rst b/doc/en/monkeypatch.rst index a1287e8dd..e85d94d6d 100644 --- a/doc/en/monkeypatch.rst +++ b/doc/en/monkeypatch.rst @@ -59,6 +59,7 @@ Method reference of the monkeypatch fixture ------------------------------------------- .. autoclass:: MonkeyPatch + :members: ``monkeypatch.setattr/delattr/delitem/delenv()`` all by default raise an Exception if the target does not exist. From c60854474abacd62b537b2cf759e305367afb75e Mon Sep 17 00:00:00 2001 From: Huayi Zhang Date: Mon, 5 Sep 2016 16:48:04 +0800 Subject: [PATCH 09/29] Fix keyword docs --- _pytest/mark.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_pytest/mark.py b/_pytest/mark.py index 40c998c3e..640c4e61c 100644 --- a/_pytest/mark.py +++ b/_pytest/mark.py @@ -19,7 +19,7 @@ def pytest_addoption(parser): help="only run tests which match the given substring expression. " "An expression is a python evaluatable expression " "where all names are substring-matched against test names " - "and their parent classes. Example: -k 'test_method or test " + "and their parent classes. Example: -k 'test_method or test_" "other' matches all test functions and classes whose name " "contains 'test_method' or 'test_other'. " "Additionally keywords are matched to classes and functions " From ceeb5149f307874d1f1c69571603178c6e334eea Mon Sep 17 00:00:00 2001 From: Floris Bruynooghe Date: Mon, 5 Sep 2016 13:09:36 +0100 Subject: [PATCH 10/29] Mention register_assert_rewrite explicitly It helps mentioning this explicitly in the changelog. Fixes #1871. --- CHANGELOG.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b7ca19734..b269723f5 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -89,7 +89,11 @@ time or change existing behaviors in order to make them less surprising/more use * Reinterpretation mode has now been removed. Only plain and rewrite mode are available, consequently the ``--assert=reinterp`` option is - no longer available. Thanks `@flub`_ for the PR. + no longer available. This also means files imported from plugins or + ``conftest.py`` will not benefit from improved assertions by + default, you should use ``pytest.register_assert_rewrite()`` to + explicitly turn on assertion rewriting for those files. Thanks + `@flub`_ for the PR. * The following deprecated commandline options were removed: From e21ae3991de032d8c0a15ba5725d7f219ca9fcad Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Mon, 5 Sep 2016 16:42:33 +0200 Subject: [PATCH 11/29] docs: remove the need for special scripts * import version from pytest since it is importable * remove makefile bits from legacy hosting * remove the script to determine the version for legacy hosting --- doc/en/Makefile | 19 +------------------ doc/en/conf.py | 7 ++----- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/doc/en/Makefile b/doc/en/Makefile index 8621f779c..5499c405e 100644 --- a/doc/en/Makefile +++ b/doc/en/Makefile @@ -19,10 +19,9 @@ REGENDOC_ARGS := \ --normalize "@/tmp/pytest-of-.*/pytest-\d+@PYTEST_TMPDIR@" \ - .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest - + help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @@ -36,22 +35,6 @@ help: clean: -rm -rf $(BUILDDIR)/* -SITETARGET=$(shell ./_getdoctarget.py) - -showtarget: - @echo $(SITETARGET) - -install: html - # for access talk to someone with login rights to - # pytest-dev@pytest.org to add your ssh key - rsync -avz _build/html/ pytest-dev@pytest.org:pytest.org/$(SITETARGET) - -installpdf: latexpdf - @scp $(BUILDDIR)/latex/pytest.pdf pytest-dev@pytest.org:pytest.org/$(SITETARGET) - -installall: clean install installpdf - @echo "done" - regen: PYTHONDONTWRITEBYTECODE=1 COLUMNS=76 regendoc --update *.rst */*.rst ${REGENDOC_ARGS} diff --git a/doc/en/conf.py b/doc/en/conf.py index 1c15c17fc..f3b8d7d1e 100644 --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -19,11 +19,8 @@ # The short X.Y version. import os, sys -sys.path.insert(0, os.path.dirname(__file__)) -import _getdoctarget - -version = _getdoctarget.get_minor_version_string() -release = _getdoctarget.get_version_string() +from _pytest import __version__ as version +release = ".".join(version.split(".")[:2]) # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the From a2b8981b50e953eedd610d5e6cfb6aa83321894d Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Mon, 5 Sep 2016 21:03:22 +0200 Subject: [PATCH 12/29] docs: remove unused helper script --- doc/en/_getdoctarget.py | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100755 doc/en/_getdoctarget.py diff --git a/doc/en/_getdoctarget.py b/doc/en/_getdoctarget.py deleted file mode 100755 index 20e487bb7..000000000 --- a/doc/en/_getdoctarget.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env python - -import py - -def get_version_string(): - fn = py.path.local(__file__).join("..", "..", "..", - "_pytest", "__init__.py") - for line in fn.readlines(): - if "version" in line and not line.strip().startswith('#'): - return eval(line.split("=")[-1]) - -def get_minor_version_string(): - return ".".join(get_version_string().split(".")[:2]) - -if __name__ == "__main__": - print (get_minor_version_string()) From 8e67dd13e756c69a285356c818345f5490e2a8b5 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 6 Sep 2016 21:17:54 +0200 Subject: [PATCH 13/29] Fix link to PDF docs Fixes #1917 --- doc/en/_templates/links.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/_templates/links.html b/doc/en/_templates/links.html index 56486a750..d855a013f 100644 --- a/doc/en/_templates/links.html +++ b/doc/en/_templates/links.html @@ -6,6 +6,6 @@
  • pytest @ GitHub
  • 3rd party plugins
  • Issue Tracker
  • -
  • PDF Documentation +
  • PDF Documentation From 138e255631bd0a9e7f49e841bf9c7075bed1c3c5 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 14 Sep 2016 17:11:58 -0300 Subject: [PATCH 14/29] Remove duplicated for Metafunc members on docs Fix #1924 --- doc/en/parametrize.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/doc/en/parametrize.rst b/doc/en/parametrize.rst index 9539cb618..825fda8e8 100644 --- a/doc/en/parametrize.rst +++ b/doc/en/parametrize.rst @@ -216,6 +216,3 @@ The **metafunc** object .. currentmodule:: _pytest.python .. autoclass:: Metafunc :members: - - .. automethod:: Metafunc.parametrize - .. automethod:: Metafunc.addcall(funcargs=None,id=_notexists,param=_notexists) From 04cf5e1df488f10a778a5d8622ec1365c0a14d8b Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 14 Sep 2016 21:59:18 -0300 Subject: [PATCH 15/29] Fixed assertion rewriting for plugins in development mode Fix #1934 --- CHANGELOG.rst | 5 ++++- _pytest/config.py | 25 +++++++++++++++---------- testing/test_assertion.py | 18 ++++++++++++++---- 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 7d6f402a6..cc4067fc1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,7 +7,9 @@ * -* +* Assertions are now being rewritten for plugins in development mode + (``pip install -e``) (`#1934`_). + Thanks `@nicoddemus`_ for the PR. * @@ -17,6 +19,7 @@ .. _@philpep: https://github.com/philpep .. _#1905: https://github.com/pytest-dev/pytest/issues/1905 +.. _#1934: https://github.com/pytest-dev/pytest/issues/1934 3.0.2 diff --git a/_pytest/config.py b/_pytest/config.py index 5b4654a24..55ad538d3 100644 --- a/_pytest/config.py +++ b/_pytest/config.py @@ -954,16 +954,21 @@ class Config(object): else: self.pluginmanager.rewrite_hook = hook for entrypoint in pkg_resources.iter_entry_points('pytest11'): - for entry in entrypoint.dist._get_metadata('RECORD'): - fn = entry.split(',')[0] - is_simple_module = os.sep not in fn and fn.endswith('.py') - is_package = fn.count(os.sep) == 1 and fn.endswith('__init__.py') - if is_simple_module: - module_name, ext = os.path.splitext(fn) - hook.mark_rewrite(module_name) - elif is_package: - package_name = os.path.dirname(fn) - hook.mark_rewrite(package_name) + # 'RECORD' available for plugins installed normally (pip install) + # 'SOURCES.txt' available for plugins installed in dev mode (pip install -e) + # for installed plugins 'SOURCES.txt' returns an empty list, and vice-versa + # so it shouldn't be an issue + for metadata in ('RECORD', 'SOURCES.txt'): + for entry in entrypoint.dist._get_metadata(metadata): + fn = entry.split(',')[0] + is_simple_module = os.sep not in fn and fn.endswith('.py') + is_package = fn.count(os.sep) == 1 and fn.endswith('__init__.py') + if is_simple_module: + module_name, ext = os.path.splitext(fn) + hook.mark_rewrite(module_name) + elif is_package: + package_name = os.path.dirname(fn) + hook.mark_rewrite(package_name) self._warn_about_missing_assertion(mode) def _warn_about_missing_assertion(self, mode): diff --git a/testing/test_assertion.py b/testing/test_assertion.py index d49815181..af7e7e0fe 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -108,7 +108,8 @@ class TestImportHookInstallation: assert result.ret == 0 @pytest.mark.parametrize('mode', ['plain', 'rewrite']) - def test_installed_plugin_rewrite(self, testdir, mode): + @pytest.mark.parametrize('plugin_state', ['development', 'installed']) + def test_installed_plugin_rewrite(self, testdir, mode, plugin_state): # Make sure the hook is installed early enough so that plugins # installed via setuptools are re-written. testdir.tmpdir.join('hampkg').ensure(dir=1) @@ -135,13 +136,22 @@ class TestImportHookInstallation: 'mainwrapper.py': """ import pytest, pkg_resources + plugin_state = "{plugin_state}" + class DummyDistInfo: project_name = 'spam' version = '1.0' def _get_metadata(self, name): - return ['spamplugin.py,sha256=abc,123', - 'hampkg/__init__.py,sha256=abc,123'] + # 'RECORD' meta-data only available in installed plugins + if name == 'RECORD' and plugin_state == "installed": + return ['spamplugin.py,sha256=abc,123', + 'hampkg/__init__.py,sha256=abc,123'] + # 'SOURCES.txt' meta-data only available for plugins in development mode + elif name == 'SOURCES.txt' and plugin_state == "development": + return ['spamplugin.py', + 'hampkg/__init__.py'] + return [] class DummyEntryPoint: name = 'spam' @@ -159,7 +169,7 @@ class TestImportHookInstallation: pkg_resources.iter_entry_points = iter_entry_points pytest.main() - """, + """.format(plugin_state=plugin_state), 'test_foo.py': """ def test(check_first): check_first([10, 30], 30) From 77d842ceb2d99883f481881a50ec6990e0116414 Mon Sep 17 00:00:00 2001 From: mbyt Date: Sat, 17 Sep 2016 19:58:27 +0200 Subject: [PATCH 16/29] better doc for #1890 based on #1932 --- doc/en/unittest.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/en/unittest.rst b/doc/en/unittest.rst index 3d1ebbbf3..853d93fde 100644 --- a/doc/en/unittest.rst +++ b/doc/en/unittest.rst @@ -38,7 +38,11 @@ the general ``pytest`` documentation for many more examples. Running tests from ``unittest.TestCase`` subclasses with ``--pdb`` will disable tearDown and cleanup methods for the case that an Exception occurs. This allows proper post mortem debugging for all applications - which have significant logic in their tearDown machinery. + which have significant logic in their tearDown machinery. However, + supporting this feature has the following side effect: If people + overwrite ``unittest.TestCase`` ``__call__`` or ``run``, they need to + to overwrite ``debug`` in the same way (this is also true for standard + unittest). Mixing pytest fixtures into unittest.TestCase style tests ----------------------------------------------------------- From 8639bf75549e50a74cb49d4f48d790d2c8788a87 Mon Sep 17 00:00:00 2001 From: Floris Bruynooghe Date: Mon, 19 Sep 2016 10:20:36 +0100 Subject: [PATCH 17/29] fixup! Merge pkg_resources workaround --- CHANGELOG.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 8933ec4d0..43bec4fb4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -16,6 +16,7 @@ .. _@philpep: https://github.com/philpep +.. _@raquel-ucl: https://github.com/raquel-ucl .. _#1905: https://github.com/pytest-dev/pytest/issues/1905 .. _#1934: https://github.com/pytest-dev/pytest/issues/1934 From 65be1231b12367de3ff14b5718125d12a54b26b1 Mon Sep 17 00:00:00 2001 From: Lev Maximov Date: Sun, 18 Sep 2016 22:34:48 +0700 Subject: [PATCH 18/29] AttributeError chaining bug #1944 fix --- _pytest/python.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/_pytest/python.py b/_pytest/python.py index 33e7cff66..f26c128ca 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -205,11 +205,10 @@ class PyobjContext(object): class PyobjMixin(PyobjContext): def obj(): def fget(self): - try: - return self._obj - except AttributeError: + obj = getattr(self, '_obj', None) + if obj is None: self._obj = obj = self._getobj() - return obj + return obj def fset(self, value): self._obj = value return property(fget, fset, None, "underlying python object") From 6b56c36ae7712cc68b3e7bf2ffd86ba90cde6e0f Mon Sep 17 00:00:00 2001 From: Lev Maximov Date: Sun, 18 Sep 2016 23:14:29 +0700 Subject: [PATCH 19/29] added to changelog and authors --- AUTHORS | 1 + CHANGELOG.rst | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/AUTHORS b/AUTHORS index 636fe7f7b..cce84a3c6 100644 --- a/AUTHORS +++ b/AUTHORS @@ -78,6 +78,7 @@ Kale Kundert Katarzyna Jachim Kevin Cox Lee Kamentsky +Lev Maximov Lukas Bednar Maciek Fijalkowski Maho diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 43bec4fb4..61fb9b5b3 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -12,14 +12,20 @@ * Fix pkg_resources import error in Jython projects (`#1853`). Thanks `@raquel-ucl`_ for the PR. +* Got rid of ``AttributeError: 'Module' object has no attribute '_obj'`` exception + in Python 3 (`#1944`_). + Thanks `@axil`_ for the PR. + * .. _@philpep: https://github.com/philpep .. _@raquel-ucl: https://github.com/raquel-ucl +.. _@axil: https://github.com/axil .. _#1905: https://github.com/pytest-dev/pytest/issues/1905 .. _#1934: https://github.com/pytest-dev/pytest/issues/1934 +.. _#1944: https://github.com/pytest-dev/pytest/issues/1944 3.0.2 From 8cba033bbb16b73726249da8723a9ffec9024ecc Mon Sep 17 00:00:00 2001 From: Lev Maximov Date: Tue, 20 Sep 2016 02:16:04 +0700 Subject: [PATCH 20/29] added a test --- testing/test_assertion.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/testing/test_assertion.py b/testing/test_assertion.py index af7e7e0fe..2d4761431 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -864,3 +864,15 @@ def test_assert_with_unicode(monkeypatch, testdir): """) result = testdir.runpytest() result.stdout.fnmatch_lines(['*AssertionError*']) + +def test_issue_1944(testdir): + testdir.makepyfile(""" + def f(): + return + + assert f() == 10 + """) + result = testdir.runpytest() + result.stdout.fnmatch_lines(["*1 error*"]) + assert "AttributeError: 'Module' object has no attribute '_obj'" not in result.stdout.str() + From 6db2f315face61d3bed7a2a95a9ed58183c0607f Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Tue, 6 Sep 2016 14:16:25 -0400 Subject: [PATCH 21/29] Explain a bad scope value to the user --- AUTHORS | 1 + CHANGELOG.rst | 5 ++++- _pytest/fixtures.py | 24 +++++++++++++++++++++++- _pytest/python.py | 5 +++-- testing/python/fixture.py | 16 ++++++++++++++++ testing/python/metafunc.py | 8 ++++++++ 6 files changed, 55 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index cce84a3c6..d8c431dc0 100644 --- a/AUTHORS +++ b/AUTHORS @@ -127,6 +127,7 @@ Ted Xiao Thomas Grainger Tom Viner Trevor Bekolay +Tyler Goodlet Vasily Kuznetsov Wouter van Ackooy Xuecong Liao diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 61fb9b5b3..a67ec4eee 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -16,18 +16,21 @@ in Python 3 (`#1944`_). Thanks `@axil`_ for the PR. -* +* Explain a bad scope value passed to ``@fixture`` declarations or + a ``MetaFunc.parametrize()`` call. Thanks `@tgoodlet`_ for the PR. .. _@philpep: https://github.com/philpep .. _@raquel-ucl: https://github.com/raquel-ucl .. _@axil: https://github.com/axil +.. _@tgoodlet: https://github.com/tgoodlet .. _#1905: https://github.com/pytest-dev/pytest/issues/1905 .. _#1934: https://github.com/pytest-dev/pytest/issues/1934 .. _#1944: https://github.com/pytest-dev/pytest/issues/1944 + 3.0.2 ===== diff --git a/_pytest/fixtures.py b/_pytest/fixtures.py index cf3e9dd93..3f08b7c6d 100644 --- a/_pytest/fixtures.py +++ b/_pytest/fixtures.py @@ -599,12 +599,29 @@ class ScopeMismatchError(Exception): which has a lower scope (e.g. a Session one calls a function one) """ + scopes = "session module class function".split() scopenum_function = scopes.index("function") + + def scopemismatch(currentscope, newscope): return scopes.index(newscope) > scopes.index(currentscope) +def scope2index(scope, descr, where=None): + """Look up the index of ``scope`` and raise a descriptive value error + if not defined. + """ + try: + return scopes.index(scope) + except ValueError: + raise ValueError( + "{0} {1}has an unsupported scope value '{2}'".format( + descr, 'from {0} '.format(where) if where else '', + scope) + ) + + class FixtureLookupError(LookupError): """ could not return a requested Fixture (missing or invalid). """ def __init__(self, argname, request, msg=None): @@ -703,6 +720,7 @@ def call_fixture_func(fixturefunc, request, kwargs): res = fixturefunc(**kwargs) return res + class FixtureDef: """ A container for a factory definition. """ def __init__(self, fixturemanager, baseid, argname, func, scope, params, @@ -713,7 +731,11 @@ class FixtureDef: self.func = func self.argname = argname self.scope = scope - self.scopenum = scopes.index(scope or "function") + self.scopenum = scope2index( + scope or "function", + descr='fixture {0}'.format(func.__name__), + where=baseid + ) self.params = params startindex = unittest and 1 or None self.argnames = getfuncargnames(func, startindex=startindex) diff --git a/_pytest/python.py b/_pytest/python.py index f26c128ca..a361af874 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -770,7 +770,7 @@ class Metafunc(fixtures.FuncargnamesCompatAttr): It will also override any fixture-function defined scope, allowing to set a dynamic scope using test context or configuration. """ - from _pytest.fixtures import scopes + from _pytest.fixtures import scope2index from _pytest.mark import extract_argvalue from py.io import saferepr @@ -799,7 +799,8 @@ class Metafunc(fixtures.FuncargnamesCompatAttr): if scope is None: scope = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect) - scopenum = scopes.index(scope) + scopenum = scope2index( + scope, descr='call to {0}'.format(self.parametrize)) valtypes = {} for arg in argnames: if arg not in self.fixturenames: diff --git a/testing/python/fixture.py b/testing/python/fixture.py index 5ff459cbc..d6b7840c6 100644 --- a/testing/python/fixture.py +++ b/testing/python/fixture.py @@ -1063,6 +1063,22 @@ class TestFixtureUsages: "*1 error*" ]) + def test_invalid_scope(self, testdir): + testdir.makepyfile(""" + import pytest + @pytest.fixture(scope="functions") + def badscope(): + pass + + def test_nothing(badscope): + pass + """) + result = testdir.runpytest_inprocess() + result.stdout.fnmatch_lines( + ("*ValueError: fixture badscope from test_invalid_scope.py has an unsupported" + " scope value 'functions'") + ) + def test_funcarg_parametrized_and_used_twice(self, testdir): testdir.makepyfile(""" import pytest diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index b9bf589c3..9d71df20a 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -96,6 +96,14 @@ class TestMetafunc: pytest.raises(ValueError, lambda: metafunc.parametrize("y", [5,6])) pytest.raises(ValueError, lambda: metafunc.parametrize("y", [5,6])) + def test_parametrize_bad_scope(self, testdir): + def func(x): pass + metafunc = self.Metafunc(func) + try: + metafunc.parametrize("x", [1], scope='doggy') + except ValueError as ve: + assert "has an unsupported scope value 'doggy'" in str(ve) + def test_parametrize_and_id(self): def func(x, y): pass metafunc = self.Metafunc(func) From 940ed7e943c63e2eb9df1d537775692021ff0a53 Mon Sep 17 00:00:00 2001 From: Roy Williams Date: Wed, 21 Sep 2016 17:44:25 -0700 Subject: [PATCH 22/29] Fix `DeprecationWarnings` found when running py.test in Python 2.7 with the -3 flag. Running through some of my tests with the `-3` flag in python2.7 I encountered some errors within py.test itself. This fixes those errors so we can use py.test in order to identify problems with Python 3. --- _pytest/_code/code.py | 4 ++++ _pytest/_code/source.py | 3 +++ _pytest/assertion/util.py | 2 +- _pytest/python.py | 6 ++++++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/_pytest/_code/code.py b/_pytest/_code/code.py index 9a6d32665..21e1460fd 100644 --- a/_pytest/_code/code.py +++ b/_pytest/_code/code.py @@ -12,6 +12,7 @@ if sys.version_info[0] >= 3: else: from ._py2traceback import format_exception_only + class Code(object): """ wrapper around Python code objects """ def __init__(self, rawcode): @@ -28,6 +29,9 @@ class Code(object): def __eq__(self, other): return self.raw == other.raw + def __hash__(self): + return hash(self.raw) + def __ne__(self, other): return not self == other diff --git a/_pytest/_code/source.py b/_pytest/_code/source.py index a1521f8a2..ae9f0463f 100644 --- a/_pytest/_code/source.py +++ b/_pytest/_code/source.py @@ -52,6 +52,9 @@ class Source(object): return str(self) == other return False + def __hash__(self): + return hash(self.lines) + def __getitem__(self, key): if isinstance(key, int): return self.lines[key] diff --git a/_pytest/assertion/util.py b/_pytest/assertion/util.py index 2481cf34c..4a0a4e431 100644 --- a/_pytest/assertion/util.py +++ b/_pytest/assertion/util.py @@ -105,7 +105,7 @@ except NameError: def assertrepr_compare(config, op, left, right): """Return specialised explanations for some operators/operands""" width = 80 - 15 - len(op) - 2 # 15 chars indentation, 1 space around op - left_repr = py.io.saferepr(left, maxsize=int(width/2)) + left_repr = py.io.saferepr(left, maxsize=int(width//2)) right_repr = py.io.saferepr(right, maxsize=width-len(left_repr)) summary = u('%s %s %s') % (ecu(left_repr), op, ecu(right_repr)) diff --git a/_pytest/python.py b/_pytest/python.py index f26c128ca..ebfb8e1d1 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -1356,6 +1356,9 @@ class approx(object): return False return all(a == x for a, x in zip(actual, self.expected)) + def __hash__(self): + return hash(self.expected) + def __ne__(self, actual): return not (actual == self) @@ -1435,6 +1438,9 @@ class ApproxNonIterable(object): # Return true if the two numbers are within the tolerance. return abs(self.expected - actual) <= self.tolerance + def __hash__(self): + return hash((self.expected, self.tolerance)) + def __ne__(self, actual): return not (actual == self) From 24db3c123d784d476bc2b5d77e04cb7bb0956174 Mon Sep 17 00:00:00 2001 From: Roy Williams Date: Thu, 22 Sep 2016 09:22:12 -0700 Subject: [PATCH 23/29] Explicitly set to None to have consistent behavior in Python 2 and Python 3 --- _pytest/_code/code.py | 3 +-- _pytest/_code/source.py | 3 +-- _pytest/python.py | 6 ++---- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/_pytest/_code/code.py b/_pytest/_code/code.py index 21e1460fd..30e12940b 100644 --- a/_pytest/_code/code.py +++ b/_pytest/_code/code.py @@ -29,8 +29,7 @@ class Code(object): def __eq__(self, other): return self.raw == other.raw - def __hash__(self): - return hash(self.raw) + __hash__ = None def __ne__(self, other): return not self == other diff --git a/_pytest/_code/source.py b/_pytest/_code/source.py index ae9f0463f..2d55cd07f 100644 --- a/_pytest/_code/source.py +++ b/_pytest/_code/source.py @@ -52,8 +52,7 @@ class Source(object): return str(self) == other return False - def __hash__(self): - return hash(self.lines) + __hash__ = None def __getitem__(self, key): if isinstance(key, int): diff --git a/_pytest/python.py b/_pytest/python.py index ebfb8e1d1..ea194f85f 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -1356,8 +1356,7 @@ class approx(object): return False return all(a == x for a, x in zip(actual, self.expected)) - def __hash__(self): - return hash(self.expected) + __hash__ = None def __ne__(self, actual): return not (actual == self) @@ -1438,8 +1437,7 @@ class ApproxNonIterable(object): # Return true if the two numbers are within the tolerance. return abs(self.expected - actual) <= self.tolerance - def __hash__(self): - return hash((self.expected, self.tolerance)) + __hash__ = None def __ne__(self, actual): return not (actual == self) From f1c4cfea2ccbc392521c12d8927519de12ca76f2 Mon Sep 17 00:00:00 2001 From: Roy Williams Date: Fri, 23 Sep 2016 09:43:56 -0700 Subject: [PATCH 24/29] Remove implementation of `__getslice__` `__getslice__` has been Deprecated since Python 2.0 and is removed in Python 3. See https://docs.python.org/2/reference/datamodel.html#object.__getslice__ Unfortunately, Python 2 will still dispatch to `__getslice__` over `__getitem__`, See http://bugs.python.org/issue2041, which causes Warnings when running with `-3` in 2.7. --- _pytest/_code/source.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/_pytest/_code/source.py b/_pytest/_code/source.py index 2d55cd07f..846e3cced 100644 --- a/_pytest/_code/source.py +++ b/_pytest/_code/source.py @@ -60,16 +60,13 @@ class Source(object): else: if key.step not in (None, 1): raise IndexError("cannot slice a Source with a step") - return self.__getslice__(key.start, key.stop) + newsource = Source() + newsource.lines = self.lines[key.start:key.stop] + return newsource def __len__(self): return len(self.lines) - def __getslice__(self, start, end): - newsource = Source() - newsource.lines = self.lines[start:end] - return newsource - def strip(self): """ return new source object with trailing and leading blank lines removed. From 73cab772490e97aa7cdf7dd61c942c51b9a07616 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 25 Sep 2016 18:39:31 -0300 Subject: [PATCH 25/29] Pin hypothesis to 3.5.0 because 3.5.1 breaks the test suite Related to HypothesisWorks/hypothesis-python#368 --- tox.ini | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tox.ini b/tox.ini index 3c12a8da3..e165b7a60 100644 --- a/tox.ini +++ b/tox.ini @@ -11,7 +11,8 @@ envlist= commands= pytest --lsof -rfsxX {posargs:testing} passenv = USER USERNAME deps= - hypothesis + # pin to 3.5.0 until 3.5.2 is released + hypothesis==3.5.0 nose mock requests @@ -47,7 +48,8 @@ commands = flake8 pytest.py _pytest testing deps=pytest-xdist>=1.13 mock nose - hypothesis + # pin to 3.5.0 until 3.5.2 is released + hypothesis==3.5.0 commands= pytest -n1 -rfsxX {posargs:testing} @@ -71,8 +73,10 @@ commands= pytest -rfsxX test_pdb.py test_terminal.py test_unittest.py [testenv:py27-nobyte] -deps=pytest-xdist>=1.13 - hypothesis +deps= + pytest-xdist>=1.13 + # pin to 3.5.0 until 3.5.2 is released + hypothesis==3.5.0 distribute=true setenv= PYTHONDONTWRITEBYTECODE=1 From da201c7d290dd8258f87c6923c5193970021c70f Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 25 Sep 2016 23:21:37 -0300 Subject: [PATCH 26/29] Disable pypy on AppVeyor until #1963 gets fixed --- appveyor.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 38f3d6fad..3144f2095 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -8,7 +8,8 @@ environment: matrix: # create multiple jobs to execute a set of tox runs on each; this is to workaround having # builds timing out in AppVeyor - - TOXENV: "linting,py26,py27,py33,py34,py35,pypy" + # pypy is disabled until #1963 gets fixed + - TOXENV: "linting,py26,py27,py33,py34,py35" - TOXENV: "py27-pexpect,py27-xdist,py27-trial,py35-pexpect,py35-xdist,py35-trial" - TOXENV: "py27-nobyte,doctesting,freeze,docs" From 94155ee62ad53995c39a5d98aa799305ebb333ea Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 24 Sep 2016 07:55:34 -0300 Subject: [PATCH 27/29] Add a note about pytest.skip not being allowed at module level --- doc/en/skipping.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/doc/en/skipping.rst b/doc/en/skipping.rst index dde89705e..662fed5df 100644 --- a/doc/en/skipping.rst +++ b/doc/en/skipping.rst @@ -293,6 +293,18 @@ imperatively, in test or setup code:: # or pytest.skip("unsupported configuration") +Note that calling ``pytest.skip`` at the module level +is not allowed since pytest 3.0. To skip +all tests in a module given some runtime condition, you can +set a ``pytestmark`` variable: + +.. code-block:: python + + if SOME_CONDITION: + pytestmark = pytest.mark.skip('skipping all tests because SOME_CONDITION') + +``pytestmark`` applies a mark or list of marks to all tests in a module. + Skipping on a missing import dependency -------------------------------------------------- From 19766ef0bcfb9dbdb8a1b274c13b4b146a86f705 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 25 Sep 2016 19:02:46 -0300 Subject: [PATCH 28/29] Add a summary on how to skip all tests in a module in different situations --- doc/en/skipping.rst | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/doc/en/skipping.rst b/doc/en/skipping.rst index 662fed5df..42c43c3dc 100644 --- a/doc/en/skipping.rst +++ b/doc/en/skipping.rst @@ -294,14 +294,16 @@ imperatively, in test or setup code:: pytest.skip("unsupported configuration") Note that calling ``pytest.skip`` at the module level -is not allowed since pytest 3.0. To skip -all tests in a module given some runtime condition, you can -set a ``pytestmark`` variable: +is not allowed since pytest 3.0. If you are upgrading +and ``pytest.skip`` was being used at the module level, you can set a +``pytestmark`` variable: .. code-block:: python - if SOME_CONDITION: - pytestmark = pytest.mark.skip('skipping all tests because SOME_CONDITION') + # before pytest 3.0 + pytest.skip('skipping all tests because of reasons') + # after pytest 3.0 + pytestmark = pytest.mark.skip('skipping all tests because of reasons') ``pytestmark`` applies a mark or list of marks to all tests in a module. @@ -383,3 +385,27 @@ The equivalent with "boolean conditions" is:: imported before pytest's argument parsing takes place. For example, ``conftest.py`` files are imported before command line parsing and thus ``config.getvalue()`` will not execute correctly. + + +Summary +------- + +Here's a quick guide on how to skip tests in a module in different situations: + +1. Skip all tests in a module unconditionally: + + .. code-block:: python + + pytestmark = pytest.mark.skip('all tests still WIP') + +2. Skip all tests in a module based on some condition: + + .. code-block:: python + + pytestmark = pytest.mark.skipif(sys.platform == 'win32', 'tests for linux only') + +3. Skip all tests in a module if some import is missing: + + .. code-block:: python + + pexpect = pytest.importorskip('pexpect') From f31447b82bf3fc85b0f40e63e0d4e3b961d9c822 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 26 Sep 2016 09:07:10 -0300 Subject: [PATCH 29/29] Use hypothesis >= 3.5.2 Related to #1962 --- tox.ini | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tox.ini b/tox.ini index e165b7a60..ca55e7813 100644 --- a/tox.ini +++ b/tox.ini @@ -10,9 +10,8 @@ envlist= [testenv] commands= pytest --lsof -rfsxX {posargs:testing} passenv = USER USERNAME -deps= - # pin to 3.5.0 until 3.5.2 is released - hypothesis==3.5.0 +deps= + hypothesis>=3.5.2 nose mock requests @@ -48,8 +47,7 @@ commands = flake8 pytest.py _pytest testing deps=pytest-xdist>=1.13 mock nose - # pin to 3.5.0 until 3.5.2 is released - hypothesis==3.5.0 + hypothesis>=3.5.2 commands= pytest -n1 -rfsxX {posargs:testing} @@ -75,8 +73,7 @@ commands= [testenv:py27-nobyte] deps= pytest-xdist>=1.13 - # pin to 3.5.0 until 3.5.2 is released - hypothesis==3.5.0 + hypothesis>=3.5.2 distribute=true setenv= PYTHONDONTWRITEBYTECODE=1