From c09087cae19706fffb4c1e1e5fe3830612f7b0fb Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 21 Oct 2019 02:52:02 +0200 Subject: [PATCH 1/4] SysCapture: allow passing in stdin --- src/_pytest/capture.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/_pytest/capture.py b/src/_pytest/capture.py index e4e58b32cf6..915ae58f162 100644 --- a/src/_pytest/capture.py +++ b/src/_pytest/capture.py @@ -633,16 +633,20 @@ def snap(self): class SysCapture: + CLOSE_STDIN = object EMPTY_BUFFER = str() _state = None - def __init__(self, fd, tmpfile=None): + def __init__(self, fd, tmpfile=None, stdin=CLOSE_STDIN): name = patchsysdict[fd] self._old = getattr(sys, name) self.name = name if tmpfile is None: if name == "stdin": - tmpfile = DontReadFromInput() + if stdin is self.CLOSE_STDIN: + tmpfile = DontReadFromInput() + else: + tmpfile = stdin else: tmpfile = CaptureIO() self.tmpfile = tmpfile From 73a998404a8d1234a1fcb0ba9a4e758ece07b45e Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 21 Oct 2019 02:57:44 +0200 Subject: [PATCH 2/4] runpytest_inprocess: allow to pass in stdin --- src/_pytest/pytester.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index a050dad09e5..83e0910834e 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -847,7 +847,7 @@ class reprec: for finalizer in finalizers: finalizer() - def runpytest_inprocess(self, *args, **kwargs): + def runpytest_inprocess(self, *args, stdin=CLOSE_STDIN, **kwargs): """Return result of running pytest in-process, providing a similar interface to what self.runpytest() provides. """ @@ -856,7 +856,18 @@ def runpytest_inprocess(self, *args, **kwargs): if syspathinsert: self.syspathinsert() now = time.time() - capture = MultiCapture(Capture=SysCapture) + if stdin is self.CLOSE_STDIN: + Capture = SysCapture + else: + if isinstance(stdin, str): + import io + import functools + + Capture = functools.partial(SysCapture, stdin=io.StringIO(stdin)) + else: + Capture = stdin + + capture = MultiCapture(Capture=Capture) capture.start_capturing() try: try: From b8a79bf4dca433bb36eefc2231889baa037643ea Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 21 Oct 2019 04:07:08 +0200 Subject: [PATCH 3/4] use runpytest/stdin with test_pdb_used_in_generate_tests --- testing/test_pdb.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/testing/test_pdb.py b/testing/test_pdb.py index 924c2f4af36..4ebdebcb208 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -724,17 +724,21 @@ def test_pdb_used_in_generate_tests(self, testdir): """ import pytest def pytest_generate_tests(metafunc): - pytest.set_trace() x = 5 + pytest.set_trace() + def test_foo(a): pass """ ) - child = testdir.spawn_pytest(str(p1)) - child.expect("x = 5") - child.expect("Pdb") - child.sendeof() - self.flush(child) + result = testdir.runpytest(str(p1), stdin="p 'x=' + str(x)\nq\n") + result.stdout.fnmatch_lines( + [ + "*> PDB set_trace (IO-capturing turned off) >*", + "'x=5'", + "E _pytest.outcomes.Exit: Quitting debugger", + ] + ) def test_pdb_collection_failure_is_shown(self, testdir): p1 = testdir.makepyfile("xxx") From 8c01c10e26ac2d05c07aaf6a5084a32685df4719 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 21 Oct 2019 04:08:27 +0200 Subject: [PATCH 4/4] Use EchoingInput wrapper --- src/_pytest/pytester.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 83e0910834e..897242fb483 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -859,11 +859,19 @@ def runpytest_inprocess(self, *args, stdin=CLOSE_STDIN, **kwargs): if stdin is self.CLOSE_STDIN: Capture = SysCapture else: + old_stdin = sys.stdin if isinstance(stdin, str): import io import functools - Capture = functools.partial(SysCapture, stdin=io.StringIO(stdin)) + class EchoingInput(io.StringIO): + def readline(self, *args, **kwargs): + ret = super().readline(*args, **kwargs) + if ret is not None: + sys.stdout.write(ret) + return ret + + Capture = functools.partial(SysCapture, stdin=EchoingInput(stdin)) else: Capture = stdin @@ -889,6 +897,9 @@ class reprec: sys.stdout.write(out) sys.stderr.write(err) + if stdin is not self.CLOSE_STDIN: + sys.stdin = old_stdin + res = RunResult(reprec.ret, out.split("\n"), err.split("\n"), time.time() - now) res.reprec = reprec return res