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