Show More
@@ -1,393 +1,392 b'' | |||
|
1 | 1 | # encoding: utf-8 |
|
2 | 2 | """ |
|
3 | 3 | An embedded IPython shell. |
|
4 | 4 | """ |
|
5 | 5 | # Copyright (c) IPython Development Team. |
|
6 | 6 | # Distributed under the terms of the Modified BSD License. |
|
7 | 7 | |
|
8 | 8 | |
|
9 | 9 | import sys |
|
10 | 10 | import warnings |
|
11 | 11 | |
|
12 | 12 | from IPython.core import ultratb, compilerop |
|
13 | 13 | from IPython.core import magic_arguments |
|
14 | 14 | from IPython.core.magic import Magics, magics_class, line_magic |
|
15 | 15 | from IPython.core.interactiveshell import DummyMod, InteractiveShell |
|
16 | 16 | from IPython.terminal.interactiveshell import TerminalInteractiveShell |
|
17 | 17 | from IPython.terminal.ipapp import load_default_config |
|
18 | 18 | |
|
19 | 19 | from traitlets import Bool, CBool, Unicode |
|
20 | 20 | from IPython.utils.io import ask_yes_no |
|
21 | 21 | |
|
22 | 22 | class KillEmbeded(Exception):pass |
|
23 | 23 | |
|
24 | 24 | # This is an additional magic that is exposed in embedded shells. |
|
25 | 25 | @magics_class |
|
26 | 26 | class EmbeddedMagics(Magics): |
|
27 | 27 | |
|
28 | 28 | @line_magic |
|
29 | 29 | @magic_arguments.magic_arguments() |
|
30 | 30 | @magic_arguments.argument('-i', '--instance', action='store_true', |
|
31 | 31 | help='Kill instance instead of call location') |
|
32 | 32 | @magic_arguments.argument('-x', '--exit', action='store_true', |
|
33 | 33 | help='Also exit the current session') |
|
34 | 34 | @magic_arguments.argument('-y', '--yes', action='store_true', |
|
35 | 35 | help='Do not ask confirmation') |
|
36 | 36 | def kill_embedded(self, parameter_s=''): |
|
37 | 37 | """%kill_embedded : deactivate for good the current embedded IPython |
|
38 | 38 | |
|
39 | 39 | This function (after asking for confirmation) sets an internal flag so |
|
40 | 40 | that an embedded IPython will never activate again for the given call |
|
41 | 41 | location. This is useful to permanently disable a shell that is being |
|
42 | 42 | called inside a loop: once you've figured out what you needed from it, |
|
43 | 43 | you may then kill it and the program will then continue to run without |
|
44 | 44 | the interactive shell interfering again. |
|
45 | 45 | |
|
46 | 46 | |
|
47 | Kill Instance Option | |
|
48 | -------------------- | |
|
49 | ||
|
50 | If for some reasons you need to kill the location where the instance is | |
|
51 | created and not called, for example if you create a single instance in | |
|
52 | one place and debug in many locations, you can use the ``--instance`` | |
|
53 | option to kill this specific instance. Like for the ``call location`` | |
|
54 | killing an "instance" should work even if it is recreated within a | |
|
55 | loop. | |
|
47 | Kill Instance Option: | |
|
48 | ||
|
49 | If for some reasons you need to kill the location where the instance | |
|
50 | is created and not called, for example if you create a single | |
|
51 | instance in one place and debug in many locations, you can use the | |
|
52 | ``--instance`` option to kill this specific instance. Like for the | |
|
53 | ``call location`` killing an "instance" should work even if it is | |
|
54 | recreated within a loop. | |
|
56 | 55 | |
|
57 | 56 | .. note:: |
|
58 | 57 | |
|
59 | 58 | This was the default behavior before IPython 5.2 |
|
60 | 59 | |
|
61 | 60 | """ |
|
62 | 61 | |
|
63 | 62 | args = magic_arguments.parse_argstring(self.kill_embedded, parameter_s) |
|
64 | 63 | print(args) |
|
65 | 64 | if args.instance: |
|
66 | 65 | # let no ask |
|
67 | 66 | if not args.yes: |
|
68 | 67 | kill = ask_yes_no( |
|
69 | 68 | "Are you sure you want to kill this embedded instance? [y/N] ", 'n') |
|
70 | 69 | else: |
|
71 | 70 | kill = True |
|
72 | 71 | if kill: |
|
73 | 72 | self.shell._disable_init_location() |
|
74 | 73 | print("This embedded IPython instance will not reactivate anymore " |
|
75 | 74 | "once you exit.") |
|
76 | 75 | else: |
|
77 | 76 | if not args.yes: |
|
78 | 77 | kill = ask_yes_no( |
|
79 | 78 | "Are you sure you want to kill this embedded call_location? [y/N] ", 'n') |
|
80 | 79 | else: |
|
81 | 80 | kill = True |
|
82 | 81 | if kill: |
|
83 | 82 | self.shell.embedded_active = False |
|
84 | 83 | print("This embedded IPython call location will not reactivate anymore " |
|
85 | 84 | "once you exit.") |
|
86 | 85 | |
|
87 | 86 | if args.exit: |
|
88 | 87 | # Ask-exit does not really ask, it just set internals flags to exit |
|
89 | 88 | # on next loop. |
|
90 | 89 | self.shell.ask_exit() |
|
91 | 90 | |
|
92 | 91 | |
|
93 | 92 | @line_magic |
|
94 | 93 | def exit_raise(self, parameter_s=''): |
|
95 | 94 | """%exit_raise Make the current embedded kernel exit and raise and exception. |
|
96 | 95 | |
|
97 | 96 | This function sets an internal flag so that an embedded IPython will |
|
98 | 97 | raise a `IPython.terminal.embed.KillEmbeded` Exception on exit, and then exit the current I. This is |
|
99 | 98 | useful to permanently exit a loop that create IPython embed instance. |
|
100 | 99 | """ |
|
101 | 100 | |
|
102 | 101 | self.shell.should_raise = True |
|
103 | 102 | self.shell.ask_exit() |
|
104 | 103 | |
|
105 | 104 | |
|
106 | 105 | |
|
107 | 106 | class InteractiveShellEmbed(TerminalInteractiveShell): |
|
108 | 107 | |
|
109 | 108 | dummy_mode = Bool(False) |
|
110 | 109 | exit_msg = Unicode('') |
|
111 | 110 | embedded = CBool(True) |
|
112 | 111 | should_raise = CBool(False) |
|
113 | 112 | # Like the base class display_banner is not configurable, but here it |
|
114 | 113 | # is True by default. |
|
115 | 114 | display_banner = CBool(True) |
|
116 | 115 | exit_msg = Unicode() |
|
117 | 116 | |
|
118 | 117 | # When embedding, by default we don't change the terminal title |
|
119 | 118 | term_title = Bool(False, |
|
120 | 119 | help="Automatically set the terminal title" |
|
121 | 120 | ).tag(config=True) |
|
122 | 121 | |
|
123 | 122 | _inactive_locations = set() |
|
124 | 123 | |
|
125 | 124 | @property |
|
126 | 125 | def embedded_active(self): |
|
127 | 126 | return (self._call_location_id not in InteractiveShellEmbed._inactive_locations)\ |
|
128 | 127 | and (self._init_location_id not in InteractiveShellEmbed._inactive_locations) |
|
129 | 128 | |
|
130 | 129 | def _disable_init_location(self): |
|
131 | 130 | """Disable the current Instance creation location""" |
|
132 | 131 | InteractiveShellEmbed._inactive_locations.add(self._init_location_id) |
|
133 | 132 | |
|
134 | 133 | @embedded_active.setter |
|
135 | 134 | def embedded_active(self, value): |
|
136 | 135 | if value: |
|
137 | 136 | InteractiveShellEmbed._inactive_locations.discard( |
|
138 | 137 | self._call_location_id) |
|
139 | 138 | InteractiveShellEmbed._inactive_locations.discard( |
|
140 | 139 | self._init_location_id) |
|
141 | 140 | else: |
|
142 | 141 | InteractiveShellEmbed._inactive_locations.add( |
|
143 | 142 | self._call_location_id) |
|
144 | 143 | |
|
145 | 144 | def __init__(self, **kw): |
|
146 | 145 | if kw.get('user_global_ns', None) is not None: |
|
147 | 146 | raise DeprecationWarning( |
|
148 | 147 | "Key word argument `user_global_ns` has been replaced by `user_module` since IPython 4.0.") |
|
149 | 148 | |
|
150 | 149 | clid = kw.pop('_init_location_id', None) |
|
151 | 150 | if not clid: |
|
152 | 151 | frame = sys._getframe(1) |
|
153 | 152 | clid = '%s:%s' % (frame.f_code.co_filename, frame.f_lineno) |
|
154 | 153 | self._init_location_id = clid |
|
155 | 154 | |
|
156 | 155 | super(InteractiveShellEmbed,self).__init__(**kw) |
|
157 | 156 | |
|
158 | 157 | # don't use the ipython crash handler so that user exceptions aren't |
|
159 | 158 | # trapped |
|
160 | 159 | sys.excepthook = ultratb.FormattedTB(color_scheme=self.colors, |
|
161 | 160 | mode=self.xmode, |
|
162 | 161 | call_pdb=self.pdb) |
|
163 | 162 | |
|
164 | 163 | def init_sys_modules(self): |
|
165 | 164 | """ |
|
166 | 165 | Explicitly overwrite :mod:`IPython.core.interactiveshell` to do nothing. |
|
167 | 166 | """ |
|
168 | 167 | pass |
|
169 | 168 | |
|
170 | 169 | def init_magics(self): |
|
171 | 170 | super(InteractiveShellEmbed, self).init_magics() |
|
172 | 171 | self.register_magics(EmbeddedMagics) |
|
173 | 172 | |
|
174 | 173 | def __call__(self, header='', local_ns=None, module=None, dummy=None, |
|
175 | 174 | stack_depth=1, global_ns=None, compile_flags=None, **kw): |
|
176 | 175 | """Activate the interactive interpreter. |
|
177 | 176 | |
|
178 | 177 | __call__(self,header='',local_ns=None,module=None,dummy=None) -> Start |
|
179 | 178 | the interpreter shell with the given local and global namespaces, and |
|
180 | 179 | optionally print a header string at startup. |
|
181 | 180 | |
|
182 | 181 | The shell can be globally activated/deactivated using the |
|
183 | 182 | dummy_mode attribute. This allows you to turn off a shell used |
|
184 | 183 | for debugging globally. |
|
185 | 184 | |
|
186 | 185 | However, *each* time you call the shell you can override the current |
|
187 | 186 | state of dummy_mode with the optional keyword parameter 'dummy'. For |
|
188 | 187 | example, if you set dummy mode on with IPShell.dummy_mode = True, you |
|
189 | 188 | can still have a specific call work by making it as IPShell(dummy=False). |
|
190 | 189 | """ |
|
191 | 190 | |
|
192 | 191 | # we are called, set the underlying interactiveshell not to exit. |
|
193 | 192 | self.keep_running = True |
|
194 | 193 | |
|
195 | 194 | # If the user has turned it off, go away |
|
196 | 195 | clid = kw.pop('_call_location_id', None) |
|
197 | 196 | if not clid: |
|
198 | 197 | frame = sys._getframe(1) |
|
199 | 198 | clid = '%s:%s' % (frame.f_code.co_filename, frame.f_lineno) |
|
200 | 199 | self._call_location_id = clid |
|
201 | 200 | |
|
202 | 201 | if not self.embedded_active: |
|
203 | 202 | return |
|
204 | 203 | |
|
205 | 204 | # Normal exits from interactive mode set this flag, so the shell can't |
|
206 | 205 | # re-enter (it checks this variable at the start of interactive mode). |
|
207 | 206 | self.exit_now = False |
|
208 | 207 | |
|
209 | 208 | # Allow the dummy parameter to override the global __dummy_mode |
|
210 | 209 | if dummy or (dummy != 0 and self.dummy_mode): |
|
211 | 210 | return |
|
212 | 211 | |
|
213 | 212 | # self.banner is auto computed |
|
214 | 213 | if header: |
|
215 | 214 | self.old_banner2 = self.banner2 |
|
216 | 215 | self.banner2 = self.banner2 + '\n' + header + '\n' |
|
217 | 216 | else: |
|
218 | 217 | self.old_banner2 = '' |
|
219 | 218 | |
|
220 | 219 | if self.display_banner: |
|
221 | 220 | self.show_banner() |
|
222 | 221 | |
|
223 | 222 | # Call the embedding code with a stack depth of 1 so it can skip over |
|
224 | 223 | # our call and get the original caller's namespaces. |
|
225 | 224 | self.mainloop(local_ns, module, stack_depth=stack_depth, |
|
226 | 225 | global_ns=global_ns, compile_flags=compile_flags) |
|
227 | 226 | |
|
228 | 227 | self.banner2 = self.old_banner2 |
|
229 | 228 | |
|
230 | 229 | if self.exit_msg is not None: |
|
231 | 230 | print(self.exit_msg) |
|
232 | 231 | |
|
233 | 232 | if self.should_raise: |
|
234 | 233 | raise KillEmbeded('Embedded IPython raising error, as user requested.') |
|
235 | 234 | |
|
236 | 235 | |
|
237 | 236 | def mainloop(self, local_ns=None, module=None, stack_depth=0, |
|
238 | 237 | display_banner=None, global_ns=None, compile_flags=None): |
|
239 | 238 | """Embeds IPython into a running python program. |
|
240 | 239 | |
|
241 | 240 | Parameters |
|
242 | 241 | ---------- |
|
243 | 242 | |
|
244 | 243 | local_ns, module |
|
245 | 244 | Working local namespace (a dict) and module (a module or similar |
|
246 | 245 | object). If given as None, they are automatically taken from the scope |
|
247 | 246 | where the shell was called, so that program variables become visible. |
|
248 | 247 | |
|
249 | 248 | stack_depth : int |
|
250 | 249 | How many levels in the stack to go to looking for namespaces (when |
|
251 | 250 | local_ns or module is None). This allows an intermediate caller to |
|
252 | 251 | make sure that this function gets the namespace from the intended |
|
253 | 252 | level in the stack. By default (0) it will get its locals and globals |
|
254 | 253 | from the immediate caller. |
|
255 | 254 | |
|
256 | 255 | compile_flags |
|
257 | 256 | A bit field identifying the __future__ features |
|
258 | 257 | that are enabled, as passed to the builtin :func:`compile` function. |
|
259 | 258 | If given as None, they are automatically taken from the scope where |
|
260 | 259 | the shell was called. |
|
261 | 260 | |
|
262 | 261 | """ |
|
263 | 262 | |
|
264 | 263 | if (global_ns is not None) and (module is None): |
|
265 | 264 | raise DeprecationWarning("'global_ns' keyword argument is deprecated, and has been removed in IPython 5.0 use `module` keyword argument instead.") |
|
266 | 265 | |
|
267 | 266 | if (display_banner is not None): |
|
268 | 267 | warnings.warn("The display_banner parameter is deprecated since IPython 4.0", DeprecationWarning) |
|
269 | 268 | |
|
270 | 269 | # Get locals and globals from caller |
|
271 | 270 | if ((local_ns is None or module is None or compile_flags is None) |
|
272 | 271 | and self.default_user_namespaces): |
|
273 | 272 | call_frame = sys._getframe(stack_depth).f_back |
|
274 | 273 | |
|
275 | 274 | if local_ns is None: |
|
276 | 275 | local_ns = call_frame.f_locals |
|
277 | 276 | if module is None: |
|
278 | 277 | global_ns = call_frame.f_globals |
|
279 | 278 | try: |
|
280 | 279 | module = sys.modules[global_ns['__name__']] |
|
281 | 280 | except KeyError: |
|
282 | 281 | warnings.warn("Failed to get module %s" % \ |
|
283 | 282 | global_ns.get('__name__', 'unknown module') |
|
284 | 283 | ) |
|
285 | 284 | module = DummyMod() |
|
286 | 285 | module.__dict__ = global_ns |
|
287 | 286 | if compile_flags is None: |
|
288 | 287 | compile_flags = (call_frame.f_code.co_flags & |
|
289 | 288 | compilerop.PyCF_MASK) |
|
290 | 289 | |
|
291 | 290 | # Save original namespace and module so we can restore them after |
|
292 | 291 | # embedding; otherwise the shell doesn't shut down correctly. |
|
293 | 292 | orig_user_module = self.user_module |
|
294 | 293 | orig_user_ns = self.user_ns |
|
295 | 294 | orig_compile_flags = self.compile.flags |
|
296 | 295 | |
|
297 | 296 | # Update namespaces and fire up interpreter |
|
298 | 297 | |
|
299 | 298 | # The global one is easy, we can just throw it in |
|
300 | 299 | if module is not None: |
|
301 | 300 | self.user_module = module |
|
302 | 301 | |
|
303 | 302 | # But the user/local one is tricky: ipython needs it to store internal |
|
304 | 303 | # data, but we also need the locals. We'll throw our hidden variables |
|
305 | 304 | # like _ih and get_ipython() into the local namespace, but delete them |
|
306 | 305 | # later. |
|
307 | 306 | if local_ns is not None: |
|
308 | 307 | reentrant_local_ns = {k: v for (k, v) in local_ns.items() if k not in self.user_ns_hidden.keys()} |
|
309 | 308 | self.user_ns = reentrant_local_ns |
|
310 | 309 | self.init_user_ns() |
|
311 | 310 | |
|
312 | 311 | # Compiler flags |
|
313 | 312 | if compile_flags is not None: |
|
314 | 313 | self.compile.flags = compile_flags |
|
315 | 314 | |
|
316 | 315 | # make sure the tab-completer has the correct frame information, so it |
|
317 | 316 | # actually completes using the frame's locals/globals |
|
318 | 317 | self.set_completer_frame() |
|
319 | 318 | |
|
320 | 319 | with self.builtin_trap, self.display_trap: |
|
321 | 320 | self.interact() |
|
322 | 321 | |
|
323 | 322 | # now, purge out the local namespace of IPython's hidden variables. |
|
324 | 323 | if local_ns is not None: |
|
325 | 324 | local_ns.update({k: v for (k, v) in self.user_ns.items() if k not in self.user_ns_hidden.keys()}) |
|
326 | 325 | |
|
327 | 326 | |
|
328 | 327 | # Restore original namespace so shell can shut down when we exit. |
|
329 | 328 | self.user_module = orig_user_module |
|
330 | 329 | self.user_ns = orig_user_ns |
|
331 | 330 | self.compile.flags = orig_compile_flags |
|
332 | 331 | |
|
333 | 332 | |
|
334 | 333 | def embed(**kwargs): |
|
335 | 334 | """Call this to embed IPython at the current point in your program. |
|
336 | 335 | |
|
337 | 336 | The first invocation of this will create an :class:`InteractiveShellEmbed` |
|
338 | 337 | instance and then call it. Consecutive calls just call the already |
|
339 | 338 | created instance. |
|
340 | 339 | |
|
341 | 340 | If you don't want the kernel to initialize the namespace |
|
342 | 341 | from the scope of the surrounding function, |
|
343 | 342 | and/or you want to load full IPython configuration, |
|
344 | 343 | you probably want `IPython.start_ipython()` instead. |
|
345 | 344 | |
|
346 | 345 | Here is a simple example:: |
|
347 | 346 | |
|
348 | 347 | from IPython import embed |
|
349 | 348 | a = 10 |
|
350 | 349 | b = 20 |
|
351 | 350 | embed(header='First time') |
|
352 | 351 | c = 30 |
|
353 | 352 | d = 40 |
|
354 | 353 | embed() |
|
355 | 354 | |
|
356 | 355 | Full customization can be done by passing a :class:`Config` in as the |
|
357 | 356 | config argument. |
|
358 | 357 | """ |
|
359 | 358 | config = kwargs.get('config') |
|
360 | 359 | header = kwargs.pop('header', u'') |
|
361 | 360 | compile_flags = kwargs.pop('compile_flags', None) |
|
362 | 361 | if config is None: |
|
363 | 362 | config = load_default_config() |
|
364 | 363 | config.InteractiveShellEmbed = config.TerminalInteractiveShell |
|
365 | 364 | kwargs['config'] = config |
|
366 | 365 | #save ps1/ps2 if defined |
|
367 | 366 | ps1 = None |
|
368 | 367 | ps2 = None |
|
369 | 368 | try: |
|
370 | 369 | ps1 = sys.ps1 |
|
371 | 370 | ps2 = sys.ps2 |
|
372 | 371 | except AttributeError: |
|
373 | 372 | pass |
|
374 | 373 | #save previous instance |
|
375 | 374 | saved_shell_instance = InteractiveShell._instance |
|
376 | 375 | if saved_shell_instance is not None: |
|
377 | 376 | cls = type(saved_shell_instance) |
|
378 | 377 | cls.clear_instance() |
|
379 | 378 | frame = sys._getframe(1) |
|
380 | 379 | shell = InteractiveShellEmbed.instance(_init_location_id='%s:%s' % ( |
|
381 | 380 | frame.f_code.co_filename, frame.f_lineno), **kwargs) |
|
382 | 381 | shell(header=header, stack_depth=2, compile_flags=compile_flags, |
|
383 | 382 | _call_location_id='%s:%s' % (frame.f_code.co_filename, frame.f_lineno)) |
|
384 | 383 | InteractiveShellEmbed.clear_instance() |
|
385 | 384 | #restore previous instance |
|
386 | 385 | if saved_shell_instance is not None: |
|
387 | 386 | cls = type(saved_shell_instance) |
|
388 | 387 | cls.clear_instance() |
|
389 | 388 | for subclass in cls._walk_mro(): |
|
390 | 389 | subclass._instance = saved_shell_instance |
|
391 | 390 | if ps1 is not None: |
|
392 | 391 | sys.ps1 = ps1 |
|
393 | 392 | sys.ps2 = ps2 |
@@ -1,518 +1,516 b'' | |||
|
1 | 1 | """IPython terminal interface using prompt_toolkit""" |
|
2 | 2 | |
|
3 | 3 | import os |
|
4 | 4 | import sys |
|
5 | 5 | import warnings |
|
6 | 6 | from warnings import warn |
|
7 | 7 | |
|
8 | 8 | from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC |
|
9 | 9 | from IPython.utils import io |
|
10 | 10 | from IPython.utils.py3compat import cast_unicode_py2, input |
|
11 | 11 | from IPython.utils.terminal import toggle_set_term_title, set_term_title |
|
12 | 12 | from IPython.utils.process import abbrev_cwd |
|
13 | 13 | from traitlets import Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union |
|
14 | 14 | |
|
15 | 15 | from prompt_toolkit.document import Document |
|
16 | 16 | from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode |
|
17 | 17 | from prompt_toolkit.filters import (HasFocus, Condition, IsDone) |
|
18 | 18 | from prompt_toolkit.history import InMemoryHistory |
|
19 | 19 | from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout, create_output |
|
20 | 20 | from prompt_toolkit.interface import CommandLineInterface |
|
21 | 21 | from prompt_toolkit.key_binding.manager import KeyBindingManager |
|
22 | 22 | from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor |
|
23 | 23 | from prompt_toolkit.styles import PygmentsStyle, DynamicStyle |
|
24 | 24 | |
|
25 | 25 | from pygments.styles import get_style_by_name, get_all_styles |
|
26 | 26 | from pygments.style import Style |
|
27 | 27 | from pygments.token import Token |
|
28 | 28 | |
|
29 | 29 | from .debugger import TerminalPdb, Pdb |
|
30 | 30 | from .magics import TerminalMagics |
|
31 | 31 | from .pt_inputhooks import get_inputhook_name_and_func |
|
32 | 32 | from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook |
|
33 | 33 | from .ptutils import IPythonPTCompleter, IPythonPTLexer |
|
34 | 34 | from .shortcuts import register_ipython_shortcuts |
|
35 | 35 | |
|
36 | 36 | DISPLAY_BANNER_DEPRECATED = object() |
|
37 | 37 | |
|
38 | 38 | |
|
39 | 39 | class _NoStyle(Style): pass |
|
40 | 40 | |
|
41 | 41 | |
|
42 | 42 | |
|
43 | 43 | _style_overrides_light_bg = { |
|
44 | 44 | Token.Prompt: '#0000ff', |
|
45 | 45 | Token.PromptNum: '#0000ee bold', |
|
46 | 46 | Token.OutPrompt: '#cc0000', |
|
47 | 47 | Token.OutPromptNum: '#bb0000 bold', |
|
48 | 48 | } |
|
49 | 49 | |
|
50 | 50 | _style_overrides_linux = { |
|
51 | 51 | Token.Prompt: '#00cc00', |
|
52 | 52 | Token.PromptNum: '#00bb00 bold', |
|
53 | 53 | Token.OutPrompt: '#cc0000', |
|
54 | 54 | Token.OutPromptNum: '#bb0000 bold', |
|
55 | 55 | } |
|
56 | 56 | |
|
57 | 57 | |
|
58 | 58 | |
|
59 | 59 | def get_default_editor(): |
|
60 | 60 | try: |
|
61 | 61 | return os.environ['EDITOR'] |
|
62 | 62 | except KeyError: |
|
63 | 63 | pass |
|
64 | 64 | except UnicodeError: |
|
65 | 65 | warn("$EDITOR environment variable is not pure ASCII. Using platform " |
|
66 | 66 | "default editor.") |
|
67 | 67 | |
|
68 | 68 | if os.name == 'posix': |
|
69 | 69 | return 'vi' # the only one guaranteed to be there! |
|
70 | 70 | else: |
|
71 | 71 | return 'notepad' # same in Windows! |
|
72 | 72 | |
|
73 | 73 | # conservatively check for tty |
|
74 | 74 | # overridden streams can result in things like: |
|
75 | 75 | # - sys.stdin = None |
|
76 | 76 | # - no isatty method |
|
77 | 77 | for _name in ('stdin', 'stdout', 'stderr'): |
|
78 | 78 | _stream = getattr(sys, _name) |
|
79 | 79 | if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty(): |
|
80 | 80 | _is_tty = False |
|
81 | 81 | break |
|
82 | 82 | else: |
|
83 | 83 | _is_tty = True |
|
84 | 84 | |
|
85 | 85 | |
|
86 | 86 | _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty) |
|
87 | 87 | |
|
88 | 88 | class TerminalInteractiveShell(InteractiveShell): |
|
89 | 89 | space_for_menu = Integer(6, help='Number of line at the bottom of the screen ' |
|
90 | 90 | 'to reserve for the completion menu' |
|
91 | 91 | ).tag(config=True) |
|
92 | 92 | |
|
93 | 93 | def _space_for_menu_changed(self, old, new): |
|
94 | 94 | self._update_layout() |
|
95 | 95 | |
|
96 | 96 | pt_cli = None |
|
97 | 97 | debugger_history = None |
|
98 | 98 | _pt_app = None |
|
99 | 99 | |
|
100 | 100 | simple_prompt = Bool(_use_simple_prompt, |
|
101 | 101 | help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors. |
|
102 | 102 | |
|
103 | 103 | Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are: |
|
104 | 104 | IPython own testing machinery, and emacs inferior-shell integration through elpy. |
|
105 | 105 | |
|
106 | 106 | This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT` |
|
107 | environment variable is set, or the current terminal is not a tty. | |
|
108 | ||
|
109 | """ | |
|
107 | environment variable is set, or the current terminal is not a tty.""" | |
|
110 | 108 | ).tag(config=True) |
|
111 | 109 | |
|
112 | 110 | @property |
|
113 | 111 | def debugger_cls(self): |
|
114 | 112 | return Pdb if self.simple_prompt else TerminalPdb |
|
115 | 113 | |
|
116 | 114 | confirm_exit = Bool(True, |
|
117 | 115 | help=""" |
|
118 | 116 | Set to confirm when you try to exit IPython with an EOF (Control-D |
|
119 | 117 | in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit', |
|
120 | 118 | you can force a direct exit without any confirmation.""", |
|
121 | 119 | ).tag(config=True) |
|
122 | 120 | |
|
123 | 121 | editing_mode = Unicode('emacs', |
|
124 | 122 | help="Shortcut style to use at the prompt. 'vi' or 'emacs'.", |
|
125 | 123 | ).tag(config=True) |
|
126 | 124 | |
|
127 | 125 | mouse_support = Bool(False, |
|
128 | 126 | help="Enable mouse support in the prompt" |
|
129 | 127 | ).tag(config=True) |
|
130 | 128 | |
|
131 | 129 | highlighting_style = Union([Unicode('legacy'), Type(klass=Style)], |
|
132 | 130 | help="""The name or class of a Pygments style to use for syntax |
|
133 | 131 | highlighting: \n %s""" % ', '.join(get_all_styles()) |
|
134 | 132 | ).tag(config=True) |
|
135 | 133 | |
|
136 | 134 | |
|
137 | 135 | @observe('highlighting_style') |
|
138 | 136 | @observe('colors') |
|
139 | 137 | def _highlighting_style_changed(self, change): |
|
140 | 138 | self.refresh_style() |
|
141 | 139 | |
|
142 | 140 | def refresh_style(self): |
|
143 | 141 | self._style = self._make_style_from_name_or_cls(self.highlighting_style) |
|
144 | 142 | |
|
145 | 143 | |
|
146 | 144 | highlighting_style_overrides = Dict( |
|
147 | 145 | help="Override highlighting format for specific tokens" |
|
148 | 146 | ).tag(config=True) |
|
149 | 147 | |
|
150 | 148 | true_color = Bool(False, |
|
151 | 149 | help=("Use 24bit colors instead of 256 colors in prompt highlighting. " |
|
152 | 150 | "If your terminal supports true color, the following command " |
|
153 | 151 | "should print 'TRUECOLOR' in orange: " |
|
154 | 152 | "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"") |
|
155 | 153 | ).tag(config=True) |
|
156 | 154 | |
|
157 | 155 | editor = Unicode(get_default_editor(), |
|
158 | 156 | help="Set the editor used by IPython (default to $EDITOR/vi/notepad)." |
|
159 | 157 | ).tag(config=True) |
|
160 | 158 | |
|
161 | 159 | prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True) |
|
162 | 160 | |
|
163 | 161 | prompts = Instance(Prompts) |
|
164 | 162 | |
|
165 | 163 | @default('prompts') |
|
166 | 164 | def _prompts_default(self): |
|
167 | 165 | return self.prompts_class(self) |
|
168 | 166 | |
|
169 | 167 | @observe('prompts') |
|
170 | 168 | def _(self, change): |
|
171 | 169 | self._update_layout() |
|
172 | 170 | |
|
173 | 171 | @default('displayhook_class') |
|
174 | 172 | def _displayhook_class_default(self): |
|
175 | 173 | return RichPromptDisplayHook |
|
176 | 174 | |
|
177 | 175 | term_title = Bool(True, |
|
178 | 176 | help="Automatically set the terminal title" |
|
179 | 177 | ).tag(config=True) |
|
180 | 178 | |
|
181 | 179 | display_completions = Enum(('column', 'multicolumn','readlinelike'), |
|
182 | 180 | help= ( "Options for displaying tab completions, 'column', 'multicolumn', and " |
|
183 | 181 | "'readlinelike'. These options are for `prompt_toolkit`, see " |
|
184 | 182 | "`prompt_toolkit` documentation for more information." |
|
185 | 183 | ), |
|
186 | 184 | default_value='multicolumn').tag(config=True) |
|
187 | 185 | |
|
188 | 186 | highlight_matching_brackets = Bool(True, |
|
189 | 187 | help="Highlight matching brackets.", |
|
190 | 188 | ).tag(config=True) |
|
191 | 189 | |
|
192 | 190 | extra_open_editor_shortcuts = Bool(False, |
|
193 | 191 | help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. " |
|
194 | 192 | "This is in addition to the F2 binding, which is always enabled." |
|
195 | 193 | ).tag(config=True) |
|
196 | 194 | |
|
197 | 195 | @observe('term_title') |
|
198 | 196 | def init_term_title(self, change=None): |
|
199 | 197 | # Enable or disable the terminal title. |
|
200 | 198 | if self.term_title: |
|
201 | 199 | toggle_set_term_title(True) |
|
202 | 200 | set_term_title('IPython: ' + abbrev_cwd()) |
|
203 | 201 | else: |
|
204 | 202 | toggle_set_term_title(False) |
|
205 | 203 | |
|
206 | 204 | def init_display_formatter(self): |
|
207 | 205 | super(TerminalInteractiveShell, self).init_display_formatter() |
|
208 | 206 | # terminal only supports plain text |
|
209 | 207 | self.display_formatter.active_types = ['text/plain'] |
|
210 | 208 | # disable `_ipython_display_` |
|
211 | 209 | self.display_formatter.ipython_display_formatter.enabled = False |
|
212 | 210 | |
|
213 | 211 | def init_prompt_toolkit_cli(self): |
|
214 | 212 | if self.simple_prompt: |
|
215 | 213 | # Fall back to plain non-interactive output for tests. |
|
216 | 214 | # This is very limited, and only accepts a single line. |
|
217 | 215 | def prompt(): |
|
218 | 216 | return cast_unicode_py2(input('In [%d]: ' % self.execution_count)) |
|
219 | 217 | self.prompt_for_code = prompt |
|
220 | 218 | return |
|
221 | 219 | |
|
222 | 220 | # Set up keyboard shortcuts |
|
223 | 221 | kbmanager = KeyBindingManager.for_prompt( |
|
224 | 222 | enable_open_in_editor=self.extra_open_editor_shortcuts, |
|
225 | 223 | ) |
|
226 | 224 | register_ipython_shortcuts(kbmanager.registry, self) |
|
227 | 225 | |
|
228 | 226 | # Pre-populate history from IPython's history database |
|
229 | 227 | history = InMemoryHistory() |
|
230 | 228 | last_cell = u"" |
|
231 | 229 | for __, ___, cell in self.history_manager.get_tail(self.history_load_length, |
|
232 | 230 | include_latest=True): |
|
233 | 231 | # Ignore blank lines and consecutive duplicates |
|
234 | 232 | cell = cell.rstrip() |
|
235 | 233 | if cell and (cell != last_cell): |
|
236 | 234 | history.append(cell) |
|
237 | 235 | last_cell = cell |
|
238 | 236 | |
|
239 | 237 | self._style = self._make_style_from_name_or_cls(self.highlighting_style) |
|
240 | 238 | style = DynamicStyle(lambda: self._style) |
|
241 | 239 | |
|
242 | 240 | editing_mode = getattr(EditingMode, self.editing_mode.upper()) |
|
243 | 241 | |
|
244 | 242 | def patch_stdout(**kwargs): |
|
245 | 243 | return self.pt_cli.patch_stdout_context(**kwargs) |
|
246 | 244 | |
|
247 | 245 | self._pt_app = create_prompt_application( |
|
248 | 246 | editing_mode=editing_mode, |
|
249 | 247 | key_bindings_registry=kbmanager.registry, |
|
250 | 248 | history=history, |
|
251 | 249 | completer=IPythonPTCompleter(shell=self, |
|
252 | 250 | patch_stdout=patch_stdout), |
|
253 | 251 | enable_history_search=True, |
|
254 | 252 | style=style, |
|
255 | 253 | mouse_support=self.mouse_support, |
|
256 | 254 | **self._layout_options() |
|
257 | 255 | ) |
|
258 | 256 | self._eventloop = create_eventloop(self.inputhook) |
|
259 | 257 | self.pt_cli = CommandLineInterface( |
|
260 | 258 | self._pt_app, eventloop=self._eventloop, |
|
261 | 259 | output=create_output(true_color=self.true_color)) |
|
262 | 260 | |
|
263 | 261 | def _make_style_from_name_or_cls(self, name_or_cls): |
|
264 | 262 | """ |
|
265 | 263 | Small wrapper that make an IPython compatible style from a style name |
|
266 | 264 | |
|
267 | 265 | We need that to add style for prompt ... etc. |
|
268 | 266 | """ |
|
269 | 267 | style_overrides = {} |
|
270 | 268 | if name_or_cls == 'legacy': |
|
271 | 269 | legacy = self.colors.lower() |
|
272 | 270 | if legacy == 'linux': |
|
273 | 271 | style_cls = get_style_by_name('monokai') |
|
274 | 272 | style_overrides = _style_overrides_linux |
|
275 | 273 | elif legacy == 'lightbg': |
|
276 | 274 | style_overrides = _style_overrides_light_bg |
|
277 | 275 | style_cls = get_style_by_name('pastie') |
|
278 | 276 | elif legacy == 'neutral': |
|
279 | 277 | # The default theme needs to be visible on both a dark background |
|
280 | 278 | # and a light background, because we can't tell what the terminal |
|
281 | 279 | # looks like. These tweaks to the default theme help with that. |
|
282 | 280 | style_cls = get_style_by_name('default') |
|
283 | 281 | style_overrides.update({ |
|
284 | 282 | Token.Number: '#007700', |
|
285 | 283 | Token.Operator: 'noinherit', |
|
286 | 284 | Token.String: '#BB6622', |
|
287 | 285 | Token.Name.Function: '#2080D0', |
|
288 | 286 | Token.Name.Class: 'bold #2080D0', |
|
289 | 287 | Token.Name.Namespace: 'bold #2080D0', |
|
290 | 288 | Token.Prompt: '#009900', |
|
291 | 289 | Token.PromptNum: '#00ff00 bold', |
|
292 | 290 | Token.OutPrompt: '#990000', |
|
293 | 291 | Token.OutPromptNum: '#ff0000 bold', |
|
294 | 292 | }) |
|
295 | 293 | |
|
296 | 294 | # Hack: Due to limited color support on the Windows console |
|
297 | 295 | # the prompt colors will be wrong without this |
|
298 | 296 | if os.name == 'nt': |
|
299 | 297 | style_overrides.update({ |
|
300 | 298 | Token.Prompt: '#ansidarkgreen', |
|
301 | 299 | Token.PromptNum: '#ansigreen bold', |
|
302 | 300 | Token.OutPrompt: '#ansidarkred', |
|
303 | 301 | Token.OutPromptNum: '#ansired bold', |
|
304 | 302 | }) |
|
305 | 303 | elif legacy =='nocolor': |
|
306 | 304 | style_cls=_NoStyle |
|
307 | 305 | style_overrides = {} |
|
308 | 306 | else : |
|
309 | 307 | raise ValueError('Got unknown colors: ', legacy) |
|
310 | 308 | else : |
|
311 | 309 | if isinstance(name_or_cls, str): |
|
312 | 310 | style_cls = get_style_by_name(name_or_cls) |
|
313 | 311 | else: |
|
314 | 312 | style_cls = name_or_cls |
|
315 | 313 | style_overrides = { |
|
316 | 314 | Token.Prompt: '#009900', |
|
317 | 315 | Token.PromptNum: '#00ff00 bold', |
|
318 | 316 | Token.OutPrompt: '#990000', |
|
319 | 317 | Token.OutPromptNum: '#ff0000 bold', |
|
320 | 318 | } |
|
321 | 319 | style_overrides.update(self.highlighting_style_overrides) |
|
322 | 320 | style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls, |
|
323 | 321 | style_dict=style_overrides) |
|
324 | 322 | |
|
325 | 323 | return style |
|
326 | 324 | |
|
327 | 325 | def _layout_options(self): |
|
328 | 326 | """ |
|
329 | 327 | Return the current layout option for the current Terminal InteractiveShell |
|
330 | 328 | """ |
|
331 | 329 | return { |
|
332 | 330 | 'lexer':IPythonPTLexer(), |
|
333 | 331 | 'reserve_space_for_menu':self.space_for_menu, |
|
334 | 332 | 'get_prompt_tokens':self.prompts.in_prompt_tokens, |
|
335 | 333 | 'get_continuation_tokens':self.prompts.continuation_prompt_tokens, |
|
336 | 334 | 'multiline':True, |
|
337 | 335 | 'display_completions_in_columns': (self.display_completions == 'multicolumn'), |
|
338 | 336 | |
|
339 | 337 | # Highlight matching brackets, but only when this setting is |
|
340 | 338 | # enabled, and only when the DEFAULT_BUFFER has the focus. |
|
341 | 339 | 'extra_input_processors': [ConditionalProcessor( |
|
342 | 340 | processor=HighlightMatchingBracketProcessor(chars='[](){}'), |
|
343 | 341 | filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() & |
|
344 | 342 | Condition(lambda cli: self.highlight_matching_brackets))], |
|
345 | 343 | } |
|
346 | 344 | |
|
347 | 345 | def _update_layout(self): |
|
348 | 346 | """ |
|
349 | 347 | Ask for a re computation of the application layout, if for example , |
|
350 | 348 | some configuration options have changed. |
|
351 | 349 | """ |
|
352 | 350 | if self._pt_app: |
|
353 | 351 | self._pt_app.layout = create_prompt_layout(**self._layout_options()) |
|
354 | 352 | |
|
355 | 353 | def prompt_for_code(self): |
|
356 | 354 | document = self.pt_cli.run( |
|
357 | 355 | pre_run=self.pre_prompt, reset_current_buffer=True) |
|
358 | 356 | return document.text |
|
359 | 357 | |
|
360 | 358 | def enable_win_unicode_console(self): |
|
361 | 359 | if sys.version_info >= (3, 6): |
|
362 | 360 | # Since PEP 528, Python uses the unicode APIs for the Windows |
|
363 | 361 | # console by default, so WUC shouldn't be needed. |
|
364 | 362 | return |
|
365 | 363 | |
|
366 | 364 | import win_unicode_console |
|
367 | 365 | win_unicode_console.enable() |
|
368 | 366 | |
|
369 | 367 | def init_io(self): |
|
370 | 368 | if sys.platform not in {'win32', 'cli'}: |
|
371 | 369 | return |
|
372 | 370 | |
|
373 | 371 | self.enable_win_unicode_console() |
|
374 | 372 | |
|
375 | 373 | import colorama |
|
376 | 374 | colorama.init() |
|
377 | 375 | |
|
378 | 376 | # For some reason we make these wrappers around stdout/stderr. |
|
379 | 377 | # For now, we need to reset them so all output gets coloured. |
|
380 | 378 | # https://github.com/ipython/ipython/issues/8669 |
|
381 | 379 | # io.std* are deprecated, but don't show our own deprecation warnings |
|
382 | 380 | # during initialization of the deprecated API. |
|
383 | 381 | with warnings.catch_warnings(): |
|
384 | 382 | warnings.simplefilter('ignore', DeprecationWarning) |
|
385 | 383 | io.stdout = io.IOStream(sys.stdout) |
|
386 | 384 | io.stderr = io.IOStream(sys.stderr) |
|
387 | 385 | |
|
388 | 386 | def init_magics(self): |
|
389 | 387 | super(TerminalInteractiveShell, self).init_magics() |
|
390 | 388 | self.register_magics(TerminalMagics) |
|
391 | 389 | |
|
392 | 390 | def init_alias(self): |
|
393 | 391 | # The parent class defines aliases that can be safely used with any |
|
394 | 392 | # frontend. |
|
395 | 393 | super(TerminalInteractiveShell, self).init_alias() |
|
396 | 394 | |
|
397 | 395 | # Now define aliases that only make sense on the terminal, because they |
|
398 | 396 | # need direct access to the console in a way that we can't emulate in |
|
399 | 397 | # GUI or web frontend |
|
400 | 398 | if os.name == 'posix': |
|
401 | 399 | for cmd in ['clear', 'more', 'less', 'man']: |
|
402 | 400 | self.alias_manager.soft_define_alias(cmd, cmd) |
|
403 | 401 | |
|
404 | 402 | |
|
405 | 403 | def __init__(self, *args, **kwargs): |
|
406 | 404 | super(TerminalInteractiveShell, self).__init__(*args, **kwargs) |
|
407 | 405 | self.init_prompt_toolkit_cli() |
|
408 | 406 | self.init_term_title() |
|
409 | 407 | self.keep_running = True |
|
410 | 408 | |
|
411 | 409 | self.debugger_history = InMemoryHistory() |
|
412 | 410 | |
|
413 | 411 | def ask_exit(self): |
|
414 | 412 | self.keep_running = False |
|
415 | 413 | |
|
416 | 414 | rl_next_input = None |
|
417 | 415 | |
|
418 | 416 | def pre_prompt(self): |
|
419 | 417 | if self.rl_next_input: |
|
420 | 418 | # We can't set the buffer here, because it will be reset just after |
|
421 | 419 | # this. Adding a callable to pre_run_callables does what we need |
|
422 | 420 | # after the buffer is reset. |
|
423 | 421 | s = cast_unicode_py2(self.rl_next_input) |
|
424 | 422 | def set_doc(): |
|
425 | 423 | self.pt_cli.application.buffer.document = Document(s) |
|
426 | 424 | if hasattr(self.pt_cli, 'pre_run_callables'): |
|
427 | 425 | self.pt_cli.pre_run_callables.append(set_doc) |
|
428 | 426 | else: |
|
429 | 427 | # Older version of prompt_toolkit; it's OK to set the document |
|
430 | 428 | # directly here. |
|
431 | 429 | set_doc() |
|
432 | 430 | self.rl_next_input = None |
|
433 | 431 | |
|
434 | 432 | def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED): |
|
435 | 433 | |
|
436 | 434 | if display_banner is not DISPLAY_BANNER_DEPRECATED: |
|
437 | 435 | warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2) |
|
438 | 436 | |
|
439 | 437 | self.keep_running = True |
|
440 | 438 | while self.keep_running: |
|
441 | 439 | print(self.separate_in, end='') |
|
442 | 440 | |
|
443 | 441 | try: |
|
444 | 442 | code = self.prompt_for_code() |
|
445 | 443 | except EOFError: |
|
446 | 444 | if (not self.confirm_exit) \ |
|
447 | 445 | or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'): |
|
448 | 446 | self.ask_exit() |
|
449 | 447 | |
|
450 | 448 | else: |
|
451 | 449 | if code: |
|
452 | 450 | self.run_cell(code, store_history=True) |
|
453 | 451 | |
|
454 | 452 | def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED): |
|
455 | 453 | # An extra layer of protection in case someone mashing Ctrl-C breaks |
|
456 | 454 | # out of our internal code. |
|
457 | 455 | if display_banner is not DISPLAY_BANNER_DEPRECATED: |
|
458 | 456 | warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2) |
|
459 | 457 | while True: |
|
460 | 458 | try: |
|
461 | 459 | self.interact() |
|
462 | 460 | break |
|
463 | 461 | except KeyboardInterrupt as e: |
|
464 | 462 | print("\n%s escaped interact()\n" % type(e).__name__) |
|
465 | 463 | finally: |
|
466 | 464 | # An interrupt during the eventloop will mess up the |
|
467 | 465 | # internal state of the prompt_toolkit library. |
|
468 | 466 | # Stopping the eventloop fixes this, see |
|
469 | 467 | # https://github.com/ipython/ipython/pull/9867 |
|
470 | 468 | if hasattr(self, '_eventloop'): |
|
471 | 469 | self._eventloop.stop() |
|
472 | 470 | |
|
473 | 471 | _inputhook = None |
|
474 | 472 | def inputhook(self, context): |
|
475 | 473 | if self._inputhook is not None: |
|
476 | 474 | self._inputhook(context) |
|
477 | 475 | |
|
478 | 476 | active_eventloop = None |
|
479 | 477 | def enable_gui(self, gui=None): |
|
480 | 478 | if gui: |
|
481 | 479 | self.active_eventloop, self._inputhook =\ |
|
482 | 480 | get_inputhook_name_and_func(gui) |
|
483 | 481 | else: |
|
484 | 482 | self.active_eventloop = self._inputhook = None |
|
485 | 483 | |
|
486 | 484 | # Run !system commands directly, not through pipes, so terminal programs |
|
487 | 485 | # work correctly. |
|
488 | 486 | system = InteractiveShell.system_raw |
|
489 | 487 | |
|
490 | 488 | def auto_rewrite_input(self, cmd): |
|
491 | 489 | """Overridden from the parent class to use fancy rewriting prompt""" |
|
492 | 490 | if not self.show_rewritten_input: |
|
493 | 491 | return |
|
494 | 492 | |
|
495 | 493 | tokens = self.prompts.rewrite_prompt_tokens() |
|
496 | 494 | if self.pt_cli: |
|
497 | 495 | self.pt_cli.print_tokens(tokens) |
|
498 | 496 | print(cmd) |
|
499 | 497 | else: |
|
500 | 498 | prompt = ''.join(s for t, s in tokens) |
|
501 | 499 | print(prompt, cmd, sep='') |
|
502 | 500 | |
|
503 | 501 | _prompts_before = None |
|
504 | 502 | def switch_doctest_mode(self, mode): |
|
505 | 503 | """Switch prompts to classic for %doctest_mode""" |
|
506 | 504 | if mode: |
|
507 | 505 | self._prompts_before = self.prompts |
|
508 | 506 | self.prompts = ClassicPrompts(self) |
|
509 | 507 | elif self._prompts_before: |
|
510 | 508 | self.prompts = self._prompts_before |
|
511 | 509 | self._prompts_before = None |
|
512 | 510 | self._update_layout() |
|
513 | 511 | |
|
514 | 512 | |
|
515 | 513 | InteractiveShellABC.register(TerminalInteractiveShell) |
|
516 | 514 | |
|
517 | 515 | if __name__ == '__main__': |
|
518 | 516 | TerminalInteractiveShell.instance().interact() |
@@ -1,594 +1,595 b'' | |||
|
1 | 1 | """Patched version of standard library tokenize, to deal with various bugs. |
|
2 | 2 | |
|
3 | 3 | Based on Python 3.2 code. |
|
4 | 4 | |
|
5 | 5 | Patches: |
|
6 | 6 | |
|
7 | 7 | - Gareth Rees' patch for Python issue #12691 (untokenizing) |
|
8 | 8 | - Except we don't encode the output of untokenize |
|
9 | 9 | - Python 2 compatible syntax, so that it can be byte-compiled at installation |
|
10 | 10 | - Newlines in comments and blank lines should be either NL or NEWLINE, depending |
|
11 | 11 | on whether they are in a multi-line statement. Filed as Python issue #17061. |
|
12 | 12 | - Export generate_tokens & TokenError |
|
13 | 13 | - u and rb literals are allowed under Python 3.3 and above. |
|
14 | 14 | |
|
15 | 15 | ------------------------------------------------------------------------------ |
|
16 | 16 | Tokenization help for Python programs. |
|
17 | 17 | |
|
18 | 18 | tokenize(readline) is a generator that breaks a stream of bytes into |
|
19 | 19 | Python tokens. It decodes the bytes according to PEP-0263 for |
|
20 | 20 | determining source file encoding. |
|
21 | 21 | |
|
22 | 22 | It accepts a readline-like method which is called repeatedly to get the |
|
23 | 23 | next line of input (or b"" for EOF). It generates 5-tuples with these |
|
24 | 24 | members: |
|
25 | 25 | |
|
26 | 26 | the token type (see token.py) |
|
27 | 27 | the token (a string) |
|
28 | 28 | the starting (row, column) indices of the token (a 2-tuple of ints) |
|
29 | 29 | the ending (row, column) indices of the token (a 2-tuple of ints) |
|
30 | 30 | the original line (string) |
|
31 | 31 | |
|
32 | 32 | It is designed to match the working of the Python tokenizer exactly, except |
|
33 | 33 | that it produces COMMENT tokens for comments and gives type OP for all |
|
34 | 34 | operators. Additionally, all token lists start with an ENCODING token |
|
35 | 35 | which tells you which encoding was used to decode the bytes stream. |
|
36 | 36 | """ |
|
37 | 37 | |
|
38 | 38 | __author__ = 'Ka-Ping Yee <ping@lfw.org>' |
|
39 | 39 | __credits__ = ('GvR, ESR, Tim Peters, Thomas Wouters, Fred Drake, ' |
|
40 | 40 | 'Skip Montanaro, Raymond Hettinger, Trent Nelson, ' |
|
41 | 41 | 'Michael Foord') |
|
42 | 42 | import builtins |
|
43 | 43 | import re |
|
44 | 44 | import sys |
|
45 | 45 | from token import * |
|
46 | 46 | from codecs import lookup, BOM_UTF8 |
|
47 | 47 | import collections |
|
48 | 48 | from io import TextIOWrapper |
|
49 | 49 | cookie_re = re.compile("coding[:=]\s*([-\w.]+)") |
|
50 | 50 | |
|
51 | 51 | import token |
|
52 | 52 | __all__ = token.__all__ + ["COMMENT", "tokenize", "detect_encoding", |
|
53 | 53 | "NL", "untokenize", "ENCODING", "TokenInfo"] |
|
54 | 54 | del token |
|
55 | 55 | |
|
56 | 56 | __all__ += ["generate_tokens", "TokenError"] |
|
57 | 57 | |
|
58 | 58 | COMMENT = N_TOKENS |
|
59 | 59 | tok_name[COMMENT] = 'COMMENT' |
|
60 | 60 | NL = N_TOKENS + 1 |
|
61 | 61 | tok_name[NL] = 'NL' |
|
62 | 62 | ENCODING = N_TOKENS + 2 |
|
63 | 63 | tok_name[ENCODING] = 'ENCODING' |
|
64 | 64 | N_TOKENS += 3 |
|
65 | 65 | |
|
66 | 66 | class TokenInfo(collections.namedtuple('TokenInfo', 'type string start end line')): |
|
67 | 67 | def __repr__(self): |
|
68 | 68 | annotated_type = '%d (%s)' % (self.type, tok_name[self.type]) |
|
69 | 69 | return ('TokenInfo(type=%s, string=%r, start=%r, end=%r, line=%r)' % |
|
70 | 70 | self._replace(type=annotated_type)) |
|
71 | 71 | |
|
72 | 72 | def group(*choices): return '(' + '|'.join(choices) + ')' |
|
73 | 73 | def any(*choices): return group(*choices) + '*' |
|
74 | 74 | def maybe(*choices): return group(*choices) + '?' |
|
75 | 75 | |
|
76 | 76 | # Note: we use unicode matching for names ("\w") but ascii matching for |
|
77 | 77 | # number literals. |
|
78 | 78 | Whitespace = r'[ \f\t]*' |
|
79 | 79 | Comment = r'#[^\r\n]*' |
|
80 | 80 | Ignore = Whitespace + any(r'\\\r?\n' + Whitespace) + maybe(Comment) |
|
81 | 81 | Name = r'\w+' |
|
82 | 82 | |
|
83 | 83 | Hexnumber = r'0[xX][0-9a-fA-F]+' |
|
84 | 84 | Binnumber = r'0[bB][01]+' |
|
85 | 85 | Octnumber = r'0[oO][0-7]+' |
|
86 | 86 | Decnumber = r'(?:0+|[1-9][0-9]*)' |
|
87 | 87 | Intnumber = group(Hexnumber, Binnumber, Octnumber, Decnumber) |
|
88 | 88 | Exponent = r'[eE][-+]?[0-9]+' |
|
89 | 89 | Pointfloat = group(r'[0-9]+\.[0-9]*', r'\.[0-9]+') + maybe(Exponent) |
|
90 | 90 | Expfloat = r'[0-9]+' + Exponent |
|
91 | 91 | Floatnumber = group(Pointfloat, Expfloat) |
|
92 | 92 | Imagnumber = group(r'[0-9]+[jJ]', Floatnumber + r'[jJ]') |
|
93 | 93 | Number = group(Imagnumber, Floatnumber, Intnumber) |
|
94 | 94 | |
|
95 | 95 | if sys.version_info.minor >= 3: |
|
96 | 96 | StringPrefix = r'(?:[bB][rR]?|[rR][bB]?|[uU])?' |
|
97 | 97 | else: |
|
98 | 98 | StringPrefix = r'(?:[bB]?[rR]?)?' |
|
99 | 99 | |
|
100 | 100 | # Tail end of ' string. |
|
101 | 101 | Single = r"[^'\\]*(?:\\.[^'\\]*)*'" |
|
102 | 102 | # Tail end of " string. |
|
103 | 103 | Double = r'[^"\\]*(?:\\.[^"\\]*)*"' |
|
104 | 104 | # Tail end of ''' string. |
|
105 | 105 | Single3 = r"[^'\\]*(?:(?:\\.|'(?!''))[^'\\]*)*'''" |
|
106 | 106 | # Tail end of """ string. |
|
107 | 107 | Double3 = r'[^"\\]*(?:(?:\\.|"(?!""))[^"\\]*)*"""' |
|
108 | 108 | Triple = group(StringPrefix + "'''", StringPrefix + '"""') |
|
109 | 109 | # Single-line ' or " string. |
|
110 | 110 | String = group(StringPrefix + r"'[^\n'\\]*(?:\\.[^\n'\\]*)*'", |
|
111 | 111 | StringPrefix + r'"[^\n"\\]*(?:\\.[^\n"\\]*)*"') |
|
112 | 112 | |
|
113 | 113 | # Because of leftmost-then-longest match semantics, be sure to put the |
|
114 | 114 | # longest operators first (e.g., if = came before ==, == would get |
|
115 | 115 | # recognized as two instances of =). |
|
116 | 116 | Operator = group(r"\*\*=?", r">>=?", r"<<=?", r"!=", |
|
117 | 117 | r"//=?", r"->", |
|
118 | 118 | r"[+\-*/%&|^=<>]=?", |
|
119 | 119 | r"~") |
|
120 | 120 | |
|
121 | 121 | Bracket = '[][(){}]' |
|
122 | 122 | Special = group(r'\r?\n', r'\.\.\.', r'[:;.,@]') |
|
123 | 123 | Funny = group(Operator, Bracket, Special) |
|
124 | 124 | |
|
125 | 125 | PlainToken = group(Number, Funny, String, Name) |
|
126 | 126 | Token = Ignore + PlainToken |
|
127 | 127 | |
|
128 | 128 | # First (or only) line of ' or " string. |
|
129 | 129 | ContStr = group(StringPrefix + r"'[^\n'\\]*(?:\\.[^\n'\\]*)*" + |
|
130 | 130 | group("'", r'\\\r?\n'), |
|
131 | 131 | StringPrefix + r'"[^\n"\\]*(?:\\.[^\n"\\]*)*' + |
|
132 | 132 | group('"', r'\\\r?\n')) |
|
133 | 133 | PseudoExtras = group(r'\\\r?\n', Comment, Triple) |
|
134 | 134 | PseudoToken = Whitespace + group(PseudoExtras, Number, Funny, ContStr, Name) |
|
135 | 135 | |
|
136 | 136 | def _compile(expr): |
|
137 | 137 | return re.compile(expr, re.UNICODE) |
|
138 | 138 | |
|
139 | 139 | tokenprog, pseudoprog, single3prog, double3prog = map( |
|
140 | 140 | _compile, (Token, PseudoToken, Single3, Double3)) |
|
141 | 141 | endprogs = {"'": _compile(Single), '"': _compile(Double), |
|
142 | 142 | "'''": single3prog, '"""': double3prog, |
|
143 | 143 | "r'''": single3prog, 'r"""': double3prog, |
|
144 | 144 | "b'''": single3prog, 'b"""': double3prog, |
|
145 | 145 | "R'''": single3prog, 'R"""': double3prog, |
|
146 | 146 | "B'''": single3prog, 'B"""': double3prog, |
|
147 | 147 | "br'''": single3prog, 'br"""': double3prog, |
|
148 | 148 | "bR'''": single3prog, 'bR"""': double3prog, |
|
149 | 149 | "Br'''": single3prog, 'Br"""': double3prog, |
|
150 | 150 | "BR'''": single3prog, 'BR"""': double3prog, |
|
151 | 151 | 'r': None, 'R': None, 'b': None, 'B': None} |
|
152 | 152 | |
|
153 | 153 | triple_quoted = {} |
|
154 | 154 | for t in ("'''", '"""', |
|
155 | 155 | "r'''", 'r"""', "R'''", 'R"""', |
|
156 | 156 | "b'''", 'b"""', "B'''", 'B"""', |
|
157 | 157 | "br'''", 'br"""', "Br'''", 'Br"""', |
|
158 | 158 | "bR'''", 'bR"""', "BR'''", 'BR"""'): |
|
159 | 159 | triple_quoted[t] = t |
|
160 | 160 | single_quoted = {} |
|
161 | 161 | for t in ("'", '"', |
|
162 | 162 | "r'", 'r"', "R'", 'R"', |
|
163 | 163 | "b'", 'b"', "B'", 'B"', |
|
164 | 164 | "br'", 'br"', "Br'", 'Br"', |
|
165 | 165 | "bR'", 'bR"', "BR'", 'BR"' ): |
|
166 | 166 | single_quoted[t] = t |
|
167 | 167 | |
|
168 | 168 | if sys.version_info.minor >= 3: |
|
169 | 169 | # Python 3.3 |
|
170 | 170 | for _prefix in ['rb', 'rB', 'Rb', 'RB', 'u', 'U']: |
|
171 | 171 | _t2 = _prefix+'"""' |
|
172 | 172 | endprogs[_t2] = double3prog |
|
173 | 173 | triple_quoted[_t2] = _t2 |
|
174 | 174 | _t1 = _prefix + "'''" |
|
175 | 175 | endprogs[_t1] = single3prog |
|
176 | 176 | triple_quoted[_t1] = _t1 |
|
177 | 177 | single_quoted[_prefix+'"'] = _prefix+'"' |
|
178 | 178 | single_quoted[_prefix+"'"] = _prefix+"'" |
|
179 | 179 | del _prefix, _t2, _t1 |
|
180 | 180 | endprogs['u'] = None |
|
181 | 181 | endprogs['U'] = None |
|
182 | 182 | |
|
183 | 183 | del _compile |
|
184 | 184 | |
|
185 | 185 | tabsize = 8 |
|
186 | 186 | |
|
187 | 187 | class TokenError(Exception): pass |
|
188 | 188 | |
|
189 | 189 | class StopTokenizing(Exception): pass |
|
190 | 190 | |
|
191 | 191 | |
|
192 | 192 | class Untokenizer: |
|
193 | 193 | |
|
194 | 194 | def __init__(self): |
|
195 | 195 | self.tokens = [] |
|
196 | 196 | self.prev_row = 1 |
|
197 | 197 | self.prev_col = 0 |
|
198 | 198 | self.encoding = 'utf-8' |
|
199 | 199 | |
|
200 | 200 | def add_whitespace(self, tok_type, start): |
|
201 | 201 | row, col = start |
|
202 | 202 | assert row >= self.prev_row |
|
203 | 203 | col_offset = col - self.prev_col |
|
204 | 204 | if col_offset > 0: |
|
205 | 205 | self.tokens.append(" " * col_offset) |
|
206 | 206 | elif row > self.prev_row and tok_type not in (NEWLINE, NL, ENDMARKER): |
|
207 | 207 | # Line was backslash-continued. |
|
208 | 208 | self.tokens.append(" ") |
|
209 | 209 | |
|
210 | 210 | def untokenize(self, tokens): |
|
211 | 211 | iterable = iter(tokens) |
|
212 | 212 | for t in iterable: |
|
213 | 213 | if len(t) == 2: |
|
214 | 214 | self.compat(t, iterable) |
|
215 | 215 | break |
|
216 | 216 | tok_type, token, start, end = t[:4] |
|
217 | 217 | if tok_type == ENCODING: |
|
218 | 218 | self.encoding = token |
|
219 | 219 | continue |
|
220 | 220 | self.add_whitespace(tok_type, start) |
|
221 | 221 | self.tokens.append(token) |
|
222 | 222 | self.prev_row, self.prev_col = end |
|
223 | 223 | if tok_type in (NEWLINE, NL): |
|
224 | 224 | self.prev_row += 1 |
|
225 | 225 | self.prev_col = 0 |
|
226 | 226 | return "".join(self.tokens) |
|
227 | 227 | |
|
228 | 228 | def compat(self, token, iterable): |
|
229 | 229 | # This import is here to avoid problems when the itertools |
|
230 | 230 | # module is not built yet and tokenize is imported. |
|
231 | 231 | from itertools import chain |
|
232 | 232 | startline = False |
|
233 | 233 | prevstring = False |
|
234 | 234 | indents = [] |
|
235 | 235 | toks_append = self.tokens.append |
|
236 | 236 | |
|
237 | 237 | for tok in chain([token], iterable): |
|
238 | 238 | toknum, tokval = tok[:2] |
|
239 | 239 | if toknum == ENCODING: |
|
240 | 240 | self.encoding = tokval |
|
241 | 241 | continue |
|
242 | 242 | |
|
243 | 243 | if toknum in (NAME, NUMBER): |
|
244 | 244 | tokval += ' ' |
|
245 | 245 | |
|
246 | 246 | # Insert a space between two consecutive strings |
|
247 | 247 | if toknum == STRING: |
|
248 | 248 | if prevstring: |
|
249 | 249 | tokval = ' ' + tokval |
|
250 | 250 | prevstring = True |
|
251 | 251 | else: |
|
252 | 252 | prevstring = False |
|
253 | 253 | |
|
254 | 254 | if toknum == INDENT: |
|
255 | 255 | indents.append(tokval) |
|
256 | 256 | continue |
|
257 | 257 | elif toknum == DEDENT: |
|
258 | 258 | indents.pop() |
|
259 | 259 | continue |
|
260 | 260 | elif toknum in (NEWLINE, NL): |
|
261 | 261 | startline = True |
|
262 | 262 | elif startline and indents: |
|
263 | 263 | toks_append(indents[-1]) |
|
264 | 264 | startline = False |
|
265 | 265 | toks_append(tokval) |
|
266 | 266 | |
|
267 | 267 | |
|
268 | 268 | def untokenize(tokens): |
|
269 | 269 | """ |
|
270 | 270 | Convert ``tokens`` (an iterable) back into Python source code. Return |
|
271 | 271 | a bytes object, encoded using the encoding specified by the last |
|
272 | 272 | ENCODING token in ``tokens``, or UTF-8 if no ENCODING token is found. |
|
273 | 273 | |
|
274 | 274 | The result is guaranteed to tokenize back to match the input so that |
|
275 | 275 | the conversion is lossless and round-trips are assured. The |
|
276 | 276 | guarantee applies only to the token type and token string as the |
|
277 | 277 | spacing between tokens (column positions) may change. |
|
278 | 278 | |
|
279 | 279 | :func:`untokenize` has two modes. If the input tokens are sequences |
|
280 | 280 | of length 2 (``type``, ``string``) then spaces are added as necessary to |
|
281 | 281 | preserve the round-trip property. |
|
282 | 282 | |
|
283 | 283 | If the input tokens are sequences of length 4 or more (``type``, |
|
284 | 284 | ``string``, ``start``, ``end``), as returned by :func:`tokenize`, then |
|
285 | 285 | spaces are added so that each token appears in the result at the |
|
286 | 286 | position indicated by ``start`` and ``end``, if possible. |
|
287 | 287 | """ |
|
288 | 288 | return Untokenizer().untokenize(tokens) |
|
289 | 289 | |
|
290 | 290 | |
|
291 | 291 | def _get_normal_name(orig_enc): |
|
292 | 292 | """Imitates get_normal_name in tokenizer.c.""" |
|
293 | 293 | # Only care about the first 12 characters. |
|
294 | 294 | enc = orig_enc[:12].lower().replace("_", "-") |
|
295 | 295 | if enc == "utf-8" or enc.startswith("utf-8-"): |
|
296 | 296 | return "utf-8" |
|
297 | 297 | if enc in ("latin-1", "iso-8859-1", "iso-latin-1") or \ |
|
298 | 298 | enc.startswith(("latin-1-", "iso-8859-1-", "iso-latin-1-")): |
|
299 | 299 | return "iso-8859-1" |
|
300 | 300 | return orig_enc |
|
301 | 301 | |
|
302 | 302 | def detect_encoding(readline): |
|
303 | 303 | """ |
|
304 | 304 | The detect_encoding() function is used to detect the encoding that should |
|
305 | 305 | be used to decode a Python source file. It requires one argment, readline, |
|
306 | 306 | in the same way as the tokenize() generator. |
|
307 | 307 | |
|
308 | 308 | It will call readline a maximum of twice, and return the encoding used |
|
309 | 309 | (as a string) and a list of any lines (left as bytes) it has read in. |
|
310 | 310 | |
|
311 | 311 | It detects the encoding from the presence of a utf-8 bom or an encoding |
|
312 | 312 | cookie as specified in pep-0263. If both a bom and a cookie are present, |
|
313 | 313 | but disagree, a SyntaxError will be raised. If the encoding cookie is an |
|
314 | 314 | invalid charset, raise a SyntaxError. Note that if a utf-8 bom is found, |
|
315 | 315 | 'utf-8-sig' is returned. |
|
316 | 316 | |
|
317 | 317 | If no encoding is specified, then the default of 'utf-8' will be returned. |
|
318 | 318 | """ |
|
319 | 319 | bom_found = False |
|
320 | 320 | encoding = None |
|
321 | 321 | default = 'utf-8' |
|
322 | 322 | def read_or_stop(): |
|
323 | 323 | try: |
|
324 | 324 | return readline() |
|
325 | 325 | except StopIteration: |
|
326 | 326 | return b'' |
|
327 | 327 | |
|
328 | 328 | def find_cookie(line): |
|
329 | 329 | try: |
|
330 | 330 | # Decode as UTF-8. Either the line is an encoding declaration, |
|
331 | 331 | # in which case it should be pure ASCII, or it must be UTF-8 |
|
332 | 332 | # per default encoding. |
|
333 | 333 | line_string = line.decode('utf-8') |
|
334 | 334 | except UnicodeDecodeError: |
|
335 | 335 | raise SyntaxError("invalid or missing encoding declaration") |
|
336 | 336 | |
|
337 | 337 | matches = cookie_re.findall(line_string) |
|
338 | 338 | if not matches: |
|
339 | 339 | return None |
|
340 | 340 | encoding = _get_normal_name(matches[0]) |
|
341 | 341 | try: |
|
342 | 342 | codec = lookup(encoding) |
|
343 | 343 | except LookupError: |
|
344 | 344 | # This behaviour mimics the Python interpreter |
|
345 | 345 | raise SyntaxError("unknown encoding: " + encoding) |
|
346 | 346 | |
|
347 | 347 | if bom_found: |
|
348 | 348 | if encoding != 'utf-8': |
|
349 | 349 | # This behaviour mimics the Python interpreter |
|
350 | 350 | raise SyntaxError('encoding problem: utf-8') |
|
351 | 351 | encoding += '-sig' |
|
352 | 352 | return encoding |
|
353 | 353 | |
|
354 | 354 | first = read_or_stop() |
|
355 | 355 | if first.startswith(BOM_UTF8): |
|
356 | 356 | bom_found = True |
|
357 | 357 | first = first[3:] |
|
358 | 358 | default = 'utf-8-sig' |
|
359 | 359 | if not first: |
|
360 | 360 | return default, [] |
|
361 | 361 | |
|
362 | 362 | encoding = find_cookie(first) |
|
363 | 363 | if encoding: |
|
364 | 364 | return encoding, [first] |
|
365 | 365 | |
|
366 | 366 | second = read_or_stop() |
|
367 | 367 | if not second: |
|
368 | 368 | return default, [first] |
|
369 | 369 | |
|
370 | 370 | encoding = find_cookie(second) |
|
371 | 371 | if encoding: |
|
372 | 372 | return encoding, [first, second] |
|
373 | 373 | |
|
374 | 374 | return default, [first, second] |
|
375 | 375 | |
|
376 | 376 | |
|
377 | 377 | def open(filename): |
|
378 | 378 | """Open a file in read only mode using the encoding detected by |
|
379 | 379 | detect_encoding(). |
|
380 | 380 | """ |
|
381 | 381 | buffer = builtins.open(filename, 'rb') |
|
382 | 382 | encoding, lines = detect_encoding(buffer.readline) |
|
383 | 383 | buffer.seek(0) |
|
384 | 384 | text = TextIOWrapper(buffer, encoding, line_buffering=True) |
|
385 | 385 | text.mode = 'r' |
|
386 | 386 | return text |
|
387 | 387 | |
|
388 | 388 | |
|
389 | 389 | def tokenize(readline): |
|
390 | 390 | """ |
|
391 | 391 | The tokenize() generator requires one argment, readline, which |
|
392 | 392 | must be a callable object which provides the same interface as the |
|
393 | 393 | readline() method of built-in file objects. Each call to the function |
|
394 | 394 | should return one line of input as bytes. Alternately, readline |
|
395 | can be a callable function terminating with StopIteration: | |
|
395 | can be a callable function terminating with :class:`StopIteration`:: | |
|
396 | ||
|
396 | 397 | readline = open(myfile, 'rb').__next__ # Example of alternate readline |
|
397 | 398 | |
|
398 | 399 | The generator produces 5-tuples with these members: the token type; the |
|
399 | 400 | token string; a 2-tuple (srow, scol) of ints specifying the row and |
|
400 | 401 | column where the token begins in the source; a 2-tuple (erow, ecol) of |
|
401 | 402 | ints specifying the row and column where the token ends in the source; |
|
402 | 403 | and the line on which the token was found. The line passed is the |
|
403 | 404 | logical line; continuation lines are included. |
|
404 | 405 | |
|
405 | 406 | The first token sequence will always be an ENCODING token |
|
406 | 407 | which tells you which encoding was used to decode the bytes stream. |
|
407 | 408 | """ |
|
408 | 409 | # This import is here to avoid problems when the itertools module is not |
|
409 | 410 | # built yet and tokenize is imported. |
|
410 | 411 | from itertools import chain, repeat |
|
411 | 412 | encoding, consumed = detect_encoding(readline) |
|
412 | 413 | rl_gen = iter(readline, b"") |
|
413 | 414 | empty = repeat(b"") |
|
414 | 415 | return _tokenize(chain(consumed, rl_gen, empty).__next__, encoding) |
|
415 | 416 | |
|
416 | 417 | |
|
417 | 418 | def _tokenize(readline, encoding): |
|
418 | 419 | lnum = parenlev = continued = 0 |
|
419 | 420 | numchars = '0123456789' |
|
420 | 421 | contstr, needcont = '', 0 |
|
421 | 422 | contline = None |
|
422 | 423 | indents = [0] |
|
423 | 424 | |
|
424 | 425 | if encoding is not None: |
|
425 | 426 | if encoding == "utf-8-sig": |
|
426 | 427 | # BOM will already have been stripped. |
|
427 | 428 | encoding = "utf-8" |
|
428 | 429 | yield TokenInfo(ENCODING, encoding, (0, 0), (0, 0), '') |
|
429 | 430 | while True: # loop over lines in stream |
|
430 | 431 | try: |
|
431 | 432 | line = readline() |
|
432 | 433 | except StopIteration: |
|
433 | 434 | line = b'' |
|
434 | 435 | |
|
435 | 436 | if encoding is not None: |
|
436 | 437 | line = line.decode(encoding) |
|
437 | 438 | lnum += 1 |
|
438 | 439 | pos, max = 0, len(line) |
|
439 | 440 | |
|
440 | 441 | if contstr: # continued string |
|
441 | 442 | if not line: |
|
442 | 443 | raise TokenError("EOF in multi-line string", strstart) |
|
443 | 444 | endmatch = endprog.match(line) |
|
444 | 445 | if endmatch: |
|
445 | 446 | pos = end = endmatch.end(0) |
|
446 | 447 | yield TokenInfo(STRING, contstr + line[:end], |
|
447 | 448 | strstart, (lnum, end), contline + line) |
|
448 | 449 | contstr, needcont = '', 0 |
|
449 | 450 | contline = None |
|
450 | 451 | elif needcont and line[-2:] != '\\\n' and line[-3:] != '\\\r\n': |
|
451 | 452 | yield TokenInfo(ERRORTOKEN, contstr + line, |
|
452 | 453 | strstart, (lnum, len(line)), contline) |
|
453 | 454 | contstr = '' |
|
454 | 455 | contline = None |
|
455 | 456 | continue |
|
456 | 457 | else: |
|
457 | 458 | contstr = contstr + line |
|
458 | 459 | contline = contline + line |
|
459 | 460 | continue |
|
460 | 461 | |
|
461 | 462 | elif parenlev == 0 and not continued: # new statement |
|
462 | 463 | if not line: break |
|
463 | 464 | column = 0 |
|
464 | 465 | while pos < max: # measure leading whitespace |
|
465 | 466 | if line[pos] == ' ': |
|
466 | 467 | column += 1 |
|
467 | 468 | elif line[pos] == '\t': |
|
468 | 469 | column = (column//tabsize + 1)*tabsize |
|
469 | 470 | elif line[pos] == '\f': |
|
470 | 471 | column = 0 |
|
471 | 472 | else: |
|
472 | 473 | break |
|
473 | 474 | pos += 1 |
|
474 | 475 | if pos == max: |
|
475 | 476 | break |
|
476 | 477 | |
|
477 | 478 | if line[pos] in '#\r\n': # skip comments or blank lines |
|
478 | 479 | if line[pos] == '#': |
|
479 | 480 | comment_token = line[pos:].rstrip('\r\n') |
|
480 | 481 | nl_pos = pos + len(comment_token) |
|
481 | 482 | yield TokenInfo(COMMENT, comment_token, |
|
482 | 483 | (lnum, pos), (lnum, pos + len(comment_token)), line) |
|
483 | 484 | yield TokenInfo(NEWLINE, line[nl_pos:], |
|
484 | 485 | (lnum, nl_pos), (lnum, len(line)), line) |
|
485 | 486 | else: |
|
486 | 487 | yield TokenInfo(NEWLINE, line[pos:], |
|
487 | 488 | (lnum, pos), (lnum, len(line)), line) |
|
488 | 489 | continue |
|
489 | 490 | |
|
490 | 491 | if column > indents[-1]: # count indents or dedents |
|
491 | 492 | indents.append(column) |
|
492 | 493 | yield TokenInfo(INDENT, line[:pos], (lnum, 0), (lnum, pos), line) |
|
493 | 494 | while column < indents[-1]: |
|
494 | 495 | if column not in indents: |
|
495 | 496 | raise IndentationError( |
|
496 | 497 | "unindent does not match any outer indentation level", |
|
497 | 498 | ("<tokenize>", lnum, pos, line)) |
|
498 | 499 | indents = indents[:-1] |
|
499 | 500 | yield TokenInfo(DEDENT, '', (lnum, pos), (lnum, pos), line) |
|
500 | 501 | |
|
501 | 502 | else: # continued statement |
|
502 | 503 | if not line: |
|
503 | 504 | raise TokenError("EOF in multi-line statement", (lnum, 0)) |
|
504 | 505 | continued = 0 |
|
505 | 506 | |
|
506 | 507 | while pos < max: |
|
507 | 508 | pseudomatch = pseudoprog.match(line, pos) |
|
508 | 509 | if pseudomatch: # scan for tokens |
|
509 | 510 | start, end = pseudomatch.span(1) |
|
510 | 511 | spos, epos, pos = (lnum, start), (lnum, end), end |
|
511 | 512 | token, initial = line[start:end], line[start] |
|
512 | 513 | |
|
513 | 514 | if (initial in numchars or # ordinary number |
|
514 | 515 | (initial == '.' and token != '.' and token != '...')): |
|
515 | 516 | yield TokenInfo(NUMBER, token, spos, epos, line) |
|
516 | 517 | elif initial in '\r\n': |
|
517 | 518 | yield TokenInfo(NL if parenlev > 0 else NEWLINE, |
|
518 | 519 | token, spos, epos, line) |
|
519 | 520 | elif initial == '#': |
|
520 | 521 | assert not token.endswith("\n") |
|
521 | 522 | yield TokenInfo(COMMENT, token, spos, epos, line) |
|
522 | 523 | elif token in triple_quoted: |
|
523 | 524 | endprog = endprogs[token] |
|
524 | 525 | endmatch = endprog.match(line, pos) |
|
525 | 526 | if endmatch: # all on one line |
|
526 | 527 | pos = endmatch.end(0) |
|
527 | 528 | token = line[start:pos] |
|
528 | 529 | yield TokenInfo(STRING, token, spos, (lnum, pos), line) |
|
529 | 530 | else: |
|
530 | 531 | strstart = (lnum, start) # multiple lines |
|
531 | 532 | contstr = line[start:] |
|
532 | 533 | contline = line |
|
533 | 534 | break |
|
534 | 535 | elif initial in single_quoted or \ |
|
535 | 536 | token[:2] in single_quoted or \ |
|
536 | 537 | token[:3] in single_quoted: |
|
537 | 538 | if token[-1] == '\n': # continued string |
|
538 | 539 | strstart = (lnum, start) |
|
539 | 540 | endprog = (endprogs[initial] or endprogs[token[1]] or |
|
540 | 541 | endprogs[token[2]]) |
|
541 | 542 | contstr, needcont = line[start:], 1 |
|
542 | 543 | contline = line |
|
543 | 544 | break |
|
544 | 545 | else: # ordinary string |
|
545 | 546 | yield TokenInfo(STRING, token, spos, epos, line) |
|
546 | 547 | elif initial.isidentifier(): # ordinary name |
|
547 | 548 | yield TokenInfo(NAME, token, spos, epos, line) |
|
548 | 549 | elif initial == '\\': # continued stmt |
|
549 | 550 | continued = 1 |
|
550 | 551 | else: |
|
551 | 552 | if initial in '([{': |
|
552 | 553 | parenlev += 1 |
|
553 | 554 | elif initial in ')]}': |
|
554 | 555 | parenlev -= 1 |
|
555 | 556 | yield TokenInfo(OP, token, spos, epos, line) |
|
556 | 557 | else: |
|
557 | 558 | yield TokenInfo(ERRORTOKEN, line[pos], |
|
558 | 559 | (lnum, pos), (lnum, pos+1), line) |
|
559 | 560 | pos += 1 |
|
560 | 561 | |
|
561 | 562 | for indent in indents[1:]: # pop remaining indent levels |
|
562 | 563 | yield TokenInfo(DEDENT, '', (lnum, 0), (lnum, 0), '') |
|
563 | 564 | yield TokenInfo(ENDMARKER, '', (lnum, 0), (lnum, 0), '') |
|
564 | 565 | |
|
565 | 566 | |
|
566 | 567 | # An undocumented, backwards compatible, API for all the places in the standard |
|
567 | 568 | # library that expect to be able to use tokenize with strings |
|
568 | 569 | def generate_tokens(readline): |
|
569 | 570 | return _tokenize(readline, None) |
|
570 | 571 | |
|
571 | 572 | if __name__ == "__main__": |
|
572 | 573 | # Quick sanity check |
|
573 | 574 | s = b'''def parseline(self, line): |
|
574 | 575 | """Parse the line into a command name and a string containing |
|
575 | 576 | the arguments. Returns a tuple containing (command, args, line). |
|
576 | 577 | 'command' and 'args' may be None if the line couldn't be parsed. |
|
577 | 578 | """ |
|
578 | 579 | line = line.strip() |
|
579 | 580 | if not line: |
|
580 | 581 | return None, None, line |
|
581 | 582 | elif line[0] == '?': |
|
582 | 583 | line = 'help ' + line[1:] |
|
583 | 584 | elif line[0] == '!': |
|
584 | 585 | if hasattr(self, 'do_shell'): |
|
585 | 586 | line = 'shell ' + line[1:] |
|
586 | 587 | else: |
|
587 | 588 | return None, None, line |
|
588 | 589 | i, n = 0, len(line) |
|
589 | 590 | while i < n and line[i] in self.identchars: i = i+1 |
|
590 | 591 | cmd, arg = line[:i], line[i:].strip() |
|
591 | 592 | return cmd, arg, line |
|
592 | 593 | ''' |
|
593 | 594 | for tok in tokenize(iter(s.splitlines()).__next__): |
|
594 | 595 | print(tok) |
@@ -1,127 +1,127 b'' | |||
|
1 | 1 | """Token-related utilities""" |
|
2 | 2 | |
|
3 | 3 | # Copyright (c) IPython Development Team. |
|
4 | 4 | # Distributed under the terms of the Modified BSD License. |
|
5 | 5 | |
|
6 | 6 | |
|
7 | 7 | from collections import namedtuple |
|
8 | 8 | from io import StringIO |
|
9 | 9 | from keyword import iskeyword |
|
10 | 10 | |
|
11 | 11 | from . import tokenize2 |
|
12 | 12 | from .py3compat import cast_unicode_py2 |
|
13 | 13 | |
|
14 | 14 | Token = namedtuple('Token', ['token', 'text', 'start', 'end', 'line']) |
|
15 | 15 | |
|
16 | 16 | def generate_tokens(readline): |
|
17 | 17 | """wrap generate_tokens to catch EOF errors""" |
|
18 | 18 | try: |
|
19 | 19 | for token in tokenize2.generate_tokens(readline): |
|
20 | 20 | yield token |
|
21 | 21 | except tokenize2.TokenError: |
|
22 | 22 | # catch EOF error |
|
23 | 23 | return |
|
24 | 24 | |
|
25 | 25 | def line_at_cursor(cell, cursor_pos=0): |
|
26 | 26 | """Return the line in a cell at a given cursor position |
|
27 | 27 | |
|
28 | 28 | Used for calling line-based APIs that don't support multi-line input, yet. |
|
29 | 29 | |
|
30 | 30 | Parameters |
|
31 | 31 | ---------- |
|
32 | 32 | |
|
33 | 33 | cell: str |
|
34 | 34 | multiline block of text |
|
35 | 35 | cursor_pos: integer |
|
36 | 36 | the cursor position |
|
37 | 37 | |
|
38 | 38 | Returns |
|
39 | 39 | ------- |
|
40 | 40 | |
|
41 |
(line, offset): ( |
|
|
41 | (line, offset): (string, integer) | |
|
42 | 42 | The line with the current cursor, and the character offset of the start of the line. |
|
43 | 43 | """ |
|
44 | 44 | offset = 0 |
|
45 | 45 | lines = cell.splitlines(True) |
|
46 | 46 | for line in lines: |
|
47 | 47 | next_offset = offset + len(line) |
|
48 | 48 | if next_offset >= cursor_pos: |
|
49 | 49 | break |
|
50 | 50 | offset = next_offset |
|
51 | 51 | else: |
|
52 | 52 | line = "" |
|
53 | 53 | return (line, offset) |
|
54 | 54 | |
|
55 | 55 | def token_at_cursor(cell, cursor_pos=0): |
|
56 | 56 | """Get the token at a given cursor |
|
57 | 57 | |
|
58 | 58 | Used for introspection. |
|
59 | 59 | |
|
60 | 60 | Function calls are prioritized, so the token for the callable will be returned |
|
61 | 61 | if the cursor is anywhere inside the call. |
|
62 | 62 | |
|
63 | 63 | Parameters |
|
64 | 64 | ---------- |
|
65 | 65 | |
|
66 | 66 | cell : unicode |
|
67 | 67 | A block of Python code |
|
68 | 68 | cursor_pos : int |
|
69 | 69 | The location of the cursor in the block where the token should be found |
|
70 | 70 | """ |
|
71 | 71 | cell = cast_unicode_py2(cell) |
|
72 | 72 | names = [] |
|
73 | 73 | tokens = [] |
|
74 | 74 | call_names = [] |
|
75 | 75 | |
|
76 | 76 | offsets = {1: 0} # lines start at 1 |
|
77 | 77 | for tup in generate_tokens(StringIO(cell).readline): |
|
78 | 78 | |
|
79 | 79 | tok = Token(*tup) |
|
80 | 80 | |
|
81 | 81 | # token, text, start, end, line = tup |
|
82 | 82 | start_line, start_col = tok.start |
|
83 | 83 | end_line, end_col = tok.end |
|
84 | 84 | if end_line + 1 not in offsets: |
|
85 | 85 | # keep track of offsets for each line |
|
86 | 86 | lines = tok.line.splitlines(True) |
|
87 | 87 | for lineno, line in enumerate(lines, start_line + 1): |
|
88 | 88 | if lineno not in offsets: |
|
89 | 89 | offsets[lineno] = offsets[lineno-1] + len(line) |
|
90 | 90 | |
|
91 | 91 | offset = offsets[start_line] |
|
92 | 92 | # allow '|foo' to find 'foo' at the beginning of a line |
|
93 | 93 | boundary = cursor_pos + 1 if start_col == 0 else cursor_pos |
|
94 | 94 | if offset + start_col >= boundary: |
|
95 | 95 | # current token starts after the cursor, |
|
96 | 96 | # don't consume it |
|
97 | 97 | break |
|
98 | 98 | |
|
99 | 99 | if tok.token == tokenize2.NAME and not iskeyword(tok.text): |
|
100 | 100 | if names and tokens and tokens[-1].token == tokenize2.OP and tokens[-1].text == '.': |
|
101 | 101 | names[-1] = "%s.%s" % (names[-1], tok.text) |
|
102 | 102 | else: |
|
103 | 103 | names.append(tok.text) |
|
104 | 104 | elif tok.token == tokenize2.OP: |
|
105 | 105 | if tok.text == '=' and names: |
|
106 | 106 | # don't inspect the lhs of an assignment |
|
107 | 107 | names.pop(-1) |
|
108 | 108 | if tok.text == '(' and names: |
|
109 | 109 | # if we are inside a function call, inspect the function |
|
110 | 110 | call_names.append(names[-1]) |
|
111 | 111 | elif tok.text == ')' and call_names: |
|
112 | 112 | call_names.pop(-1) |
|
113 | 113 | |
|
114 | 114 | tokens.append(tok) |
|
115 | 115 | |
|
116 | 116 | if offsets[end_line] + end_col > cursor_pos: |
|
117 | 117 | # we found the cursor, stop reading |
|
118 | 118 | break |
|
119 | 119 | |
|
120 | 120 | if call_names: |
|
121 | 121 | return call_names[-1] |
|
122 | 122 | elif names: |
|
123 | 123 | return names[-1] |
|
124 | 124 | else: |
|
125 | 125 | return '' |
|
126 | 126 | |
|
127 | 127 |
@@ -1,230 +1,230 b'' | |||
|
1 | 1 | ======================= |
|
2 | 2 | Specific config details |
|
3 | 3 | ======================= |
|
4 | 4 | |
|
5 | 5 | .. _custom_prompts: |
|
6 | 6 | |
|
7 | 7 | Custom Prompts |
|
8 | 8 | ============== |
|
9 | 9 | |
|
10 | 10 | .. versionchanged:: 5.0 |
|
11 | 11 | |
|
12 | 12 | From IPython 5, prompts are produced as a list of Pygments tokens, which are |
|
13 | 13 | tuples of (token_type, text). You can customise prompts by writing a method |
|
14 | 14 | which generates a list of tokens. |
|
15 | 15 | |
|
16 | 16 | There are four kinds of prompt: |
|
17 | 17 | |
|
18 | 18 | * The **in** prompt is shown before the first line of input |
|
19 | 19 | (default like ``In [1]:``). |
|
20 | 20 | * The **continuation** prompt is shown before further lines of input |
|
21 | 21 | (default like ``...:``). |
|
22 | 22 | * The **rewrite** prompt is shown to highlight how special syntax has been |
|
23 | 23 | interpreted (default like ``----->``). |
|
24 | 24 | * The **out** prompt is shown before the result from evaluating the input |
|
25 | 25 | (default like ``Out[1]:``). |
|
26 | 26 | |
|
27 | 27 | Custom prompts are supplied together as a class. If you want to customise only |
|
28 | 28 | some of the prompts, inherit from :class:`IPython.terminal.prompts.Prompts`, |
|
29 | 29 | which defines the defaults. The required interface is like this: |
|
30 | 30 | |
|
31 | 31 | .. class:: MyPrompts(shell) |
|
32 | 32 | |
|
33 | 33 | Prompt style definition. *shell* is a reference to the |
|
34 | 34 | :class:`~.TerminalInteractiveShell` instance. |
|
35 | 35 | |
|
36 | 36 | .. method:: in_prompt_tokens(cli=None) |
|
37 | 37 | continuation_prompt_tokens(self, cli=None, width=None) |
|
38 | 38 | rewrite_prompt_tokens() |
|
39 | 39 | out_prompt_tokens() |
|
40 | 40 | |
|
41 | 41 | Return the respective prompts as lists of ``(token_type, text)`` tuples. |
|
42 | 42 | |
|
43 | 43 | For continuation prompts, *width* is an integer representing the width of |
|
44 | 44 | the prompt area in terminal columns. |
|
45 | 45 | |
|
46 | 46 | *cli*, where used, is the prompt_toolkit ``CommandLineInterface`` instance. |
|
47 | 47 | This is mainly for compatibility with the API prompt_toolkit expects. |
|
48 | 48 | |
|
49 | 49 | Here is an example Prompt class that will show the current working directory |
|
50 | 50 | in the input prompt: |
|
51 | 51 | |
|
52 | 52 | .. code-block:: python |
|
53 | 53 | |
|
54 | 54 | from IPython.terminal.prompts import Prompts, Token |
|
55 | 55 | import os |
|
56 | 56 | |
|
57 | 57 | class MyPrompt(Prompts): |
|
58 | 58 | def in_prompt_tokens(self, cli=None): |
|
59 | 59 | return [(Token, os.getcwd()), |
|
60 | 60 | (Token.Prompt, ' >>>')] |
|
61 | 61 | |
|
62 | 62 | To set the new prompt, assign it to the ``prompts`` attribute of the IPython |
|
63 | 63 | shell: |
|
64 | 64 | |
|
65 | 65 | .. code-block:: python |
|
66 | 66 | |
|
67 | 67 | In [2]: ip = get_ipython() |
|
68 | 68 | ...: ip.prompts = MyPrompt(ip) |
|
69 | 69 | |
|
70 | 70 | /home/bob >>> # it works |
|
71 | 71 | |
|
72 | 72 | See ``IPython/example/utils/cwd_prompt.py`` for an example of how to write an |
|
73 | 73 | extensions to customise prompts. |
|
74 | 74 | |
|
75 | 75 | Inside IPython or in a startup script, you can use a custom prompts class |
|
76 | 76 | by setting ``get_ipython().prompts`` to an *instance* of the class. |
|
77 | 77 | In configuration, ``TerminalInteractiveShell.prompts_class`` may be set to |
|
78 | 78 | either the class object, or a string of its full importable name. |
|
79 | 79 | |
|
80 | 80 | To include invisible terminal control sequences in a prompt, use |
|
81 | 81 | ``Token.ZeroWidthEscape`` as the token type. Tokens with this type are ignored |
|
82 | 82 | when calculating the width. |
|
83 | 83 | |
|
84 | 84 | Colours in the prompt are determined by the token types and the highlighting |
|
85 | 85 | style; see below for more details. The tokens used in the default prompts are |
|
86 | 86 | ``Prompt``, ``PromptNum``, ``OutPrompt`` and ``OutPromptNum``. |
|
87 | 87 | |
|
88 | 88 | .. _termcolour: |
|
89 | 89 | |
|
90 | 90 | Terminal Colors |
|
91 | 91 | =============== |
|
92 | 92 | |
|
93 | 93 | .. versionchanged:: 5.0 |
|
94 | 94 | |
|
95 | 95 | There are two main configuration options controlling colours. |
|
96 | 96 | |
|
97 | 97 | ``InteractiveShell.colors`` sets the colour of tracebacks and object info (the |
|
98 | 98 | output from e.g. ``zip?``). It may also affect other things if the option below |
|
99 | 99 | is set to ``'legacy'``. It has four case-insensitive values: |
|
100 | 100 | ``'nocolor', 'neutral', 'linux', 'lightbg'``. The default is *neutral*, which |
|
101 | 101 | should be legible on either dark or light terminal backgrounds. *linux* is |
|
102 | 102 | optimised for dark backgrounds and *lightbg* for light ones. |
|
103 | 103 | |
|
104 | 104 | ``TerminalInteractiveShell.highlighting_style`` determines prompt colours and |
|
105 | 105 | syntax highlighting. It takes the name (as a string) or class (as a subclass of |
|
106 | 106 | ``pygments.style.Style``) of a Pygments style, or the special value ``'legacy'`` |
|
107 | 107 | to pick a style in accordance with ``InteractiveShell.colors``. |
|
108 | 108 | |
|
109 | 109 | You can see the Pygments styles available on your system by running:: |
|
110 | 110 | |
|
111 | 111 | import pygments |
|
112 | 112 | list(pygments.styles.get_all_styles()) |
|
113 | 113 | |
|
114 | 114 | Additionally, ``TerminalInteractiveShell.highlighting_style_overrides`` can override |
|
115 | 115 | specific styles in the highlighting. It should be a dictionary mapping Pygments |
|
116 | 116 | token types to strings defining the style. See `Pygments' documentation |
|
117 | 117 | <http://pygments.org/docs/styles/#creating-own-styles>`__ for the language used |
|
118 | 118 | to define styles. |
|
119 | 119 | |
|
120 | 120 | Colors in the pager |
|
121 | 121 | ------------------- |
|
122 | 122 | |
|
123 | 123 | On some systems, the default pager has problems with ANSI colour codes. |
|
124 | 124 | To configure your default pager to allow these: |
|
125 | 125 | |
|
126 | 126 | 1. Set the environment PAGER variable to ``less``. |
|
127 | 127 | 2. Set the environment LESS variable to ``-r`` (plus any other options |
|
128 | 128 | you always want to pass to less by default). This tells less to |
|
129 | 129 | properly interpret control sequences, which is how color |
|
130 | 130 | information is given to your terminal. |
|
131 | 131 | |
|
132 | 132 | .. _editors: |
|
133 | 133 | |
|
134 | 134 | Editor configuration |
|
135 | 135 | ==================== |
|
136 | 136 | |
|
137 | 137 | IPython can integrate with text editors in a number of different ways: |
|
138 | 138 | |
|
139 | 139 | * Editors (such as `(X)Emacs`_, vim_ and TextMate_) can |
|
140 | 140 | send code to IPython for execution. |
|
141 | 141 | |
|
142 | 142 | * IPython's ``%edit`` magic command can open an editor of choice to edit |
|
143 | 143 | a code block. |
|
144 | 144 | |
|
145 | 145 | The %edit command (and its alias %ed) will invoke the editor set in your |
|
146 | 146 | environment as :envvar:`EDITOR`. If this variable is not set, it will default |
|
147 | 147 | to vi under Linux/Unix and to notepad under Windows. You may want to set this |
|
148 | 148 | variable properly and to a lightweight editor which doesn't take too long to |
|
149 | 149 | start (that is, something other than a new instance of Emacs). This way you |
|
150 | 150 | can edit multi-line code quickly and with the power of a real editor right |
|
151 | 151 | inside IPython. |
|
152 | 152 | |
|
153 | 153 | You can also control the editor by setting :attr:`TerminalInteractiveShell.editor` |
|
154 | 154 | in :file:`ipython_config.py`. |
|
155 | 155 | |
|
156 | 156 | Vim |
|
157 | 157 | --- |
|
158 | 158 | |
|
159 | 159 | Paul Ivanov's `vim-ipython <https://github.com/ivanov/vim-ipython>`_ provides |
|
160 | 160 | powerful IPython integration for vim. |
|
161 | 161 | |
|
162 | 162 | .. _emacs: |
|
163 | 163 | |
|
164 | 164 | (X)Emacs |
|
165 | 165 | -------- |
|
166 | 166 | |
|
167 | 167 | If you are a dedicated Emacs user, and want to use Emacs when IPython's |
|
168 | 168 | ``%edit`` magic command is called you should set up the Emacs server so that |
|
169 | 169 | new requests are handled by the original process. This means that almost no |
|
170 | 170 | time is spent in handling the request (assuming an Emacs process is already |
|
171 | 171 | running). For this to work, you need to set your EDITOR environment variable |
|
172 | 172 | to 'emacsclient'. The code below, supplied by Francois Pinard, can then be |
|
173 | 173 | used in your :file:`.emacs` file to enable the server: |
|
174 | 174 | |
|
175 | 175 | .. code-block:: common-lisp |
|
176 | 176 | |
|
177 | 177 | (defvar server-buffer-clients) |
|
178 | 178 | (when (and (fboundp 'server-start) (string-equal (getenv "TERM") 'xterm)) |
|
179 | 179 | (server-start) |
|
180 | 180 | (defun fp-kill-server-with-buffer-routine () |
|
181 | 181 | (and server-buffer-clients (server-done))) |
|
182 | 182 | (add-hook 'kill-buffer-hook 'fp-kill-server-with-buffer-routine)) |
|
183 | 183 | |
|
184 | 184 | Thanks to the work of Alexander Schmolck and Prabhu Ramachandran, |
|
185 | 185 | currently (X)Emacs and IPython get along very well in other ways. |
|
186 | 186 | |
|
187 | 187 | With (X)EMacs >= 24, You can enable IPython in python-mode with: |
|
188 | 188 | |
|
189 | 189 | .. code-block:: common-lisp |
|
190 | 190 | |
|
191 | 191 | (require 'python) |
|
192 | 192 | (setq python-shell-interpreter "ipython") |
|
193 | 193 | |
|
194 | 194 | .. _`(X)Emacs`: http://www.gnu.org/software/emacs/ |
|
195 | 195 | .. _TextMate: http://macromates.com/ |
|
196 | 196 | .. _vim: http://www.vim.org/ |
|
197 | 197 | |
|
198 | .. _custom_keyboard_shortcuts | |
|
198 | .. _custom_keyboard_shortcuts: | |
|
199 | 199 | |
|
200 | 200 | Keyboard Shortcuts |
|
201 | 201 | ================== |
|
202 | 202 | |
|
203 | 203 |
|
|
204 | 204 | |
|
205 | 205 | You can customise keyboard shortcuts for terminal IPython. Put code like this in |
|
206 | 206 | a :ref:`startup file <startup_files>`:: |
|
207 | 207 | |
|
208 | 208 | from IPython import get_ipython |
|
209 | 209 | from prompt_toolkit.enums import DEFAULT_BUFFER |
|
210 | 210 | from prompt_toolkit.keys import Keys |
|
211 | 211 | from prompt_toolkit.filters import HasFocus, HasSelection, ViInsertMode, EmacsInsertMode |
|
212 | 212 | |
|
213 | 213 | ip = get_ipython() |
|
214 | 214 | insert_mode = ViInsertMode() | EmacsInsertMode() |
|
215 | 215 | |
|
216 | 216 | def insert_unexpected(event): |
|
217 | 217 | buf = event.current_buffer |
|
218 | 218 | buf.insert_text('The Spanish Inquisition') |
|
219 | 219 | |
|
220 | 220 | # Register the shortcut if IPython is using prompt_toolkit |
|
221 | 221 | if getattr(ip, 'pt_cli'): |
|
222 | 222 | registry = ip.pt_cli.application.key_bindings_registry |
|
223 | 223 | registry.add_binding(Keys.ControlN, |
|
224 | 224 | filter=(HasFocus(DEFAULT_BUFFER) |
|
225 | 225 | & ~HasSelection() |
|
226 | 226 | & insert_mode))(insert_unexpected) |
|
227 | 227 | |
|
228 | 228 | For more information on filters and what you can do with the ``event`` object, |
|
229 | 229 | `see the prompt_toolkit docs |
|
230 | 230 | <http://python-prompt-toolkit.readthedocs.io/en/latest/pages/building_prompts.html#adding-custom-key-bindings>`__. |
@@ -1,175 +1,176 b'' | |||
|
1 | 1 | ===================== |
|
2 | 2 | Development version |
|
3 | 3 | ===================== |
|
4 | 4 | |
|
5 | 5 | This document describes in-flight development work. |
|
6 | 6 | |
|
7 | 7 | .. warning:: |
|
8 | 8 | |
|
9 | 9 | Please do not edit this file by hand (doing so will likely cause merge |
|
10 | 10 | conflicts for other Pull Requests). Instead, create a new file in the |
|
11 | 11 | `docs/source/whatsnew/pr` folder |
|
12 | 12 | |
|
13 | 13 | IPython 6.0 |
|
14 | 14 | =========== |
|
15 | 15 | |
|
16 | 16 | Released .... ...., 2017 |
|
17 | 17 | |
|
18 | 18 | IPython 6 feature a major improvement in the completion machinery which is now |
|
19 | 19 | capable of completing non-executed code. It is also the first version of IPython |
|
20 | 20 | to stop compatibility with Python 2, which is still supported on the bugfix only |
|
21 | 21 | 5.x branch. Read below to have a non-exhaustive list of new features. |
|
22 | 22 | |
|
23 | 23 | Make sure you have pip > 9.0 before upgrading. |
|
24 | 24 | You should be able to update by using: |
|
25 | 25 | |
|
26 | 26 | .. code:: |
|
27 | 27 | |
|
28 | 28 | pip install ipython --upgrade |
|
29 | 29 | |
|
30 | 30 | New completion API and Interface |
|
31 | 31 | -------------------------------- |
|
32 | 32 | |
|
33 | 33 | The completer Completion API has seen an overhaul, and the new completer have |
|
34 | 34 | plenty of improvement both from the end users of terminal IPython or for |
|
35 | 35 | consumers of the API. |
|
36 | 36 | |
|
37 | 37 | This new API is capable of pulling completions from :any:`jedi`, thus allowing |
|
38 | 38 | type inference on non-executed code. If :any:`jedi` is installed completion like |
|
39 | 39 | the following are now becoming possible without code evaluation: |
|
40 | 40 | |
|
41 | 41 | >>> data = ['Number of users', 123_456] |
|
42 | 42 | ... data[0].<tab> |
|
43 | 43 | |
|
44 | 44 | That is to say, IPython is now capable of inferring that `data[0]` is a string, |
|
45 | 45 | and will suggest completions like `.capitalize`. The completion power of IPython |
|
46 | 46 | will increase with new Jedi releases, and a number of bugs and more completions |
|
47 | 47 | are already available on development version of :any:`jedi` if you are curious. |
|
48 | 48 | |
|
49 | 49 | With the help of prompt toolkit, types of completions can be shown in the |
|
50 | 50 | completer interface: |
|
51 | 51 | |
|
52 | 52 | .. image:: ../_images/jedi_type_inference_60.png |
|
53 | 53 | :alt: Jedi showing ability to do type inference |
|
54 | 54 | :align: center |
|
55 | :width: 400px | |
|
55 | 56 | :target: ../_images/jedi_type_inference_60.png |
|
56 | 57 | |
|
57 | 58 | The appearance of the completer is controlled by the |
|
58 | 59 | ``c.TerminalInteractiveShell.display_completions`` option that will show the |
|
59 | 60 | type differently depending on the value among ``'column'``, ``'multicolumn'`` |
|
60 | 61 | and ``'readlinelike'`` |
|
61 | 62 | |
|
62 | 63 | The use of Jedi also full fill a number of request and fix a number of bugs |
|
63 | 64 | like case insensitive completion, completion after division operator: See |
|
64 | 65 | :ghpull:`10182`. |
|
65 | 66 | |
|
66 |
Extra patches and updates will be needed to the : |
|
|
67 | Extra patches and updates will be needed to the :mod:`ipykernel` package for | |
|
67 | 68 | this feature to be available to other clients like jupyter Notebook, Lab, |
|
68 | 69 | Nteract, Hydrogen... |
|
69 | 70 | |
|
70 | 71 | The use of Jedi can is barely noticeable on recent enough machines, but can be |
|
71 | 72 | feel on older ones, in cases were Jedi behavior need to be adjusted, the amount |
|
72 | 73 | of time given to Jedi to compute type inference can be adjusted with |
|
73 | 74 | ``c.IPCompleter.jedi_compute_type_timeout``, with object whose type were not |
|
74 | 75 | inferred will be shown as ``<unknown>``. Jedi can also be completely deactivated |
|
75 | 76 | by using the ``c.Completer.use_jedi=False`` option. |
|
76 | 77 | |
|
77 | 78 | |
|
78 | 79 | The old ``Completer.complete()`` API is waiting deprecation and should be |
|
79 | 80 | replaced replaced by ``Completer.completions()`` in a near future. Feedback on |
|
80 | 81 | the current state of the API and suggestions welcome. |
|
81 | 82 | |
|
82 | 83 | Python 3 only codebase |
|
83 | 84 | ---------------------- |
|
84 | 85 | |
|
85 | 86 | One of the large challenges in IPython 6.0 has been the adoption of a pure |
|
86 | 87 | Python 3 code base, which lead us to great length to upstream patches in pip, |
|
87 | 88 | pypi and warehouse to make sure Python 2 system still upgrade to the latest |
|
88 | 89 | compatible Python version compatible. |
|
89 | 90 | |
|
90 | 91 | We remind our Python 2 users that IPython 5 is still compatible with Python 2.7, |
|
91 | 92 | still maintained and get regular releases. Using pip 9+, upgrading IPython will |
|
92 | 93 | automatically upgrade to the latest version compatible with your system. |
|
93 | 94 | |
|
94 | 95 | .. warning:: |
|
95 | 96 | |
|
96 | 97 | If you are on a system using an older verison of pip on Python 2, pip may |
|
97 | 98 | still install IPython 6.0 on your system, and IPython will refuse to start. |
|
98 | 99 | You can fix this by ugrading pip, and reinstalling ipython, or forcing pip to |
|
99 | 100 | install an earlier version: ``pip install 'ipython<6'`` |
|
100 | 101 | |
|
101 | 102 | The ability to use only Python 3 on the code base of IPython has bring a number |
|
102 | 103 | of advantage. Most of the newly written code make use of `optional function type |
|
103 | 104 | anotation <https://www.python.org/dev/peps/pep-0484/>`_ leading to clearer code |
|
104 | 105 | and better documentation. |
|
105 | 106 | |
|
106 | 107 | The total size of the repository has also for a first time between releases |
|
107 | 108 | (excluding the big split for 4.0) decreased by about 1500 lines, potentially |
|
108 | 109 | quite a bit more codewide as some documents like this one are append only and |
|
109 | 110 | are about 300 lines long. |
|
110 | 111 | |
|
111 | 112 | The removal as of Python2/Python3 shim layer has made the code quite clearer and |
|
112 | 113 | more idiomatic in a number of location, and much friendlier to work with and |
|
113 | 114 | understand. We hope to further embrace Python 3 capability in the next release |
|
114 | 115 | cycle and introduce more of the Python 3 only idioms (yield from, kwarg only, |
|
115 | 116 | general unpacking) in the code base of IPython, and see if we can take advantage |
|
116 | 117 | of these as well to improve user experience with better error messages and |
|
117 | 118 | hints. |
|
118 | 119 | |
|
119 | 120 | |
|
120 | 121 | Miscs improvements |
|
121 | 122 | ------------------ |
|
122 | 123 | |
|
123 | 124 | |
|
124 | 125 | - The :cellmagic:`capture` magic can now capture the result of a cell (from an |
|
125 | 126 | expression on the last line), as well as printed and displayed output. |
|
126 | 127 | :ghpull:`9851`. |
|
127 | 128 | |
|
128 | 129 | - Pressing Ctrl-Z in the terminal debugger now suspends IPython, as it already |
|
129 | 130 | does in the main terminal prompt. |
|
130 | 131 | |
|
131 | 132 | - autoreload can now reload ``Enum``. See :ghissue:`10232` and :ghpull:`10316` |
|
132 | 133 | |
|
133 | 134 | - IPython.display has gained a :any:`GeoJSON <IPython.display.GeoJSON>` object. |
|
134 | 135 | :ghpull:`10288` and :ghpull:`10253` |
|
135 | 136 | |
|
136 | 137 | .. DO NOT EDIT THIS LINE BEFORE RELEASE. FEATURE INSERTION POINT. |
|
137 | 138 | |
|
138 | 139 | |
|
139 | 140 | Functions Deprecated in 6.x Development cycle |
|
140 | 141 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
141 | 142 | |
|
142 | 143 | - Loading extensions from ``ipython_extension_dir`` print a warning that this |
|
143 | 144 | location is pending deprecation. This should only affect users still having |
|
144 | 145 | extensions installed with ``%install_ext`` which has been deprecated since |
|
145 | 146 | IPython 4.0, and removed in 5.0. Extensions still present in |
|
146 | 147 | ``ipython_extension_dir`` may shadow more recently installed versions using |
|
147 | 148 | pip. It is thus recommended to clean ``ipython_extension_dir`` of any |
|
148 | 149 | extension now available as a package. |
|
149 | 150 | |
|
150 | 151 | |
|
151 | 152 | - ``IPython.utils.warn`` was deprecated in IPython 4.0, and has now been removed. |
|
152 | 153 | instead of ``IPython.utils.warn`` inbuilt :any:`warnings` module is used. |
|
153 | 154 | |
|
154 | 155 | |
|
155 | 156 | - The function `IPython.core.oinspect.py:call_tip` is unused, was marked as |
|
156 | 157 | Deprecated (raising a Deprecation Warning) and marked for later removal |
|
157 | 158 | :ghpull:`10104` |
|
158 | 159 | |
|
159 | 160 | Backwards incompatible changes |
|
160 | 161 | ------------------------------ |
|
161 | 162 | |
|
162 | 163 | Functions Removed in 6.x Development cycle |
|
163 | 164 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
164 | 165 | |
|
165 | 166 | The following functions have been removed in the |
|
166 | 167 | development cycle marked for Milestone 6.0. |
|
167 | 168 | |
|
168 | 169 | - ``IPython/utils/process.py`` - ``is_cmd_found`` |
|
169 | 170 | - ``IPython/utils/process.py`` - ``pycmd2argv`` |
|
170 | 171 | |
|
171 | 172 | - The `--deep-reload` flag and the corresponding options to inject `dreload` or |
|
172 | 173 | `reload` into the interactive namespace have been removed. You have to |
|
173 | 174 | explicitly import `reload` from `IPython.lib.deepreload` to use it. |
|
174 | 175 | |
|
175 | 176 | .. DO NOT EDIT THIS LINE BEFORE RELEASE. INCOMPAT INSERTION POINT. |
General Comments 0
You need to be logged in to leave comments.
Login now