From 89446af51ec723cb628f6d2cf16f10ae2628e25d Mon Sep 17 00:00:00 2001 From: wim glenn Date: Wed, 22 Aug 2018 00:42:10 -0500 Subject: [PATCH 1/5] 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 2/5] 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 3/5] 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 4/5] 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 8804c7333a9bc26956950443b3d75c657762ba28 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 22 Aug 2018 20:06:13 -0300 Subject: [PATCH 5/5] 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.