Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/IPython/core/displayhook.py: 21%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

173 statements  

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 

10import builtins as builtin_mod 

11import sys 

12import io as _io 

13import tokenize 

14 

15from traitlets.config.configurable import Configurable 

16from traitlets import Instance, Float 

17from warnings import warn 

18 

19from .history import HistoryOutput 

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 self._is_active = False 

41 cache_size_min = 3 

42 if cache_size <= 0: 

43 self.do_full_cache = 0 

44 cache_size = 0 

45 elif cache_size < cache_size_min: 

46 self.do_full_cache = 0 

47 cache_size = 0 

48 warn('caching was disabled (min value for cache size is %s).' % 

49 cache_size_min,stacklevel=3) 

50 else: 

51 self.do_full_cache = 1 

52 

53 self.cache_size = cache_size 

54 

55 # we need a reference to the user-level namespace 

56 self.shell = shell 

57 

58 self._,self.__,self.___ = '','','' 

59 

60 # these are deliberately global: 

61 to_user_ns = {'_':self._,'__':self.__,'___':self.___} 

62 self.shell.user_ns.update(to_user_ns) 

63 

64 @property 

65 def prompt_count(self): 

66 return self.shell.execution_count 

67 

68 #------------------------------------------------------------------------- 

69 # Methods used in __call__. Override these methods to modify the behavior 

70 # of the displayhook. 

71 #------------------------------------------------------------------------- 

72 

73 def check_for_underscore(self): 

74 """Check if the user has set the '_' variable by hand.""" 

75 # If something injected a '_' variable in __builtin__, delete 

76 # ipython's automatic one so we don't clobber that. gettext() in 

77 # particular uses _, so we need to stay away from it. 

78 if '_' in builtin_mod.__dict__: 

79 try: 

80 user_value = self.shell.user_ns['_'] 

81 if user_value is not self._: 

82 return 

83 del self.shell.user_ns['_'] 

84 except KeyError: 

85 pass 

86 

87 def quiet(self): 

88 """Should we silence the display hook because of ';'?""" 

89 # do not print output if input ends in ';' 

90 

91 try: 

92 cell = self.shell.history_manager.input_hist_parsed[-1] 

93 except IndexError: 

94 # some uses of ipshellembed may fail here 

95 return False 

96 

97 return self.semicolon_at_end_of_expression(cell) 

98 

99 @staticmethod 

100 def semicolon_at_end_of_expression(expression): 

101 """Parse Python expression and detects whether last token is ';'""" 

102 

103 sio = _io.StringIO(expression) 

104 tokens = list(tokenize.generate_tokens(sio.readline)) 

105 

106 for token in reversed(tokens): 

107 if token[0] in (tokenize.ENDMARKER, tokenize.NL, tokenize.NEWLINE, tokenize.COMMENT): 

108 continue 

109 if (token[0] == tokenize.OP) and (token[1] == ';'): 

110 return True 

111 else: 

112 return False 

113 

114 def start_displayhook(self): 

115 """Start the displayhook, initializing resources.""" 

116 self._is_active = True 

117 

118 @property 

119 def is_active(self): 

120 return self._is_active 

121 

122 def write_output_prompt(self): 

123 """Write the output prompt. 

124 

125 The default implementation simply writes the prompt to 

126 ``sys.stdout``. 

127 """ 

128 # Use write, not print which adds an extra space. 

129 sys.stdout.write(self.shell.separate_out) 

130 outprompt = 'Out[{}]: '.format(self.shell.execution_count) 

131 if self.do_full_cache: 

132 sys.stdout.write(outprompt) 

133 

134 def compute_format_data(self, result): 

