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