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