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