diff --git a/changelog/3848.bugfix.rst b/changelog/3848.bugfix.rst new file mode 100644 index 000000000..4442d7a89 --- /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. 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): 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. diff --git a/testing/test_pytester.py b/testing/test_pytester.py index 86dc35796..99e62e5bc 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