135 """Compute format data of the object to be displayed. 

136 

137 The format data is a generalization of the :func:`repr` of an object. 

138 In the default implementation the format data is a :class:`dict` of 

139 key value pair where the keys are valid MIME types and the values 

140 are JSON'able data structure containing the raw data for that MIME 

141 type. It is up to frontends to determine pick a MIME to to use and 

142 display that data in an appropriate manner. 

143 

144 This method only computes the format data for the object and should 

145 NOT actually print or write that to a stream. 

146 

147 Parameters 

148 ---------- 

149 result : object 

150 The Python object passed to the display hook, whose format will be 

151 computed. 

152 

153 Returns 

154 ------- 

155 (format_dict, md_dict) : dict 

156 format_dict is a :class:`dict` whose keys are valid MIME types and values are 

157 JSON'able raw data for that MIME type. It is recommended that 

158 all return values of this should always include the "text/plain" 

159 MIME type representation of the object. 

160 md_dict is a :class:`dict` with the same MIME type keys 

161 of metadata associated with each output. 

162 

163 """ 

164 return self.shell.display_formatter.format(result) 

165 

166 # This can be set to True by the write_output_prompt method in a subclass 

167 prompt_end_newline = False 

168 

169 def write_format_data(self, format_dict, md_dict=None) -> None: 

170 """Write the format data dict to the frontend. 

171 

172 This default version of this method simply writes the plain text 

173 representation of the object to ``sys.stdout``. Subclasses should 

174 override this method to send the entire `format_dict` to the 

175 frontends. 

176 

177 Parameters 

178 ---------- 

179 format_dict : dict 

180 The format dict for the object passed to `sys.displayhook`. 

181 md_dict : dict (optional) 

182 The metadata dict to be associated with the display data. 

183 """ 

184 if 'text/plain' not in format_dict: 

185 # nothing to do 

186 return 

187 # We want to print because we want to always make sure we have a 

188 # newline, even if all the prompt separators are ''. This is the 

189 # standard IPython behavior. 

190 result_repr = format_dict['text/plain'] 

191 if '\n' in result_repr: 

192 # So that multi-line strings line up with the left column of 

193 # the screen, instead of having the output prompt mess up 

194 # their first line. 

195 # We use the prompt template instead of the expanded prompt 

196 # because the expansion may add ANSI escapes that will interfere 

197 # with our ability to determine whether or not we should add 

198 # a newline. 

199 if not self.prompt_end_newline: 

200 # But avoid extraneous empty lines. 

201 result_repr = '\n' + result_repr 

202 

203 try: 

204 print(result_repr) 

205 except UnicodeEncodeError: 

206 # If a character is not supported by the terminal encoding replace 

207 # it with its \u or \x representation 

208 print(result_repr.encode(sys.stdout.encoding,'backslashreplace').decode(sys.stdout.encoding)) 

209 

210 def update_user_ns(self, result): 

211 """Update user_ns with various things like _, __, _1, etc.""" 

212 

213 # Avoid recursive reference when displaying _oh/Out 

214 if self.cache_size and result is not self.shell.user_ns['_oh']: 

215 if len(self.shell.user_ns['_oh']) >= self.cache_size and self.do_full_cache: 

216 self.cull_cache() 

217 

218 # Don't overwrite '_' and friends if '_' is in __builtin__ 

219 # (otherwise we cause buggy behavior for things like gettext). and 

220 # do not overwrite _, __ or ___ if one of these has been assigned 

221 # by the user. 

222 update_unders = True 

223 for unders in ['_'*i for i in range(1,4)]: 

224 if unders not in self.shell.user_ns: 

225 continue 

226 if getattr(self, unders) is not self.shell.user_ns.get(unders): 

227 update_unders = False 

228 

229 self.___ = self.__ 

230 self.__ = self._ 

231 self._ = result 

232 

233 if ('_' not in builtin_mod.__dict__) and (update_unders): 

234 self.shell.push({'_':self._, 

235 '__':self.__, 

236 '___':self.___}, interactive=False) 

237 

238 # hackish access to top-level namespace to create _1,_2... dynamically 

239 to_main = {} 

240 if self.do_full_cache: 

241 new_result = '_%s' % self.prompt_count 

242 to_main[new_result] = result 

