pytester: typing

This commit is contained in:
Daniel Hahler 2019-10-22 00:05:02 +02:00
parent cbc39dd86e
commit 886a3ad609
2 changed files with 52 additions and 21 deletions

View File

@ -11,6 +11,7 @@ import traceback
from collections.abc import Sequence from collections.abc import Sequence
from fnmatch import fnmatch from fnmatch import fnmatch
from io import StringIO from io import StringIO
from typing import Union
from weakref import WeakKeyDictionary from weakref import WeakKeyDictionary
import py import py
@ -362,9 +363,9 @@ class RunResult:
:ivar duration: duration in seconds :ivar duration: duration in seconds
""" """
def __init__(self, ret, outlines, errlines, duration): def __init__(self, ret: Union[int, ExitCode], outlines, errlines, duration) -> None:
try: try:
self.ret = pytest.ExitCode(ret) self.ret = pytest.ExitCode(ret) # type: Union[int, ExitCode]
except ValueError: except ValueError:
self.ret = ret self.ret = ret
self.outlines = outlines self.outlines = outlines
@ -483,11 +484,7 @@ class Testdir:
self._sys_modules_snapshot = self.__take_sys_modules_snapshot() self._sys_modules_snapshot = self.__take_sys_modules_snapshot()
self.chdir() self.chdir()
self.request.addfinalizer(self.finalize) self.request.addfinalizer(self.finalize)
method = self.request.config.getoption("--runpytest") self._method = self.request.config.getoption("--runpytest")
if method == "inprocess":
self._runpytest_method = self.runpytest_inprocess
elif method == "subprocess":
self._runpytest_method = self.runpytest_subprocess
mp = self.monkeypatch = MonkeyPatch() mp = self.monkeypatch = MonkeyPatch()
mp.setenv("PYTEST_DEBUG_TEMPROOT", str(self.test_tmproot)) mp.setenv("PYTEST_DEBUG_TEMPROOT", str(self.test_tmproot))
@ -835,7 +832,7 @@ class Testdir:
reprec = rec.pop() reprec = rec.pop()
else: else:
class reprec: class reprec: # type: ignore
pass pass
reprec.ret = ret reprec.ret = ret
@ -851,7 +848,7 @@ class Testdir:
for finalizer in finalizers: for finalizer in finalizers:
finalizer() finalizer()
def runpytest_inprocess(self, *args, **kwargs): def runpytest_inprocess(self, *args, **kwargs) -> RunResult:
"""Return result of running pytest in-process, providing a similar """Return result of running pytest in-process, providing a similar
interface to what self.runpytest() provides. interface to what self.runpytest() provides.
""" """
@ -866,15 +863,20 @@ class Testdir:
try: try:
reprec = self.inline_run(*args, **kwargs) reprec = self.inline_run(*args, **kwargs)
except SystemExit as e: except SystemExit as e:
ret = e.args[0]
try:
ret = ExitCode(e.args[0])
except ValueError:
pass
class reprec: class reprec: # type: ignore
ret = e.args[0] ret = ret
except Exception: except Exception:
traceback.print_exc() traceback.print_exc()
class reprec: class reprec: # type: ignore
ret = 3 ret = ExitCode(3)
finally: finally:
out, err = capture.readouterr() out, err = capture.readouterr()
@ -885,16 +887,20 @@ class Testdir:
res = RunResult( res = RunResult(
reprec.ret, out.splitlines(), err.splitlines(), time.time() - now reprec.ret, out.splitlines(), err.splitlines(), time.time() - now
) )
res.reprec = reprec res.reprec = reprec # type: ignore
return res return res
def runpytest(self, *args, **kwargs): def runpytest(self, *args, **kwargs) -> RunResult:
"""Run pytest inline or in a subprocess, depending on the command line """Run pytest inline or in a subprocess, depending on the command line
option "--runpytest" and return a :py:class:`RunResult`. option "--runpytest" and return a :py:class:`RunResult`.
""" """
args = self._ensure_basetemp(args) args = self._ensure_basetemp(args)
return self._runpytest_method(*args, **kwargs) if self._method == "inprocess":
return self.runpytest_inprocess(*args, **kwargs)
elif self._method == "subprocess":
return self.runpytest_subprocess(*args, **kwargs)
raise RuntimeError("Unrecognized runpytest option: {}".format(self._method))
def _ensure_basetemp(self, args): def _ensure_basetemp(self, args):
args = list(args) args = list(args)
@ -1051,7 +1057,7 @@ class Testdir:
return popen return popen
def run(self, *cmdargs, timeout=None, stdin=CLOSE_STDIN): def run(self, *cmdargs, timeout=None, stdin=CLOSE_STDIN) -> RunResult:
"""Run a command with arguments. """Run a command with arguments.
Run a process using subprocess.Popen saving the stdout and stderr. Run a process using subprocess.Popen saving the stdout and stderr.
@ -1069,9 +1075,9 @@ class Testdir:
""" """
__tracebackhide__ = True __tracebackhide__ = True
cmdargs = [ cmdargs = tuple(
str(arg) if isinstance(arg, py.path.local) else arg for arg in cmdargs str(arg) if isinstance(arg, py.path.local) else arg for arg in cmdargs
] )
p1 = self.tmpdir.join("stdout") p1 = self.tmpdir.join("stdout")
p2 = self.tmpdir.join("stderr") p2 = self.tmpdir.join("stderr")
print("running:", *cmdargs) print("running:", *cmdargs)
@ -1122,6 +1128,10 @@ class Testdir:
f2.close() f2.close()
self._dump_lines(out, sys.stdout) self._dump_lines(out, sys.stdout)
self._dump_lines(err, sys.stderr) self._dump_lines(err, sys.stderr)
try:
ret = ExitCode(ret)
except ValueError:
pass
return RunResult(ret, out, err, time.time() - now) return RunResult(ret, out, err, time.time() - now)
def _dump_lines(self, lines, fp): def _dump_lines(self, lines, fp):
@ -1134,7 +1144,7 @@ class Testdir:
def _getpytestargs(self): def _getpytestargs(self):
return sys.executable, "-mpytest" return sys.executable, "-mpytest"
def runpython(self, script): def runpython(self, script) -> RunResult:
"""Run a python script using sys.executable as interpreter. """Run a python script using sys.executable as interpreter.
Returns a :py:class:`RunResult`. Returns a :py:class:`RunResult`.
@ -1146,7 +1156,7 @@ class Testdir:
"""Run python -c "command", return a :py:class:`RunResult`.""" """Run python -c "command", return a :py:class:`RunResult`."""
return self.run(sys.executable, "-c", command) return self.run(sys.executable, "-c", command)
def runpytest_subprocess(self, *args, timeout=None): def runpytest_subprocess(self, *args, timeout=None) -> RunResult:
"""Run pytest as a subprocess with given arguments. """Run pytest as a subprocess with given arguments.
Any plugins added to the :py:attr:`plugins` list will be added using the Any plugins added to the :py:attr:`plugins` list will be added using the

View File

@ -395,6 +395,27 @@ def test_testdir_subprocess(testdir):
assert testdir.runpytest_subprocess(testfile).ret == 0 assert testdir.runpytest_subprocess(testfile).ret == 0
def test_testdir_subprocess_via_runpytest_arg(testdir) -> None:
testfile = testdir.makepyfile(
"""
def test_testdir_subprocess(testdir):
import os
testfile = testdir.makepyfile(
\"""
import os
def test_one():
assert {} != os.getpid()
\""".format(os.getpid())
)
assert testdir.runpytest(testfile).ret == 0
"""
)
result = testdir.runpytest_subprocess(
"-p", "pytester", "--runpytest", "subprocess", testfile
)
assert result.ret == 0
def test_unicode_args(testdir): def test_unicode_args(testdir):
result = testdir.runpytest("-k", "💩") result = testdir.runpytest("-k", "💩")
assert result.ret == ExitCode.NO_TESTS_COLLECTED assert result.ret == ExitCode.NO_TESTS_COLLECTED