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