From 89446af51ec723cb628f6d2cf16f10ae2628e25d Mon Sep 17 00:00:00 2001 From: wim glenn Date: Wed, 22 Aug 2018 00:42:10 -0500 Subject: [PATCH 01/16] fixed a bunch of unicode bugs in pytester.py --- changelog/3848.bugfix.rst | 1 + src/_pytest/pytester.py | 26 ++++++++++++-------------- 2 files changed, 13 insertions(+), 14 deletions(-) create mode 100644 changelog/3848.bugfix.rst diff --git a/changelog/3848.bugfix.rst b/changelog/3848.bugfix.rst new file mode 100644 index 000000000..a2456477a --- /dev/null +++ b/changelog/3848.bugfix.rst @@ -0,0 +1 @@ +Fix bugs where unicode arguments could not be passed to testdir.runpytest on Python 2.x diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index b40a9e267..2372ea663 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -22,6 +22,7 @@ import pytest from _pytest.main import Session, EXIT_OK from _pytest.assertion.rewrite import AssertionRewritingHook from _pytest.compat import Path +from _pytest.compat import safe_str IGNORE_PAM = [ # filenames added when obtaining details about the current user u"/var/lib/sss/mc/passwd" @@ -34,7 +35,7 @@ def pytest_addoption(parser): action="store_true", dest="lsof", default=False, - help=("run FD checks if lsof is available"), + help="run FD checks if lsof is available", ) parser.addoption( @@ -273,7 +274,7 @@ class HookRecorder(object): del self.calls[i] return call lines = ["could not find call %r, in:" % (name,)] - lines.extend([" %s" % str(x) for x in self.calls]) + lines.extend([" %s" % x for x in self.calls]) pytest.fail("\n".join(lines)) def getcall(self, name): @@ -885,14 +886,12 @@ class Testdir(object): return self._runpytest_method(*args, **kwargs) def _ensure_basetemp(self, args): - args = [str(x) for x in args] + args = list(args) for x in args: - if str(x).startswith("--basetemp"): - # print("basedtemp exists: %s" %(args,)) + if safe_str(x).startswith("--basetemp"): break else: args.append("--basetemp=%s" % self.tmpdir.dirpath("basetemp")) - # print("added basetemp: %s" %(args,)) return args def parseconfig(self, *args): @@ -1018,7 +1017,7 @@ class Testdir(object): """ env = os.environ.copy() env["PYTHONPATH"] = os.pathsep.join( - filter(None, [str(os.getcwd()), env.get("PYTHONPATH", "")]) + filter(None, [os.getcwd(), env.get("PYTHONPATH", "")]) ) kw["env"] = env @@ -1037,14 +1036,13 @@ class Testdir(object): Returns a :py:class:`RunResult`. """ - return self._run(*cmdargs) - - def _run(self, *cmdargs): - cmdargs = [str(x) for x in cmdargs] + cmdargs = [ + str(arg) if isinstance(arg, py.path.local) else arg for arg in cmdargs + ] p1 = self.tmpdir.join("stdout") p2 = self.tmpdir.join("stderr") - print("running:", " ".join(cmdargs)) - print(" in:", str(py.path.local())) + print("running:", *cmdargs) + print(" in:", py.path.local()) f1 = codecs.open(str(p1), "w", encoding="utf8") f2 = codecs.open(str(p2), "w", encoding="utf8") try: @@ -1076,7 +1074,7 @@ class Testdir(object): print("couldn't print to %s because of encoding" % (fp,)) def _getpytestargs(self): - return (sys.executable, "-mpytest") + return sys.executable, "-mpytest" def runpython(self, script): """Run a python script using sys.executable as interpreter. From 8e2c7b4979e12429641e91acd46b3357ceecddf2 Mon Sep 17 00:00:00 2001 From: wim glenn Date: Wed, 22 Aug 2018 11:00:51 -0500 Subject: [PATCH 02/16] Add a failing testcase for PR #3848 --- testing/test_pytester.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/testing/test_pytester.py b/testing/test_pytester.py index 86dc35796..cab9d8e97 100644 --- a/testing/test_pytester.py +++ b/testing/test_pytester.py @@ -8,7 +8,7 @@ import _pytest.pytester as pytester from _pytest.pytester import HookRecorder from _pytest.pytester import CwdSnapshot, SysModulesSnapshot, SysPathsSnapshot from _pytest.config import PytestPluginManager -from _pytest.main import EXIT_OK, EXIT_TESTSFAILED +from _pytest.main import EXIT_OK, EXIT_TESTSFAILED, EXIT_NOTESTSCOLLECTED def test_make_hook_recorder(testdir): @@ -396,3 +396,8 @@ class TestSysPathsSnapshot(object): def test_testdir_subprocess(testdir): testfile = testdir.makepyfile("def test_one(): pass") assert testdir.runpytest_subprocess(testfile).ret == 0 + + +def test_unicode_args(testdir): + result = testdir.runpytest("-k", u"💩") + assert result.ret == EXIT_NOTESTSCOLLECTED From b08e156b7943db70a94f96943312b912730504aa Mon Sep 17 00:00:00 2001 From: wim glenn Date: Wed, 22 Aug 2018 11:27:36 -0500 Subject: [PATCH 03/16] strip trailing whitespace --- testing/test_pytester.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/test_pytester.py b/testing/test_pytester.py index cab9d8e97..99e62e5bc 100644 --- a/testing/test_pytester.py +++ b/testing/test_pytester.py @@ -397,7 +397,7 @@ def test_testdir_subprocess(testdir): testfile = testdir.makepyfile("def test_one(): pass") assert testdir.runpytest_subprocess(testfile).ret == 0 - + def test_unicode_args(testdir): result = testdir.runpytest("-k", u"💩") assert result.ret == EXIT_NOTESTSCOLLECTED From 917b99e4382626505296e94201dd7f6b253cdd78 Mon Sep 17 00:00:00 2001 From: wim glenn Date: Wed, 22 Aug 2018 13:40:21 -0500 Subject: [PATCH 04/16] More unicode whack-a-mole It seems pytest's very comprehensive CI sniffed out a few other places with similar bugs. Ideally we should find all the places where args are not stringy and solve it at the source, but who knows how many people are relying on the implicit string conversion. See [here](https://github.com/pytest-dev/pytest/blob/master/src/_pytest/config/__init__.py#L160-L166) for one such problem area (args with a single py.path.local instance is converted here, but a list or tuple containing some are not). --- src/_pytest/config/argparsing.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/_pytest/config/argparsing.py b/src/_pytest/config/argparsing.py index 5a4e35b88..3a2a11af4 100644 --- a/src/_pytest/config/argparsing.py +++ b/src/_pytest/config/argparsing.py @@ -2,6 +2,8 @@ import six import warnings import argparse +import py + FILE_OR_DIR = "file_or_dir" @@ -70,7 +72,8 @@ class Parser(object): self.optparser = self._getparser() try_argcomplete(self.optparser) - return self.optparser.parse_args([str(x) for x in args], namespace=namespace) + args = [str(x) if isinstance(x, py.path.local) else x for x in args] + return self.optparser.parse_args(args, namespace=namespace) def _getparser(self): from _pytest._argcomplete import filescompleter @@ -106,7 +109,7 @@ class Parser(object): the remaining arguments unknown at this point. """ optparser = self._getparser() - args = [str(x) for x in args] + args = [str(x) if isinstance(x, py.path.local) else x for x in args] return optparser.parse_known_args(args, namespace=namespace) def addini(self, name, help, type=None, default=None): From cd07c4d4ffad1c9a6fa1c1d8857e7aee1290ccf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20S=C3=BAkup?= Date: Wed, 22 Aug 2018 23:49:40 +0200 Subject: [PATCH 05/16] Use unittest.mock if is only aviable from Python 3.3 is mock part of python standard library in unittest namespace --- .../example_scripts/acceptance/fixture_mock_integration.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/testing/example_scripts/acceptance/fixture_mock_integration.py b/testing/example_scripts/acceptance/fixture_mock_integration.py index 51f46f82c..c005c9193 100644 --- a/testing/example_scripts/acceptance/fixture_mock_integration.py +++ b/testing/example_scripts/acceptance/fixture_mock_integration.py @@ -1,6 +1,9 @@ """Reproduces issue #3774""" -import mock +try: + import mock +except ImportError: + import unittest.mock as mock import pytest From 8804c7333a9bc26956950443b3d75c657762ba28 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 22 Aug 2018 20:06:13 -0300 Subject: [PATCH 06/16] Fix CHANGELOG formatting --- changelog/3848.bugfix.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/3848.bugfix.rst b/changelog/3848.bugfix.rst index a2456477a..4442d7a89 100644 --- a/changelog/3848.bugfix.rst +++ b/changelog/3848.bugfix.rst @@ -1 +1 @@ -Fix bugs where unicode arguments could not be passed to testdir.runpytest on Python 2.x +Fix bugs where unicode arguments could not be passed to ``testdir.runpytest`` on Python 2. From 8bb8b913570f215a9bff505799418ef9034609ec Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Wed, 22 Aug 2018 18:30:42 -0700 Subject: [PATCH 07/16] pyupgrade 1.4: tests --- .../global_testmodule_config/conftest.py | 2 +- doc/en/example/multipython.py | 49 ++++++++++--------- testing/acceptance_test.py | 6 +-- testing/code/test_excinfo.py | 2 +- testing/test_argcomplete.py | 2 +- testing/test_assertrewrite.py | 2 +- testing/test_helpconfig.py | 4 +- testing/test_parseopt.py | 2 +- testing/test_pdb.py | 6 ++- testing/test_terminal.py | 4 +- 10 files changed, 43 insertions(+), 36 deletions(-) diff --git a/doc/en/example/assertion/global_testmodule_config/conftest.py b/doc/en/example/assertion/global_testmodule_config/conftest.py index 4859bea78..3597ec06d 100644 --- a/doc/en/example/assertion/global_testmodule_config/conftest.py +++ b/doc/en/example/assertion/global_testmodule_config/conftest.py @@ -10,4 +10,4 @@ def pytest_runtest_setup(item): return mod = item.getparent(pytest.Module).obj if hasattr(mod, "hello"): - print("mod.hello %r" % (mod.hello,)) + print("mod.hello {!r}".format(mod.hello)) diff --git a/doc/en/example/multipython.py b/doc/en/example/multipython.py index 299833f71..5e12c9b03 100644 --- a/doc/en/example/multipython.py +++ b/doc/en/example/multipython.py @@ -2,9 +2,10 @@ module containing a parametrized tests testing cross-python serialization via the pickle module. """ +import textwrap + import py import pytest -import _pytest._code pythonlist = ["python2.7", "python3.4", "python3.5"] @@ -24,42 +25,44 @@ class Python(object): def __init__(self, version, picklefile): self.pythonpath = py.path.local.sysfind(version) if not self.pythonpath: - pytest.skip("%r not found" % (version,)) + pytest.skip("{!r} not found".format(version)) self.picklefile = picklefile def dumps(self, obj): dumpfile = self.picklefile.dirpath("dump.py") dumpfile.write( - _pytest._code.Source( - """ - import pickle - f = open(%r, 'wb') - s = pickle.dump(%r, f, protocol=2) - f.close() - """ - % (str(self.picklefile), obj) + textwrap.dedent( + """\ + import pickle + f = open({!r}, 'wb') + s = pickle.dump({!r}, f, protocol=2) + f.close() + """.format( + str(self.picklefile), obj + ) ) ) - py.process.cmdexec("%s %s" % (self.pythonpath, dumpfile)) + py.process.cmdexec("{} {}".format(self.pythonpath, dumpfile)) def load_and_is_true(self, expression): loadfile = self.picklefile.dirpath("load.py") loadfile.write( - _pytest._code.Source( - """ - import pickle - f = open(%r, 'rb') - obj = pickle.load(f) - f.close() - res = eval(%r) - if not res: - raise SystemExit(1) - """ - % (str(self.picklefile), expression) + textwrap.dedent( + """\ + import pickle + f = open({!r}, 'rb') + obj = pickle.load(f) + f.close() + res = eval({!r}) + if not res: + raise SystemExit(1) + """.format( + str(self.picklefile), expression + ) ) ) print(loadfile) - py.process.cmdexec("%s %s" % (self.pythonpath, loadfile)) + py.process.cmdexec("{} {}".format(self.pythonpath, loadfile)) @pytest.mark.parametrize("obj", [42, {}, {1: 3}]) diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 5d6baf121..20f748555 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -131,7 +131,7 @@ class TestGeneralUsage(object): p2 = testdir.makefile(".pyc", "123") result = testdir.runpytest(p1, p2) assert result.ret - result.stderr.fnmatch_lines(["*ERROR: not found:*%s" % (p2.basename,)]) + result.stderr.fnmatch_lines(["*ERROR: not found:*{}".format(p2.basename)]) def test_issue486_better_reporting_on_conftest_load_failure(self, testdir): testdir.makepyfile("") @@ -453,7 +453,7 @@ class TestInvocationVariants(object): @pytest.mark.xfail("sys.platform.startswith('java')") def test_pydoc(self, testdir): for name in ("py.test", "pytest"): - result = testdir.runpython_c("import %s;help(%s)" % (name, name)) + result = testdir.runpython_c("import {};help({})".format(name, name)) assert result.ret == 0 s = result.stdout.str() assert "MarkGenerator" in s @@ -836,7 +836,7 @@ class TestDurations(object): if ("test_%s" % x) in line and y in line: break else: - raise AssertionError("not found %s %s" % (x, y)) + raise AssertionError("not found {} {}".format(x, y)) def test_with_deselected(self, testdir): testdir.makepyfile(self.source) diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index fbdaeacf7..350dc26fb 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -269,7 +269,7 @@ class TestTraceback_f_g_h(object): decorator = pytest.importorskip("decorator").decorator def log(f, *k, **kw): - print("%s %s" % (k, kw)) + print("{} {}".format(k, kw)) f(*k, **kw) log = decorator(log) diff --git a/testing/test_argcomplete.py b/testing/test_argcomplete.py index 9e6b711a2..fc2306b00 100644 --- a/testing/test_argcomplete.py +++ b/testing/test_argcomplete.py @@ -11,7 +11,7 @@ def equal_with_bash(prefix, ffc, fc, out=None): res_bash = set(fc(prefix)) retval = set(res) == res_bash if out: - out.write("equal_with_bash %s %s\n" % (retval, res)) + out.write("equal_with_bash {} {}\n".format(retval, res)) if not retval: out.write(" python - bash: %s\n" % (set(res) - res_bash)) out.write(" bash - python: %s\n" % (res_bash - set(res))) diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index 6cec7f003..e3ea3ccfb 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -560,7 +560,7 @@ class TestAssertionRewrite(object): assert getmsg(f) == "assert 42" def my_reprcompare(op, left, right): - return "%s %s %s" % (left, op, right) + return "{} {} {}".format(left, op, right) monkeypatch.setattr(util, "_reprcompare", my_reprcompare) diff --git a/testing/test_helpconfig.py b/testing/test_helpconfig.py index b5424235b..ceea56ccc 100644 --- a/testing/test_helpconfig.py +++ b/testing/test_helpconfig.py @@ -7,7 +7,9 @@ def test_version(testdir, pytestconfig): result = testdir.runpytest("--version") assert result.ret == 0 # p = py.path.local(py.__file__).dirpath() - result.stderr.fnmatch_lines(["*pytest*%s*imported from*" % (pytest.__version__,)]) + result.stderr.fnmatch_lines( + ["*pytest*{}*imported from*".format(pytest.__version__)] + ) if pytestconfig.pluginmanager.list_plugin_distinfo(): result.stderr.fnmatch_lines(["*setuptools registered plugins:", "*at*"]) diff --git a/testing/test_parseopt.py b/testing/test_parseopt.py index 3870ad419..fab288e7f 100644 --- a/testing/test_parseopt.py +++ b/testing/test_parseopt.py @@ -294,7 +294,7 @@ def test_argcomplete(testdir, monkeypatch): script = str(testdir.tmpdir.join("test_argcomplete")) pytest_bin = sys.argv[0] if "pytest" not in os.path.basename(pytest_bin): - pytest.skip("need to be run with pytest executable, not %s" % (pytest_bin,)) + pytest.skip("need to be run with pytest executable, not {}".format(pytest_bin)) with open(str(script), "w") as fp: # redirect output from argcomplete to stdin and stderr is not trivial diff --git a/testing/test_pdb.py b/testing/test_pdb.py index 43a78908c..ed1c49a1a 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -260,7 +260,9 @@ class TestPDB(object): assert False """ ) - child = testdir.spawn_pytest("--show-capture=%s --pdb %s" % (showcapture, p1)) + child = testdir.spawn_pytest( + "--show-capture={} --pdb {}".format(showcapture, p1) + ) if showcapture in ("all", "log"): child.expect("captured log") child.expect("get rekt") @@ -473,7 +475,7 @@ class TestPDB(object): x = 5 """ ) - child = testdir.spawn("%s %s" % (sys.executable, p1)) + child = testdir.spawn("{} {}".format(sys.executable, p1)) child.expect("x = 5") child.sendeof() self.flush(child) diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 88e5287e8..ac29c3d30 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -1118,9 +1118,9 @@ def test_terminal_summary_warnings_are_displayed(testdir): ) def test_summary_stats(exp_line, exp_color, stats_arg): print("Based on stats: %s" % stats_arg) - print('Expect summary: "%s"; with color "%s"' % (exp_line, exp_color)) + print('Expect summary: "{}"; with color "{}"'.format(exp_line, exp_color)) (line, color) = build_summary_stats_line(stats_arg) - print('Actually got: "%s"; with color "%s"' % (line, color)) + print('Actually got: "{}"; with color "{}"'.format(line, color)) assert line == exp_line assert color == exp_color From 0d65783dce057dbfb276d1d7867037a8467dd20f Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Wed, 22 Aug 2018 19:00:43 -0700 Subject: [PATCH 08/16] Fix unicode errors when changing to .format(...) --- src/_pytest/_code/_py2traceback.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/_pytest/_code/_py2traceback.py b/src/_pytest/_code/_py2traceback.py index 2dd100c33..cceed40ed 100644 --- a/src/_pytest/_code/_py2traceback.py +++ b/src/_pytest/_code/_py2traceback.py @@ -2,7 +2,7 @@ # CHANGES: # - some_str is replaced, trying to create unicode strings # -from __future__ import absolute_import, division, print_function +from __future__ import absolute_import, division, print_function, unicode_literals import types from six import text_type @@ -51,17 +51,17 @@ def format_exception_only(etype, value): pass else: filename = filename or "" - lines.append(' File "%s", line %d\n' % (filename, lineno)) + lines.append(' File "{}", line {}\n'.format(filename, lineno)) if badline is not None: if isinstance(badline, bytes): # python 2 only badline = badline.decode("utf-8", "replace") - lines.append(u" %s\n" % badline.strip()) + lines.append(" {}\n".format(badline.strip())) if offset is not None: caretspace = badline.rstrip("\n")[:offset].lstrip() # non-space whitespace (likes tabs) must be kept for alignment caretspace = ((c.isspace() and c or " ") for c in caretspace) # only three spaces to account for offset1 == pos 0 - lines.append(" %s^\n" % "".join(caretspace)) + lines.append(" {}^\n".format("".join(caretspace))) value = msg lines.append(_format_final_exc_line(stype, value)) @@ -72,9 +72,9 @@ def _format_final_exc_line(etype, value): """Return a list of a single line -- normal case for format_exception_only""" valuestr = _some_str(value) if value is None or not valuestr: - line = "%s\n" % etype + line = "{}\n".format(etype) else: - line = "%s: %s\n" % (etype, valuestr) + line = "{}: {}\n".format(etype, valuestr) return line @@ -83,7 +83,7 @@ def _some_str(value): return text_type(value) except Exception: try: - return str(value) + return bytes(value).decode("UTF-8", "replace") except Exception: pass - return "" % type(value).__name__ + return "".format(type(value).__name__) From 0fc4a806e517ed64717df107a9dca3995326a06f Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Wed, 22 Aug 2018 19:21:00 -0700 Subject: [PATCH 09/16] py.builtins._totext -> string literals or six.text_type --- src/_pytest/outcomes.py | 3 +-- src/_pytest/python.py | 2 +- testing/code/test_code.py | 5 ++--- testing/code/test_excinfo.py | 7 ++++--- testing/python/metafunc.py | 8 ++------ testing/test_assertion.py | 12 ++++++------ testing/test_assertrewrite.py | 6 ++---- testing/test_capture.py | 7 ++++--- testing/test_junitxml.py | 2 +- 9 files changed, 23 insertions(+), 29 deletions(-) diff --git a/src/_pytest/outcomes.py b/src/_pytest/outcomes.py index 63b8453b7..0a66fcab4 100644 --- a/src/_pytest/outcomes.py +++ b/src/_pytest/outcomes.py @@ -3,7 +3,6 @@ exception classes and constants handling test outcomes as well as functions creating them """ from __future__ import absolute_import, division, print_function -import py import sys @@ -21,7 +20,7 @@ class OutcomeException(BaseException): if self.msg: val = self.msg if isinstance(val, bytes): - val = py._builtin._totext(val, errors="replace") + val = val.decode("UTF-8", errors="replace") return val return "<%s instance>" % (self.__class__.__name__,) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index e269b3bb4..45ef3be61 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -741,7 +741,7 @@ class FunctionMixin(PyobjMixin): def _repr_failure_py(self, excinfo, style="long"): if excinfo.errisinstance(fail.Exception): if not excinfo.value.pytrace: - return py._builtin._totext(excinfo.value) + return six.text_type(excinfo.value) return super(FunctionMixin, self)._repr_failure_py(excinfo, style=style) def repr_failure(self, excinfo, outerr=None): diff --git a/testing/code/test_code.py b/testing/code/test_code.py index e098f136d..27916d64f 100644 --- a/testing/code/test_code.py +++ b/testing/code/test_code.py @@ -3,7 +3,6 @@ from __future__ import absolute_import, division, print_function import sys import _pytest._code -import py import pytest from test_excinfo import TWMock from six import text_type @@ -83,7 +82,7 @@ def test_code_from_func(): def test_unicode_handling(): - value = py.builtin._totext("\xc4\x85\xc4\x87\n", "utf-8").encode("utf8") + value = u"ąć".encode("UTF-8") def f(): raise Exception(value) @@ -96,7 +95,7 @@ def test_unicode_handling(): @pytest.mark.skipif(sys.version_info[0] >= 3, reason="python 2 only issue") def test_unicode_handling_syntax_error(): - value = py.builtin._totext("\xc4\x85\xc4\x87\n", "utf-8").encode("utf8") + value = u"ąć".encode("UTF-8") def f(): raise SyntaxError("invalid syntax", (None, 1, 3, value)) diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index fbdaeacf7..f4fe9bd53 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -8,6 +8,7 @@ import textwrap import _pytest import py import pytest +import six from _pytest._code.code import ( ExceptionInfo, FormattedExcinfo, @@ -884,10 +885,10 @@ raise ValueError() class MyRepr(TerminalRepr): def toterminal(self, tw): - tw.line(py.builtin._totext("я", "utf-8")) + tw.line(u"я") - x = py.builtin._totext(MyRepr()) - assert x == py.builtin._totext("я", "utf-8") + x = six.text_type(MyRepr()) + assert x == u"я" def test_toterminal_long(self, importasmod): mod = importasmod( diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 7ef34678c..db1cd77c5 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -3,7 +3,6 @@ import re import sys import attr import _pytest._code -import py import pytest from _pytest import python, fixtures @@ -295,9 +294,7 @@ class TestMetafunc(object): ) assert result == ["a0-1.0", "a1-b1"] # unicode mixing, issue250 - result = idmaker( - (py.builtin._totext("a"), "b"), [pytest.param({}, b"\xc3\xb4")] - ) + result = idmaker((u"a", "b"), [pytest.param({}, b"\xc3\xb4")]) assert result == ["a0-\\xc3\\xb4"] def test_idmaker_with_bytes_regex(self): @@ -309,7 +306,6 @@ class TestMetafunc(object): def test_idmaker_native_strings(self): from _pytest.python import idmaker - totext = py.builtin._totext result = idmaker( ("a", "b"), [ @@ -324,7 +320,7 @@ class TestMetafunc(object): pytest.param({7}, set("seven")), pytest.param(tuple("eight"), (8, -8, 8)), pytest.param(b"\xc3\xb4", b"name"), - pytest.param(b"\xc3\xb4", totext("other")), + pytest.param(b"\xc3\xb4", u"other"), ], ) assert result == [ diff --git a/testing/test_assertion.py b/testing/test_assertion.py index 23763f078..501477810 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -509,12 +509,12 @@ class TestAssert_reprcompare(object): assert "raised in repr()" not in expl def test_unicode(self): - left = py.builtin._totext("£€", "utf-8") - right = py.builtin._totext("£", "utf-8") + left = u"£€" + right = u"£" expl = callequal(left, right) - assert expl[0] == py.builtin._totext("'£€' == '£'", "utf-8") - assert expl[1] == py.builtin._totext("- £€", "utf-8") - assert expl[2] == py.builtin._totext("+ £", "utf-8") + assert expl[0] == u"'£€' == '£'" + assert expl[1] == u"- £€" + assert expl[2] == u"+ £" def test_nonascii_text(self): """ @@ -542,7 +542,7 @@ class TestAssert_reprcompare(object): expl = callequal(left, right) for line in expl: assert isinstance(line, py.builtin.text) - msg = py.builtin._totext("\n").join(expl) + msg = u"\n".join(expl) assert msg diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index 6cec7f003..c5cabf79b 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -654,12 +654,10 @@ class TestRewriteOnImport(object): def test_readonly(self, testdir): sub = testdir.mkdir("testing") sub.join("test_readonly.py").write( - py.builtin._totext( - """ + b""" def test_rewritten(): assert "@py_builtins" in globals() - """ - ).encode("utf-8"), + """, "wb", ) old_mode = sub.stat().mode diff --git a/testing/test_capture.py b/testing/test_capture.py index 93eaaa85c..475f61924 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from __future__ import absolute_import, division, print_function # note: py.io capture tests where copied from @@ -1083,9 +1084,9 @@ class TestStdCapture(object): def test_capturing_readouterr_unicode(self): with self.getcapture() as cap: - print("hx\xc4\x85\xc4\x87") + print("hxąć") out, err = cap.readouterr() - assert out == py.builtin._totext("hx\xc4\x85\xc4\x87\n", "utf8") + assert out == u"hxąć\n" @pytest.mark.skipif( "sys.version_info >= (3,)", reason="text output different for bytes on python3" @@ -1095,7 +1096,7 @@ class TestStdCapture(object): # triggered an internal error in pytest print("\xa6") out, err = cap.readouterr() - assert out == py.builtin._totext("\ufffd\n", "unicode-escape") + assert out == u"\ufffd\n" def test_reset_twice_error(self): with self.getcapture() as cap: diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index ae2b4ea76..0678d59e8 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -941,7 +941,7 @@ def test_double_colon_split_method_issue469(testdir): def test_unicode_issue368(testdir): path = testdir.tmpdir.join("test.xml") log = LogXML(str(path), None) - ustr = py.builtin._totext("ВНИ!", "utf-8") + ustr = u"ВНИ!" class Report(BaseReport): longrepr = ustr From c2cd3378864ba5e3756413e41042b30742030f04 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Wed, 22 Aug 2018 19:24:33 -0700 Subject: [PATCH 10/16] py.builtin.exec_ => six.exec_ --- bench/empty.py | 4 ++-- doc/en/example/assertion/failure_demo.py | 4 ++-- src/_pytest/_code/code.py | 3 ++- src/_pytest/assertion/rewrite.py | 2 +- src/_pytest/python_api.py | 6 +++--- src/_pytest/recwarn.py | 6 +++--- testing/code/test_source.py | 6 +++--- testing/test_assertrewrite.py | 3 ++- 8 files changed, 18 insertions(+), 16 deletions(-) diff --git a/bench/empty.py b/bench/empty.py index b90319936..338ebf138 100644 --- a/bench/empty.py +++ b/bench/empty.py @@ -1,4 +1,4 @@ -import py +import six for i in range(1000): - py.builtin.exec_("def test_func_%d(): pass" % i) + six.exec_("def test_func_%d(): pass" % i) diff --git a/doc/en/example/assertion/failure_demo.py b/doc/en/example/assertion/failure_demo.py index 0a104578c..def6ae2ef 100644 --- a/doc/en/example/assertion/failure_demo.py +++ b/doc/en/example/assertion/failure_demo.py @@ -1,6 +1,6 @@ from pytest import raises import _pytest._code -import py +import six def otherfunc(a, b): @@ -177,7 +177,7 @@ def test_dynamic_compile_shows_nicely(): name = "abc-123" module = imp.new_module(name) code = _pytest._code.compile(src, name, "exec") - py.builtin.exec_(code, module.__dict__) + six.exec_(code, module.__dict__) sys.modules[name] = module module.foo() diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index d6c5cd90e..2662e4320 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -11,6 +11,7 @@ from weakref import ref from _pytest.compat import _PY2, _PY3, PY35, safe_str from six import text_type import py +import six builtin_repr = repr @@ -128,7 +129,7 @@ class Frame(object): """ f_locals = self.f_locals.copy() f_locals.update(vars) - py.builtin.exec_(code, self.f_globals, f_locals) + six.exec_(code, self.f_globals, f_locals) def repr(self, object): """ return a 'safe' (non-recursive, one-line) string repr for 'object' diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index 4f96b9e8c..952cfb594 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -223,7 +223,7 @@ class AssertionRewritingHook(object): mod.__loader__ = self # Normally, this attribute is 3.4+ mod.__spec__ = spec_from_file_location(name, co.co_filename, loader=self) - py.builtin.exec_(co, mod.__dict__) + six.exec_(co, mod.__dict__) except: # noqa if name in sys.modules: del sys.modules[name] diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index abc4d1e17..6cd17de25 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -4,7 +4,7 @@ import sys from numbers import Number from decimal import Decimal -import py +import six from six.moves import zip, filterfalse from more_itertools.more import always_iterable @@ -680,8 +680,8 @@ def raises(expected_exception, *args, **kwargs): # print "raises frame scope: %r" % frame.f_locals try: code = _pytest._code.Source(code).compile() - py.builtin.exec_(code, frame.f_globals, loc) - # XXX didn'T mean f_globals == f_locals something special? + six.exec_(code, frame.f_globals, loc) + # XXX didn't mean f_globals == f_locals something special? # this is destroyed here ... except expected_exception: return _pytest._code.ExceptionInfo() diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index 177757f27..0eee4c841 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -4,11 +4,11 @@ from __future__ import absolute_import, division, print_function import inspect import _pytest._code -import py +import re import sys import warnings -import re +import six from _pytest.fixtures import yield_fixture from _pytest.outcomes import fail @@ -130,7 +130,7 @@ def warns(expected_warning, *args, **kwargs): with WarningsChecker(expected_warning, match_expr=match_expr): code = _pytest._code.Source(code).compile() - py.builtin.exec_(code, frame.f_globals, loc) + six.exec_(code, frame.f_globals, loc) else: func = args[0] with WarningsChecker(expected_warning, match_expr=match_expr): diff --git a/testing/code/test_source.py b/testing/code/test_source.py index 995fabcf4..d7e8fe422 100644 --- a/testing/code/test_source.py +++ b/testing/code/test_source.py @@ -6,8 +6,8 @@ import inspect import sys import _pytest._code -import py import pytest +import six from _pytest._code import Source from _pytest._code.source import ast @@ -323,7 +323,7 @@ class TestSourceParsingAndCompiling(object): def test_compile_and_getsource(self): co = self.source.compile() - py.builtin.exec_(co, globals()) + six.exec_(co, globals()) f(7) excinfo = pytest.raises(AssertionError, "f(6)") frame = excinfo.traceback[-1].frame @@ -392,7 +392,7 @@ def test_getfuncsource_dynamic(): def g(): pass """ co = _pytest._code.compile(source) - py.builtin.exec_(co, globals()) + six.exec_(co, globals()) assert str(_pytest._code.Source(f)).strip() == "def f():\n raise ValueError" assert str(_pytest._code.Source(g)).strip() == "def g(): pass" diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index c5cabf79b..1ba3f9473 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -9,6 +9,7 @@ import textwrap import zipfile import py import pytest +import six import _pytest._code from _pytest.assertion import util @@ -49,7 +50,7 @@ def getmsg(f, extra_ns=None, must_pass=False): ns = {} if extra_ns is not None: ns.update(extra_ns) - py.builtin.exec_(code, ns) + six.exec_(code, ns) func = ns[f.__name__] try: func() From dccac69d82b5677bcd8ec7775a12b0d13f6e69ac Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Wed, 22 Aug 2018 19:26:11 -0700 Subject: [PATCH 11/16] py.builtin.text -> six.text_type --- testing/test_assertion.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testing/test_assertion.py b/testing/test_assertion.py index 501477810..a9e624713 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -6,6 +6,7 @@ import textwrap import _pytest.assertion as plugin import py import pytest +import six from _pytest.assertion import util from _pytest.assertion import truncate @@ -541,7 +542,7 @@ class TestAssert_reprcompare(object): right = bytes(right, "utf-8") expl = callequal(left, right) for line in expl: - assert isinstance(line, py.builtin.text) + assert isinstance(line, six.text_type) msg = u"\n".join(expl) assert msg From 7099ea9bb0a4fa2b03d3793729bf7cf3254aa04f Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Wed, 22 Aug 2018 19:28:42 -0700 Subject: [PATCH 12/16] py.builtin._reraise -> six.reraise --- src/_pytest/fixtures.py | 4 ++-- src/_pytest/runner.py | 8 ++++---- testing/code/test_excinfo.py | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index cc8921e65..c12691caa 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -858,7 +858,7 @@ class FixtureDef(object): if exceptions: e = exceptions[0] del exceptions # ensure we don't keep all frames alive because of the traceback - py.builtin._reraise(*e) + six.reraise(*e) finally: hook = self._fixturemanager.session.gethookproxy(request.node.fspath) @@ -885,7 +885,7 @@ class FixtureDef(object): result, cache_key, err = cached_result if my_cache_key == cache_key: if err is not None: - py.builtin._reraise(*err) + six.reraise(*err) else: return result # we have a previous but differently parametrized fixture instance diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index 5739bbef0..1ba9ff310 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -6,7 +6,7 @@ import os import sys from time import time -import py +import six from _pytest._code.code import ExceptionInfo from _pytest.outcomes import skip, Skipped, TEST_OUTCOME @@ -317,7 +317,7 @@ class SetupState(object): if exc is None: exc = sys.exc_info() if exc: - py.builtin._reraise(*exc) + six.reraise(*exc) def _teardown_with_finalization(self, colitem): self._callfinalizers(colitem) @@ -352,7 +352,7 @@ class SetupState(object): if exc is None: exc = sys.exc_info() if exc: - py.builtin._reraise(*exc) + six.reraise(*exc) def prepare(self, colitem): """ setup objects along the collector chain to the test-method @@ -363,7 +363,7 @@ class SetupState(object): # check if the last collection node has raised an error for col in self.stack: if hasattr(col, "_prepare_exc"): - py.builtin._reraise(*col._prepare_exc) + six.reraise(*col._prepare_exc) for col in needed_collectors[len(self.stack) :]: self.stack.append(col) try: diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index f4fe9bd53..f8bf25b68 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -252,7 +252,7 @@ class TestTraceback_f_g_h(object): import sys exc, val, tb = sys.exc_info() - py.builtin._reraise(exc, val, tb) + six.reraise(exc, val, tb) def f(n): try: From 85482d575e6a868c829fe6f9b9d9fe3a6cab4a53 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Thu, 23 Aug 2018 09:06:17 -0700 Subject: [PATCH 13/16] Replace Source with dedent where possible --- testing/acceptance_test.py | 20 +- testing/code/test_code.py | 9 +- testing/code/test_excinfo.py | 8 +- testing/python/collect.py | 137 +++++------ testing/python/fixture.py | 438 +++++++++++++++++----------------- testing/python/metafunc.py | 18 +- testing/test_assertrewrite.py | 14 +- testing/test_cacheprovider.py | 74 +++--- testing/test_capture.py | 95 ++++---- testing/test_collection.py | 14 +- testing/test_config.py | 36 +-- testing/test_conftest.py | 195 ++++++++------- testing/test_doctest.py | 48 ++-- testing/test_terminal.py | 24 +- 14 files changed, 562 insertions(+), 568 deletions(-) diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 5d6baf121..52879f15c 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -2,11 +2,11 @@ from __future__ import absolute_import, division, print_function import os import sys +import textwrap import types import six -import _pytest._code import py import pytest from _pytest.main import EXIT_NOTESTSCOLLECTED, EXIT_USAGEERROR @@ -201,16 +201,16 @@ class TestGeneralUsage(object): testdir.tmpdir.join("py").mksymlinkto(py._pydir) p = testdir.tmpdir.join("main.py") p.write( - _pytest._code.Source( + textwrap.dedent( + """\ + import sys, os + sys.path.insert(0, '') + import py + print(py.__file__) + print(py.__path__) + os.chdir(os.path.dirname(os.getcwd())) + print(py.log) """ - import sys, os - sys.path.insert(0, '') - import py - print (py.__file__) - print (py.__path__) - os.chdir(os.path.dirname(os.getcwd())) - print (py.log) - """ ) ) result = testdir.runpython(p) diff --git a/testing/code/test_code.py b/testing/code/test_code.py index 27916d64f..f7a8a4dbd 100644 --- a/testing/code/test_code.py +++ b/testing/code/test_code.py @@ -4,6 +4,7 @@ import sys import _pytest._code import pytest +import mock from test_excinfo import TWMock from six import text_type @@ -67,12 +68,8 @@ def test_getstatement_empty_fullsource(): f = func() f = _pytest._code.Frame(f) - prop = f.code.__class__.fullsource - try: - f.code.__class__.fullsource = None - assert f.statement == _pytest._code.Source("") - finally: - f.code.__class__.fullsource = prop + with mock.patch.object(f.code.__class__, "fullsource", None): + assert f.statement == "" def test_code_from_func(): diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index f8bf25b68..52cad6a3e 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -149,7 +149,7 @@ class TestTraceback_f_g_h(object): except somenoname: pass xyz() - """ + """ ) try: exec(source.compile()) @@ -426,7 +426,7 @@ class TestFormattedExcinfo(object): @pytest.fixture def importasmod(self, request): def importasmod(source): - source = _pytest._code.Source(source) + source = textwrap.dedent(source) tmpdir = request.getfixturevalue("tmpdir") modpath = tmpdir.join("mod.py") tmpdir.ensure("__init__.py") @@ -450,10 +450,10 @@ class TestFormattedExcinfo(object): def test_repr_source(self): pr = FormattedExcinfo() source = _pytest._code.Source( - """ + """\ def f(x): pass - """ + """ ).strip() pr.flow_marker = "|" lines = pr.get_source(source, 0) diff --git a/testing/python/collect.py b/testing/python/collect.py index c040cc09e..b3b19802a 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import os import sys -from textwrap import dedent +import textwrap import _pytest._code import pytest @@ -47,13 +47,14 @@ class TestModule(object): p = root2.join("test_x456.py") monkeypatch.syspath_prepend(str(root1)) p.write( - dedent( + textwrap.dedent( """\ - import x456 - def test(): - assert x456.__file__.startswith(%r) - """ - % str(root2) + import x456 + def test(): + assert x456.__file__.startswith({!r}) + """.format( + str(root2) + ) ) ) with root2.as_cwd(): @@ -929,23 +930,23 @@ class TestConftestCustomization(object): def test_customized_pymakemodule_issue205_subdir(self, testdir): b = testdir.mkdir("a").mkdir("b") b.join("conftest.py").write( - _pytest._code.Source( + textwrap.dedent( + """\ + import pytest + @pytest.hookimpl(hookwrapper=True) + def pytest_pycollect_makemodule(): + outcome = yield + mod = outcome.get_result() + mod.obj.hello = "world" """ - import pytest - @pytest.hookimpl(hookwrapper=True) - def pytest_pycollect_makemodule(): - outcome = yield - mod = outcome.get_result() - mod.obj.hello = "world" - """ ) ) b.join("test_module.py").write( - _pytest._code.Source( + textwrap.dedent( + """\ + def test_hello(): + assert hello == "world" """ - def test_hello(): - assert hello == "world" - """ ) ) reprec = testdir.inline_run() @@ -954,31 +955,31 @@ class TestConftestCustomization(object): def test_customized_pymakeitem(self, testdir): b = testdir.mkdir("a").mkdir("b") b.join("conftest.py").write( - _pytest._code.Source( + textwrap.dedent( + """\ + import pytest + @pytest.hookimpl(hookwrapper=True) + def pytest_pycollect_makeitem(): + outcome = yield + if outcome.excinfo is None: + result = outcome.get_result() + if result: + for func in result: + func._some123 = "world" """ - import pytest - @pytest.hookimpl(hookwrapper=True) - def pytest_pycollect_makeitem(): - outcome = yield - if outcome.excinfo is None: - result = outcome.get_result() - if result: - for func in result: - func._some123 = "world" - """ ) ) b.join("test_module.py").write( - _pytest._code.Source( - """ - import pytest + textwrap.dedent( + """\ + import pytest - @pytest.fixture() - def obj(request): - return request.node._some123 - def test_hello(obj): - assert obj == "world" - """ + @pytest.fixture() + def obj(request): + return request.node._some123 + def test_hello(obj): + assert obj == "world" + """ ) ) reprec = testdir.inline_run() @@ -1033,7 +1034,7 @@ class TestConftestCustomization(object): ) testdir.makefile( ".narf", - """ + """\ def test_something(): assert 1 + 1 == 2""", ) @@ -1046,29 +1047,29 @@ def test_setup_only_available_in_subdir(testdir): sub1 = testdir.mkpydir("sub1") sub2 = testdir.mkpydir("sub2") sub1.join("conftest.py").write( - _pytest._code.Source( + textwrap.dedent( + """\ + import pytest + def pytest_runtest_setup(item): + assert item.fspath.purebasename == "test_in_sub1" + def pytest_runtest_call(item): + assert item.fspath.purebasename == "test_in_sub1" + def pytest_runtest_teardown(item): + assert item.fspath.purebasename == "test_in_sub1" """ - import pytest - def pytest_runtest_setup(item): - assert item.fspath.purebasename == "test_in_sub1" - def pytest_runtest_call(item): - assert item.fspath.purebasename == "test_in_sub1" - def pytest_runtest_teardown(item): - assert item.fspath.purebasename == "test_in_sub1" - """ ) ) sub2.join("conftest.py").write( - _pytest._code.Source( + textwrap.dedent( + """\ + import pytest + def pytest_runtest_setup(item): + assert item.fspath.purebasename == "test_in_sub2" + def pytest_runtest_call(item): + assert item.fspath.purebasename == "test_in_sub2" + def pytest_runtest_teardown(item): + assert item.fspath.purebasename == "test_in_sub2" """ - import pytest - def pytest_runtest_setup(item): - assert item.fspath.purebasename == "test_in_sub2" - def pytest_runtest_call(item): - assert item.fspath.purebasename == "test_in_sub2" - def pytest_runtest_teardown(item): - assert item.fspath.purebasename == "test_in_sub2" - """ ) ) sub1.join("test_in_sub1.py").write("def test_1(): pass") @@ -1547,12 +1548,12 @@ def test_skip_duplicates_by_default(testdir): a = testdir.mkdir("a") fh = a.join("test_a.py") fh.write( - _pytest._code.Source( + textwrap.dedent( + """\ + import pytest + def test_real(): + pass """ - import pytest - def test_real(): - pass - """ ) ) result = testdir.runpytest(a.strpath, a.strpath) @@ -1567,12 +1568,12 @@ def test_keep_duplicates(testdir): a = testdir.mkdir("a") fh = a.join("test_a.py") fh.write( - _pytest._code.Source( + textwrap.dedent( + """\ + import pytest + def test_real(): + pass """ - import pytest - def test_real(): - pass - """ ) ) result = testdir.runpytest("--keep-duplicates", a.strpath, a.strpath) diff --git a/testing/python/fixture.py b/testing/python/fixture.py index bbfcf775b..47be7800c 100644 --- a/testing/python/fixture.py +++ b/testing/python/fixture.py @@ -1,6 +1,5 @@ -from textwrap import dedent +import textwrap -import _pytest._code import pytest from _pytest.pytester import get_public_names from _pytest.fixtures import FixtureLookupError, FixtureRequest @@ -208,23 +207,23 @@ class TestFillFixtures(object): ) subdir = testdir.mkpydir("subdir") subdir.join("conftest.py").write( - _pytest._code.Source( - """ - import pytest + textwrap.dedent( + """\ + import pytest - @pytest.fixture - def spam(): - return 'spam' - """ + @pytest.fixture + def spam(): + return 'spam' + """ ) ) testfile = subdir.join("test_spam.py") testfile.write( - _pytest._code.Source( + textwrap.dedent( + """\ + def test_spam(spam): + assert spam == "spam" """ - def test_spam(spam): - assert spam == "spam" - """ ) ) result = testdir.runpytest() @@ -276,26 +275,26 @@ class TestFillFixtures(object): ) subdir = testdir.mkpydir("subdir") subdir.join("conftest.py").write( - _pytest._code.Source( - """ - import pytest + textwrap.dedent( + """\ + import pytest - @pytest.fixture(params=[1, 2, 3]) - def spam(request): - return request.param - """ + @pytest.fixture(params=[1, 2, 3]) + def spam(request): + return request.param + """ ) ) testfile = subdir.join("test_spam.py") testfile.write( - _pytest._code.Source( - """ - params = {'spam': 1} + textwrap.dedent( + """\ + params = {'spam': 1} - def test_spam(spam): - assert spam == params['spam'] - params['spam'] += 1 - """ + def test_spam(spam): + assert spam == params['spam'] + params['spam'] += 1 + """ ) ) result = testdir.runpytest() @@ -320,26 +319,26 @@ class TestFillFixtures(object): ) subdir = testdir.mkpydir("subdir") subdir.join("conftest.py").write( - _pytest._code.Source( - """ - import pytest + textwrap.dedent( + """\ + import pytest - @pytest.fixture(params=[1, 2, 3]) - def spam(request): - return request.param - """ + @pytest.fixture(params=[1, 2, 3]) + def spam(request): + return request.param + """ ) ) testfile = subdir.join("test_spam.py") testfile.write( - _pytest._code.Source( - """ - params = {'spam': 1} + textwrap.dedent( + """\ + params = {'spam': 1} - def test_spam(spam): - assert spam == params['spam'] - params['spam'] += 1 - """ + def test_spam(spam): + assert spam == params['spam'] + params['spam'] += 1 + """ ) ) result = testdir.runpytest() @@ -807,13 +806,13 @@ class TestRequestBasic(object): # this tests that normalization of nodeids takes place b = testdir.mkdir("tests").mkdir("unit") b.join("conftest.py").write( - _pytest._code.Source( + textwrap.dedent( + """\ + import pytest + @pytest.fixture + def arg1(): + pass """ - import pytest - @pytest.fixture - def arg1(): - pass - """ ) ) p = b.join("test_module.py") @@ -1484,41 +1483,41 @@ class TestFixtureManagerParseFactories(object): runner = testdir.mkdir("runner") package = testdir.mkdir("package") package.join("conftest.py").write( - dedent( + textwrap.dedent( """\ import pytest @pytest.fixture def one(): return 1 - """ + """ ) ) package.join("test_x.py").write( - dedent( + textwrap.dedent( """\ - def test_x(one): - assert one == 1 - """ + def test_x(one): + assert one == 1 + """ ) ) sub = package.mkdir("sub") sub.join("__init__.py").ensure() sub.join("conftest.py").write( - dedent( + textwrap.dedent( """\ - import pytest - @pytest.fixture - def one(): - return 2 - """ + import pytest + @pytest.fixture + def one(): + return 2 + """ ) ) sub.join("test_y.py").write( - dedent( + textwrap.dedent( """\ - def test_x(one): - assert one == 2 - """ + def test_x(one): + assert one == 2 + """ ) ) reprec = testdir.inline_run() @@ -1535,44 +1534,44 @@ class TestFixtureManagerParseFactories(object): ) package = testdir.mkdir("package") package.join("__init__.py").write( - dedent( + textwrap.dedent( """\ - from .. import values - def setup_module(): - values.append("package") - def teardown_module(): - values[:] = [] - """ + from .. import values + def setup_module(): + values.append("package") + def teardown_module(): + values[:] = [] + """ ) ) package.join("test_x.py").write( - dedent( + textwrap.dedent( """\ - from .. import values - def test_x(): - assert values == ["package"] - """ + from .. import values + def test_x(): + assert values == ["package"] + """ ) ) package = testdir.mkdir("package2") package.join("__init__.py").write( - dedent( + textwrap.dedent( """\ - from .. import values - def setup_module(): - values.append("package2") - def teardown_module(): - values[:] = [] - """ + from .. import values + def setup_module(): + values.append("package2") + def teardown_module(): + values[:] = [] + """ ) ) package.join("test_x.py").write( - dedent( + textwrap.dedent( """\ - from .. import values - def test_x(): - assert values == ["package2"] - """ + from .. import values + def test_x(): + assert values == ["package2"] + """ ) ) reprec = testdir.inline_run() @@ -1587,32 +1586,32 @@ class TestFixtureManagerParseFactories(object): package = testdir.mkdir("package") package.join("__init__.py").write("") package.join("conftest.py").write( - dedent( + textwrap.dedent( """\ - import pytest - from .. import values - @pytest.fixture(scope="package") - def one(): - values.append("package") - yield values - values.pop() - @pytest.fixture(scope="package", autouse=True) - def two(): - values.append("package-auto") - yield values - values.pop() - """ + import pytest + from .. import values + @pytest.fixture(scope="package") + def one(): + values.append("package") + yield values + values.pop() + @pytest.fixture(scope="package", autouse=True) + def two(): + values.append("package-auto") + yield values + values.pop() + """ ) ) package.join("test_x.py").write( - dedent( + textwrap.dedent( """\ - from .. import values - def test_package_autouse(): - assert values == ["package-auto"] - def test_package(one): - assert values == ["package-auto", "package"] - """ + from .. import values + def test_package_autouse(): + assert values == ["package-auto"] + def test_package(one): + assert values == ["package-auto", "package"] + """ ) ) reprec = testdir.inline_run() @@ -1804,24 +1803,24 @@ class TestAutouseManagement(object): def test_autouse_conftest_mid_directory(self, testdir): pkgdir = testdir.mkpydir("xyz123") pkgdir.join("conftest.py").write( - _pytest._code.Source( + textwrap.dedent( + """\ + import pytest + @pytest.fixture(autouse=True) + def app(): + import sys + sys._myapp = "hello" """ - import pytest - @pytest.fixture(autouse=True) - def app(): - import sys - sys._myapp = "hello" - """ ) ) t = pkgdir.ensure("tests", "test_app.py") t.write( - _pytest._code.Source( + textwrap.dedent( + """\ + import sys + def test_app(): + assert sys._myapp == "hello" """ - import sys - def test_app(): - assert sys._myapp == "hello" - """ ) ) reprec = testdir.inline_run("-s") @@ -2715,17 +2714,17 @@ class TestFixtureMarker(object): ) b = testdir.mkdir("subdir") b.join("test_overridden_fixture_finalizer.py").write( - dedent( - """ - import pytest - @pytest.fixture - def browser(browser): - browser['visited'] = True - return browser + textwrap.dedent( + """\ + import pytest + @pytest.fixture + def browser(browser): + browser['visited'] = True + return browser - def test_browser(browser): - assert browser['visited'] is True - """ + def test_browser(browser): + assert browser['visited'] is True + """ ) ) reprec = testdir.runpytest("-s") @@ -3217,120 +3216,119 @@ class TestShowFixtures(object): def test_show_fixtures_trimmed_doc(self, testdir): p = testdir.makepyfile( - dedent( + textwrap.dedent( + '''\ + import pytest + @pytest.fixture + def arg1(): + """ + line1 + line2 + + """ + @pytest.fixture + def arg2(): + """ + line1 + line2 + + """ ''' - import pytest - @pytest.fixture - def arg1(): - """ - line1 - line2 - - """ - @pytest.fixture - def arg2(): - """ - line1 - line2 - - """ - ''' ) ) result = testdir.runpytest("--fixtures", p) result.stdout.fnmatch_lines( - dedent( + textwrap.dedent( + """\ + * fixtures defined from test_show_fixtures_trimmed_doc * + arg2 + line1 + line2 + arg1 + line1 + line2 """ - * fixtures defined from test_show_fixtures_trimmed_doc * - arg2 - line1 - line2 - arg1 - line1 - line2 - - """ ) ) def test_show_fixtures_indented_doc(self, testdir): p = testdir.makepyfile( - dedent( + textwrap.dedent( + '''\ + import pytest + @pytest.fixture + def fixture1(): + """ + line1 + indented line + """ ''' - import pytest - @pytest.fixture - def fixture1(): - """ - line1 - indented line - """ - ''' ) ) result = testdir.runpytest("--fixtures", p) result.stdout.fnmatch_lines( - dedent( + textwrap.dedent( + """\ + * fixtures defined from test_show_fixtures_indented_doc * + fixture1 + line1 + indented line """ - * fixtures defined from test_show_fixtures_indented_doc * - fixture1 - line1 - indented line - """ ) ) def test_show_fixtures_indented_doc_first_line_unindented(self, testdir): p = testdir.makepyfile( - dedent( + textwrap.dedent( + '''\ + import pytest + @pytest.fixture + def fixture1(): + """line1 + line2 + indented line + """ ''' - import pytest - @pytest.fixture - def fixture1(): - """line1 - line2 - indented line - """ - ''' ) ) result = testdir.runpytest("--fixtures", p) result.stdout.fnmatch_lines( - dedent( + textwrap.dedent( + """\ + * fixtures defined from test_show_fixtures_indented_doc_first_line_unindented * + fixture1 + line1 + line2 + indented line """ - * fixtures defined from test_show_fixtures_indented_doc_first_line_unindented * - fixture1 - line1 - line2 - indented line - """ ) ) def test_show_fixtures_indented_in_class(self, testdir): p = testdir.makepyfile( - dedent( + textwrap.dedent( + '''\ + import pytest + class TestClass(object): + @pytest.fixture + def fixture1(self): + """line1 + line2 + indented line + """ ''' - import pytest - class TestClass(object): - @pytest.fixture - def fixture1(self): - """line1 - line2 - indented line - """ - ''' ) ) result = testdir.runpytest("--fixtures", p) result.stdout.fnmatch_lines( - dedent( + textwrap.dedent( + """\ + * fixtures defined from test_show_fixtures_indented_in_class * + fixture1 + line1 + line2 + indented line """ - * fixtures defined from test_show_fixtures_indented_in_class * - fixture1 - line1 - line2 - indented line - """ ) ) @@ -3667,26 +3665,26 @@ class TestParameterizedSubRequest(object): fixdir = testdir.mkdir("fixtures") fixfile = fixdir.join("fix.py") fixfile.write( - _pytest._code.Source( - """ - import pytest + textwrap.dedent( + """\ + import pytest - @pytest.fixture(params=[0, 1, 2]) - def fix_with_param(request): - return request.param - """ + @pytest.fixture(params=[0, 1, 2]) + def fix_with_param(request): + return request.param + """ ) ) testfile = tests_dir.join("test_foos.py") testfile.write( - _pytest._code.Source( - """ - from fix import fix_with_param + textwrap.dedent( + """\ + from fix import fix_with_param - def test_foo(request): - request.getfixturevalue('fix_with_param') - """ + def test_foo(request): + request.getfixturevalue('fix_with_param') + """ ) ) @@ -3698,9 +3696,9 @@ class TestParameterizedSubRequest(object): E*Failed: The requested fixture has no parameter defined for the current test. E* E*Requested fixture 'fix_with_param' defined in: - E*fix.py:5 + E*fix.py:4 E*Requested here: - E*test_foos.py:5 + E*test_foos.py:4 *1 failed* """ ) diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index db1cd77c5..f5d839f08 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -2,7 +2,7 @@ import re import sys import attr -import _pytest._code +import textwrap import pytest from _pytest import python, fixtures @@ -1271,19 +1271,19 @@ class TestMetafuncFunctional(object): sub1 = testdir.mkpydir("sub1") sub2 = testdir.mkpydir("sub2") sub1.join("conftest.py").write( - _pytest._code.Source( + textwrap.dedent( + """\ + def pytest_generate_tests(metafunc): + assert metafunc.function.__name__ == "test_1" """ - def pytest_generate_tests(metafunc): - assert metafunc.function.__name__ == "test_1" - """ ) ) sub2.join("conftest.py").write( - _pytest._code.Source( + textwrap.dedent( + """\ + def pytest_generate_tests(metafunc): + assert metafunc.function.__name__ == "test_2" """ - def pytest_generate_tests(metafunc): - assert metafunc.function.__name__ == "test_2" - """ ) ) sub1.join("test_in_sub1.py").write("def test_1(): pass") diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index 1ba3f9473..4074b80dc 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -1039,14 +1039,14 @@ class TestAssertionRewriteHookDetails(object): """ path = testdir.mkpydir("foo") path.join("test_foo.py").write( - _pytest._code.Source( + textwrap.dedent( + """\ + class Test(object): + def test_foo(self): + import pkgutil + data = pkgutil.get_data('foo.test_foo', 'data.txt') + assert data == b'Hey' """ - class Test(object): - def test_foo(self): - import pkgutil - data = pkgutil.get_data('foo.test_foo', 'data.txt') - assert data == b'Hey' - """ ) ) path.join("data.txt").write("Hey") diff --git a/testing/test_cacheprovider.py b/testing/test_cacheprovider.py index 7ec73ec63..cfeb4a0cf 100644 --- a/testing/test_cacheprovider.py +++ b/testing/test_cacheprovider.py @@ -1,9 +1,9 @@ from __future__ import absolute_import, division, print_function import sys +import textwrap import py -import _pytest import pytest import os import shutil @@ -224,17 +224,17 @@ class TestLastFailed(object): result = testdir.runpytest() result.stdout.fnmatch_lines(["*2 failed*"]) p.write( - _pytest._code.Source( + textwrap.dedent( + """\ + def test_1(): + assert 1 + + def test_2(): + assert 1 + + def test_3(): + assert 0 """ - def test_1(): - assert 1 - - def test_2(): - assert 1 - - def test_3(): - assert 0 - """ ) ) result = testdir.runpytest("--lf") @@ -252,19 +252,19 @@ class TestLastFailed(object): def test_failedfirst_order(self, testdir): testdir.tmpdir.join("test_a.py").write( - _pytest._code.Source( + textwrap.dedent( + """\ + def test_always_passes(): + assert 1 """ - def test_always_passes(): - assert 1 - """ ) ) testdir.tmpdir.join("test_b.py").write( - _pytest._code.Source( + textwrap.dedent( + """\ + def test_always_fails(): + assert 0 """ - def test_always_fails(): - assert 0 - """ ) ) result = testdir.runpytest() @@ -277,14 +277,14 @@ class TestLastFailed(object): def test_lastfailed_failedfirst_order(self, testdir): testdir.makepyfile( **{ - "test_a.py": """ + "test_a.py": """\ def test_always_passes(): assert 1 - """, - "test_b.py": """ + """, + "test_b.py": """\ def test_always_fails(): assert 0 - """, + """, } ) result = testdir.runpytest() @@ -298,16 +298,16 @@ class TestLastFailed(object): def test_lastfailed_difference_invocations(self, testdir, monkeypatch): monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", 1) testdir.makepyfile( - test_a=""" + test_a="""\ def test_a1(): assert 0 def test_a2(): assert 1 - """, - test_b=""" + """, + test_b="""\ def test_b1(): assert 0 - """, + """, ) p = testdir.tmpdir.join("test_a.py") p2 = testdir.tmpdir.join("test_b.py") @@ -317,11 +317,11 @@ class TestLastFailed(object): result = testdir.runpytest("--lf", p2) result.stdout.fnmatch_lines(["*1 failed*"]) p2.write( - _pytest._code.Source( + textwrap.dedent( + """\ + def test_b1(): + assert 1 """ - def test_b1(): - assert 1 - """ ) ) result = testdir.runpytest("--lf", p2) @@ -332,18 +332,18 @@ class TestLastFailed(object): def test_lastfailed_usecase_splice(self, testdir, monkeypatch): monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", 1) testdir.makepyfile( - """ + """\ def test_1(): assert 0 - """ + """ ) p2 = testdir.tmpdir.join("test_something.py") p2.write( - _pytest._code.Source( + textwrap.dedent( + """\ + def test_2(): + assert 0 """ - def test_2(): - assert 0 - """ ) ) result = testdir.runpytest() diff --git a/testing/test_capture.py b/testing/test_capture.py index 475f61924..ec08f235f 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -6,9 +6,9 @@ from __future__ import absolute_import, division, print_function import pickle import os import sys +import textwrap from io import UnsupportedOperation -import _pytest._code import py import pytest import contextlib @@ -269,7 +269,7 @@ class TestPerTestCapturing(object): def test_capturing_outerr(self, testdir): p1 = testdir.makepyfile( - """ + """\ import sys def test_capturing(): print (42) @@ -278,7 +278,7 @@ class TestPerTestCapturing(object): print (1) sys.stderr.write(str(2)) raise ValueError - """ + """ ) result = testdir.runpytest(p1) result.stdout.fnmatch_lines( @@ -298,21 +298,21 @@ class TestPerTestCapturing(object): class TestLoggingInteraction(object): def test_logging_stream_ownership(self, testdir): p = testdir.makepyfile( - """ + """\ def test_logging(): import logging import pytest stream = capture.CaptureIO() logging.basicConfig(stream=stream) stream.close() # to free memory/release resources - """ + """ ) result = testdir.runpytest_subprocess(p) assert result.stderr.str().find("atexit") == -1 def test_logging_and_immediate_setupteardown(self, testdir): p = testdir.makepyfile( - """ + """\ import logging def setup_function(function): logging.warn("hello1") @@ -324,7 +324,7 @@ class TestLoggingInteraction(object): def teardown_function(function): logging.warn("hello3") assert 0 - """ + """ ) for optargs in (("--capture=sys",), ("--capture=fd",)): print(optargs) @@ -338,7 +338,7 @@ class TestLoggingInteraction(object): def test_logging_and_crossscope_fixtures(self, testdir): p = testdir.makepyfile( - """ + """\ import logging def setup_module(function): logging.warn("hello1") @@ -350,7 +350,7 @@ class TestLoggingInteraction(object): def teardown_module(function): logging.warn("hello3") assert 0 - """ + """ ) for optargs in (("--capture=sys",), ("--capture=fd",)): print(optargs) @@ -364,11 +364,11 @@ class TestLoggingInteraction(object): def test_conftestlogging_is_shown(self, testdir): testdir.makeconftest( - """ + """\ import logging logging.basicConfig() logging.warn("hello435") - """ + """ ) # make sure that logging is still captured in tests result = testdir.runpytest_subprocess("-s", "-p", "no:capturelog") @@ -378,19 +378,19 @@ class TestLoggingInteraction(object): def test_conftestlogging_and_test_logging(self, testdir): testdir.makeconftest( - """ + """\ import logging logging.basicConfig() - """ + """ ) # make sure that logging is still captured in tests p = testdir.makepyfile( - """ + """\ def test_hello(): import logging logging.warn("hello433") assert 0 - """ + """ ) result = testdir.runpytest_subprocess(p, "-p", "no:capturelog") assert result.ret != 0 @@ -403,24 +403,24 @@ class TestCaptureFixture(object): @pytest.mark.parametrize("opt", [[], ["-s"]]) def test_std_functional(self, testdir, opt): reprec = testdir.inline_runsource( - """ + """\ def test_hello(capsys): print (42) out, err = capsys.readouterr() assert out.startswith("42") - """, + """, *opt ) reprec.assertoutcome(passed=1) def test_capsyscapfd(self, testdir): p = testdir.makepyfile( - """ + """\ def test_one(capsys, capfd): pass def test_two(capfd, capsys): pass - """ + """ ) result = testdir.runpytest(p) result.stdout.fnmatch_lines( @@ -438,12 +438,12 @@ class TestCaptureFixture(object): in the same test is an error. """ testdir.makepyfile( - """ + """\ def test_one(capsys, request): request.getfixturevalue("capfd") def test_two(capfd, request): request.getfixturevalue("capsys") - """ + """ ) result = testdir.runpytest() result.stdout.fnmatch_lines( @@ -458,10 +458,10 @@ class TestCaptureFixture(object): def test_capsyscapfdbinary(self, testdir): p = testdir.makepyfile( - """ + """\ def test_one(capsys, capfdbinary): pass - """ + """ ) result = testdir.runpytest(p) result.stdout.fnmatch_lines( @@ -471,12 +471,13 @@ class TestCaptureFixture(object): @pytest.mark.parametrize("method", ["sys", "fd"]) def test_capture_is_represented_on_failure_issue128(self, testdir, method): p = testdir.makepyfile( - """ - def test_hello(cap%s): + """\ + def test_hello(cap{}): print ("xxx42xxx") assert 0 - """ - % method + """.format( + method + ) ) result = testdir.runpytest(p) result.stdout.fnmatch_lines(["xxx42xxx"]) @@ -484,21 +485,21 @@ class TestCaptureFixture(object): @needsosdup def test_stdfd_functional(self, testdir): reprec = testdir.inline_runsource( - """ + """\ def test_hello(capfd): import os os.write(1, "42".encode('ascii')) out, err = capfd.readouterr() assert out.startswith("42") capfd.close() - """ + """ ) reprec.assertoutcome(passed=1) @needsosdup def test_capfdbinary(self, testdir): reprec = testdir.inline_runsource( - """ + """\ def test_hello(capfdbinary): import os # some likely un-decodable bytes @@ -506,7 +507,7 @@ class TestCaptureFixture(object): out, err = capfdbinary.readouterr() assert out == b'\\xfe\\x98\\x20' assert err == b'' - """ + """ ) reprec.assertoutcome(passed=1) @@ -515,7 +516,7 @@ class TestCaptureFixture(object): ) def test_capsysbinary(self, testdir): reprec = testdir.inline_runsource( - """ + """\ def test_hello(capsysbinary): import sys # some likely un-decodable bytes @@ -523,7 +524,7 @@ class TestCaptureFixture(object): out, err = capsysbinary.readouterr() assert out == b'\\xfe\\x98\\x20' assert err == b'' - """ + """ ) reprec.assertoutcome(passed=1) @@ -532,10 +533,10 @@ class TestCaptureFixture(object): ) def test_capsysbinary_forbidden_in_python2(self, testdir): testdir.makepyfile( - """ + """\ def test_hello(capsysbinary): pass - """ + """ ) result = testdir.runpytest() result.stdout.fnmatch_lines( @@ -548,10 +549,10 @@ class TestCaptureFixture(object): def test_partial_setup_failure(self, testdir): p = testdir.makepyfile( - """ + """\ def test_hello(capsys, missingarg): pass - """ + """ ) result = testdir.runpytest(p) result.stdout.fnmatch_lines(["*test_partial_setup_failure*", "*1 error*"]) @@ -559,12 +560,12 @@ class TestCaptureFixture(object): @needsosdup def test_keyboardinterrupt_disables_capturing(self, testdir): p = testdir.makepyfile( - """ + """\ def test_hello(capfd): import os os.write(1, str(42).encode('ascii')) raise KeyboardInterrupt() - """ + """ ) result = testdir.runpytest_subprocess(p) result.stdout.fnmatch_lines(["*KeyboardInterrupt*"]) @@ -573,7 +574,7 @@ class TestCaptureFixture(object): @pytest.mark.issue14 def test_capture_and_logging(self, testdir): p = testdir.makepyfile( - """ + """\ import logging def test_log(capsys): logging.error('x') @@ -586,7 +587,7 @@ class TestCaptureFixture(object): @pytest.mark.parametrize("no_capture", [True, False]) def test_disabled_capture_fixture(self, testdir, fixture, no_capture): testdir.makepyfile( - """ + """\ def test_disabled({fixture}): print('captured before') with {fixture}.disabled(): @@ -620,7 +621,7 @@ class TestCaptureFixture(object): Ensure that capsys and capfd can be used by other fixtures during setup and teardown. """ testdir.makepyfile( - """ + """\ from __future__ import print_function import sys import pytest @@ -656,7 +657,7 @@ class TestCaptureFixture(object): def test_fixture_use_by_other_fixtures_teardown(self, testdir, cap): """Ensure we can access setup and teardown buffers from teardown when using capsys/capfd (##3033)""" testdir.makepyfile( - """ + """\ import sys import pytest import os @@ -684,11 +685,11 @@ class TestCaptureFixture(object): def test_setup_failure_does_not_kill_capturing(testdir): sub1 = testdir.mkpydir("sub1") sub1.join("conftest.py").write( - _pytest._code.Source( + textwrap.dedent( + """\ + def pytest_runtest_setup(item): + raise ValueError(42) """ - def pytest_runtest_setup(item): - raise ValueError(42) - """ ) ) sub1.join("test_mod.py").write("def test_func1(): pass") diff --git a/testing/test_collection.py b/testing/test_collection.py index 5b494ba31..ce0e3a920 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -1,9 +1,9 @@ from __future__ import absolute_import, division, print_function import pprint import sys +import textwrap import pytest -import _pytest._code from _pytest.main import Session, EXIT_NOTESTSCOLLECTED, _in_venv @@ -913,13 +913,13 @@ def test_fixture_scope_sibling_conftests(testdir): """Regression test case for https://github.com/pytest-dev/pytest/issues/2836""" foo_path = testdir.mkdir("foo") foo_path.join("conftest.py").write( - _pytest._code.Source( + textwrap.dedent( + """\ + import pytest + @pytest.fixture + def fix(): + return 1 """ - import pytest - @pytest.fixture - def fix(): - return 1 - """ ) ) foo_path.join("test_foo.py").write("def test_foo(fix): assert fix == 1") diff --git a/testing/test_config.py b/testing/test_config.py index ef9dacd9c..756b51de4 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -17,11 +17,11 @@ class TestParseIni(object): sub = tmpdir.mkdir("sub") sub.chdir() tmpdir.join(filename).write( - _pytest._code.Source( - """ - [{section}] - name = value - """.format( + textwrap.dedent( + """\ + [{section}] + name = value + """.format( section=section ) ) @@ -38,11 +38,11 @@ class TestParseIni(object): def test_append_parse_args(self, testdir, tmpdir, monkeypatch): monkeypatch.setenv("PYTEST_ADDOPTS", '--color no -rs --tb="short"') tmpdir.join("pytest.ini").write( - _pytest._code.Source( + textwrap.dedent( + """\ + [pytest] + addopts = --verbose """ - [pytest] - addopts = --verbose - """ ) ) config = testdir.parseconfig(tmpdir) @@ -438,11 +438,11 @@ class TestConfigFromdictargs(object): def test_inifilename(self, tmpdir): tmpdir.join("foo/bar.ini").ensure().write( - _pytest._code.Source( + textwrap.dedent( + """\ + [pytest] + name = value """ - [pytest] - name = value - """ ) ) @@ -453,12 +453,12 @@ class TestConfigFromdictargs(object): cwd = tmpdir.join("a/b") cwd.join("pytest.ini").ensure().write( - _pytest._code.Source( + textwrap.dedent( + """\ + [pytest] + name = wrong-value + should_not_be_set = true """ - [pytest] - name = wrong-value - should_not_be_set = true - """ ) ) with cwd.ensure(dir=True).as_cwd(): diff --git a/testing/test_conftest.py b/testing/test_conftest.py index 449ef5281..f3b5bac38 100644 --- a/testing/test_conftest.py +++ b/testing/test_conftest.py @@ -1,7 +1,6 @@ from __future__ import absolute_import, division, print_function -from textwrap import dedent +import textwrap -import _pytest._code import py import pytest from _pytest.config import PytestPluginManager @@ -174,11 +173,11 @@ def test_conftest_confcutdir(testdir): testdir.makeconftest("assert 0") x = testdir.mkdir("x") x.join("conftest.py").write( - _pytest._code.Source( + textwrap.dedent( + """\ + def pytest_addoption(parser): + parser.addoption("--xyz", action="store_true") """ - def pytest_addoption(parser): - parser.addoption("--xyz", action="store_true") - """ ) ) result = testdir.runpytest("-h", "--confcutdir=%s" % x, x) @@ -198,11 +197,11 @@ def test_no_conftest(testdir): def test_conftest_existing_resultlog(testdir): x = testdir.mkdir("tests") x.join("conftest.py").write( - _pytest._code.Source( + textwrap.dedent( + """\ + def pytest_addoption(parser): + parser.addoption("--xyz", action="store_true") """ - def pytest_addoption(parser): - parser.addoption("--xyz", action="store_true") - """ ) ) testdir.makefile(ext=".log", result="") # Writes result.log @@ -213,11 +212,11 @@ def test_conftest_existing_resultlog(testdir): def test_conftest_existing_junitxml(testdir): x = testdir.mkdir("tests") x.join("conftest.py").write( - _pytest._code.Source( + textwrap.dedent( + """\ + def pytest_addoption(parser): + parser.addoption("--xyz", action="store_true") """ - def pytest_addoption(parser): - parser.addoption("--xyz", action="store_true") - """ ) ) testdir.makefile(ext=".xml", junit="") # Writes junit.xml @@ -247,38 +246,38 @@ def test_fixture_dependency(testdir, monkeypatch): sub = testdir.mkdir("sub") sub.join("__init__.py").write("") sub.join("conftest.py").write( - dedent( + textwrap.dedent( + """\ + import pytest + + @pytest.fixture + def not_needed(): + assert False, "Should not be called!" + + @pytest.fixture + def foo(): + assert False, "Should not be called!" + + @pytest.fixture + def bar(foo): + return 'bar' """ - import pytest - - @pytest.fixture - def not_needed(): - assert False, "Should not be called!" - - @pytest.fixture - def foo(): - assert False, "Should not be called!" - - @pytest.fixture - def bar(foo): - return 'bar' - """ ) ) subsub = sub.mkdir("subsub") subsub.join("__init__.py").write("") subsub.join("test_bar.py").write( - dedent( + textwrap.dedent( + """\ + import pytest + + @pytest.fixture + def bar(): + return 'sub bar' + + def test_event_fixture(bar): + assert bar == 'sub bar' """ - import pytest - - @pytest.fixture - def bar(): - return 'sub bar' - - def test_event_fixture(bar): - assert bar == 'sub bar' - """ ) ) result = testdir.runpytest("sub") @@ -288,11 +287,11 @@ def test_fixture_dependency(testdir, monkeypatch): def test_conftest_found_with_double_dash(testdir): sub = testdir.mkdir("sub") sub.join("conftest.py").write( - dedent( + textwrap.dedent( + """\ + def pytest_addoption(parser): + parser.addoption("--hello-world", action="store_true") """ - def pytest_addoption(parser): - parser.addoption("--hello-world", action="store_true") - """ ) ) p = sub.join("test_hello.py") @@ -313,56 +312,54 @@ class TestConftestVisibility(object): package = testdir.mkdir("package") package.join("conftest.py").write( - dedent( + textwrap.dedent( """\ - import pytest - @pytest.fixture - def fxtr(): - return "from-package" - """ + import pytest + @pytest.fixture + def fxtr(): + return "from-package" + """ ) ) package.join("test_pkgroot.py").write( - dedent( + textwrap.dedent( """\ - def test_pkgroot(fxtr): - assert fxtr == "from-package" - """ + def test_pkgroot(fxtr): + assert fxtr == "from-package" + """ ) ) swc = package.mkdir("swc") swc.join("__init__.py").ensure() swc.join("conftest.py").write( - dedent( + textwrap.dedent( """\ - import pytest - @pytest.fixture - def fxtr(): - return "from-swc" - """ + import pytest + @pytest.fixture + def fxtr(): + return "from-swc" + """ ) ) swc.join("test_with_conftest.py").write( - dedent( + textwrap.dedent( """\ - def test_with_conftest(fxtr): - assert fxtr == "from-swc" - - """ + def test_with_conftest(fxtr): + assert fxtr == "from-swc" + """ ) ) snc = package.mkdir("snc") snc.join("__init__.py").ensure() snc.join("test_no_conftest.py").write( - dedent( + textwrap.dedent( """\ - def test_no_conftest(fxtr): - assert fxtr == "from-package" # No local conftest.py, so should - # use value from parent dir's - - """ + def test_no_conftest(fxtr): + assert fxtr == "from-package" # No local conftest.py, so should + # use value from parent dir's + """ ) ) print("created directory structure:") @@ -422,31 +419,31 @@ def test_search_conftest_up_to_inifile(testdir, confcutdir, passed, error): src = root.join("src").ensure(dir=1) src.join("pytest.ini").write("[pytest]") src.join("conftest.py").write( - _pytest._code.Source( + textwrap.dedent( + """\ + import pytest + @pytest.fixture + def fix1(): pass """ - import pytest - @pytest.fixture - def fix1(): pass - """ ) ) src.join("test_foo.py").write( - _pytest._code.Source( + textwrap.dedent( + """\ + def test_1(fix1): + pass + def test_2(out_of_reach): + pass """ - def test_1(fix1): - pass - def test_2(out_of_reach): - pass - """ ) ) root.join("conftest.py").write( - _pytest._code.Source( + textwrap.dedent( + """\ + import pytest + @pytest.fixture + def out_of_reach(): pass """ - import pytest - @pytest.fixture - def out_of_reach(): pass - """ ) ) @@ -464,19 +461,19 @@ def test_search_conftest_up_to_inifile(testdir, confcutdir, passed, error): def test_issue1073_conftest_special_objects(testdir): testdir.makeconftest( - """ + """\ class DontTouchMe(object): def __getattr__(self, x): raise Exception('cant touch me') x = DontTouchMe() - """ + """ ) testdir.makepyfile( - """ + """\ def test_some(): pass - """ + """ ) res = testdir.runpytest() assert res.ret == 0 @@ -484,15 +481,15 @@ def test_issue1073_conftest_special_objects(testdir): def test_conftest_exception_handling(testdir): testdir.makeconftest( - """ + """\ raise ValueError() - """ + """ ) testdir.makepyfile( - """ + """\ def test_some(): pass - """ + """ ) res = testdir.runpytest() assert res.ret == 4 @@ -507,7 +504,7 @@ def test_hook_proxy(testdir): **{ "root/demo-0/test_foo1.py": "def test1(): pass", "root/demo-a/test_foo2.py": "def test1(): pass", - "root/demo-a/conftest.py": """ + "root/demo-a/conftest.py": """\ def pytest_ignore_collect(path, config): return True """, @@ -525,11 +522,11 @@ def test_required_option_help(testdir): testdir.makeconftest("assert 0") x = testdir.mkdir("x") x.join("conftest.py").write( - _pytest._code.Source( + textwrap.dedent( + """\ + def pytest_addoption(parser): + parser.addoption("--xyz", action="store_true", required=True) """ - def pytest_addoption(parser): - parser.addoption("--xyz", action="store_true", required=True) - """ ) ) result = testdir.runpytest("-h", x) diff --git a/testing/test_doctest.py b/testing/test_doctest.py index 6a84c5feb..d7815b1cf 100644 --- a/testing/test_doctest.py +++ b/testing/test_doctest.py @@ -1,7 +1,7 @@ # encoding: utf-8 from __future__ import absolute_import, division, print_function import sys -import _pytest._code +import textwrap from _pytest.compat import MODULE_NOT_FOUND_ERROR from _pytest.doctest import DoctestItem, DoctestModule, DoctestTextfile import pytest @@ -258,16 +258,16 @@ class TestDoctests(object): def test_doctest_linedata_missing(self, testdir): testdir.tmpdir.join("hello.py").write( - _pytest._code.Source( + textwrap.dedent( + """\ + class Fun(object): + @property + def test(self): + ''' + >>> a = 1 + >>> 1/0 + ''' """ - class Fun(object): - @property - def test(self): - ''' - >>> a = 1 - >>> 1/0 - ''' - """ ) ) result = testdir.runpytest("--doctest-modules") @@ -300,10 +300,10 @@ class TestDoctests(object): def test_doctest_unex_importerror_with_module(self, testdir): testdir.tmpdir.join("hello.py").write( - _pytest._code.Source( + textwrap.dedent( + """\ + import asdalsdkjaslkdjasd """ - import asdalsdkjaslkdjasd - """ ) ) testdir.maketxtfile( @@ -339,27 +339,27 @@ class TestDoctests(object): def test_doctestmodule_external_and_issue116(self, testdir): p = testdir.mkpydir("hello") p.join("__init__.py").write( - _pytest._code.Source( + textwrap.dedent( + """\ + def somefunc(): + ''' + >>> i = 0 + >>> i + 1 + 2 + ''' """ - def somefunc(): - ''' - >>> i = 0 - >>> i + 1 - 2 - ''' - """ ) ) result = testdir.runpytest(p, "--doctest-modules") result.stdout.fnmatch_lines( [ - "004 *>>> i = 0", - "005 *>>> i + 1", + "003 *>>> i = 0", + "004 *>>> i + 1", "*Expected:", "* 2", "*Got:", "* 1", - "*:5: DocTestFailure", + "*:4: DocTestFailure", ] ) diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 88e5287e8..0f674a34f 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -4,9 +4,9 @@ terminal reporting of the full testing process. from __future__ import absolute_import, division, print_function import collections import sys +import textwrap import pluggy -import _pytest._code import py import pytest from _pytest.main import EXIT_NOTESTSCOLLECTED @@ -161,12 +161,12 @@ class TestTerminal(object): def test_itemreport_directclasses_not_shown_as_subclasses(self, testdir): a = testdir.mkpydir("a123") a.join("test_hello123.py").write( - _pytest._code.Source( + textwrap.dedent( + """\ + class TestClass(object): + def test_method(self): + pass """ - class TestClass(object): - def test_method(self): - pass - """ ) ) result = testdir.runpytest("-v") @@ -312,13 +312,13 @@ class TestCollectonly(object): result = testdir.runpytest("--collect-only", p) assert result.ret == 2 result.stdout.fnmatch_lines( - _pytest._code.Source( + textwrap.dedent( + """\ + *ERROR* + *ImportError* + *No module named *Errlk* + *1 error* """ - *ERROR* - *ImportError* - *No module named *Errlk* - *1 error* - """ ).strip() ) From 3f336869e2e632c6f66432065073a45046f39ccc Mon Sep 17 00:00:00 2001 From: Gandalf Saxe Date: Thu, 23 Aug 2018 18:07:28 +0200 Subject: [PATCH 14/16] Move information on `pip install -e` to the top Should fix complaints in #2421. --- doc/en/goodpractices.rst | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/doc/en/goodpractices.rst b/doc/en/goodpractices.rst index d9c685299..b1bb0e81a 100644 --- a/doc/en/goodpractices.rst +++ b/doc/en/goodpractices.rst @@ -4,6 +4,27 @@ Good Integration Practices ================================================= +Install package with pip +------------------------------------------------- + +For development, we recommend to use virtualenv_ environments and pip_ +for installing your application and any dependencies +as well as the ``pytest`` package itself. This ensures your code and +dependencies are isolated from the system Python installation. + +First you need to place a `setup.py` file in the root of your package with the following minimum content: + + from setuptools import setup, find_packages + + setup(name="PACKAGENAME", packages=find_packages()) + +Where `PACKAGENAME` is the name of your package. You can then install your package in "editable" mode by running from the same directory:: + + pip install -e . + +which lets you change your source code (both tests and application) and rerun tests at will. +This is similar to running ``python setup.py develop`` or ``conda develop`` in that it installs +your package using a symlink to your development code. .. _`test discovery`: .. _`Python test discovery`: @@ -177,19 +198,6 @@ Note that this layout also works in conjunction with the ``src`` layout mentione tox ------ -For development, we recommend to use virtualenv_ environments and pip_ -for installing your application and any dependencies -as well as the ``pytest`` package itself. This ensures your code and -dependencies are isolated from the system Python installation. - -You can then install your package in "editable" mode:: - - pip install -e . - -which lets you change your source code (both tests and application) and rerun tests at will. -This is similar to running ``python setup.py develop`` or ``conda develop`` in that it installs -your package using a symlink to your development code. - Once you are done with your work and want to make sure that your actual package passes all tests you may want to look into `tox`_, the virtualenv test automation tool and its `pytest support From 40b4fe64af7446f56b612d0ea26bb2e0a5b4705a Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 23 Aug 2018 22:11:17 -0300 Subject: [PATCH 15/16] Fix linting --- doc/en/goodpractices.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/en/goodpractices.rst b/doc/en/goodpractices.rst index b1bb0e81a..87d446d0d 100644 --- a/doc/en/goodpractices.rst +++ b/doc/en/goodpractices.rst @@ -12,13 +12,14 @@ for installing your application and any dependencies as well as the ``pytest`` package itself. This ensures your code and dependencies are isolated from the system Python installation. -First you need to place a `setup.py` file in the root of your package with the following minimum content: +First you need to place a ``setup.py`` file in the root of your package with the following minimum content: from setuptools import setup, find_packages - + + setup(name="PACKAGENAME", packages=find_packages()) -Where `PACKAGENAME` is the name of your package. You can then install your package in "editable" mode by running from the same directory:: +Where ``PACKAGENAME`` is the name of your package. You can then install your package in "editable" mode by running from the same directory:: pip install -e . From 99e31f6fb17177b60046dd2fc44c0a313cf4b8aa Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Thu, 23 Aug 2018 18:55:21 -0700 Subject: [PATCH 16/16] Use `bytes` directly instead of `binary_type` `bytes` is an alias for `str` in python2.6+ --- src/_pytest/assertion/rewrite.py | 14 ++++++-------- src/_pytest/assertion/util.py | 4 ++-- testing/test_capture.py | 6 +++--- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index 952cfb594..5cf63a063 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -402,12 +402,11 @@ def _saferepr(obj): JSON reprs. """ - repr = py.io.saferepr(obj) - if isinstance(repr, six.text_type): - t = six.text_type + r = py.io.saferepr(obj) + if isinstance(r, six.text_type): + return r.replace(u"\n", u"\\n") else: - t = six.binary_type - return repr.replace(t("\n"), t("\\n")) + return r.replace(b"\n", b"\\n") from _pytest.assertion.util import format_explanation as _format_explanation # noqa @@ -446,10 +445,9 @@ def _should_repr_global_name(obj): def _format_boolop(explanations, is_or): explanation = "(" + (is_or and " or " or " and ").join(explanations) + ")" if isinstance(explanation, six.text_type): - t = six.text_type + return explanation.replace(u"%", u"%%") else: - t = six.binary_type - return explanation.replace(t("%"), t("%%")) + return explanation.replace(b"%", b"%%") def _call_reprcompare(ops, results, expls, each_obj): diff --git a/src/_pytest/assertion/util.py b/src/_pytest/assertion/util.py index 08213c80e..a3013cb98 100644 --- a/src/_pytest/assertion/util.py +++ b/src/_pytest/assertion/util.py @@ -187,9 +187,9 @@ def _diff_text(left, right, verbose=False): r = r.replace(r"\r", "\r") return r - if isinstance(left, six.binary_type): + if isinstance(left, bytes): left = escape_for_readable_diff(left) - if isinstance(right, six.binary_type): + if isinstance(right, bytes): right = escape_for_readable_diff(right) if not verbose: i = 0 # just in case left or right has zero length diff --git a/testing/test_capture.py b/testing/test_capture.py index ec08f235f..75d82ecde 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -12,7 +12,7 @@ from io import UnsupportedOperation import py import pytest import contextlib -from six import binary_type, text_type +from six import text_type from _pytest import capture from _pytest.capture import CaptureManager from _pytest.main import EXIT_NOTESTSCOLLECTED @@ -24,12 +24,12 @@ needsosdup = pytest.mark.xfail("not hasattr(os, 'dup')") def tobytes(obj): if isinstance(obj, text_type): obj = obj.encode("UTF-8") - assert isinstance(obj, binary_type) + assert isinstance(obj, bytes) return obj def totext(obj): - if isinstance(obj, binary_type): + if isinstance(obj, bytes): obj = text_type(obj, "UTF-8") assert isinstance(obj, text_type) return obj