##// END OF EJS Templates
Backport PR #9851: Also capture execution results using sys.displayhook...
Matthias Bussonnier -
Show More
@@ -0,0 +1,3 b''
1 - The :cellmagic:`capture` magic can now capture the result of a cell (from an
2 expression on the last line), as well as printed and displayed output.
3 :ghpull:`9851`.
@@ -1,295 +1,309 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Displayhook for IPython.
2 """Displayhook for IPython.
3
3
4 This defines a callable class that IPython uses for `sys.displayhook`.
4 This defines a callable class that IPython uses for `sys.displayhook`.
5 """
5 """
6
6
7 # Copyright (c) IPython Development Team.
7 # Copyright (c) IPython Development Team.
8 # Distributed under the terms of the Modified BSD License.
8 # Distributed under the terms of the Modified BSD License.
9
9
10 from __future__ import print_function
10 from __future__ import print_function
11
11
12 import sys
12 import sys
13 import io as _io
13 import io as _io
14 import tokenize
14 import tokenize
15
15
16 from traitlets.config.configurable import Configurable
16 from traitlets.config.configurable import Configurable
17 from IPython.utils.py3compat import builtin_mod, cast_unicode_py2
17 from IPython.utils.py3compat import builtin_mod, cast_unicode_py2
18 from traitlets import Instance, Float
18 from traitlets import Instance, Float
19 from warnings import warn
19 from warnings import warn
20
20
21 # TODO: Move the various attributes (cache_size, [others now moved]). Some
21 # TODO: Move the various attributes (cache_size, [others now moved]). Some
22 # of these are also attributes of InteractiveShell. They should be on ONE object
22 # of these are also attributes of InteractiveShell. They should be on ONE object
23 # only and the other objects should ask that one object for their values.
23 # only and the other objects should ask that one object for their values.
24
24
25 class DisplayHook(Configurable):
25 class DisplayHook(Configurable):
26 """The custom IPython displayhook to replace sys.displayhook.
26 """The custom IPython displayhook to replace sys.displayhook.
27
27
28 This class does many things, but the basic idea is that it is a callable
28 This class does many things, but the basic idea is that it is a callable
29 that gets called anytime user code returns a value.
29 that gets called anytime user code returns a value.
30 """
30 """
31
31
32 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC',
32 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC',
33 allow_none=True)
33 allow_none=True)
34 exec_result = Instance('IPython.core.interactiveshell.ExecutionResult',
34 exec_result = Instance('IPython.core.interactiveshell.ExecutionResult',
35 allow_none=True)
35 allow_none=True)
36 cull_fraction = Float(0.2)
36 cull_fraction = Float(0.2)
37
37
38 def __init__(self, shell=None, cache_size=1000, **kwargs):
38 def __init__(self, shell=None, cache_size=1000, **kwargs):
39 super(DisplayHook, self).__init__(shell=shell, **kwargs)
39 super(DisplayHook, self).__init__(shell=shell, **kwargs)
40 cache_size_min = 3
40 cache_size_min = 3
41 if cache_size <= 0:
41 if cache_size <= 0:
42 self.do_full_cache = 0
42 self.do_full_cache = 0
43 cache_size = 0
43 cache_size = 0
44 elif cache_size < cache_size_min:
44 elif cache_size < cache_size_min:
45 self.do_full_cache = 0
45 self.do_full_cache = 0
46 cache_size = 0
46 cache_size = 0
47 warn('caching was disabled (min value for cache size is %s).' %
47 warn('caching was disabled (min value for cache size is %s).' %
48 cache_size_min,level=3)
48 cache_size_min,level=3)
49 else:
49 else:
50 self.do_full_cache = 1
50 self.do_full_cache = 1
51
51
52 self.cache_size = cache_size
52 self.cache_size = cache_size
53
53
54 # we need a reference to the user-level namespace
54 # we need a reference to the user-level namespace
55 self.shell = shell
55 self.shell = shell
56
56
57 self._,self.__,self.___ = '','',''
57 self._,self.__,self.___ = '','',''
58
58
59 # these are deliberately global:
59 # these are deliberately global:
60 to_user_ns = {'_':self._,'__':self.__,'___':self.___}
60 to_user_ns = {'_':self._,'__':self.__,'___':self.___}
61 self.shell.user_ns.update(to_user_ns)
61 self.shell.user_ns.update(to_user_ns)
62
62
63 @property
63 @property
64 def prompt_count(self):
64 def prompt_count(self):
65 return self.shell.execution_count
65 return self.shell.execution_count
66
66
67 #-------------------------------------------------------------------------
67 #-------------------------------------------------------------------------
68 # Methods used in __call__. Override these methods to modify the behavior
68 # Methods used in __call__. Override these methods to modify the behavior
69 # of the displayhook.
69 # of the displayhook.
70 #-------------------------------------------------------------------------
70 #-------------------------------------------------------------------------
71
71
72 def check_for_underscore(self):
72 def check_for_underscore(self):
73 """Check if the user has set the '_' variable by hand."""
73 """Check if the user has set the '_' variable by hand."""
74 # If something injected a '_' variable in __builtin__, delete
74 # If something injected a '_' variable in __builtin__, delete
75 # ipython's automatic one so we don't clobber that. gettext() in
75 # ipython's automatic one so we don't clobber that. gettext() in
76 # particular uses _, so we need to stay away from it.
76 # particular uses _, so we need to stay away from it.
77 if '_' in builtin_mod.__dict__:
77 if '_' in builtin_mod.__dict__:
78 try:
78 try:
79 del self.shell.user_ns['_']
79 del self.shell.user_ns['_']
80 except KeyError:
80 except KeyError:
81 pass
81 pass
82
82
83 def quiet(self):
83 def quiet(self):
84 """Should we silence the display hook because of ';'?"""
84 """Should we silence the display hook because of ';'?"""
85 # do not print output if input ends in ';'
85 # do not print output if input ends in ';'
86
86
87 try:
87 try:
88 cell = cast_unicode_py2(self.shell.history_manager.input_hist_parsed[-1])
88 cell = cast_unicode_py2(self.shell.history_manager.input_hist_parsed[-1])
89 except IndexError:
89 except IndexError:
90 # some uses of ipshellembed may fail here
90 # some uses of ipshellembed may fail here
91 return False
91 return False
92
92
93 sio = _io.StringIO(cell)
93 sio = _io.StringIO(cell)
94 tokens = list(tokenize.generate_tokens(sio.readline))
94 tokens = list(tokenize.generate_tokens(sio.readline))
95
95
96 for token in reversed(tokens):
96 for token in reversed(tokens):
97 if token[0] in (tokenize.ENDMARKER, tokenize.NL, tokenize.NEWLINE, tokenize.COMMENT):
97 if token[0] in (tokenize.ENDMARKER, tokenize.NL, tokenize.NEWLINE, tokenize.COMMENT):
98 continue
98 continue
99 if (token[0] == tokenize.OP) and (token[1] == ';'):
99 if (token[0] == tokenize.OP) and (token[1] == ';'):
100 return True
100 return True
101 else:
101 else:
102 return False
102 return False
103
103
104 def start_displayhook(self):
104 def start_displayhook(self):
105 """Start the displayhook, initializing resources."""
105 """Start the displayhook, initializing resources."""
106 pass
106 pass
107
107
108 def write_output_prompt(self):
108 def write_output_prompt(self):
109 """Write the output prompt.
109 """Write the output prompt.
110
110
111 The default implementation simply writes the prompt to
111 The default implementation simply writes the prompt to
112 ``sys.stdout``.
112 ``sys.stdout``.
113 """
113 """
114 # Use write, not print which adds an extra space.
114 # Use write, not print which adds an extra space.
115 sys.stdout.write(self.shell.separate_out)
115 sys.stdout.write(self.shell.separate_out)
116 outprompt = 'Out[{}]: '.format(self.shell.execution_count)
116 outprompt = 'Out[{}]: '.format(self.shell.execution_count)
117 if self.do_full_cache:
117 if self.do_full_cache:
118 sys.stdout.write(outprompt)
118 sys.stdout.write(outprompt)
119
119
120 def compute_format_data(self, result):
120 def compute_format_data(self, result):
121 """Compute format data of the object to be displayed.
121 """Compute format data of the object to be displayed.
122
122
123 The format data is a generalization of the :func:`repr` of an object.
123 The format data is a generalization of the :func:`repr` of an object.
124 In the default implementation the format data is a :class:`dict` of
124 In the default implementation the format data is a :class:`dict` of
125 key value pair where the keys are valid MIME types and the values
125 key value pair where the keys are valid MIME types and the values
126 are JSON'able data structure containing the raw data for that MIME
126 are JSON'able data structure containing the raw data for that MIME
127 type. It is up to frontends to determine pick a MIME to to use and
127 type. It is up to frontends to determine pick a MIME to to use and
128 display that data in an appropriate manner.
128 display that data in an appropriate manner.
129
129
130 This method only computes the format data for the object and should
130 This method only computes the format data for the object and should
131 NOT actually print or write that to a stream.
131 NOT actually print or write that to a stream.
132
132
133 Parameters
133 Parameters
134 ----------
134 ----------
135 result : object
135 result : object
136 The Python object passed to the display hook, whose format will be
136 The Python object passed to the display hook, whose format will be
137 computed.
137 computed.
138
138
139 Returns
139 Returns
140 -------
140 -------
141 (format_dict, md_dict) : dict
141 (format_dict, md_dict) : dict
142 format_dict is a :class:`dict` whose keys are valid MIME types and values are
142 format_dict is a :class:`dict` whose keys are valid MIME types and values are
143 JSON'able raw data for that MIME type. It is recommended that
143 JSON'able raw data for that MIME type. It is recommended that
144 all return values of this should always include the "text/plain"
144 all return values of this should always include the "text/plain"
145 MIME type representation of the object.
145 MIME type representation of the object.
146 md_dict is a :class:`dict` with the same MIME type keys
146 md_dict is a :class:`dict` with the same MIME type keys
147 of metadata associated with each output.
147 of metadata associated with each output.
148
148
149 """
149 """
150 return self.shell.display_formatter.format(result)
150 return self.shell.display_formatter.format(result)
151
151
152 # This can be set to True by the write_output_prompt method in a subclass
152 # This can be set to True by the write_output_prompt method in a subclass
153 prompt_end_newline = False
153 prompt_end_newline = False
154
154
155 def write_format_data(self, format_dict, md_dict=None):
155 def write_format_data(self, format_dict, md_dict=None):
156 """Write the format data dict to the frontend.
156 """Write the format data dict to the frontend.
157
157
158 This default version of this method simply writes the plain text
158 This default version of this method simply writes the plain text
159 representation of the object to ``sys.stdout``. Subclasses should
159 representation of the object to ``sys.stdout``. Subclasses should
160 override this method to send the entire `format_dict` to the
160 override this method to send the entire `format_dict` to the
161 frontends.
161 frontends.
162
162
163 Parameters
163 Parameters
164 ----------
164 ----------
165 format_dict : dict
165 format_dict : dict
166 The format dict for the object passed to `sys.displayhook`.
166 The format dict for the object passed to `sys.displayhook`.
167 md_dict : dict (optional)
167 md_dict : dict (optional)
168 The metadata dict to be associated with the display data.
168 The metadata dict to be associated with the display data.
169 """
169 """
170 if 'text/plain' not in format_dict:
170 if 'text/plain' not in format_dict:
171 # nothing to do
171 # nothing to do
172 return
172 return
173 # We want to print because we want to always make sure we have a
173 # We want to print because we want to always make sure we have a
174 # newline, even if all the prompt separators are ''. This is the
174 # newline, even if all the prompt separators are ''. This is the
175 # standard IPython behavior.
175 # standard IPython behavior.
176 result_repr = format_dict['text/plain']
176 result_repr = format_dict['text/plain']
177 if '\n' in result_repr:
177 if '\n' in result_repr:
178 # So that multi-line strings line up with the left column of
178 # So that multi-line strings line up with the left column of
179 # the screen, instead of having the output prompt mess up
179 # the screen, instead of having the output prompt mess up
180 # their first line.
180 # their first line.
181 # We use the prompt template instead of the expanded prompt
181 # We use the prompt template instead of the expanded prompt
182 # because the expansion may add ANSI escapes that will interfere
182 # because the expansion may add ANSI escapes that will interfere
183 # with our ability to determine whether or not we should add
183 # with our ability to determine whether or not we should add
184 # a newline.
184 # a newline.
185 if not self.prompt_end_newline:
185 if not self.prompt_end_newline:
186 # But avoid extraneous empty lines.
186 # But avoid extraneous empty lines.
187 result_repr = '\n' + result_repr
187 result_repr = '\n' + result_repr
188
188
189 print(result_repr)
189 print(result_repr)
190
190
191 def update_user_ns(self, result):
191 def update_user_ns(self, result):
192 """Update user_ns with various things like _, __, _1, etc."""
192 """Update user_ns with various things like _, __, _1, etc."""
193
193
194 # Avoid recursive reference when displaying _oh/Out
194 # Avoid recursive reference when displaying _oh/Out
195 if result is not self.shell.user_ns['_oh']:
195 if result is not self.shell.user_ns['_oh']:
196 if len(self.shell.user_ns['_oh']) >= self.cache_size and self.do_full_cache:
196 if len(self.shell.user_ns['_oh']) >= self.cache_size and self.do_full_cache:
197 self.cull_cache()
197 self.cull_cache()
198 # Don't overwrite '_' and friends if '_' is in __builtin__ (otherwise
198 # Don't overwrite '_' and friends if '_' is in __builtin__ (otherwise
199 # we cause buggy behavior for things like gettext).
199 # we cause buggy behavior for things like gettext).
200
200
201 if '_' not in builtin_mod.__dict__:
201 if '_' not in builtin_mod.__dict__:
202 self.___ = self.__
202 self.___ = self.__
203 self.__ = self._
203 self.__ = self._
204 self._ = result
204 self._ = result
205 self.shell.push({'_':self._,
205 self.shell.push({'_':self._,
206 '__':self.__,
206 '__':self.__,
207 '___':self.___}, interactive=False)
207 '___':self.___}, interactive=False)
208
208
209 # hackish access to top-level namespace to create _1,_2... dynamically
209 # hackish access to top-level namespace to create _1,_2... dynamically
210 to_main = {}
210 to_main = {}
211 if self.do_full_cache:
211 if self.do_full_cache:
212 new_result = '_'+repr(self.prompt_count)
212 new_result = '_'+repr(self.prompt_count)
213 to_main[new_result] = result
213 to_main[new_result] = result
214 self.shell.push(to_main, interactive=False)
214 self.shell.push(to_main, interactive=False)
215 self.shell.user_ns['_oh'][self.prompt_count] = result
215 self.shell.user_ns['_oh'][self.prompt_count] = result
216
216
217 def fill_exec_result(self, result):
217 def fill_exec_result(self, result):
218 if self.exec_result is not None:
218 if self.exec_result is not None:
219 self.exec_result.result = result
219 self.exec_result.result = result
220
220
221 def log_output(self, format_dict):
221 def log_output(self, format_dict):
222 """Log the output."""
222 """Log the output."""
223 if 'text/plain' not in format_dict:
223 if 'text/plain' not in format_dict:
224 # nothing to do
224 # nothing to do
225 return
225 return
226 if self.shell.logger.log_output:
226 if self.shell.logger.log_output:
227 self.shell.logger.log_write(format_dict['text/plain'], 'output')
227 self.shell.logger.log_write(format_dict['text/plain'], 'output')
228 self.shell.history_manager.output_hist_reprs[self.prompt_count] = \
228 self.shell.history_manager.output_hist_reprs[self.prompt_count] = \
229 format_dict['text/plain']
229 format_dict['text/plain']
230
230
231 def finish_displayhook(self):
231 def finish_displayhook(self):
232 """Finish up all displayhook activities."""
232 """Finish up all displayhook activities."""
233 sys.stdout.write(self.shell.separate_out2)
233 sys.stdout.write(self.shell.separate_out2)
234 sys.stdout.flush()
234 sys.stdout.flush()
235
235
236 def __call__(self, result=None):
236 def __call__(self, result=None):
237 """Printing with history cache management.
237 """Printing with history cache management.
238
238
239 This is invoked everytime the interpreter needs to print, and is
239 This is invoked everytime the interpreter needs to print, and is
240 activated by setting the variable sys.displayhook to it.
240 activated by setting the variable sys.displayhook to it.
241 """
241 """
242 self.check_for_underscore()
242 self.check_for_underscore()
243 if result is not None and not self.quiet():
243 if result is not None and not self.quiet():
244 self.start_displayhook()
244 self.start_displayhook()
245 self.write_output_prompt()
245 self.write_output_prompt()
246 format_dict, md_dict = self.compute_format_data(result)
246 format_dict, md_dict = self.compute_format_data(result)
247 self.update_user_ns(result)
247 self.update_user_ns(result)
248 self.fill_exec_result(result)
248 self.fill_exec_result(result)
249 if format_dict:
249 if format_dict:
250 self.write_format_data(format_dict, md_dict)
250 self.write_format_data(format_dict, md_dict)
251 self.log_output(format_dict)
251 self.log_output(format_dict)
252 self.finish_displayhook()
252 self.finish_displayhook()
253
253
254 def cull_cache(self):
254 def cull_cache(self):
255 """Output cache is full, cull the oldest entries"""
255 """Output cache is full, cull the oldest entries"""
256 oh = self.shell.user_ns.get('_oh', {})
256 oh = self.shell.user_ns.get('_oh', {})
257 sz = len(oh)
257 sz = len(oh)
258 cull_count = max(int(sz * self.cull_fraction), 2)
258 cull_count = max(int(sz * self.cull_fraction), 2)
259 warn('Output cache limit (currently {sz} entries) hit.\n'
259 warn('Output cache limit (currently {sz} entries) hit.\n'
260 'Flushing oldest {cull_count} entries.'.format(sz=sz, cull_count=cull_count))
260 'Flushing oldest {cull_count} entries.'.format(sz=sz, cull_count=cull_count))
261
261
262 for i, n in enumerate(sorted(oh)):
262 for i, n in enumerate(sorted(oh)):
263 if i >= cull_count:
263 if i >= cull_count:
264 break
264 break
265 self.shell.user_ns.pop('_%i' % n, None)
265 self.shell.user_ns.pop('_%i' % n, None)
266 oh.pop(n, None)
266 oh.pop(n, None)
267
267
268
268
269 def flush(self):
269 def flush(self):
270 if not self.do_full_cache:
270 if not self.do_full_cache:
271 raise ValueError("You shouldn't have reached the cache flush "
271 raise ValueError("You shouldn't have reached the cache flush "
272 "if full caching is not enabled!")
272 "if full caching is not enabled!")
273 # delete auto-generated vars from global namespace
273 # delete auto-generated vars from global namespace
274
274
275 for n in range(1,self.prompt_count + 1):
275 for n in range(1,self.prompt_count + 1):
276 key = '_'+repr(n)
276 key = '_'+repr(n)
277 try:
277 try:
278 del self.shell.user_ns[key]
278 del self.shell.user_ns[key]
279 except: pass
279 except: pass
280 # In some embedded circumstances, the user_ns doesn't have the
280 # In some embedded circumstances, the user_ns doesn't have the
281 # '_oh' key set up.
281 # '_oh' key set up.
282 oh = self.shell.user_ns.get('_oh', None)
282 oh = self.shell.user_ns.get('_oh', None)
283 if oh is not None:
283 if oh is not None:
284 oh.clear()
284 oh.clear()
285
285
286 # Release our own references to objects:
286 # Release our own references to objects:
287 self._, self.__, self.___ = '', '', ''
287 self._, self.__, self.___ = '', '', ''
288
288
289 if '_' not in builtin_mod.__dict__:
289 if '_' not in builtin_mod.__dict__:
290 self.shell.user_ns.update({'_':None,'__':None, '___':None})
290 self.shell.user_ns.update({'_':None,'__':None, '___':None})
291 import gc
291 import gc
292 # TODO: Is this really needed?
292 # TODO: Is this really needed?
293 # IronPython blocks here forever
293 # IronPython blocks here forever
294 if sys.platform != "cli":
294 if sys.platform != "cli":
295 gc.collect()
295 gc.collect()
296
297
298 class CapturingDisplayHook(object):
299 def __init__(self, shell, outputs=None):
300 self.shell = shell
301 if outputs is None:
302 outputs = []
303 self.outputs = outputs
304
305 def __call__(self, result=None):
306 if result is None:
307 return
308 format_dict, md_dict = self.shell.display_formatter.format(result)
309 self.outputs.append((format_dict, md_dict))
@@ -1,169 +1,173 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """IO capturing utilities."""
2 """IO capturing utilities."""
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7 from __future__ import print_function, absolute_import
7 from __future__ import print_function, absolute_import
8
8
9 import sys
9 import sys
10
10
11 from IPython.utils.py3compat import PY3
11 from IPython.utils.py3compat import PY3
12
12
13 if PY3:
13 if PY3:
14 from io import StringIO
14 from io import StringIO
15 else:
15 else:
16 from StringIO import StringIO
16 from StringIO import StringIO
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Classes and functions
19 # Classes and functions
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22
22
23 class RichOutput(object):
23 class RichOutput(object):
24 def __init__(self, data=None, metadata=None):
24 def __init__(self, data=None, metadata=None):
25 self.data = data or {}
25 self.data = data or {}
26 self.metadata = metadata or {}
26 self.metadata = metadata or {}
27
27
28 def display(self):
28 def display(self):
29 from IPython.display import publish_display_data
29 from IPython.display import publish_display_data
30 publish_display_data(data=self.data, metadata=self.metadata)
30 publish_display_data(data=self.data, metadata=self.metadata)
31
31
32 def _repr_mime_(self, mime):
32 def _repr_mime_(self, mime):
33 if mime not in self.data:
33 if mime not in self.data:
34 return
34 return
35 data = self.data[mime]
35 data = self.data[mime]
36 if mime in self.metadata:
36 if mime in self.metadata:
37 return data, self.metadata[mime]
37 return data, self.metadata[mime]
38 else:
38 else:
39 return data
39 return data
40
40
41 def _repr_html_(self):
41 def _repr_html_(self):
42 return self._repr_mime_("text/html")
42 return self._repr_mime_("text/html")
43
43
44 def _repr_latex_(self):
44 def _repr_latex_(self):
45 return self._repr_mime_("text/latex")
45 return self._repr_mime_("text/latex")
46
46
47 def _repr_json_(self):
47 def _repr_json_(self):
48 return self._repr_mime_("application/json")
48 return self._repr_mime_("application/json")
49
49
50 def _repr_javascript_(self):
50 def _repr_javascript_(self):
51 return self._repr_mime_("application/javascript")
51 return self._repr_mime_("application/javascript")
52
52
53 def _repr_png_(self):
53 def _repr_png_(self):
54 return self._repr_mime_("image/png")
54 return self._repr_mime_("image/png")
55
55
56 def _repr_jpeg_(self):
56 def _repr_jpeg_(self):
57 return self._repr_mime_("image/jpeg")
57 return self._repr_mime_("image/jpeg")
58
58
59 def _repr_svg_(self):
59 def _repr_svg_(self):
60 return self._repr_mime_("image/svg+xml")
60 return self._repr_mime_("image/svg+xml")
61
61
62
62
63 class CapturedIO(object):
63 class CapturedIO(object):
64 """Simple object for containing captured stdout/err and rich display StringIO objects
64 """Simple object for containing captured stdout/err and rich display StringIO objects
65
65
66 Each instance `c` has three attributes:
66 Each instance `c` has three attributes:
67
67
68 - ``c.stdout`` : standard output as a string
68 - ``c.stdout`` : standard output as a string
69 - ``c.stderr`` : standard error as a string
69 - ``c.stderr`` : standard error as a string
70 - ``c.outputs``: a list of rich display outputs
70 - ``c.outputs``: a list of rich display outputs
71
71
72 Additionally, there's a ``c.show()`` method which will print all of the
72 Additionally, there's a ``c.show()`` method which will print all of the
73 above in the same order, and can be invoked simply via ``c()``.
73 above in the same order, and can be invoked simply via ``c()``.
74 """
74 """
75
75
76 def __init__(self, stdout, stderr, outputs=None):
76 def __init__(self, stdout, stderr, outputs=None):
77 self._stdout = stdout
77 self._stdout = stdout
78 self._stderr = stderr
78 self._stderr = stderr
79 if outputs is None:
79 if outputs is None:
80 outputs = []
80 outputs = []
81 self._outputs = outputs
81 self._outputs = outputs
82
82
83 def __str__(self):
83 def __str__(self):
84 return self.stdout
84 return self.stdout
85
85
86 @property
86 @property
87 def stdout(self):
87 def stdout(self):
88 "Captured standard output"
88 "Captured standard output"
89 if not self._stdout:
89 if not self._stdout:
90 return ''
90 return ''
91 return self._stdout.getvalue()
91 return self._stdout.getvalue()
92
92
93 @property
93 @property
94 def stderr(self):
94 def stderr(self):
95 "Captured standard error"
95 "Captured standard error"
96 if not self._stderr:
96 if not self._stderr:
97 return ''
97 return ''
98 return self._stderr.getvalue()
98 return self._stderr.getvalue()
99
99
100 @property
100 @property
101 def outputs(self):
101 def outputs(self):
102 """A list of the captured rich display outputs, if any.
102 """A list of the captured rich display outputs, if any.
103
103
104 If you have a CapturedIO object ``c``, these can be displayed in IPython
104 If you have a CapturedIO object ``c``, these can be displayed in IPython
105 using::
105 using::
106
106
107 from IPython.display import display
107 from IPython.display import display
108 for o in c.outputs:
108 for o in c.outputs:
109 display(o)
109 display(o)
110 """
110 """
111 return [ RichOutput(d, md) for d, md in self._outputs ]
111 return [ RichOutput(d, md) for d, md in self._outputs ]
112
112
113 def show(self):
113 def show(self):
114 """write my output to sys.stdout/err as appropriate"""
114 """write my output to sys.stdout/err as appropriate"""
115 sys.stdout.write(self.stdout)
115 sys.stdout.write(self.stdout)
116 sys.stderr.write(self.stderr)
116 sys.stderr.write(self.stderr)
117 sys.stdout.flush()
117 sys.stdout.flush()
118 sys.stderr.flush()
118 sys.stderr.flush()
119 for data, metadata in self._outputs:
119 for data, metadata in self._outputs:
120 RichOutput(data, metadata).display()
120 RichOutput(data, metadata).display()
121
121
122 __call__ = show
122 __call__ = show
123
123
124
124
125 class capture_output(object):
125 class capture_output(object):
126 """context manager for capturing stdout/err"""
126 """context manager for capturing stdout/err"""
127 stdout = True
127 stdout = True
128 stderr = True
128 stderr = True
129 display = True
129 display = True
130
130
131 def __init__(self, stdout=True, stderr=True, display=True):
131 def __init__(self, stdout=True, stderr=True, display=True):
132 self.stdout = stdout
132 self.stdout = stdout
133 self.stderr = stderr
133 self.stderr = stderr
134 self.display = display
134 self.display = display
135 self.shell = None
135 self.shell = None
136
136
137 def __enter__(self):
137 def __enter__(self):
138 from IPython.core.getipython import get_ipython
138 from IPython.core.getipython import get_ipython
139 from IPython.core.displaypub import CapturingDisplayPublisher
139 from IPython.core.displaypub import CapturingDisplayPublisher
140 from IPython.core.displayhook import CapturingDisplayHook
140
141
141 self.sys_stdout = sys.stdout
142 self.sys_stdout = sys.stdout
142 self.sys_stderr = sys.stderr
143 self.sys_stderr = sys.stderr
143
144
144 if self.display:
145 if self.display:
145 self.shell = get_ipython()
146 self.shell = get_ipython()
146 if self.shell is None:
147 if self.shell is None:
147 self.save_display_pub = None
148 self.save_display_pub = None
148 self.display = False
149 self.display = False
149
150
150 stdout = stderr = outputs = None
151 stdout = stderr = outputs = None
151 if self.stdout:
152 if self.stdout:
152 stdout = sys.stdout = StringIO()
153 stdout = sys.stdout = StringIO()
153 if self.stderr:
154 if self.stderr:
154 stderr = sys.stderr = StringIO()
155 stderr = sys.stderr = StringIO()
155 if self.display:
156 if self.display:
156 self.save_display_pub = self.shell.display_pub
157 self.save_display_pub = self.shell.display_pub
157 self.shell.display_pub = CapturingDisplayPublisher()
158 self.shell.display_pub = CapturingDisplayPublisher()
158 outputs = self.shell.display_pub.outputs
159 outputs = self.shell.display_pub.outputs
159
160 self.save_display_hook = sys.displayhook
161 sys.displayhook = CapturingDisplayHook(shell=self.shell,
162 outputs=outputs)
160
163
161 return CapturedIO(stdout, stderr, outputs)
164 return CapturedIO(stdout, stderr, outputs)
162
165
163 def __exit__(self, exc_type, exc_value, traceback):
166 def __exit__(self, exc_type, exc_value, traceback):
164 sys.stdout = self.sys_stdout
167 sys.stdout = self.sys_stdout
165 sys.stderr = self.sys_stderr
168 sys.stderr = self.sys_stderr
166 if self.display and self.shell:
169 if self.display and self.shell:
167 self.shell.display_pub = self.save_display_pub
170 self.shell.display_pub = self.save_display_pub
171 sys.displayhook = self.save_display_hook
168
172
169
173
General Comments 0
You need to be logged in to leave comments. Login now