243 self.shell.push(to_main, interactive=False) 

244 self.shell.user_ns['_oh'][self.prompt_count] = result 

245 

246 def fill_exec_result(self, result): 

247 if self.exec_result is not None: 

248 self.exec_result.result = result 

249 

250 def log_output(self, format_dict): 

251 """Log the output.""" 

252 self.shell.history_manager.outputs[self.prompt_count].append( 

253 HistoryOutput(output_type="execute_result", bundle=format_dict) 

254 ) 

255 if "text/plain" not in format_dict: 

256 # nothing to do 

257 return 

258 if self.shell.logger.log_output: 

259 self.shell.logger.log_write(format_dict['text/plain'], 'output') 

260 self.shell.history_manager.output_hist_reprs[self.prompt_count] = \ 

261 format_dict['text/plain'] 

262 

263 def finish_displayhook(self): 

264 """Finish up all displayhook activities.""" 

265 sys.stdout.write(self.shell.separate_out2) 

266 sys.stdout.flush() 

267 self._is_active = False 

268 

269 def __call__(self, result=None): 

270 """Printing with history cache management. 

271 

272 This is invoked every time the interpreter needs to print, and is 

273 activated by setting the variable sys.displayhook to it. 

274 """ 

275 self.check_for_underscore() 

276 if result is not None and not self.quiet(): 

277 self.start_displayhook() 

278 self.write_output_prompt() 

279 format_dict, md_dict = self.compute_format_data(result) 

280 self.update_user_ns(result) 

281 self.fill_exec_result(result) 

282 if format_dict: 

283 self.write_format_data(format_dict, md_dict) 

284 self.log_output(format_dict) 

285 self.finish_displayhook() 

286 

287 def cull_cache(self): 

288 """Output cache is full, cull the oldest entries""" 

289 oh = self.shell.user_ns.get('_oh', {}) 

290 sz = len(oh) 

291 cull_count = max(int(sz * self.cull_fraction), 2) 

292 warn('Output cache limit (currently {sz} entries) hit.\n' 

293 'Flushing oldest {cull_count} entries.'.format(sz=sz, cull_count=cull_count)) 

294 

295 for i, n in enumerate(sorted(oh)): 

296 if i >= cull_count: 

297 break 

298 self.shell.user_ns.pop('_%i' % n, None) 

299 oh.pop(n, None) 

300 

301 def flush(self): 

302 if not self.do_full_cache: 

303 raise ValueError("You shouldn't have reached the cache flush " 

304 "if full caching is not enabled!") 

305 # delete auto-generated vars from global namespace 

306 

307 for n in range(1,self.prompt_count + 1): 

308 key = '_'+repr(n) 

309 try: 

310 del self.shell.user_ns_hidden[key] 

311 except KeyError: 

312 pass 

313 try: 

314 del self.shell.user_ns[key] 

315 except KeyError: 

316 pass 

317 # In some embedded circumstances, the user_ns doesn't have the 

318 # '_oh' key set up. 

319 oh = self.shell.user_ns.get('_oh', None) 

320 if oh is not None: 

321 oh.clear() 

322 

323 # Release our own references to objects: 

324 self._, self.__, self.___ = '', '', '' 

325 

326 if '_' not in builtin_mod.__dict__: 

327 self.shell.user_ns.update({'_':self._,'__':self.__,'___':self.___}) 

328 import gc 

329 # TODO: Is this really needed? 

330 # IronPython blocks here forever 

331 if sys.platform != "cli": 

332 gc.collect() 

333 

334 

335class CapturingDisplayHook: 

336 def __init__(self, shell, outputs=None): 

337 self.shell = shell 

338 if outputs is None: 

339 outputs = [] 

340 self.outputs = outputs 

341 

342 def __call__(self, result=None): 

343 if result is None: 

344 return 

345 format_dict, md_dict = self.shell.display_formatter.format(result) 

346 self.outputs.append({ 'data': format_dict, 'metadata': md_dict })