From 7684b3af7b33fc02887d608029b1000b1ac286af Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 16 Mar 2017 22:20:38 -0300 Subject: [PATCH 1/6] Recommend using py36 for testing on CONTRIBUTING --- CONTRIBUTING.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 71dc04d91..edf71dad7 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -206,12 +206,12 @@ but here is a simple overview: #. Run all the tests - You need to have Python 2.7 and 3.5 available in your system. Now + You need to have Python 2.7 and 3.6 available in your system. Now running tests is as simple as issuing this command:: - $ tox -e linting,py27,py35 + $ tox -e linting,py27,py36 - This command will run tests via the "tox" tool against Python 2.7 and 3.5 + This command will run tests via the "tox" tool against Python 2.7 and 3.6 and also perform "lint" coding-style checks. #. You can now edit your local working copy. @@ -223,9 +223,9 @@ but here is a simple overview: $ tox -e py27 -- --pdb - Or to only run tests in a particular test module on Python 3.5:: + Or to only run tests in a particular test module on Python 3.6:: - $ tox -e py35 -- testing/test_config.py + $ tox -e py36 -- testing/test_config.py #. Commit and push once your tests pass and you are happy with your change(s):: From 42a5d6bdfa620fc13809c861a05077ef6d6f2a6a Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 16 Mar 2017 22:21:30 -0300 Subject: [PATCH 2/6] Add __future__ imports to all pytest modules This prevents silly errors from creeping in Python 2 when testing in Python 3 --- _pytest/_argcomplete.py | 2 +- _pytest/_code/__init__.py | 1 + _pytest/_code/_py2traceback.py | 1 + _pytest/_code/code.py | 1 + _pytest/_code/source.py | 2 +- _pytest/_pluggy.py | 2 +- _pytest/assertion/__init__.py | 1 + _pytest/assertion/rewrite.py | 2 +- _pytest/assertion/truncate.py | 2 +- _pytest/assertion/util.py | 1 + _pytest/cacheprovider.py | 2 +- _pytest/capture.py | 2 +- _pytest/compat.py | 1 + _pytest/config.py | 1 + _pytest/debugging.py | 2 +- _pytest/deprecated.py | 2 +- _pytest/doctest.py | 2 +- _pytest/fixtures.py | 1 + _pytest/freeze_support.py | 4 +++- _pytest/helpconfig.py | 2 ++ _pytest/junitxml.py | 6 ++++-- _pytest/main.py | 2 ++ _pytest/mark.py | 2 ++ _pytest/monkeypatch.py | 1 + _pytest/nose.py | 1 + _pytest/pastebin.py | 2 ++ _pytest/pytester.py | 2 ++ _pytest/python.py | 1 + _pytest/recwarn.py | 2 ++ _pytest/resultlog.py | 1 + _pytest/runner.py | 2 ++ _pytest/setuponly.py | 2 ++ _pytest/setupplan.py | 2 ++ _pytest/skipping.py | 2 ++ _pytest/terminal.py | 2 ++ _pytest/tmpdir.py | 2 ++ _pytest/unittest.py | 2 +- testing/acceptance_test.py | 1 + testing/code/test_code.py | 1 + testing/code/test_excinfo.py | 1 + testing/code/test_source.py | 1 + testing/deprecated_test.py | 1 + testing/test_argcomplete.py | 2 +- testing/test_assertion.py | 1 + testing/test_assertrewrite.py | 1 + testing/test_cache.py | 1 + testing/test_capture.py | 1 + testing/test_collection.py | 1 + testing/test_compat.py | 1 + testing/test_config.py | 1 + testing/test_conftest.py | 1 + testing/test_doctest.py | 1 + testing/test_entry_points.py | 1 + testing/test_helpconfig.py | 1 + testing/test_junitxml.py | 4 ++-- testing/test_mark.py | 1 + testing/test_monkeypatch.py | 1 + testing/test_nose.py | 1 + testing/test_parseopt.py | 2 +- testing/test_pastebin.py | 1 + testing/test_pdb.py | 1 + testing/test_pluginmanager.py | 1 + testing/test_pytester.py | 1 + testing/test_recwarn.py | 1 + testing/test_resultlog.py | 1 + testing/test_runner.py | 2 +- testing/test_runner_xunit.py | 8 +++++--- testing/test_session.py | 1 + testing/test_skipping.py | 1 + testing/test_terminal.py | 1 + testing/test_tmpdir.py | 1 + testing/test_unittest.py | 1 + 72 files changed, 94 insertions(+), 22 deletions(-) diff --git a/_pytest/_argcomplete.py b/_pytest/_argcomplete.py index 8fbbf2660..12040b53a 100644 --- a/_pytest/_argcomplete.py +++ b/_pytest/_argcomplete.py @@ -57,7 +57,7 @@ If things do not work right away: which should throw a KeyError: 'COMPLINE' (which is properly set by the global argcomplete script). """ - +from __future__ import absolute_import, division, print_function import sys import os from glob import glob diff --git a/_pytest/_code/__init__.py b/_pytest/_code/__init__.py index 3463c11ea..815c13b42 100644 --- a/_pytest/_code/__init__.py +++ b/_pytest/_code/__init__.py @@ -1,4 +1,5 @@ """ python inspection/code generation API """ +from __future__ import absolute_import, division, print_function from .code import Code # noqa from .code import ExceptionInfo # noqa from .code import Frame # noqa diff --git a/_pytest/_code/_py2traceback.py b/_pytest/_code/_py2traceback.py index a830d9899..d45ee01fa 100644 --- a/_pytest/_code/_py2traceback.py +++ b/_pytest/_code/_py2traceback.py @@ -2,6 +2,7 @@ # CHANGES: # - some_str is replaced, trying to create unicode strings # +from __future__ import absolute_import, division, print_function import types def format_exception_only(etype, value): diff --git a/_pytest/_code/code.py b/_pytest/_code/code.py index 6eceb0c7f..2f1ac7fb0 100644 --- a/_pytest/_code/code.py +++ b/_pytest/_code/code.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import, division, print_function import sys from inspect import CO_VARARGS, CO_VARKEYWORDS import re diff --git a/_pytest/_code/source.py b/_pytest/_code/source.py index fcec0f5ca..8e6148410 100644 --- a/_pytest/_code/source.py +++ b/_pytest/_code/source.py @@ -1,4 +1,4 @@ -from __future__ import generators +from __future__ import absolute_import, division, generators, print_function from bisect import bisect_right import sys diff --git a/_pytest/_pluggy.py b/_pytest/_pluggy.py index 87d32cf8d..6cc1d3d54 100644 --- a/_pytest/_pluggy.py +++ b/_pytest/_pluggy.py @@ -2,7 +2,7 @@ imports symbols from vendored "pluggy" if available, otherwise falls back to importing "pluggy" from the default namespace. """ - +from __future__ import absolute_import, division, print_function try: from _pytest.vendored_packages.pluggy import * # noqa from _pytest.vendored_packages.pluggy import __version__ # noqa diff --git a/_pytest/assertion/__init__.py b/_pytest/assertion/__init__.py index fe0653bb7..edda06345 100644 --- a/_pytest/assertion/__init__.py +++ b/_pytest/assertion/__init__.py @@ -1,6 +1,7 @@ """ support for presenting detailed information in failing assertions. """ +from __future__ import absolute_import, division, print_function import py import sys diff --git a/_pytest/assertion/rewrite.py b/_pytest/assertion/rewrite.py index 7408c4746..484d5bd8b 100644 --- a/_pytest/assertion/rewrite.py +++ b/_pytest/assertion/rewrite.py @@ -1,5 +1,5 @@ """Rewrite assertion AST to produce nice error messages""" - +from __future__ import absolute_import, division, print_function import ast import _ast import errno diff --git a/_pytest/assertion/truncate.py b/_pytest/assertion/truncate.py index 3c031b11f..1e1306356 100644 --- a/_pytest/assertion/truncate.py +++ b/_pytest/assertion/truncate.py @@ -4,7 +4,7 @@ Utilities for truncating assertion output. Current default behaviour is to truncate assertion explanations at ~8 terminal lines, unless running in "-vv" mode or running on CI. """ - +from __future__ import absolute_import, division, print_function import os import py diff --git a/_pytest/assertion/util.py b/_pytest/assertion/util.py index 7b4edb6fd..06eda8d91 100644 --- a/_pytest/assertion/util.py +++ b/_pytest/assertion/util.py @@ -1,4 +1,5 @@ """Utilities for assertion debugging""" +from __future__ import absolute_import, division, print_function import pprint import _pytest._code diff --git a/_pytest/cacheprovider.py b/_pytest/cacheprovider.py index 782fdadd9..0b8e71a71 100755 --- a/_pytest/cacheprovider.py +++ b/_pytest/cacheprovider.py @@ -4,7 +4,7 @@ merged implementation of the cache provider the name cache was not chosen to ensure pluggy automatically ignores the external pytest-cache """ - +from __future__ import absolute_import, division, print_function import py import pytest import json diff --git a/_pytest/capture.py b/_pytest/capture.py index 6a1cae41d..c6fc80c0b 100644 --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -2,7 +2,7 @@ per-test stdout/stderr capturing mechanism. """ -from __future__ import with_statement +from __future__ import absolute_import, division, print_function import contextlib import sys diff --git a/_pytest/compat.py b/_pytest/compat.py index 09df385d1..b688ae509 100644 --- a/_pytest/compat.py +++ b/_pytest/compat.py @@ -1,6 +1,7 @@ """ python version compatibility code """ +from __future__ import absolute_import, division, print_function import sys import inspect import types diff --git a/_pytest/config.py b/_pytest/config.py index 6a607c7e3..92f9005b4 100644 --- a/_pytest/config.py +++ b/_pytest/config.py @@ -1,4 +1,5 @@ """ command line options, ini-file and conftest.py processing. """ +from __future__ import absolute_import, division, print_function import argparse import shlex import traceback diff --git a/_pytest/debugging.py b/_pytest/debugging.py index c21e0977d..1e7dcad5d 100644 --- a/_pytest/debugging.py +++ b/_pytest/debugging.py @@ -1,5 +1,5 @@ """ interactive debugging with PDB, the Python Debugger. """ -from __future__ import absolute_import +from __future__ import absolute_import, division, print_function import pdb import sys diff --git a/_pytest/deprecated.py b/_pytest/deprecated.py index 6edc475f6..e75ff099e 100644 --- a/_pytest/deprecated.py +++ b/_pytest/deprecated.py @@ -5,7 +5,7 @@ that is planned to be removed in the next pytest release. Keeping it in a central location makes it easy to track what is deprecated and should be removed when the time comes. """ - +from __future__ import absolute_import, division, print_function MAIN_STR_ARGS = 'passing a string to pytest.main() is deprecated, ' \ 'pass a list of arguments instead.' diff --git a/_pytest/doctest.py b/_pytest/doctest.py index 4ee21b12d..f9299be72 100644 --- a/_pytest/doctest.py +++ b/_pytest/doctest.py @@ -1,5 +1,5 @@ """ discover and run doctests in modules and test files.""" -from __future__ import absolute_import +from __future__ import absolute_import, division, print_function import traceback diff --git a/_pytest/fixtures.py b/_pytest/fixtures.py index c4d21635f..a151d615d 100644 --- a/_pytest/fixtures.py +++ b/_pytest/fixtures.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import, division, print_function import sys from py._code.code import FormattedExcinfo diff --git a/_pytest/freeze_support.py b/_pytest/freeze_support.py index f78ccd298..6c2120559 100644 --- a/_pytest/freeze_support.py +++ b/_pytest/freeze_support.py @@ -2,6 +2,8 @@ Provides a function to report all internal modules for using freezing tools pytest """ +from __future__ import absolute_import, division, print_function + def pytest_namespace(): return {'freeze_includes': freeze_includes} @@ -42,4 +44,4 @@ def _iter_all_modules(package, prefix=''): for m in _iter_all_modules(os.path.join(path, name), prefix=name + '.'): yield prefix + m else: - yield prefix + name \ No newline at end of file + yield prefix + name diff --git a/_pytest/helpconfig.py b/_pytest/helpconfig.py index 6e66b11c4..abc792f7e 100644 --- a/_pytest/helpconfig.py +++ b/_pytest/helpconfig.py @@ -1,4 +1,6 @@ """ version info, help messages, tracing configuration. """ +from __future__ import absolute_import, division, print_function + import py import pytest import os, sys diff --git a/_pytest/junitxml.py b/_pytest/junitxml.py index e5f20b6f6..4bd334a16 100644 --- a/_pytest/junitxml.py +++ b/_pytest/junitxml.py @@ -4,9 +4,11 @@ Based on initial code from Ross Lawley. + +Output conforms to https://github.com/jenkinsci/xunit-plugin/blob/master/ +src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd """ -# Output conforms to https://github.com/jenkinsci/xunit-plugin/blob/master/ -# src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd +from __future__ import absolute_import, division, print_function import functools import py diff --git a/_pytest/main.py b/_pytest/main.py index f100b7974..3d7b456d2 100644 --- a/_pytest/main.py +++ b/_pytest/main.py @@ -1,4 +1,6 @@ """ core implementation of testing process: init, session, runtest loop. """ +from __future__ import absolute_import, division, print_function + import functools import os import sys diff --git a/_pytest/mark.py b/_pytest/mark.py index 582eb1277..643f43ce0 100644 --- a/_pytest/mark.py +++ b/_pytest/mark.py @@ -1,4 +1,6 @@ """ generic mechanism for marking and selecting python functions. """ +from __future__ import absolute_import, division, print_function + import inspect from collections import namedtuple from operator import attrgetter diff --git a/_pytest/monkeypatch.py b/_pytest/monkeypatch.py index 30618cc57..9151db3ad 100644 --- a/_pytest/monkeypatch.py +++ b/_pytest/monkeypatch.py @@ -1,4 +1,5 @@ """ monkeypatching and mocking functionality. """ +from __future__ import absolute_import, division, print_function import os, sys import re diff --git a/_pytest/nose.py b/_pytest/nose.py index 038746868..828a919f9 100644 --- a/_pytest/nose.py +++ b/_pytest/nose.py @@ -1,4 +1,5 @@ """ run test suites written for nose. """ +from __future__ import absolute_import, division, print_function import sys diff --git a/_pytest/pastebin.py b/_pytest/pastebin.py index 9f1cf9063..6f3ce8fed 100644 --- a/_pytest/pastebin.py +++ b/_pytest/pastebin.py @@ -1,4 +1,6 @@ """ submit failure or test session information to a pastebin service. """ +from __future__ import absolute_import, division, print_function + import pytest import sys import tempfile diff --git a/_pytest/pytester.py b/_pytest/pytester.py index 97fc62312..48d9b0956 100644 --- a/_pytest/pytester.py +++ b/_pytest/pytester.py @@ -1,4 +1,6 @@ """ (disabled by default) support for testing pytest and pytest plugins. """ +from __future__ import absolute_import, division, print_function + import codecs import gc import os diff --git a/_pytest/python.py b/_pytest/python.py index 471a9563f..7d94ed61a 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -1,4 +1,5 @@ """ Python test discovery, setup and run of test functions. """ +from __future__ import absolute_import, division, print_function import fnmatch import inspect diff --git a/_pytest/recwarn.py b/_pytest/recwarn.py index 43f68ed12..91cc85fdf 100644 --- a/_pytest/recwarn.py +++ b/_pytest/recwarn.py @@ -1,4 +1,6 @@ """ recording warnings during test function execution. """ +from __future__ import absolute_import, division, print_function + import inspect import _pytest._code diff --git a/_pytest/resultlog.py b/_pytest/resultlog.py index fc0025983..fbf06d630 100644 --- a/_pytest/resultlog.py +++ b/_pytest/resultlog.py @@ -1,6 +1,7 @@ """ log machine-parseable test session result information in a plain text file. """ +from __future__ import absolute_import, division, print_function import py import os diff --git a/_pytest/runner.py b/_pytest/runner.py index f17155dae..4277f8ee3 100644 --- a/_pytest/runner.py +++ b/_pytest/runner.py @@ -1,4 +1,6 @@ """ basic collect and runtest protocol implementations """ +from __future__ import absolute_import, division, print_function + import bdb import sys from time import time diff --git a/_pytest/setuponly.py b/_pytest/setuponly.py index 1752c575f..15e195ad5 100644 --- a/_pytest/setuponly.py +++ b/_pytest/setuponly.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import, division, print_function + import pytest import sys diff --git a/_pytest/setupplan.py b/_pytest/setupplan.py index f0853dee5..e11bd4069 100644 --- a/_pytest/setupplan.py +++ b/_pytest/setupplan.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import, division, print_function + import pytest diff --git a/_pytest/skipping.py b/_pytest/skipping.py index 86176acaf..228c52935 100644 --- a/_pytest/skipping.py +++ b/_pytest/skipping.py @@ -1,4 +1,6 @@ """ support for skip/xfail functions and markers. """ +from __future__ import absolute_import, division, print_function + import os import sys import traceback diff --git a/_pytest/terminal.py b/_pytest/terminal.py index b07945426..4607b752e 100644 --- a/_pytest/terminal.py +++ b/_pytest/terminal.py @@ -2,6 +2,8 @@ This is a good source for looking at the various reporting hooks. """ +from __future__ import absolute_import, division, print_function + from _pytest.main import EXIT_OK, EXIT_TESTSFAILED, EXIT_INTERRUPTED, \ EXIT_USAGEERROR, EXIT_NOTESTSCOLLECTED import pytest diff --git a/_pytest/tmpdir.py b/_pytest/tmpdir.py index 67f999e5a..7c5fac17c 100644 --- a/_pytest/tmpdir.py +++ b/_pytest/tmpdir.py @@ -1,4 +1,6 @@ """ support for providing temporary directories to test functions. """ +from __future__ import absolute_import, division, print_function + import re import pytest diff --git a/_pytest/unittest.py b/_pytest/unittest.py index 276b9ba16..5a8cb9d66 100644 --- a/_pytest/unittest.py +++ b/_pytest/unittest.py @@ -1,5 +1,5 @@ """ discovery and running of std-library "unittest" style tests. """ -from __future__ import absolute_import +from __future__ import absolute_import, division, print_function import sys import traceback diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 0d3fc1016..debda79ca 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import absolute_import, division, print_function import os import sys diff --git a/testing/code/test_code.py b/testing/code/test_code.py index a1f31b4a9..479a2e7cc 100644 --- a/testing/code/test_code.py +++ b/testing/code/test_code.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import, division, print_function import sys import _pytest._code diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index e2354ff5d..06b61c831 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import absolute_import, division, print_function import operator import _pytest diff --git a/testing/code/test_source.py b/testing/code/test_source.py index c161f75f8..bdbc00d19 100644 --- a/testing/code/test_source.py +++ b/testing/code/test_source.py @@ -1,6 +1,7 @@ # flake8: noqa # disable flake check on this file because some constructs are strange # or redundant on purpose and can't be disable on a line-by-line basis +from __future__ import absolute_import, division, print_function import sys import _pytest._code diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index 6473989e6..ad2f2021d 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import, division, print_function import pytest diff --git a/testing/test_argcomplete.py b/testing/test_argcomplete.py index ed6db4c78..6887c419c 100644 --- a/testing/test_argcomplete.py +++ b/testing/test_argcomplete.py @@ -1,4 +1,4 @@ -from __future__ import with_statement +from __future__ import absolute_import, division, print_function import py, pytest # test for _argcomplete but not specific for any application diff --git a/testing/test_assertion.py b/testing/test_assertion.py index e5c18b43a..789352a7f 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import absolute_import, division, print_function import sys import textwrap diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index 047a2ac6e..f4fc0c464 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import, division, print_function import glob import os import py_compile diff --git a/testing/test_cache.py b/testing/test_cache.py index c1b98489e..47072e07e 100755 --- a/testing/test_cache.py +++ b/testing/test_cache.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import, division, print_function import sys import _pytest diff --git a/testing/test_capture.py b/testing/test_capture.py index 28326fa73..6296abe78 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import, division, print_function # note: py.io capture tests where copied from # pylib 1.4.20.dev2 (rev 13d9af95547e) from __future__ import with_statement diff --git a/testing/test_collection.py b/testing/test_collection.py index 8575aa63a..c19fc0e72 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import, division, print_function import pytest, py from _pytest.main import Session, EXIT_NOTESTSCOLLECTED diff --git a/testing/test_compat.py b/testing/test_compat.py index 1736e8e2a..7b2251ef6 100644 --- a/testing/test_compat.py +++ b/testing/test_compat.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import, division, print_function import sys import pytest diff --git a/testing/test_config.py b/testing/test_config.py index 171e9486e..94ad2b130 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import, division, print_function import py, pytest import _pytest._code diff --git a/testing/test_conftest.py b/testing/test_conftest.py index e9fd1927a..db67a0cc8 100644 --- a/testing/test_conftest.py +++ b/testing/test_conftest.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import, division, print_function from textwrap import dedent import _pytest._code diff --git a/testing/test_doctest.py b/testing/test_doctest.py index 429bb8de9..82597b477 100644 --- a/testing/test_doctest.py +++ b/testing/test_doctest.py @@ -1,4 +1,5 @@ # encoding: utf-8 +from __future__ import absolute_import, division, print_function import sys import _pytest._code from _pytest.compat import MODULE_NOT_FOUND_ERROR diff --git a/testing/test_entry_points.py b/testing/test_entry_points.py index 370b93129..6ca68b481 100644 --- a/testing/test_entry_points.py +++ b/testing/test_entry_points.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import, division, print_function import pkg_resources import pytest diff --git a/testing/test_helpconfig.py b/testing/test_helpconfig.py index fc3c8fdf6..6eecbfd37 100644 --- a/testing/test_helpconfig.py +++ b/testing/test_helpconfig.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import, division, print_function from _pytest.main import EXIT_NOTESTSCOLLECTED import pytest diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index a0f51fc71..3d07e54e5 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- - +from __future__ import absolute_import, division, print_function from xml.dom import minidom import py import sys @@ -1028,4 +1028,4 @@ def test_url_property(testdir): test_case = minidom.parse(str(path)).getElementsByTagName('testcase')[0] - assert (test_case.getAttribute('url') == test_url), "The URL did not get written to the xml" \ No newline at end of file + assert (test_case.getAttribute('url') == test_url), "The URL did not get written to the xml" diff --git a/testing/test_mark.py b/testing/test_mark.py index 9474d1033..79e485438 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import, division, print_function import os import py, pytest diff --git a/testing/test_monkeypatch.py b/testing/test_monkeypatch.py index 04a53d93a..1efcf7f95 100644 --- a/testing/test_monkeypatch.py +++ b/testing/test_monkeypatch.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import, division, print_function import os import sys import textwrap diff --git a/testing/test_nose.py b/testing/test_nose.py index 11fb83176..798badc1c 100644 --- a/testing/test_nose.py +++ b/testing/test_nose.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import, division, print_function import pytest def setup_module(mod): diff --git a/testing/test_parseopt.py b/testing/test_parseopt.py index 03d71de43..f990e8b04 100644 --- a/testing/test_parseopt.py +++ b/testing/test_parseopt.py @@ -1,4 +1,4 @@ -from __future__ import with_statement +from __future__ import absolute_import, division, print_function import sys import os import py, pytest diff --git a/testing/test_pastebin.py b/testing/test_pastebin.py index f0d2a9ba2..3fe66e972 100644 --- a/testing/test_pastebin.py +++ b/testing/test_pastebin.py @@ -1,4 +1,5 @@ # encoding: utf-8 +from __future__ import absolute_import, division, print_function import sys import pytest diff --git a/testing/test_pdb.py b/testing/test_pdb.py index 161b4f5f7..ec5862082 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import, division, print_function import sys import platform diff --git a/testing/test_pluginmanager.py b/testing/test_pluginmanager.py index 3214d868b..ec8389746 100644 --- a/testing/test_pluginmanager.py +++ b/testing/test_pluginmanager.py @@ -1,4 +1,5 @@ # encoding: UTF-8 +from __future__ import absolute_import, division, print_function import pytest import py import os diff --git a/testing/test_pytester.py b/testing/test_pytester.py index 9c9b56371..932427ad3 100644 --- a/testing/test_pytester.py +++ b/testing/test_pytester.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import, division, print_function import pytest import os from _pytest.pytester import HookRecorder diff --git a/testing/test_recwarn.py b/testing/test_recwarn.py index 1269af431..890d2ce0a 100644 --- a/testing/test_recwarn.py +++ b/testing/test_recwarn.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import, division, print_function import warnings import re import py diff --git a/testing/test_resultlog.py b/testing/test_resultlog.py index c2f4a801c..cb083225c 100644 --- a/testing/test_resultlog.py +++ b/testing/test_resultlog.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import, division, print_function import os import _pytest._code diff --git a/testing/test_runner.py b/testing/test_runner.py index 265e99d54..51d430fc8 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import with_statement +from __future__ import absolute_import, division, print_function import _pytest._code import os diff --git a/testing/test_runner_xunit.py b/testing/test_runner_xunit.py index 9bf15bd66..92ba97202 100644 --- a/testing/test_runner_xunit.py +++ b/testing/test_runner_xunit.py @@ -1,6 +1,8 @@ -# -# test correct setup/teardowns at -# module, class, and instance level +""" + test correct setup/teardowns at + module, class, and instance level +""" +from __future__ import absolute_import, division, print_function import pytest diff --git a/testing/test_session.py b/testing/test_session.py index 66a0f5978..d08f7b3e2 100644 --- a/testing/test_session.py +++ b/testing/test_session.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import, division, print_function import pytest from _pytest.main import EXIT_NOTESTSCOLLECTED diff --git a/testing/test_skipping.py b/testing/test_skipping.py index f621a010f..5f25c3e6e 100644 --- a/testing/test_skipping.py +++ b/testing/test_skipping.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import, division, print_function import pytest import sys diff --git a/testing/test_terminal.py b/testing/test_terminal.py index d72f6a12b..9d7a2d7d2 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -1,6 +1,7 @@ """ terminal reporting of the full testing process. """ +from __future__ import absolute_import, division, print_function import collections import sys diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index 32ac76331..ccd70ed8b 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import, division, print_function import sys import py import pytest diff --git a/testing/test_unittest.py b/testing/test_unittest.py index 832ea08cc..af9851997 100644 --- a/testing/test_unittest.py +++ b/testing/test_unittest.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import, division, print_function from _pytest.main import EXIT_NOTESTSCOLLECTED import pytest import gc From e5021dc9dc59c73e03e3beadd14de771a4c6b1bd Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 16 Mar 2017 22:27:28 -0300 Subject: [PATCH 3/6] Replace py.builtin.print_() calls by builtin print() function --- _pytest/pytester.py | 16 +++++++--------- _pytest/resultlog.py | 4 ++-- testing/python/collect.py | 10 ++++++---- testing/test_capture.py | 6 +++--- testing/test_mark.py | 2 +- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/_pytest/pytester.py b/_pytest/pytester.py index 48d9b0956..ee0e5bbe7 100644 --- a/_pytest/pytester.py +++ b/_pytest/pytester.py @@ -14,8 +14,6 @@ from fnmatch import fnmatch from weakref import WeakKeyDictionary -from py.builtin import print_ - from _pytest.capture import MultiCapture, SysCapture from _pytest._code import Source import py @@ -231,15 +229,15 @@ class HookRecorder(object): name, check = entries.pop(0) for ind, call in enumerate(self.calls[i:]): if call._name == name: - print_("NAMEMATCH", name, call) + print("NAMEMATCH", name, call) if eval(check, backlocals, call.__dict__): - print_("CHECKERMATCH", repr(check), "->", call) + print("CHECKERMATCH", repr(check), "->", call) else: - print_("NOCHECKERMATCH", repr(check), "-", call) + print("NOCHECKERMATCH", repr(check), "-", call) continue i += ind + 1 break - print_("NONAMEMATCH", name, "with", call) + print("NONAMEMATCH", name, "with", call) else: pytest.fail("could not find %r check %r" % (name, check)) @@ -926,8 +924,8 @@ class Testdir(object): cmdargs = [str(x) for x in cmdargs] p1 = self.tmpdir.join("stdout") p2 = self.tmpdir.join("stderr") - print_("running:", ' '.join(cmdargs)) - print_(" in:", str(py.path.local())) + print("running:", ' '.join(cmdargs)) + print(" in:", str(py.path.local())) f1 = codecs.open(str(p1), "w", encoding="utf8") f2 = codecs.open(str(p2), "w", encoding="utf8") try: @@ -953,7 +951,7 @@ class Testdir(object): def _dump_lines(self, lines, fp): try: for line in lines: - py.builtin.print_(line, file=fp) + print(line, file=fp) except UnicodeEncodeError: print("couldn't print to %s because of encoding" % (fp,)) diff --git a/_pytest/resultlog.py b/_pytest/resultlog.py index fbf06d630..3e4b00cf9 100644 --- a/_pytest/resultlog.py +++ b/_pytest/resultlog.py @@ -62,9 +62,9 @@ class ResultLog(object): self.logfile = logfile # preferably line buffered def write_log_entry(self, testpath, lettercode, longrepr): - py.builtin.print_("%s %s" % (lettercode, testpath), file=self.logfile) + print("%s %s" % (lettercode, testpath), file=self.logfile) for line in longrepr.splitlines(): - py.builtin.print_(" %s" % line, file=self.logfile) + print(" %s" % line, file=self.logfile) def log_outcome(self, report, lettercode, longrepr): testpath = getattr(report, 'nodeid', None) diff --git a/testing/python/collect.py b/testing/python/collect.py index e4069983a..e67b6bc84 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -274,6 +274,7 @@ class TestGenerator(object): def test_order_of_execution_generator_same_codeline(self, testdir, tmpdir): o = testdir.makepyfile(""" + from __future__ import print_function def test_generative_order_of_execution(): import py, pytest test_list = [] @@ -283,8 +284,8 @@ class TestGenerator(object): test_list.append(item) def assert_order_of_execution(): - py.builtin.print_('expected order', expected_list) - py.builtin.print_('but got ', test_list) + print('expected order', expected_list) + print('but got ', test_list) assert test_list == expected_list for i in expected_list: @@ -298,6 +299,7 @@ class TestGenerator(object): def test_order_of_execution_generator_different_codeline(self, testdir): o = testdir.makepyfile(""" + from __future__ import print_function def test_generative_tests_different_codeline(): import py, pytest test_list = [] @@ -313,8 +315,8 @@ class TestGenerator(object): test_list.append(0) def assert_order_of_execution(): - py.builtin.print_('expected order', expected_list) - py.builtin.print_('but got ', test_list) + print('expected order', expected_list) + print('but got ', test_list) assert test_list == expected_list yield list_append_0 diff --git a/testing/test_capture.py b/testing/test_capture.py index 6296abe78..aa2a3bae5 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -14,7 +14,7 @@ import contextlib from _pytest import capture from _pytest.capture import CaptureManager from _pytest.main import EXIT_NOTESTSCOLLECTED -from py.builtin import print_ + needsosdup = pytest.mark.xfail("not hasattr(os, 'dup')") @@ -712,7 +712,7 @@ def test_dupfile(tmpfile): assert nf != tmpfile assert nf.fileno() != tmpfile.fileno() assert nf not in flist - print_(i, end="", file=nf) + print(i, end="", file=nf) flist.append(nf) for i in range(5): f = flist[i] @@ -786,7 +786,7 @@ class TestFDCapture(object): def test_stderr(self): cap = capture.FDCapture(2) cap.start() - print_("hello", file=sys.stderr) + print("hello", file=sys.stderr) s = cap.snap() cap.done() assert s == "hello\n" diff --git a/testing/test_mark.py b/testing/test_mark.py index 79e485438..3bec1d0d0 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -674,7 +674,7 @@ class TestKeywordSelection(object): item.extra_keyword_matches.add("xxx") """) reprec = testdir.inline_run(p.dirpath(), '-s', '-k', keyword) - py.builtin.print_("keyword", repr(keyword)) + print("keyword", repr(keyword)) passed, skipped, failed = reprec.listoutcomes() assert len(passed) == 1 assert passed[0].nodeid.endswith("test_2") From 4d947077bbed4862d4e0a055ce967ba490be96af Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 16 Mar 2017 23:07:03 -0300 Subject: [PATCH 4/6] Fix test in py26 that expected a floor division error message --- testing/code/test_excinfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index 06b61c831..b7dafdb46 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -370,7 +370,7 @@ def test_codepath_Queue_example(): def test_match_succeeds(): with pytest.raises(ZeroDivisionError) as excinfo: - 0 / 0 + 0 // 0 excinfo.match(r'.*zero.*') def test_match_raises_error(testdir): From e368fb4b29912fe618d523b2ca9197016738d7d6 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 7 Sep 2016 11:00:27 +0200 Subject: [PATCH 5/6] implement pytest.param this allows a clear addition of parameterization parameters that carry along marks instead of nesting multiple mark objects and destroying the possibility of creating function valued parameters, it just folders everything together into one object carrfying parameters, and the marks. --- _pytest/mark.py | 77 +++++++++++++++++++++++------ _pytest/python.py | 71 +++++++++++++-------------- doc/en/parametrize.rst | 51 ++++++++++++++------ testing/python/metafunc.py | 99 ++++++++++++++++++++++++++------------ testing/test_mark.py | 28 ++++++++--- 5 files changed, 224 insertions(+), 102 deletions(-) diff --git a/_pytest/mark.py b/_pytest/mark.py index 643f43ce0..a06b02b14 100644 --- a/_pytest/mark.py +++ b/_pytest/mark.py @@ -6,17 +6,72 @@ from collections import namedtuple from operator import attrgetter from .compat import imap + def alias(name): return property(attrgetter(name), doc='alias for ' + name) +class ParameterSet(namedtuple('ParameterSet', 'values, marks, id')): + @classmethod + def param(cls, *values, **kw): + marks = kw.pop('marks', ()) + if isinstance(marks, MarkDecorator): + marks = marks, + else: + assert isinstance(marks, (tuple, list, set)) + + def param_extract_id(id=None): + return id + + id = param_extract_id(**kw) + return cls(values, marks, id) + + @classmethod + def extract_from(cls, parameterset, legacy_force_tuple=False): + """ + :param parameterset: + a legacy style parameterset that may or may not be a tuple, + and may or may not be wrapped into a mess of mark objects + + :param legacy_force_tuple: + enforce tuple wrapping so single argument tuple values + don't get decomposed and break tests + + """ + + if isinstance(parameterset, cls): + return parameterset + if not isinstance(parameterset, MarkDecorator) and legacy_force_tuple: + return cls.param(parameterset) + + newmarks = [] + argval = parameterset + while isinstance(argval, MarkDecorator): + newmarks.append(MarkDecorator(Mark( + argval.markname, argval.args[:-1], argval.kwargs))) + argval = argval.args[-1] + assert not isinstance(argval, ParameterSet) + if legacy_force_tuple: + argval = argval, + + return cls(argval, marks=newmarks, id=None) + + @property + def deprecated_arg_dict(self): + return dict((mark.name, mark) for mark in self.marks) + + class MarkerError(Exception): """Error in use of a pytest marker/attribute.""" + def pytest_namespace(): - return {'mark': MarkGenerator()} + return { + 'mark': MarkGenerator(), + 'param': ParameterSet.param, + } def pytest_addoption(parser): @@ -212,6 +267,7 @@ def istestfunc(func): return hasattr(func, "__call__") and \ getattr(func, "__name__", "") != "" + class MarkDecorator(object): """ A decorator for test functions and test classes. When applied it will create :class:`MarkInfo` objects which may be @@ -257,8 +313,11 @@ class MarkDecorator(object): def markname(self): return self.name # for backward-compat (2.4.1 had this attr) + def __eq__(self, other): + return self.mark == other.mark + def __repr__(self): - return "" % self.mark + return "" % (self.mark,) def __call__(self, *args, **kwargs): """ if passed a single callable argument: decorate it with mark info. @@ -291,19 +350,7 @@ class MarkDecorator(object): return self.__class__(self.mark.combined_with(mark)) -def extract_argvalue(maybe_marked_args): - # TODO: incorrect mark data, the old code wanst able to collect lists - # individual parametrized argument sets can be wrapped in a series - # of markers in which case we unwrap the values and apply the mark - # at Function init - newmarks = {} - argval = maybe_marked_args - while isinstance(argval, MarkDecorator): - newmark = MarkDecorator(Mark( - argval.markname, argval.args[:-1], argval.kwargs)) - newmarks[newmark.name] = newmark - argval = argval.args[-1] - return argval, newmarks + class Mark(namedtuple('Mark', 'name, args, kwargs')): diff --git a/_pytest/python.py b/_pytest/python.py index 7d94ed61a..81eed00f0 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -788,36 +788,35 @@ class Metafunc(fixtures.FuncargnamesCompatAttr): to set a dynamic scope using test context or configuration. """ from _pytest.fixtures import scope2index - from _pytest.mark import extract_argvalue + from _pytest.mark import ParameterSet from py.io import saferepr - unwrapped_argvalues = [] - newkeywords = [] - for maybe_marked_args in argvalues: - argval, newmarks = extract_argvalue(maybe_marked_args) - unwrapped_argvalues.append(argval) - newkeywords.append(newmarks) - argvalues = unwrapped_argvalues - if not isinstance(argnames, (tuple, list)): argnames = [x.strip() for x in argnames.split(",") if x.strip()] - if len(argnames) == 1: - argvalues = [(val,) for val in argvalues] - if not argvalues: - argvalues = [(NOTSET,) * len(argnames)] - # we passed a empty list to parameterize, skip that test - # + force_tuple = len(argnames) == 1 + else: + force_tuple = False + parameters = [ + ParameterSet.extract_from(x, legacy_force_tuple=force_tuple) + for x in argvalues] + del argvalues + + + if not parameters: fs, lineno = getfslineno(self.function) - newmark = pytest.mark.skip( - reason="got empty parameter set %r, function %s at %s:%d" % ( - argnames, self.function.__name__, fs, lineno)) - newkeywords = [{newmark.markname: newmark}] + reason = "got empty parameter set %r, function %s at %s:%d" % ( + argnames, self.function.__name__, fs, lineno) + mark = pytest.mark.skip(reason=reason) + parameters.append(ParameterSet( + values=(NOTSET,) * len(argnames), + marks=[mark], + id=None, + )) if scope is None: scope = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect) - scopenum = scope2index( - scope, descr='call to {0}'.format(self.parametrize)) + scopenum = scope2index(scope, descr='call to {0}'.format(self.parametrize)) valtypes = {} for arg in argnames: if arg not in self.fixturenames: @@ -845,22 +844,22 @@ class Metafunc(fixtures.FuncargnamesCompatAttr): idfn = ids ids = None if ids: - if len(ids) != len(argvalues): - raise ValueError('%d tests specified with %d ids' %( - len(argvalues), len(ids))) + if len(ids) != len(parameters): + raise ValueError('%d tests specified with %d ids' % ( + len(parameters), len(ids))) for id_value in ids: 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) + ids = idmaker(argnames, parameters, idfn, ids, self.config) newcalls = [] for callspec in self._calls or [CallSpec2(self)]: - elements = zip(ids, argvalues, newkeywords, count()) - for a_id, valset, keywords, param_index in elements: - assert len(valset) == len(argnames) + elements = zip(ids, parameters, count()) + for a_id, param, param_index in elements: + assert len(param.values) == len(argnames) newcallspec = callspec.copy(self) - newcallspec.setmulti(valtypes, argnames, valset, a_id, - keywords, scopenum, param_index) + newcallspec.setmulti(valtypes, argnames, param.values, a_id, + param.deprecated_arg_dict, scopenum, param_index) newcalls.append(newcallspec) self._calls = newcalls @@ -959,17 +958,19 @@ def _idval(val, argname, idx, idfn, config=None): return val.__name__ return str(argname)+str(idx) -def _idvalset(idx, valset, argnames, idfn, ids, config=None): +def _idvalset(idx, parameterset, argnames, idfn, ids, config=None): + if parameterset.id is not None: + return parameterset.id if ids is None or (idx >= len(ids) or ids[idx] is None): this_id = [_idval(val, argname, idx, idfn, config) - for val, argname in zip(valset, argnames)] + for val, argname in zip(parameterset.values, argnames)] return "-".join(this_id) else: return _escape_strings(ids[idx]) -def idmaker(argnames, argvalues, idfn=None, ids=None, config=None): - ids = [_idvalset(valindex, valset, argnames, idfn, ids, config) - for valindex, valset in enumerate(argvalues)] +def idmaker(argnames, parametersets, idfn=None, ids=None, config=None): + ids = [_idvalset(valindex, parameterset, argnames, idfn, ids, config) + for valindex, parameterset in enumerate(parametersets)] if len(set(ids)) != len(ids): # The ids are not unique duplicates = [testid for testid in ids if ids.count(testid) > 1] diff --git a/doc/en/parametrize.rst b/doc/en/parametrize.rst index b93099e7d..10de065c7 100644 --- a/doc/en/parametrize.rst +++ b/doc/en/parametrize.rst @@ -55,17 +55,17 @@ them in turn:: $ pytest ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.7, py-1.4.32, pluggy-0.4.0 + platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0 rootdir: $REGENDOC_TMPDIR, inifile: collected 3 items - + test_expectation.py ..F - + ======= FAILURES ======== _______ test_eval[6*9-42] ________ - + test_input = '6*9', expected = 42 - + @pytest.mark.parametrize("test_input,expected", [ ("3+5", 8), ("2+4", 6), @@ -73,9 +73,9 @@ them in turn:: ]) def test_eval(test_input, expected): > assert eval(test_input) == expected - E AssertionError: assert 54 == 42 + E assert 54 == 42 E + where 54 = eval('6*9') - + test_expectation.py:8: AssertionError ======= 1 failed, 2 passed in 0.12 seconds ======== @@ -94,21 +94,42 @@ for example with the builtin ``mark.xfail``:: @pytest.mark.parametrize("test_input,expected", [ ("3+5", 8), ("2+4", 6), - pytest.mark.xfail(("6*9", 42)), + pytest.param("6*9", 42, + marks=pytest.mark.xfail), ]) def test_eval(test_input, expected): assert eval(test_input) == expected +.. note:: + + prior to version 3.1 the supported mechanism for marking values + used the syntax:: + + import pytest + @pytest.mark.parametrize("test_input,expected", [ + ("3+5", 8), + ("2+4", 6), + pytest.mark.xfail(("6*9", 42),), + ]) + def test_eval(test_input, expected): + assert eval(test_input) == expected + + + This was an initial hack to support the feature but soon was demonstrated to be incomplete, + broken for passing functions or applying multiple marks with the same name but different parameters. + The old syntax will be removed in pytest-4.0. + + Let's run this:: $ pytest ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.7, py-1.4.32, pluggy-0.4.0 + platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0 rootdir: $REGENDOC_TMPDIR, inifile: collected 3 items - + test_expectation.py ..x - + ======= 2 passed, 1 xfailed in 0.12 seconds ======== The one parameter set which caused a failure previously now @@ -181,15 +202,15 @@ Let's also run with a stringinput that will lead to a failing test:: F ======= FAILURES ======== _______ test_valid_string[!] ________ - + stringinput = '!' - + def test_valid_string(stringinput): > assert stringinput.isalpha() - E AssertionError: assert False + E assert False E + where False = () E + where = '!'.isalpha - + test_strings.py:3: AssertionError 1 failed in 0.12 seconds diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 949b265a3..c347dc9e2 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -207,37 +207,40 @@ class TestMetafunc(object): @pytest.mark.issue250 def test_idmaker_autoname(self): from _pytest.python import idmaker - result = idmaker(("a", "b"), [("string", 1.0), - ("st-ring", 2.0)]) + result = idmaker(("a", "b"), [pytest.param("string", 1.0), + pytest.param("st-ring", 2.0)]) assert result == ["string-1.0", "st-ring-2.0"] - result = idmaker(("a", "b"), [(object(), 1.0), - (object(), object())]) + result = idmaker(("a", "b"), [pytest.param(object(), 1.0), + pytest.param(object(), object())]) assert result == ["a0-1.0", "a1-b1"] # unicode mixing, issue250 - result = idmaker((py.builtin._totext("a"), "b"), [({}, b'\xc3\xb4')]) + result = idmaker( + (py.builtin._totext("a"), "b"), + [pytest.param({}, b'\xc3\xb4')]) assert result == ['a0-\\xc3\\xb4'] def test_idmaker_with_bytes_regex(self): from _pytest.python import idmaker - result = idmaker(("a"), [(re.compile(b'foo'), 1.0)]) + result = idmaker(("a"), [pytest.param(re.compile(b'foo'), 1.0)]) assert result == ["foo"] def test_idmaker_native_strings(self): from _pytest.python import idmaker totext = py.builtin._totext - result = idmaker(("a", "b"), [(1.0, -1.1), - (2, -202), - ("three", "three hundred"), - (True, False), - (None, None), - (re.compile('foo'), re.compile('bar')), - (str, int), - (list("six"), [66, 66]), - (set([7]), set("seven")), - (tuple("eight"), (8, -8, 8)), - (b'\xc3\xb4', b"name"), - (b'\xc3\xb4', totext("other")), + result = idmaker(("a", "b"), [ + pytest.param(1.0, -1.1), + pytest.param(2, -202), + pytest.param("three", "three hundred"), + pytest.param(True, False), + pytest.param(None, None), + pytest.param(re.compile('foo'), re.compile('bar')), + pytest.param(str, int), + pytest.param(list("six"), [66, 66]), + pytest.param(set([7]), set("seven")), + pytest.param(tuple("eight"), (8, -8, 8)), + pytest.param(b'\xc3\xb4', b"name"), + pytest.param(b'\xc3\xb4', totext("other")), ]) assert result == ["1.0--1.1", "2--202", @@ -257,7 +260,7 @@ class TestMetafunc(object): from _pytest.python import idmaker enum = pytest.importorskip("enum") e = enum.Enum("Foo", "one, two") - result = idmaker(("a", "b"), [(e.one, e.two)]) + result = idmaker(("a", "b"), [pytest.param(e.one, e.two)]) assert result == ["Foo.one-Foo.two"] @pytest.mark.issue351 @@ -268,9 +271,10 @@ class TestMetafunc(object): if isinstance(val, Exception): return repr(val) - result = idmaker(("a", "b"), [(10.0, IndexError()), - (20, KeyError()), - ("three", [1, 2, 3]), + result = idmaker(("a", "b"), [ + pytest.param(10.0, IndexError()), + pytest.param(20, KeyError()), + pytest.param("three", [1, 2, 3]), ], idfn=ids) assert result == ["10.0-IndexError()", "20-KeyError()", @@ -284,9 +288,9 @@ class TestMetafunc(object): def ids(val): return 'a' - result = idmaker(("a", "b"), [(10.0, IndexError()), - (20, KeyError()), - ("three", [1, 2, 3]), + result = idmaker(("a", "b"), [pytest.param(10.0, IndexError()), + pytest.param(20, KeyError()), + pytest.param("three", [1, 2, 3]), ], idfn=ids) assert result == ["a-a0", "a-a1", @@ -306,9 +310,10 @@ class TestMetafunc(object): rec = WarningsRecorder() with rec: - idmaker(("a", "b"), [(10.0, IndexError()), - (20, KeyError()), - ("three", [1, 2, 3]), + idmaker(("a", "b"), [ + pytest.param(10.0, IndexError()), + pytest.param(20, KeyError()), + pytest.param("three", [1, 2, 3]), ], idfn=ids) assert [str(i.message) for i in rec.list] == [ @@ -351,14 +356,21 @@ class TestMetafunc(object): def test_idmaker_with_ids(self): from _pytest.python import idmaker - result = idmaker(("a", "b"), [(1, 2), - (3, 4)], + result = idmaker(("a", "b"), [pytest.param(1, 2), + pytest.param(3, 4)], ids=["a", None]) assert result == ["a", "3-4"] + def test_idmaker_with_paramset_id(self): + from _pytest.python import idmaker + result = idmaker(("a", "b"), [pytest.param(1, 2, id="me"), + pytest.param(3, 4, id="you")], + ids=["a", None]) + assert result == ["me", "you"] + def test_idmaker_with_ids_unique_names(self): from _pytest.python import idmaker - result = idmaker(("a"), [1,2,3,4,5], + result = idmaker(("a"), map(pytest.param, [1,2,3,4,5]), ids=["a", "a", "b", "c", "b"]) assert result == ["a0", "a1", "b0", "c", "b1"] @@ -1438,6 +1450,31 @@ class TestMarkersWithParametrization(object): reprec = testdir.inline_run() reprec.assertoutcome(passed=2) + + @pytest.mark.parametrize('strict', [True, False]) + def test_parametrize_marked_value(self, testdir, strict): + s = """ + import pytest + + @pytest.mark.parametrize(("n", "expected"), [ + pytest.param( + 2,3, + marks=pytest.mark.xfail("sys.version_info > (0, 0, 0)", reason="some bug", strict={strict}), + ), + pytest.param( + 2,3, + marks=[pytest.mark.xfail("sys.version_info > (0, 0, 0)", reason="some bug", strict={strict})], + ), + ]) + def test_increment(n, expected): + assert n + 1 == expected + """.format(strict=strict) + testdir.makepyfile(s) + reprec = testdir.inline_run() + passed, failed = (0, 2) if strict else (2, 0) + reprec.assertoutcome(passed=passed, failed=failed) + + def test_pytest_make_parametrize_id(self, testdir): testdir.makeconftest(""" def pytest_make_parametrize_id(config, val): diff --git a/testing/test_mark.py b/testing/test_mark.py index 3bec1d0d0..9a8c35bf3 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -1,8 +1,9 @@ from __future__ import absolute_import, division, print_function import os +import sys -import py, pytest -from _pytest.mark import MarkGenerator as Mark +import pytest +from _pytest.mark import MarkGenerator as Mark, ParameterSet class TestMark(object): def test_markinfo_repr(self): @@ -10,9 +11,11 @@ class TestMark(object): m = MarkInfo(Mark("hello", (1,2), {})) repr(m) - def test_pytest_exists_in_namespace_all(self): - assert 'mark' in py.test.__all__ - assert 'mark' in pytest.__all__ + @pytest.mark.parametrize('attr', ['mark', 'param']) + @pytest.mark.parametrize('modulename', ['py.test', 'pytest']) + def test_pytest_exists_in_namespace_all(self, attr, modulename): + module = sys.modules[modulename] + assert attr in module.__all__ def test_pytest_mark_notcallable(self): mark = Mark() @@ -415,7 +418,7 @@ class TestFunctional(object): """) items, rec = testdir.inline_genitems(p) for item in items: - print (item, item.keywords) + print(item, item.keywords) assert 'a' in item.keywords def test_mark_decorator_subclass_does_not_propagate_to_base(self, testdir): @@ -739,3 +742,16 @@ class TestKeywordSelection(object): assert_test_is_not_selected("__") assert_test_is_not_selected("()") + + +@pytest.mark.parametrize('argval, expected', [ + (pytest.mark.skip()((1, 2)), + ParameterSet(values=(1, 2), marks=[pytest.mark.skip], id=None)), + (pytest.mark.xfail(pytest.mark.skip()((1, 2))), + ParameterSet(values=(1, 2), + marks=[pytest.mark.xfail, pytest.mark.skip], id=None)), + +]) +def test_parameterset_extractfrom(argval, expected): + extracted = ParameterSet.extract_from(argval) + assert extracted == expected From e8a1b36c82aa987364452b1a40bcc1b82000fda0 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Mon, 20 Mar 2017 10:21:47 +0100 Subject: [PATCH 6/6] add changelog --- CHANGELOG.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e91059b54..37570ed43 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -16,6 +16,9 @@ New Features * ``pytest.raises`` now asserts that the error message matches a text or regex with the ``match`` keyword argument. Thanks `@Kriechi`_ for the PR. +* ``pytest.param`` can be used to declare test parameter sets with marks and test ids. + Thanks `@RonnyPfannschmidt`_ for the PR. + Changes -------