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