From 924a9667e16fc64483e264a3ea35915982ffcb41 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Thu, 24 Dec 2015 14:56:57 -0500 Subject: [PATCH 1/5] Make sure readline has been imported before duping any stdio handles--otherwise pyreadline fails to connect to the correct handle for the console's stdout/in. --- _pytest/capture.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/_pytest/capture.py b/_pytest/capture.py index f0fa72f46..478d475f1 100644 --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -308,6 +308,14 @@ class FDCapture: """ Capture IO to/from a given os-level filedescriptor. """ def __init__(self, targetfd, tmpfile=None): + # ensure readline is imported so that it attaches to the correct + # stdio handles on Windows + if targetfd in (0, 1, 2): + try: + import readline + except ImportError: + pass + self.targetfd = targetfd try: self.targetfd_save = os.dup(self.targetfd) From 6e170a4a1c4903b85a47f90e441edc7ff5222589 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Thu, 24 Dec 2015 16:43:34 -0500 Subject: [PATCH 2/5] * Moved workaround to its own function, mostly for the sake of adding a more descriptive docstring (the workaround itself is just to import readline earlier). I removed the conditional on targetfd from the workaround since it doesn't really matter. * Added # noqa marker. * Added changelog entry, and self to authors. --- AUTHORS | 1 + CHANGELOG | 3 +++ _pytest/capture.py | 34 ++++++++++++++++++++++++++-------- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/AUTHORS b/AUTHORS index 7206125c8..3a2c3b39d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -31,6 +31,7 @@ Eduardo Schettino Elizaveta Shashkova Eric Hunsberger Eric Siegerman +Erik M. Bray Florian Bruhin Floris Bruynooghe Gabriel Reis diff --git a/CHANGELOG b/CHANGELOG index b0d66d976..ac45c1426 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -5,6 +5,9 @@ this was a regression failing plugins combinations like pytest-pep8 + pytest-flakes +- Workaround for exception that occurs in pyreadline when using + ``--pdb`` with standard I/O capture enabled. + 2.8.5 ----- diff --git a/_pytest/capture.py b/_pytest/capture.py index 478d475f1..0d839d7d3 100644 --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -308,14 +308,7 @@ class FDCapture: """ Capture IO to/from a given os-level filedescriptor. """ def __init__(self, targetfd, tmpfile=None): - # ensure readline is imported so that it attaches to the correct - # stdio handles on Windows - if targetfd in (0, 1, 2): - try: - import readline - except ImportError: - pass - + readline_workaround() self.targetfd = targetfd try: self.targetfd_save = os.dup(self.targetfd) @@ -450,3 +443,28 @@ class DontReadFromInput: def close(self): pass + + +def readline_workaround(): + """ + Ensure readline is imported so that it attaches to the correct stdio + handles on Windows. + + Pdb uses readline support where available--when not running from the Python + prompt, the readline module is not imported until running the pdb REPL. If + running py.test with the --pdb option this means the readline module is not + imported until after I/O capture has been started. + + This is a problem for pyreadline, which is often used to implement readline + support on Windows, as it does not attach to the correct handles for stdout + and/or stdin if they have been redirected by the FDCapture mechanism. This + workaround ensures that readline is imported before I/O capture is setup so + that it can attach to the actual stdin/out for the console. + + See https://github.com/pytest-dev/pytest/pull/1281 + """ + + try: + import readline # noqa + except ImportError: + pass From 57bc14caa0a9715cca597fb7912b2b6f5dd118a5 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 25 Dec 2015 17:46:19 -0200 Subject: [PATCH 3/5] Apply readline workaround during initial conftest loading --- _pytest/capture.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/_pytest/capture.py b/_pytest/capture.py index 0d839d7d3..75acb178b 100644 --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -31,6 +31,7 @@ def pytest_addoption(parser): @pytest.hookimpl(hookwrapper=True) def pytest_load_initial_conftests(early_config, parser, args): + _readline_workaround() ns = early_config.known_args_namespace pluginmanager = early_config.pluginmanager capman = CaptureManager(ns.capture) @@ -307,8 +308,7 @@ class NoCapture: class FDCapture: """ Capture IO to/from a given os-level filedescriptor. """ - def __init__(self, targetfd, tmpfile=None): - readline_workaround() + def __init__(self, targetfd, tmpfile=None): self.targetfd = targetfd try: self.targetfd_save = os.dup(self.targetfd) @@ -445,7 +445,7 @@ class DontReadFromInput: pass -def readline_workaround(): +def _readline_workaround(): """ Ensure readline is imported so that it attaches to the correct stdio handles on Windows. @@ -464,6 +464,8 @@ def readline_workaround(): See https://github.com/pytest-dev/pytest/pull/1281 """ + if not sys.platform.startswith('win32'): + return try: import readline # noqa except ImportError: From fbfab6778c9a719cef22c427c1c1027f7f8f443e Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 25 Dec 2015 17:48:03 -0200 Subject: [PATCH 4/5] Give proper thanks for the PR in the CHANGELOG --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index ac45c1426..b35aae163 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,6 +7,7 @@ - Workaround for exception that occurs in pyreadline when using ``--pdb`` with standard I/O capture enabled. + Thanks Erik M. Bray for the PR. 2.8.5 ----- From 5afb61ad26c6779cf5923a8901c66711a6aa8ebb Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 25 Dec 2015 17:51:55 -0200 Subject: [PATCH 5/5] Fix trailing white-space --- _pytest/capture.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_pytest/capture.py b/_pytest/capture.py index 75acb178b..3895a714a 100644 --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -308,7 +308,7 @@ class NoCapture: class FDCapture: """ Capture IO to/from a given os-level filedescriptor. """ - def __init__(self, targetfd, tmpfile=None): + def __init__(self, targetfd, tmpfile=None): self.targetfd = targetfd try: self.targetfd_save = os.dup(self.targetfd)