1# encoding: utf-8
2"""IO capturing utilities."""
3
4# Copyright (c) IPython Development Team.
5# Distributed under the terms of the Modified BSD License.
6
7
8import sys
9from io import StringIO
10from types import TracebackType
11from typing import Any, List, Optional, Type
12
13#-----------------------------------------------------------------------------
14# Classes and functions
15#-----------------------------------------------------------------------------
16
17
18class RichOutput:
19 def __init__(self, data=None, metadata=None, transient=None, update=False):
20 self.data = data or {}
21 self.metadata = metadata or {}
22 self.transient = transient or {}
23 self.update = update
24
25 def display(self):
26 from IPython.display import publish_display_data
27 publish_display_data(data=self.data, metadata=self.metadata,
28 transient=self.transient, update=self.update)
29
30 def _repr_mime_(self, mime):
31 if mime not in self.data:
32 return
33 data = self.data[mime]
34 if mime in self.metadata:
35 return data, self.metadata[mime]
36 else:
37 return data
38
39 def _repr_mimebundle_(self, include=None, exclude=None):
40 return self.data, self.metadata
41
42 def _repr_html_(self):
43 return self._repr_mime_("text/html")
44
45 def _repr_latex_(self):
46 return self._repr_mime_("text/latex")
47
48 def _repr_json_(self):
49 return self._repr_mime_("application/json")
50
51 def _repr_javascript_(self):
52 return self._repr_mime_("application/javascript")
53
54 def _repr_png_(self):
55 return self._repr_mime_("image/png")
56
57 def _repr_jpeg_(self):
58 return self._repr_mime_("image/jpeg")
59
60 def _repr_svg_(self):
61 return self._repr_mime_("image/svg+xml")
62
63
64class CapturedIO:
65 """Simple object for containing captured stdout/err and rich display StringIO objects
66
67 Each instance `c` has three attributes:
68
69 - ``c.stdout`` : standard output as a string
70 - ``c.stderr`` : standard error as a string
71 - ``c.outputs``: a list of rich display outputs
72
73 Additionally, there's a ``c.show()`` method which will print all of the
74 above in the same order, and can be invoked simply via ``c()``.
75 """
76
77 def __init__(
78 self,
79 stdout: Optional[StringIO],
80 stderr: Optional[StringIO],
81 outputs: Optional[List[Any]] = None,
82 ):
83 self._stdout = stdout
84 self._stderr = stderr
85 if outputs is None:
86 outputs = []
87 self._outputs = outputs
88
89 def __str__(self):
90 return self.stdout
91
92 @property
93 def stdout(self) -> str:
94 "Captured standard output"
95 if not self._stdout:
96 return ''
97 return self._stdout.getvalue()
98
99 @property
100 def stderr(self) -> str:
101 "Captured standard error"
102 if not self._stderr:
103 return ''
104 return self._stderr.getvalue()
105
106 @property
107 def outputs(self):
108 """A list of the captured rich display outputs, if any.
109
110 If you have a CapturedIO object ``c``, these can be displayed in IPython
111 using::
112
113 from IPython.display import display
114 for o in c.outputs:
115 display(o)
116 """
117 return [ RichOutput(**kargs) for kargs in self._outputs ]
118
119 def show(self):
120 """write my output to sys.stdout/err as appropriate"""
121 sys.stdout.write(self.stdout)
122 sys.stderr.write(self.stderr)
123 sys.stdout.flush()
124 sys.stderr.flush()
125 for kargs in self._outputs:
126 RichOutput(**kargs).display()
127
128 __call__ = show
129
130
131class capture_output:
132 """context manager for capturing stdout/err"""
133 stdout = True
134 stderr = True
135 display = True
136
137 def __init__(self, stdout: bool=True, stderr: bool=True, display: bool=True):
138 self.stdout = stdout
139 self.stderr = stderr
140 self.display = display
141 self.shell = None
142
143 def __enter__(self) -> CapturedIO:
144 from IPython.core.getipython import get_ipython
145 from IPython.core.displaypub import CapturingDisplayPublisher
146 from IPython.core.displayhook import CapturingDisplayHook
147
148 self.sys_stdout = sys.stdout
149 self.sys_stderr = sys.stderr
150
151 if self.display:
152 self.shell = get_ipython()
153 if self.shell is None:
154 self.save_display_pub = None
155 self.display = False
156
157 stdout = stderr = outputs = None
158 if self.stdout:
159 stdout = sys.stdout = StringIO()
160 if self.stderr:
161 stderr = sys.stderr = StringIO()
162 if self.display:
163 self.save_display_pub = self.shell.display_pub
164 self.shell.display_pub = CapturingDisplayPublisher()
165 outputs = self.shell.display_pub.outputs
166 self.save_display_hook = sys.displayhook
167 sys.displayhook = CapturingDisplayHook(shell=self.shell,
168 outputs=outputs)
169
170 return CapturedIO(stdout, stderr, outputs)
171
172 def __exit__(self, exc_type: Optional[Type[BaseException]], exc_value: Optional[BaseException], traceback: Optional[TracebackType]):
173 sys.stdout = self.sys_stdout
174 sys.stderr = self.sys_stderr
175 if self.display and self.shell:
176 self.shell.display_pub = self.save_display_pub
177 sys.displayhook = self.save_display_hook