Repository: ipython/ipython

Issue:
IPython sphinx directive no longer reports errors in the code block
In pandas we are trying to switch from our vendored version to the upstream version of `IPython.sphinxext.ipython_directive`, but we noticed that if the code block contains an error, and thus when the code is run an exception is raised, this is no longer reported in the sphinx log.

I also identified the potential reason, and I think it is related to https://github.com/ipython/ipython/pull/9823 (cc @minrk)
This PR replaced a `io.stdout` with `sys.stdout` (https://github.com/ipython/ipython/pull/9823/files#diff-6420af2c1a12b55af7351690ec339cedL298), thus overwriting `sys.stdout` with `self.cout` of the `EmbeddedSphinxShell` (which is a StringIO object, I suppose to capture output of the embedded interactive shell).
However, `sys.stdout` is also used to print errors and warnings to the sphinx log:

https://github.com/ipython/ipython/blob/8c7ab85c0f4677b687cbb3d025189a86d7982619/IPython/sphinxext/ipython_directive.py#L505-L511

and those use cases seems to conflict with each other, because the result is that the error message is no longer written to the sphinx log.

I am not very familiar with stdout at al, but it seems this replacement was done because `IPython.utils.io` is deprecated? So I can't just revert it?

Another option is to better use the Sphinx logging utilities instead of writing the log warnings to stdout (so write the informative messages about errors to the actual sphinx logging). However, currently there is no dependency on Sphinx (no import of sphinx, only of docutils). Is this deliberate? (is there a usecase of this ipython directive outside of sphinx ?) Or can a dependency be added? (it should only be an optional dependency, which is IMO implicitly already the case for users of the ipython directive) 

If a sphinx import can be added, I can do a PR to use the actual sphinx logging.


Discussion:
Yup, `io.stdout` is deprecated, because it seemed to be unnecessary everywhere that it was being used. We may not have checked the sphinxext code carefully enough in that regard. But I'd rather not un-deprecate it now, because it makes life more confusing to have two different stdout objects around.

I don't think there's any reason why `IPython.sphinxext` needs to avoid importing Sphinx, so your logging idea sounds good to me.

The sphinxext code is somewhat unloved, because we don't actually use that ourselves, so it would be good to have some more attention on it from pandas.
Another side effect from using the main sys.stdout for catching the embedded shell's output is that it makes debugging the code more difficult: you can't put print statements in code to check what is happening (of course you can also use sphinx logging, but I think typically a contributor will try printing), and it seems it also makes it impossible to set pdb breakpoints in the code (not sure why that is, but that is what I am experiencing, it just hangs). 
But not sure that is worth keeping the io module around (it could also be private for ipython directive) .. Or would there be other ways to solve this? Eg can you somehow re-use the sys.stdout implementation / object for a separate stream?

(independently from that, I think using the sphinx logging for output is a good idea anyhow)
You could switch `sys.stdout` while it's executing code in the embedded shell, and switch it back afterwards so it functions for normal debugging. It actually looks like there's already code to do that (in `process_input_line()`), but `sys.stdout` is also assigned in `__init__`, making the other code redundant. Maybe you can just get rid of the assignment in `__init__`?
Indeed, those lines are just redundant, and removing the sys.stdout assignment in init seems to fix everything! It makes debugging possible, and also fixes the original issue of output not printed (I suppose you only cannot put print/debug statements inside the inner part of `process_input_line`, but that is already the case right now). 
Thanks for looking at it! Will do a PR for that.


@jorisvandenbossche great! FWIW, for debugging when stdout is redirected, you can always print to `sys.__stdout__`, which should never be redirected and is always the original stdout (same for stderr).

Resolving patch:
diff --git a/IPython/sphinxext/ipython_directive.py b/IPython/sphinxext/ipython_directive.py
--- a/IPython/sphinxext/ipython_directive.py
+++ b/IPython/sphinxext/ipython_directive.py
@@ -287,14 +287,6 @@ def __init__(self, exec_lines=None):
         IP = InteractiveShell.instance(config=config, profile_dir=profile)
         atexit.register(self.cleanup)
 
-        sys.stdout = self.cout
-        sys.stderr = self.cout
-
-        # For debugging, so we can see normal output, use this:
-        #from IPython.utils.io import Tee
-        #sys.stdout = Tee(self.cout, channel='stdout') # dbg
-        #sys.stderr = Tee(self.cout, channel='stderr') # dbg
-
         # Store a few parts of IPython we'll need.
         self.IP = IP
         self.user_ns = self.IP.user_ns


Test patch: