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

163 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-25 06:05 +0000

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 

19# TODO: Move the various attributes (cache_size, [others now moved]). Some 

20# of these are also attributes of InteractiveShell. They should be on ONE object 

21# only and the other objects should ask that one object for their values. 

22 

23class DisplayHook(Configurable): 

24 """The custom IPython displayhook to replace sys.displayhook. 

25 

26 This class does many things, but the basic idea is that it is a callable 

27 that gets called anytime user code returns a value. 

28 """ 

29 

30 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', 

31 allow_none=True) 

32 exec_result = Instance('IPython.core.interactiveshell.ExecutionResult', 

33 allow_none=True) 

34 cull_fraction = Float(0.2) 

35 

36 def __init__(self, shell=None, cache_size=1000, **kwargs): 

37 super(DisplayHook, self).__init__(shell=shell, **kwargs) 

38 cache_size_min = 3 

39 if cache_size <= 0: 

40 self.do_full_cache = 0 

41 cache_size = 0 

42 elif cache_size < cache_size_min: 

43 self.do_full_cache = 0 

44 cache_size = 0 

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

46 cache_size_min,stacklevel=3) 

47 else: 

48 self.do_full_cache = 1 

49 

50 self.cache_size = cache_size 

51 

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

53 self.shell = shell 

54 

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

56 

57 # these are deliberately global: 

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

59 self.shell.user_ns.update(to_user_ns) 

60 

61 @property 

62 def prompt_count(self): 

63 return self.shell.execution_count 

64 

65 #------------------------------------------------------------------------- 

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

67 # of the displayhook. 

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

69 

70 def check_for_underscore(self): 

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

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

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

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

75 if '_' in builtin_mod.__dict__: 

76 try: 

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

78 if user_value is not self._: 

79 return 

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

81 except KeyError: 

82 pass 

83 

84 def quiet(self): 

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

86 if self.exec_result is not None: 

87 return self.semicolon_at_end_of_expression(self.exec_result.info.raw_cell) 

88 return False 

89 

90 @staticmethod 

91 def semicolon_at_end_of_expression(expression): 

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

93 

94 sio = _io.StringIO(expression) 

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

96 

97 for token in reversed(tokens): 

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

99 continue 

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

101 return True 

102 else: 

103 return False 

104 

105 def start_displayhook(self): 

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

107 pass 

108 

109 def write_output_prompt(self): 

110 """Write the output prompt. 

111 

112 The default implementation simply writes the prompt to 

113 ``sys.stdout``. 

114 """ 

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

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

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

118 if self.do_full_cache: 

119 sys.stdout.write(outprompt) 

120 

121 def compute_format_data(self, result): 

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

123 

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

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

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

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

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

129 display that data in an appropriate manner. 

130 

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

132 NOT actually print or write that to a stream. 

133 

134 Parameters 

135 ---------- 

136 result : object 

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

138 computed. 

139 

140 Returns 

141 ------- 

142 (format_dict, md_dict) : dict 

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

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

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

146 MIME type representation of the object. 

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

148 of metadata associated with each output. 

149 

150 """ 

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

152 

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

154 prompt_end_newline = False 

155 

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

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

158 

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

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

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

162 frontends. 

163 

164 Parameters 

165 ---------- 

166 format_dict : dict 

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

168 md_dict : dict (optional) 

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

170 """ 

171 if 'text/plain' not in format_dict: 

172 # nothing to do 

173 return 

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

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

176 # standard IPython behavior. 

177 result_repr = format_dict['text/plain'] 

178 if '\n' in result_repr: 

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

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

181 # their first line. 

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

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

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

185 # a newline. 

186 if not self.prompt_end_newline: 

187 # But avoid extraneous empty lines. 

188 result_repr = '\n' + result_repr 

189 

190 try: 

191 print(result_repr) 

192 except UnicodeEncodeError: 

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

194 # it with its \u or \x representation 

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

196 

197 def update_user_ns(self, result): 

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

199 

200 # Avoid recursive reference when displaying _oh/Out 

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

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

203 self.cull_cache() 

204 

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

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

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

208 # by the user. 

209 update_unders = True 

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

211 if not unders in self.shell.user_ns: 

212 continue 

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

214 update_unders = False 

215 

216 self.___ = self.__ 

217 self.__ = self._ 

218 self._ = result 

219 

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

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

222 '__':self.__, 

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

224 

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

226 to_main = {} 

227 if self.do_full_cache: 

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

229 to_main[new_result] = result 

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

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

232 

233 def fill_exec_result(self, result): 

234 if self.exec_result is not None: 

235 self.exec_result.result = result 

236 

237 def log_output(self, format_dict): 

238 """Log the output.""" 

239 if 'text/plain' not in format_dict: 

240 # nothing to do 

241 return 

242 if self.shell.logger.log_output: 

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

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

245 format_dict['text/plain'] 

246 

247 def finish_displayhook(self): 

248 """Finish up all displayhook activities.""" 

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

250 sys.stdout.flush() 

251 

252 def __call__(self, result=None): 

253 """Printing with history cache management. 

254 

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

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

257 """ 

258 self.check_for_underscore() 

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

260 self.start_displayhook() 

261 self.write_output_prompt() 

262 format_dict, md_dict = self.compute_format_data(result) 

263 self.update_user_ns(result) 

264 self.fill_exec_result(result) 

265 if format_dict: 

266 self.write_format_data(format_dict, md_dict) 

267 self.log_output(format_dict) 

268 self.finish_displayhook() 

269 

270 def cull_cache(self): 

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

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

273 sz = len(oh) 

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

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

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

277 

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

279 if i >= cull_count: 

280 break 

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

282 oh.pop(n, None) 

283 

284 def flush(self): 

285 if not self.do_full_cache: 

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

287 "if full caching is not enabled!") 

288 # delete auto-generated vars from global namespace 

289 

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

291 key = '_'+repr(n) 

292 try: 

293 del self.shell.user_ns_hidden[key] 

294 except KeyError: 

295 pass 

296 try: 

297 del self.shell.user_ns[key] 

298 except KeyError: 

299 pass 

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

301 # '_oh' key set up. 

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

303 if oh is not None: 

304 oh.clear() 

305 

306 # Release our own references to objects: 

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

308 

309 if '_' not in builtin_mod.__dict__: 

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

311 import gc 

312 # TODO: Is this really needed? 

313 # IronPython blocks here forever 

314 if sys.platform != "cli": 

315 gc.collect() 

316 

317 

318class CapturingDisplayHook(object): 

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

320 self.shell = shell 

321 if outputs is None: 

322 outputs = [] 

323 self.outputs = outputs 

324 

325 def __call__(self, result=None): 

326 if result is None: 

327 return 

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

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