Show More
The requested changes are too big and content was truncated. Show full diff
This diff has been collapsed as it changes many lines, (670 lines changed) Show them Hide them | |||
@@ -0,0 +1,670 b'' | |||
|
1 | """ | |
|
2 | Module to define and register Terminal IPython shortcuts with | |
|
3 | :mod:`prompt_toolkit` | |
|
4 | """ | |
|
5 | ||
|
6 | # Copyright (c) IPython Development Team. | |
|
7 | # Distributed under the terms of the Modified BSD License. | |
|
8 | ||
|
9 | import os | |
|
10 | import re | |
|
11 | import signal | |
|
12 | import sys | |
|
13 | import warnings | |
|
14 | from typing import Callable, Dict, Union | |
|
15 | ||
|
16 | from prompt_toolkit.application.current import get_app | |
|
17 | from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER | |
|
18 | from prompt_toolkit.filters import Condition, emacs_insert_mode, has_completions | |
|
19 | from prompt_toolkit.filters import has_focus as has_focus_impl | |
|
20 | from prompt_toolkit.filters import ( | |
|
21 | has_selection, | |
|
22 | has_suggestion, | |
|
23 | vi_insert_mode, | |
|
24 | vi_mode, | |
|
25 | ) | |
|
26 | from prompt_toolkit.key_binding import KeyBindings | |
|
27 | from prompt_toolkit.key_binding.bindings import named_commands as nc | |
|
28 | from prompt_toolkit.key_binding.bindings.completion import ( | |
|
29 | display_completions_like_readline, | |
|
30 | ) | |
|
31 | from prompt_toolkit.key_binding.vi_state import InputMode, ViState | |
|
32 | from prompt_toolkit.layout.layout import FocusableElement | |
|
33 | ||
|
34 | from IPython.terminal.shortcuts import auto_match as match | |
|
35 | from IPython.terminal.shortcuts import auto_suggest | |
|
36 | from IPython.utils.decorators import undoc | |
|
37 | ||
|
38 | __all__ = ["create_ipython_shortcuts"] | |
|
39 | ||
|
40 | ||
|
41 | @undoc | |
|
42 | @Condition | |
|
43 | def cursor_in_leading_ws(): | |
|
44 | before = get_app().current_buffer.document.current_line_before_cursor | |
|
45 | return (not before) or before.isspace() | |
|
46 | ||
|
47 | ||
|
48 | def has_focus(value: FocusableElement): | |
|
49 | """Wrapper around has_focus adding a nice `__name__` to tester function""" | |
|
50 | tester = has_focus_impl(value).func | |
|
51 | tester.__name__ = f"is_focused({value})" | |
|
52 | return Condition(tester) | |
|
53 | ||
|
54 | ||
|
55 | @undoc | |
|
56 | @Condition | |
|
57 | def has_line_below() -> bool: | |
|
58 | document = get_app().current_buffer.document | |
|
59 | return document.cursor_position_row < len(document.lines) - 1 | |
|
60 | ||
|
61 | ||
|
62 | @undoc | |
|
63 | @Condition | |
|
64 | def has_line_above() -> bool: | |
|
65 | document = get_app().current_buffer.document | |
|
66 | return document.cursor_position_row != 0 | |
|
67 | ||
|
68 | ||
|
69 | def create_ipython_shortcuts(shell, for_all_platforms: bool = False) -> KeyBindings: | |
|
70 | """Set up the prompt_toolkit keyboard shortcuts for IPython. | |
|
71 | ||
|
72 | Parameters | |
|
73 | ---------- | |
|
74 | shell: InteractiveShell | |
|
75 | The current IPython shell Instance | |
|
76 | for_all_platforms: bool (default false) | |
|
77 | This parameter is mostly used in generating the documentation | |
|
78 | to create the shortcut binding for all the platforms, and export | |
|
79 | them. | |
|
80 | ||
|
81 | Returns | |
|
82 | ------- | |
|
83 | KeyBindings | |
|
84 | the keybinding instance for prompt toolkit. | |
|
85 | ||
|
86 | """ | |
|
87 | # Warning: if possible, do NOT define handler functions in the locals | |
|
88 | # scope of this function, instead define functions in the global | |
|
89 | # scope, or a separate module, and include a user-friendly docstring | |
|
90 | # describing the action. | |
|
91 | ||
|
92 | kb = KeyBindings() | |
|
93 | insert_mode = vi_insert_mode | emacs_insert_mode | |
|
94 | ||
|
95 | if getattr(shell, "handle_return", None): | |
|
96 | return_handler = shell.handle_return(shell) | |
|
97 | else: | |
|
98 | return_handler = newline_or_execute_outer(shell) | |
|
99 | ||
|
100 | kb.add("enter", filter=(has_focus(DEFAULT_BUFFER) & ~has_selection & insert_mode))( | |
|
101 | return_handler | |
|
102 | ) | |
|
103 | ||
|
104 | @Condition | |
|
105 | def ebivim(): | |
|
106 | return shell.emacs_bindings_in_vi_insert_mode | |
|
107 | ||
|
108 | @kb.add( | |
|
109 | "escape", | |
|
110 | "enter", | |
|
111 | filter=(has_focus(DEFAULT_BUFFER) & ~has_selection & insert_mode & ebivim), | |
|
112 | ) | |
|
113 | def reformat_and_execute(event): | |
|
114 | """Reformat code and execute it""" | |
|
115 | reformat_text_before_cursor( | |
|
116 | event.current_buffer, event.current_buffer.document, shell | |
|
117 | ) | |
|
118 | event.current_buffer.validate_and_handle() | |
|
119 | ||
|
120 | kb.add("c-\\")(quit) | |
|
121 | ||
|
122 | kb.add("c-p", filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER)))( | |
|
123 | previous_history_or_previous_completion | |
|
124 | ) | |
|
125 | ||
|
126 | kb.add("c-n", filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER)))( | |
|
127 | next_history_or_next_completion | |
|
128 | ) | |
|
129 | ||
|
130 | kb.add("c-g", filter=(has_focus(DEFAULT_BUFFER) & has_completions))( | |
|
131 | dismiss_completion | |
|
132 | ) | |
|
133 | ||
|
134 | kb.add("c-c", filter=has_focus(DEFAULT_BUFFER))(reset_buffer) | |
|
135 | ||
|
136 | kb.add("c-c", filter=has_focus(SEARCH_BUFFER))(reset_search_buffer) | |
|
137 | ||
|
138 | supports_suspend = Condition(lambda: hasattr(signal, "SIGTSTP")) | |
|
139 | kb.add("c-z", filter=supports_suspend)(suspend_to_bg) | |
|
140 | ||
|
141 | # Ctrl+I == Tab | |
|
142 | kb.add( | |
|
143 | "tab", | |
|
144 | filter=( | |
|
145 | has_focus(DEFAULT_BUFFER) | |
|
146 | & ~has_selection | |
|
147 | & insert_mode | |
|
148 | & cursor_in_leading_ws | |
|
149 | ), | |
|
150 | )(indent_buffer) | |
|
151 | kb.add("c-o", filter=(has_focus(DEFAULT_BUFFER) & emacs_insert_mode))( | |
|
152 | newline_autoindent_outer(shell.input_transformer_manager) | |
|
153 | ) | |
|
154 | ||
|
155 | kb.add("f2", filter=has_focus(DEFAULT_BUFFER))(open_input_in_editor) | |
|
156 | ||
|
157 | @Condition | |
|
158 | def auto_match(): | |
|
159 | return shell.auto_match | |
|
160 | ||
|
161 | def all_quotes_paired(quote, buf): | |
|
162 | paired = True | |
|
163 | i = 0 | |
|
164 | while i < len(buf): | |
|
165 | c = buf[i] | |
|
166 | if c == quote: | |
|
167 | paired = not paired | |
|
168 | elif c == "\\": | |
|
169 | i += 1 | |
|
170 | i += 1 | |
|
171 | return paired | |
|
172 | ||
|
173 | focused_insert = (vi_insert_mode | emacs_insert_mode) & has_focus(DEFAULT_BUFFER) | |
|
174 | _preceding_text_cache: Dict[Union[str, Callable], Condition] = {} | |
|
175 | _following_text_cache: Dict[Union[str, Callable], Condition] = {} | |
|
176 | ||
|
177 | def preceding_text(pattern: Union[str, Callable]): | |
|
178 | if pattern in _preceding_text_cache: | |
|
179 | return _preceding_text_cache[pattern] | |
|
180 | ||
|
181 | if callable(pattern): | |
|
182 | ||
|
183 | def _preceding_text(): | |
|
184 | app = get_app() | |
|
185 | before_cursor = app.current_buffer.document.current_line_before_cursor | |
|
186 | # mypy can't infer if(callable): https://github.com/python/mypy/issues/3603 | |
|
187 | return bool(pattern(before_cursor)) # type: ignore[operator] | |
|
188 | ||
|
189 | else: | |
|
190 | m = re.compile(pattern) | |
|
191 | ||
|
192 | def _preceding_text(): | |
|
193 | app = get_app() | |
|
194 | before_cursor = app.current_buffer.document.current_line_before_cursor | |
|
195 | return bool(m.match(before_cursor)) | |
|
196 | ||
|
197 | _preceding_text.__name__ = f"preceding_text({pattern!r})" | |
|
198 | ||
|
199 | condition = Condition(_preceding_text) | |
|
200 | _preceding_text_cache[pattern] = condition | |
|
201 | return condition | |
|
202 | ||
|
203 | def following_text(pattern): | |
|
204 | try: | |
|
205 | return _following_text_cache[pattern] | |
|
206 | except KeyError: | |
|
207 | pass | |
|
208 | m = re.compile(pattern) | |
|
209 | ||
|
210 | def _following_text(): | |
|
211 | app = get_app() | |
|
212 | return bool(m.match(app.current_buffer.document.current_line_after_cursor)) | |
|
213 | ||
|
214 | _following_text.__name__ = f"following_text({pattern!r})" | |
|
215 | ||
|
216 | condition = Condition(_following_text) | |
|
217 | _following_text_cache[pattern] = condition | |
|
218 | return condition | |
|
219 | ||
|
220 | @Condition | |
|
221 | def not_inside_unclosed_string(): | |
|
222 | app = get_app() | |
|
223 | s = app.current_buffer.document.text_before_cursor | |
|
224 | # remove escaped quotes | |
|
225 | s = s.replace('\\"', "").replace("\\'", "") | |
|
226 | # remove triple-quoted string literals | |
|
227 | s = re.sub(r"(?:\"\"\"[\s\S]*\"\"\"|'''[\s\S]*''')", "", s) | |
|
228 | # remove single-quoted string literals | |
|
229 | s = re.sub(r"""(?:"[^"]*["\n]|'[^']*['\n])""", "", s) | |
|
230 | return not ('"' in s or "'" in s) | |
|
231 | ||
|
232 | # auto match | |
|
233 | for key, cmd in match.auto_match_parens.items(): | |
|
234 | kb.add(key, filter=focused_insert & auto_match & following_text(r"[,)}\]]|$"))( | |
|
235 | cmd | |
|
236 | ) | |
|
237 | ||
|
238 | # raw string | |
|
239 | for key, cmd in match.auto_match_parens_raw_string.items(): | |
|
240 | kb.add( | |
|
241 | key, | |
|
242 | filter=focused_insert & auto_match & preceding_text(r".*(r|R)[\"'](-*)$"), | |
|
243 | )(cmd) | |
|
244 | ||
|
245 | kb.add( | |
|
246 | '"', | |
|
247 | filter=focused_insert | |
|
248 | & auto_match | |
|
249 | & not_inside_unclosed_string | |
|
250 | & preceding_text(lambda line: all_quotes_paired('"', line)) | |
|
251 | & following_text(r"[,)}\]]|$"), | |
|
252 | )(match.double_quote) | |
|
253 | ||
|
254 | kb.add( | |
|
255 | "'", | |
|
256 | filter=focused_insert | |
|
257 | & auto_match | |
|
258 | & not_inside_unclosed_string | |
|
259 | & preceding_text(lambda line: all_quotes_paired("'", line)) | |
|
260 | & following_text(r"[,)}\]]|$"), | |
|
261 | )(match.single_quote) | |
|
262 | ||
|
263 | kb.add( | |
|
264 | '"', | |
|
265 | filter=focused_insert | |
|
266 | & auto_match | |
|
267 | & not_inside_unclosed_string | |
|
268 | & preceding_text(r'^.*""$'), | |
|
269 | )(match.docstring_double_quotes) | |
|
270 | ||
|
271 | kb.add( | |
|
272 | "'", | |
|
273 | filter=focused_insert | |
|
274 | & auto_match | |
|
275 | & not_inside_unclosed_string | |
|
276 | & preceding_text(r"^.*''$"), | |
|
277 | )(match.docstring_single_quotes) | |
|
278 | ||
|
279 | # just move cursor | |
|
280 | kb.add(")", filter=focused_insert & auto_match & following_text(r"^\)"))( | |
|
281 | match.skip_over | |
|
282 | ) | |
|
283 | kb.add("]", filter=focused_insert & auto_match & following_text(r"^\]"))( | |
|
284 | match.skip_over | |
|
285 | ) | |
|
286 | kb.add("}", filter=focused_insert & auto_match & following_text(r"^\}"))( | |
|
287 | match.skip_over | |
|
288 | ) | |
|
289 | kb.add('"', filter=focused_insert & auto_match & following_text('^"'))( | |
|
290 | match.skip_over | |
|
291 | ) | |
|
292 | kb.add("'", filter=focused_insert & auto_match & following_text("^'"))( | |
|
293 | match.skip_over | |
|
294 | ) | |
|
295 | ||
|
296 | kb.add( | |
|
297 | "backspace", | |
|
298 | filter=focused_insert | |
|
299 | & preceding_text(r".*\($") | |
|
300 | & auto_match | |
|
301 | & following_text(r"^\)"), | |
|
302 | )(match.delete_pair) | |
|
303 | kb.add( | |
|
304 | "backspace", | |
|
305 | filter=focused_insert | |
|
306 | & preceding_text(r".*\[$") | |
|
307 | & auto_match | |
|
308 | & following_text(r"^\]"), | |
|
309 | )(match.delete_pair) | |
|
310 | kb.add( | |
|
311 | "backspace", | |
|
312 | filter=focused_insert | |
|
313 | & preceding_text(r".*\{$") | |
|
314 | & auto_match | |
|
315 | & following_text(r"^\}"), | |
|
316 | )(match.delete_pair) | |
|
317 | kb.add( | |
|
318 | "backspace", | |
|
319 | filter=focused_insert | |
|
320 | & preceding_text('.*"$') | |
|
321 | & auto_match | |
|
322 | & following_text('^"'), | |
|
323 | )(match.delete_pair) | |
|
324 | kb.add( | |
|
325 | "backspace", | |
|
326 | filter=focused_insert | |
|
327 | & preceding_text(r".*'$") | |
|
328 | & auto_match | |
|
329 | & following_text(r"^'"), | |
|
330 | )(match.delete_pair) | |
|
331 | ||
|
332 | if shell.display_completions == "readlinelike": | |
|
333 | kb.add( | |
|
334 | "c-i", | |
|
335 | filter=( | |
|
336 | has_focus(DEFAULT_BUFFER) | |
|
337 | & ~has_selection | |
|
338 | & insert_mode | |
|
339 | & ~cursor_in_leading_ws | |
|
340 | ), | |
|
341 | )(display_completions_like_readline) | |
|
342 | ||
|
343 | if sys.platform == "win32" or for_all_platforms: | |
|
344 | kb.add("c-v", filter=(has_focus(DEFAULT_BUFFER) & ~vi_mode))(win_paste) | |
|
345 | ||
|
346 | focused_insert_vi = has_focus(DEFAULT_BUFFER) & vi_insert_mode | |
|
347 | ||
|
348 | # autosuggestions | |
|
349 | @Condition | |
|
350 | def navigable_suggestions(): | |
|
351 | return isinstance( | |
|
352 | shell.auto_suggest, auto_suggest.NavigableAutoSuggestFromHistory | |
|
353 | ) | |
|
354 | ||
|
355 | kb.add("end", filter=has_focus(DEFAULT_BUFFER) & (ebivim | ~vi_insert_mode))( | |
|
356 | auto_suggest.accept_in_vi_insert_mode | |
|
357 | ) | |
|
358 | kb.add("c-e", filter=focused_insert_vi & ebivim)( | |
|
359 | auto_suggest.accept_in_vi_insert_mode | |
|
360 | ) | |
|
361 | kb.add("c-f", filter=focused_insert_vi)(auto_suggest.accept) | |
|
362 | kb.add("escape", "f", filter=focused_insert_vi & ebivim)(auto_suggest.accept_word) | |
|
363 | kb.add("c-right", filter=has_suggestion & has_focus(DEFAULT_BUFFER))( | |
|
364 | auto_suggest.accept_token | |
|
365 | ) | |
|
366 | kb.add( | |
|
367 | "escape", filter=has_suggestion & has_focus(DEFAULT_BUFFER) & emacs_insert_mode | |
|
368 | )(auto_suggest.discard) | |
|
369 | kb.add( | |
|
370 | "up", | |
|
371 | filter=navigable_suggestions | |
|
372 | & ~has_line_above | |
|
373 | & has_suggestion | |
|
374 | & has_focus(DEFAULT_BUFFER), | |
|
375 | )(auto_suggest.swap_autosuggestion_up(shell.auto_suggest)) | |
|
376 | kb.add( | |
|
377 | "down", | |
|
378 | filter=navigable_suggestions | |
|
379 | & ~has_line_below | |
|
380 | & has_suggestion | |
|
381 | & has_focus(DEFAULT_BUFFER), | |
|
382 | )(auto_suggest.swap_autosuggestion_down(shell.auto_suggest)) | |
|
383 | kb.add( | |
|
384 | "up", filter=has_line_above & navigable_suggestions & has_focus(DEFAULT_BUFFER) | |
|
385 | )(auto_suggest.up_and_update_hint) | |
|
386 | kb.add( | |
|
387 | "down", | |
|
388 | filter=has_line_below & navigable_suggestions & has_focus(DEFAULT_BUFFER), | |
|
389 | )(auto_suggest.down_and_update_hint) | |
|
390 | kb.add("right", filter=has_suggestion & has_focus(DEFAULT_BUFFER))( | |
|
391 | auto_suggest.accept_character | |
|
392 | ) | |
|
393 | kb.add("c-left", filter=has_suggestion & has_focus(DEFAULT_BUFFER))( | |
|
394 | auto_suggest.accept_and_move_cursor_left | |
|
395 | ) | |
|
396 | kb.add("c-down", filter=has_suggestion & has_focus(DEFAULT_BUFFER))( | |
|
397 | auto_suggest.accept_and_keep_cursor | |
|
398 | ) | |
|
399 | kb.add("backspace", filter=has_suggestion & has_focus(DEFAULT_BUFFER))( | |
|
400 | auto_suggest.backspace_and_resume_hint | |
|
401 | ) | |
|
402 | ||
|
403 | # Simple Control keybindings | |
|
404 | key_cmd_dict = { | |
|
405 | "c-a": nc.beginning_of_line, | |
|
406 | "c-b": nc.backward_char, | |
|
407 | "c-k": nc.kill_line, | |
|
408 | "c-w": nc.backward_kill_word, | |
|
409 | "c-y": nc.yank, | |
|
410 | "c-_": nc.undo, | |
|
411 | } | |
|
412 | ||
|
413 | for key, cmd in key_cmd_dict.items(): | |
|
414 | kb.add(key, filter=focused_insert_vi & ebivim)(cmd) | |
|
415 | ||
|
416 | # Alt and Combo Control keybindings | |
|
417 | keys_cmd_dict = { | |
|
418 | # Control Combos | |
|
419 | ("c-x", "c-e"): nc.edit_and_execute, | |
|
420 | ("c-x", "e"): nc.edit_and_execute, | |
|
421 | # Alt | |
|
422 | ("escape", "b"): nc.backward_word, | |
|
423 | ("escape", "c"): nc.capitalize_word, | |
|
424 | ("escape", "d"): nc.kill_word, | |
|
425 | ("escape", "h"): nc.backward_kill_word, | |
|
426 | ("escape", "l"): nc.downcase_word, | |
|
427 | ("escape", "u"): nc.uppercase_word, | |
|
428 | ("escape", "y"): nc.yank_pop, | |
|
429 | ("escape", "."): nc.yank_last_arg, | |
|
430 | } | |
|
431 | ||
|
432 | for keys, cmd in keys_cmd_dict.items(): | |
|
433 | kb.add(*keys, filter=focused_insert_vi & ebivim)(cmd) | |
|
434 | ||
|
435 | def get_input_mode(self): | |
|
436 | app = get_app() | |
|
437 | app.ttimeoutlen = shell.ttimeoutlen | |
|
438 | app.timeoutlen = shell.timeoutlen | |
|
439 | ||
|
440 | return self._input_mode | |
|
441 | ||
|
442 | def set_input_mode(self, mode): | |
|
443 | shape = {InputMode.NAVIGATION: 2, InputMode.REPLACE: 4}.get(mode, 6) | |
|
444 | cursor = "\x1b[{} q".format(shape) | |
|
445 | ||
|
446 | sys.stdout.write(cursor) | |
|
447 | sys.stdout.flush() | |
|
448 | ||
|
449 | self._input_mode = mode | |
|
450 | ||
|
451 | if shell.editing_mode == "vi" and shell.modal_cursor: | |
|
452 | ViState._input_mode = InputMode.INSERT # type: ignore | |
|
453 | ViState.input_mode = property(get_input_mode, set_input_mode) # type: ignore | |
|
454 | return kb | |
|
455 | ||
|
456 | ||
|
457 | def reformat_text_before_cursor(buffer, document, shell): | |
|
458 | text = buffer.delete_before_cursor(len(document.text[: document.cursor_position])) | |
|
459 | try: | |
|
460 | formatted_text = shell.reformat_handler(text) | |
|
461 | buffer.insert_text(formatted_text) | |
|
462 | except Exception as e: | |
|
463 | buffer.insert_text(text) | |
|
464 | ||
|
465 | ||
|
466 | def newline_or_execute_outer(shell): | |
|
467 | def newline_or_execute(event): | |
|
468 | """When the user presses return, insert a newline or execute the code.""" | |
|
469 | b = event.current_buffer | |
|
470 | d = b.document | |
|
471 | ||
|
472 | if b.complete_state: | |
|
473 | cc = b.complete_state.current_completion | |
|
474 | if cc: | |
|
475 | b.apply_completion(cc) | |
|
476 | else: | |
|
477 | b.cancel_completion() | |
|
478 | return | |
|
479 | ||
|
480 | # If there's only one line, treat it as if the cursor is at the end. | |
|
481 | # See https://github.com/ipython/ipython/issues/10425 | |
|
482 | if d.line_count == 1: | |
|
483 | check_text = d.text | |
|
484 | else: | |
|
485 | check_text = d.text[: d.cursor_position] | |
|
486 | status, indent = shell.check_complete(check_text) | |
|
487 | ||
|
488 | # if all we have after the cursor is whitespace: reformat current text | |
|
489 | # before cursor | |
|
490 | after_cursor = d.text[d.cursor_position :] | |
|
491 | reformatted = False | |
|
492 | if not after_cursor.strip(): | |
|
493 | reformat_text_before_cursor(b, d, shell) | |
|
494 | reformatted = True | |
|
495 | if not ( | |
|
496 | d.on_last_line | |
|
497 | or d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end() | |
|
498 | ): | |
|
499 | if shell.autoindent: | |
|
500 | b.insert_text("\n" + indent) | |
|
501 | else: | |
|
502 | b.insert_text("\n") | |
|
503 | return | |
|
504 | ||
|
505 | if (status != "incomplete") and b.accept_handler: | |
|
506 | if not reformatted: | |
|
507 | reformat_text_before_cursor(b, d, shell) | |
|
508 | b.validate_and_handle() | |
|
509 | else: | |
|
510 | if shell.autoindent: | |
|
511 | b.insert_text("\n" + indent) | |
|
512 | else: | |
|
513 | b.insert_text("\n") | |
|
514 | ||
|
515 | newline_or_execute.__qualname__ = "newline_or_execute" | |
|
516 | ||
|
517 | return newline_or_execute | |
|
518 | ||
|
519 | ||
|
520 | def previous_history_or_previous_completion(event): | |
|
521 | """ | |
|
522 | Control-P in vi edit mode on readline is history next, unlike default prompt toolkit. | |
|
523 | ||
|
524 | If completer is open this still select previous completion. | |
|
525 | """ | |
|
526 | event.current_buffer.auto_up() | |
|
527 | ||
|
528 | ||
|
529 | def next_history_or_next_completion(event): | |
|
530 | """ | |
|
531 | Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit. | |
|
532 | ||
|
533 | If completer is open this still select next completion. | |
|
534 | """ | |
|
535 | event.current_buffer.auto_down() | |
|
536 | ||
|
537 | ||
|
538 | def dismiss_completion(event): | |
|
539 | """Dismiss completion""" | |
|
540 | b = event.current_buffer | |
|
541 | if b.complete_state: | |
|
542 | b.cancel_completion() | |
|
543 | ||
|
544 | ||
|
545 | def reset_buffer(event): | |
|
546 | """Reset buffer""" | |
|
547 | b = event.current_buffer | |
|
548 | if b.complete_state: | |
|
549 | b.cancel_completion() | |
|
550 | else: | |
|
551 | b.reset() | |
|
552 | ||
|
553 | ||
|
554 | def reset_search_buffer(event): | |
|
555 | """Reset search buffer""" | |
|
556 | if event.current_buffer.document.text: | |
|
557 | event.current_buffer.reset() | |
|
558 | else: | |
|
559 | event.app.layout.focus(DEFAULT_BUFFER) | |
|
560 | ||
|
561 | ||
|
562 | def suspend_to_bg(event): | |
|
563 | """Suspend to background""" | |
|
564 | event.app.suspend_to_background() | |
|
565 | ||
|
566 | ||
|
567 | def quit(event): | |
|
568 | """ | |
|
569 | Quit application with ``SIGQUIT`` if supported or ``sys.exit`` otherwise. | |
|
570 | ||
|
571 | On platforms that support SIGQUIT, send SIGQUIT to the current process. | |
|
572 | On other platforms, just exit the process with a message. | |
|
573 | """ | |
|
574 | sigquit = getattr(signal, "SIGQUIT", None) | |
|
575 | if sigquit is not None: | |
|
576 | os.kill(0, signal.SIGQUIT) | |
|
577 | else: | |
|
578 | sys.exit("Quit") | |
|
579 | ||
|
580 | ||
|
581 | def indent_buffer(event): | |
|
582 | """Indent buffer""" | |
|
583 | event.current_buffer.insert_text(" " * 4) | |
|
584 | ||
|
585 | ||
|
586 | @undoc | |
|
587 | def newline_with_copy_margin(event): | |
|
588 | """ | |
|
589 | DEPRECATED since IPython 6.0 | |
|
590 | ||
|
591 | See :any:`newline_autoindent_outer` for a replacement. | |
|
592 | ||
|
593 | Preserve margin and cursor position when using | |
|
594 | Control-O to insert a newline in EMACS mode | |
|
595 | """ | |
|
596 | warnings.warn( | |
|
597 | "`newline_with_copy_margin(event)` is deprecated since IPython 6.0. " | |
|
598 | "see `newline_autoindent_outer(shell)(event)` for a replacement.", | |
|
599 | DeprecationWarning, | |
|
600 | stacklevel=2, | |
|
601 | ) | |
|
602 | ||
|
603 | b = event.current_buffer | |
|
604 | cursor_start_pos = b.document.cursor_position_col | |
|
605 | b.newline(copy_margin=True) | |
|
606 | b.cursor_up(count=1) | |
|
607 | cursor_end_pos = b.document.cursor_position_col | |
|
608 | if cursor_start_pos != cursor_end_pos: | |
|
609 | pos_diff = cursor_start_pos - cursor_end_pos | |
|
610 | b.cursor_right(count=pos_diff) | |
|
611 | ||
|
612 | ||
|
613 | def newline_autoindent_outer(inputsplitter) -> Callable[..., None]: | |
|
614 | """ | |
|
615 | Return a function suitable for inserting a indented newline after the cursor. | |
|
616 | ||
|
617 | Fancier version of deprecated ``newline_with_copy_margin`` which should | |
|
618 | compute the correct indentation of the inserted line. That is to say, indent | |
|
619 | by 4 extra space after a function definition, class definition, context | |
|
620 | manager... And dedent by 4 space after ``pass``, ``return``, ``raise ...``. | |
|
621 | """ | |
|
622 | ||
|
623 | def newline_autoindent(event): | |
|
624 | """Insert a newline after the cursor indented appropriately.""" | |
|
625 | b = event.current_buffer | |
|
626 | d = b.document | |
|
627 | ||
|
628 | if b.complete_state: | |
|
629 | b.cancel_completion() | |
|
630 | text = d.text[: d.cursor_position] + "\n" | |
|
631 | _, indent = inputsplitter.check_complete(text) | |
|
632 | b.insert_text("\n" + (" " * (indent or 0)), move_cursor=False) | |
|
633 | ||
|
634 | newline_autoindent.__qualname__ = "newline_autoindent" | |
|
635 | ||
|
636 | return newline_autoindent | |
|
637 | ||
|
638 | ||
|
639 | def open_input_in_editor(event): | |
|
640 | """Open code from input in external editor""" | |
|
641 | event.app.current_buffer.open_in_editor() | |
|
642 | ||
|
643 | ||
|
644 | if sys.platform == "win32": | |
|
645 | from IPython.core.error import TryNext | |
|
646 | from IPython.lib.clipboard import ( | |
|
647 | ClipboardEmpty, | |
|
648 | tkinter_clipboard_get, | |
|
649 | win32_clipboard_get, | |
|
650 | ) | |
|
651 | ||
|
652 | @undoc | |
|
653 | def win_paste(event): | |
|
654 | try: | |
|
655 | text = win32_clipboard_get() | |
|
656 | except TryNext: | |
|
657 | try: | |
|
658 | text = tkinter_clipboard_get() | |
|
659 | except (TryNext, ClipboardEmpty): | |
|
660 | return | |
|
661 | except ClipboardEmpty: | |
|
662 | return | |
|
663 | event.current_buffer.insert_text(text.replace("\t", " " * 4)) | |
|
664 | ||
|
665 | else: | |
|
666 | ||
|
667 | @undoc | |
|
668 | def win_paste(event): | |
|
669 | """Stub used when auto-generating shortcuts for documentation""" | |
|
670 | pass |
@@ -0,0 +1,104 b'' | |||
|
1 | """ | |
|
2 | Utilities function for keybinding with prompt toolkit. | |
|
3 | ||
|
4 | This will be bound to specific key press and filter modes, | |
|
5 | like whether we are in edit mode, and whether the completer is open. | |
|
6 | """ | |
|
7 | import re | |
|
8 | from prompt_toolkit.key_binding import KeyPressEvent | |
|
9 | ||
|
10 | ||
|
11 | def parenthesis(event: KeyPressEvent): | |
|
12 | """Auto-close parenthesis""" | |
|
13 | event.current_buffer.insert_text("()") | |
|
14 | event.current_buffer.cursor_left() | |
|
15 | ||
|
16 | ||
|
17 | def brackets(event: KeyPressEvent): | |
|
18 | """Auto-close brackets""" | |
|
19 | event.current_buffer.insert_text("[]") | |
|
20 | event.current_buffer.cursor_left() | |
|
21 | ||
|
22 | ||
|
23 | def braces(event: KeyPressEvent): | |
|
24 | """Auto-close braces""" | |
|
25 | event.current_buffer.insert_text("{}") | |
|
26 | event.current_buffer.cursor_left() | |
|
27 | ||
|
28 | ||
|
29 | def double_quote(event: KeyPressEvent): | |
|
30 | """Auto-close double quotes""" | |
|
31 | event.current_buffer.insert_text('""') | |
|
32 | event.current_buffer.cursor_left() | |
|
33 | ||
|
34 | ||
|
35 | def single_quote(event: KeyPressEvent): | |
|
36 | """Auto-close single quotes""" | |
|
37 | event.current_buffer.insert_text("''") | |
|
38 | event.current_buffer.cursor_left() | |
|
39 | ||
|
40 | ||
|
41 | def docstring_double_quotes(event: KeyPressEvent): | |
|
42 | """Auto-close docstring (double quotes)""" | |
|
43 | event.current_buffer.insert_text('""""') | |
|
44 | event.current_buffer.cursor_left(3) | |
|
45 | ||
|
46 | ||
|
47 | def docstring_single_quotes(event: KeyPressEvent): | |
|
48 | """Auto-close docstring (single quotes)""" | |
|
49 | event.current_buffer.insert_text("''''") | |
|
50 | event.current_buffer.cursor_left(3) | |
|
51 | ||
|
52 | ||
|
53 | def raw_string_parenthesis(event: KeyPressEvent): | |
|
54 | """Auto-close parenthesis in raw strings""" | |
|
55 | matches = re.match( | |
|
56 | r".*(r|R)[\"'](-*)", | |
|
57 | event.current_buffer.document.current_line_before_cursor, | |
|
58 | ) | |
|
59 | dashes = matches.group(2) if matches else "" | |
|
60 | event.current_buffer.insert_text("()" + dashes) | |
|
61 | event.current_buffer.cursor_left(len(dashes) + 1) | |
|
62 | ||
|
63 | ||
|
64 | def raw_string_bracket(event: KeyPressEvent): | |
|
65 | """Auto-close bracker in raw strings""" | |
|
66 | matches = re.match( | |
|
67 | r".*(r|R)[\"'](-*)", | |
|
68 | event.current_buffer.document.current_line_before_cursor, | |
|
69 | ) | |
|
70 | dashes = matches.group(2) if matches else "" | |
|
71 | event.current_buffer.insert_text("[]" + dashes) | |
|
72 | event.current_buffer.cursor_left(len(dashes) + 1) | |
|
73 | ||
|
74 | ||
|
75 | def raw_string_braces(event: KeyPressEvent): | |
|
76 | """Auto-close braces in raw strings""" | |
|
77 | matches = re.match( | |
|
78 | r".*(r|R)[\"'](-*)", | |
|
79 | event.current_buffer.document.current_line_before_cursor, | |
|
80 | ) | |
|
81 | dashes = matches.group(2) if matches else "" | |
|
82 | event.current_buffer.insert_text("{}" + dashes) | |
|
83 | event.current_buffer.cursor_left(len(dashes) + 1) | |
|
84 | ||
|
85 | ||
|
86 | def skip_over(event: KeyPressEvent): | |
|
87 | """Skip over automatically added parenthesis. | |
|
88 | ||
|
89 | (rather than adding another parenthesis)""" | |
|
90 | event.current_buffer.cursor_right() | |
|
91 | ||
|
92 | ||
|
93 | def delete_pair(event: KeyPressEvent): | |
|
94 | """Delete auto-closed parenthesis""" | |
|
95 | event.current_buffer.delete() | |
|
96 | event.current_buffer.delete_before_cursor() | |
|
97 | ||
|
98 | ||
|
99 | auto_match_parens = {"(": parenthesis, "[": brackets, "{": braces} | |
|
100 | auto_match_parens_raw_string = { | |
|
101 | "(": raw_string_parenthesis, | |
|
102 | "[": raw_string_bracket, | |
|
103 | "{": raw_string_braces, | |
|
104 | } |
@@ -0,0 +1,378 b'' | |||
|
1 | import re | |
|
2 | import tokenize | |
|
3 | from io import StringIO | |
|
4 | from typing import Callable, List, Optional, Union, Generator, Tuple, Sequence | |
|
5 | ||
|
6 | from prompt_toolkit.buffer import Buffer | |
|
7 | from prompt_toolkit.key_binding import KeyPressEvent | |
|
8 | from prompt_toolkit.key_binding.bindings import named_commands as nc | |
|
9 | from prompt_toolkit.auto_suggest import AutoSuggestFromHistory, Suggestion | |
|
10 | from prompt_toolkit.document import Document | |
|
11 | from prompt_toolkit.history import History | |
|
12 | from prompt_toolkit.shortcuts import PromptSession | |
|
13 | from prompt_toolkit.layout.processors import ( | |
|
14 | Processor, | |
|
15 | Transformation, | |
|
16 | TransformationInput, | |
|
17 | ) | |
|
18 | ||
|
19 | from IPython.utils.tokenutil import generate_tokens | |
|
20 | ||
|
21 | ||
|
22 | def _get_query(document: Document): | |
|
23 | return document.lines[document.cursor_position_row] | |
|
24 | ||
|
25 | ||
|
26 | class AppendAutoSuggestionInAnyLine(Processor): | |
|
27 | """ | |
|
28 | Append the auto suggestion to lines other than the last (appending to the | |
|
29 | last line is natively supported by the prompt toolkit). | |
|
30 | """ | |
|
31 | ||
|
32 | def __init__(self, style: str = "class:auto-suggestion") -> None: | |
|
33 | self.style = style | |
|
34 | ||
|
35 | def apply_transformation(self, ti: TransformationInput) -> Transformation: | |
|
36 | is_last_line = ti.lineno == ti.document.line_count - 1 | |
|
37 | is_active_line = ti.lineno == ti.document.cursor_position_row | |
|
38 | ||
|
39 | if not is_last_line and is_active_line: | |
|
40 | buffer = ti.buffer_control.buffer | |
|
41 | ||
|
42 | if buffer.suggestion and ti.document.is_cursor_at_the_end_of_line: | |
|
43 | suggestion = buffer.suggestion.text | |
|
44 | else: | |
|
45 | suggestion = "" | |
|
46 | ||
|
47 | return Transformation(fragments=ti.fragments + [(self.style, suggestion)]) | |
|
48 | else: | |
|
49 | return Transformation(fragments=ti.fragments) | |
|
50 | ||
|
51 | ||
|
52 | class NavigableAutoSuggestFromHistory(AutoSuggestFromHistory): | |
|
53 | """ | |
|
54 | A subclass of AutoSuggestFromHistory that allow navigation to next/previous | |
|
55 | suggestion from history. To do so it remembers the current position, but it | |
|
56 | state need to carefully be cleared on the right events. | |
|
57 | """ | |
|
58 | ||
|
59 | def __init__( | |
|
60 | self, | |
|
61 | ): | |
|
62 | self.skip_lines = 0 | |
|
63 | self._connected_apps = [] | |
|
64 | ||
|
65 | def reset_history_position(self, _: Buffer): | |
|
66 | self.skip_lines = 0 | |
|
67 | ||
|
68 | def disconnect(self): | |
|
69 | for pt_app in self._connected_apps: | |
|
70 | text_insert_event = pt_app.default_buffer.on_text_insert | |
|
71 | text_insert_event.remove_handler(self.reset_history_position) | |
|
72 | ||
|
73 | def connect(self, pt_app: PromptSession): | |
|
74 | self._connected_apps.append(pt_app) | |
|
75 | # note: `on_text_changed` could be used for a bit different behaviour | |
|
76 | # on character deletion (i.e. reseting history position on backspace) | |
|
77 | pt_app.default_buffer.on_text_insert.add_handler(self.reset_history_position) | |
|
78 | pt_app.default_buffer.on_cursor_position_changed.add_handler(self._dismiss) | |
|
79 | ||
|
80 | def get_suggestion( | |
|
81 | self, buffer: Buffer, document: Document | |
|
82 | ) -> Optional[Suggestion]: | |
|
83 | text = _get_query(document) | |
|
84 | ||
|
85 | if text.strip(): | |
|
86 | for suggestion, _ in self._find_next_match( | |
|
87 | text, self.skip_lines, buffer.history | |
|
88 | ): | |
|
89 | return Suggestion(suggestion) | |
|
90 | ||
|
91 | return None | |
|
92 | ||
|
93 | def _dismiss(self, buffer, *args, **kwargs): | |
|
94 | buffer.suggestion = None | |
|
95 | ||
|
96 | def _find_match( | |
|
97 | self, text: str, skip_lines: float, history: History, previous: bool | |
|
98 | ) -> Generator[Tuple[str, float], None, None]: | |
|
99 | """ | |
|
100 | text : str | |
|
101 | Text content to find a match for, the user cursor is most of the | |
|
102 | time at the end of this text. | |
|
103 | skip_lines : float | |
|
104 | number of items to skip in the search, this is used to indicate how | |
|
105 | far in the list the user has navigated by pressing up or down. | |
|
106 | The float type is used as the base value is +inf | |
|
107 | history : History | |
|
108 | prompt_toolkit History instance to fetch previous entries from. | |
|
109 | previous : bool | |
|
110 | Direction of the search, whether we are looking previous match | |
|
111 | (True), or next match (False). | |
|
112 | ||
|
113 | Yields | |
|
114 | ------ | |
|
115 | Tuple with: | |
|
116 | str: | |
|
117 | current suggestion. | |
|
118 | float: | |
|
119 | will actually yield only ints, which is passed back via skip_lines, | |
|
120 | which may be a +inf (float) | |
|
121 | ||
|
122 | ||
|
123 | """ | |
|
124 | line_number = -1 | |
|
125 | for string in reversed(list(history.get_strings())): | |
|
126 | for line in reversed(string.splitlines()): | |
|
127 | line_number += 1 | |
|
128 | if not previous and line_number < skip_lines: | |
|
129 | continue | |
|
130 | # do not return empty suggestions as these | |
|
131 | # close the auto-suggestion overlay (and are useless) | |
|
132 | if line.startswith(text) and len(line) > len(text): | |
|
133 | yield line[len(text) :], line_number | |
|
134 | if previous and line_number >= skip_lines: | |
|
135 | return | |
|
136 | ||
|
137 | def _find_next_match( | |
|
138 | self, text: str, skip_lines: float, history: History | |
|
139 | ) -> Generator[Tuple[str, float], None, None]: | |
|
140 | return self._find_match(text, skip_lines, history, previous=False) | |
|
141 | ||
|
142 | def _find_previous_match(self, text: str, skip_lines: float, history: History): | |
|
143 | return reversed( | |
|
144 | list(self._find_match(text, skip_lines, history, previous=True)) | |
|
145 | ) | |
|
146 | ||
|
147 | def up(self, query: str, other_than: str, history: History) -> None: | |
|
148 | for suggestion, line_number in self._find_next_match( | |
|
149 | query, self.skip_lines, history | |
|
150 | ): | |
|
151 | # if user has history ['very.a', 'very', 'very.b'] and typed 'very' | |
|
152 | # we want to switch from 'very.b' to 'very.a' because a) if the | |
|
153 | # suggestion equals current text, prompt-toolkit aborts suggesting | |
|
154 | # b) user likely would not be interested in 'very' anyways (they | |
|
155 | # already typed it). | |
|
156 | if query + suggestion != other_than: | |
|
157 | self.skip_lines = line_number | |
|
158 | break | |
|
159 | else: | |
|
160 | # no matches found, cycle back to beginning | |
|
161 | self.skip_lines = 0 | |
|
162 | ||
|
163 | def down(self, query: str, other_than: str, history: History) -> None: | |
|
164 | for suggestion, line_number in self._find_previous_match( | |
|
165 | query, self.skip_lines, history | |
|
166 | ): | |
|
167 | if query + suggestion != other_than: | |
|
168 | self.skip_lines = line_number | |
|
169 | break | |
|
170 | else: | |
|
171 | # no matches found, cycle to end | |
|
172 | for suggestion, line_number in self._find_previous_match( | |
|
173 | query, float("Inf"), history | |
|
174 | ): | |
|
175 | if query + suggestion != other_than: | |
|
176 | self.skip_lines = line_number | |
|
177 | break | |
|
178 | ||
|
179 | ||
|
180 | # Needed for to accept autosuggestions in vi insert mode | |
|
181 | def accept_in_vi_insert_mode(event: KeyPressEvent): | |
|
182 | """Apply autosuggestion if at end of line.""" | |
|
183 | buffer = event.current_buffer | |
|
184 | d = buffer.document | |
|
185 | after_cursor = d.text[d.cursor_position :] | |
|
186 | lines = after_cursor.split("\n") | |
|
187 | end_of_current_line = lines[0].strip() | |
|
188 | suggestion = buffer.suggestion | |
|
189 | if (suggestion is not None) and (suggestion.text) and (end_of_current_line == ""): | |
|
190 | buffer.insert_text(suggestion.text) | |
|
191 | else: | |
|
192 | nc.end_of_line(event) | |
|
193 | ||
|
194 | ||
|
195 | def accept(event: KeyPressEvent): | |
|
196 | """Accept autosuggestion""" | |
|
197 | buffer = event.current_buffer | |
|
198 | suggestion = buffer.suggestion | |
|
199 | if suggestion: | |
|
200 | buffer.insert_text(suggestion.text) | |
|
201 | else: | |
|
202 | nc.forward_char(event) | |
|
203 | ||
|
204 | ||
|
205 | def discard(event: KeyPressEvent): | |
|
206 | """Discard autosuggestion""" | |
|
207 | buffer = event.current_buffer | |
|
208 | buffer.suggestion = None | |
|
209 | ||
|
210 | ||
|
211 | def accept_word(event: KeyPressEvent): | |
|
212 | """Fill partial autosuggestion by word""" | |
|
213 | buffer = event.current_buffer | |
|
214 | suggestion = buffer.suggestion | |
|
215 | if suggestion: | |
|
216 | t = re.split(r"(\S+\s+)", suggestion.text) | |
|
217 | buffer.insert_text(next((x for x in t if x), "")) | |
|
218 | else: | |
|
219 | nc.forward_word(event) | |
|
220 | ||
|
221 | ||
|
222 | def accept_character(event: KeyPressEvent): | |
|
223 | """Fill partial autosuggestion by character""" | |
|
224 | b = event.current_buffer | |
|
225 | suggestion = b.suggestion | |
|
226 | if suggestion and suggestion.text: | |
|
227 | b.insert_text(suggestion.text[0]) | |
|
228 | ||
|
229 | ||
|
230 | def accept_and_keep_cursor(event: KeyPressEvent): | |
|
231 | """Accept autosuggestion and keep cursor in place""" | |
|
232 | buffer = event.current_buffer | |
|
233 | old_position = buffer.cursor_position | |
|
234 | suggestion = buffer.suggestion | |
|
235 | if suggestion: | |
|
236 | buffer.insert_text(suggestion.text) | |
|
237 | buffer.cursor_position = old_position | |
|
238 | ||
|
239 | ||
|
240 | def accept_and_move_cursor_left(event: KeyPressEvent): | |
|
241 | """Accept autosuggestion and move cursor left in place""" | |
|
242 | accept_and_keep_cursor(event) | |
|
243 | nc.backward_char(event) | |
|
244 | ||
|
245 | ||
|
246 | def _update_hint(buffer: Buffer): | |
|
247 | if buffer.auto_suggest: | |
|
248 | suggestion = buffer.auto_suggest.get_suggestion(buffer, buffer.document) | |
|
249 | buffer.suggestion = suggestion | |
|
250 | ||
|
251 | ||
|
252 | def backspace_and_resume_hint(event: KeyPressEvent): | |
|
253 | """Resume autosuggestions after deleting last character""" | |
|
254 | current_buffer = event.current_buffer | |
|
255 | ||
|
256 | def resume_hinting(buffer: Buffer): | |
|
257 | _update_hint(buffer) | |
|
258 | current_buffer.on_text_changed.remove_handler(resume_hinting) | |
|
259 | ||
|
260 | current_buffer.on_text_changed.add_handler(resume_hinting) | |
|
261 | nc.backward_delete_char(event) | |
|
262 | ||
|
263 | ||
|
264 | def up_and_update_hint(event: KeyPressEvent): | |
|
265 | """Go up and update hint""" | |
|
266 | current_buffer = event.current_buffer | |
|
267 | ||
|
268 | current_buffer.auto_up(count=event.arg) | |
|
269 | _update_hint(current_buffer) | |
|
270 | ||
|
271 | ||
|
272 | def down_and_update_hint(event: KeyPressEvent): | |
|
273 | """Go down and update hint""" | |
|
274 | current_buffer = event.current_buffer | |
|
275 | ||
|
276 | current_buffer.auto_down(count=event.arg) | |
|
277 | _update_hint(current_buffer) | |
|
278 | ||
|
279 | ||
|
280 | def accept_token(event: KeyPressEvent): | |
|
281 | """Fill partial autosuggestion by token""" | |
|
282 | b = event.current_buffer | |
|
283 | suggestion = b.suggestion | |
|
284 | ||
|
285 | if suggestion: | |
|
286 | prefix = _get_query(b.document) | |
|
287 | text = prefix + suggestion.text | |
|
288 | ||
|
289 | tokens: List[Optional[str]] = [None, None, None] | |
|
290 | substrings = [""] | |
|
291 | i = 0 | |
|
292 | ||
|
293 | for token in generate_tokens(StringIO(text).readline): | |
|
294 | if token.type == tokenize.NEWLINE: | |
|
295 | index = len(text) | |
|
296 | else: | |
|
297 | index = text.index(token[1], len(substrings[-1])) | |
|
298 | substrings.append(text[:index]) | |
|
299 | tokenized_so_far = substrings[-1] | |
|
300 | if tokenized_so_far.startswith(prefix): | |
|
301 | if i == 0 and len(tokenized_so_far) > len(prefix): | |
|
302 | tokens[0] = tokenized_so_far[len(prefix) :] | |
|
303 | substrings.append(tokenized_so_far) | |
|
304 | i += 1 | |
|
305 | tokens[i] = token[1] | |
|
306 | if i == 2: | |
|
307 | break | |
|
308 | i += 1 | |
|
309 | ||
|
310 | if tokens[0]: | |
|
311 | to_insert: str | |
|
312 | insert_text = substrings[-2] | |
|
313 | if tokens[1] and len(tokens[1]) == 1: | |
|
314 | insert_text = substrings[-1] | |
|
315 | to_insert = insert_text[len(prefix) :] | |
|
316 | b.insert_text(to_insert) | |
|
317 | return | |
|
318 | ||
|
319 | nc.forward_word(event) | |
|
320 | ||
|
321 | ||
|
322 | Provider = Union[AutoSuggestFromHistory, NavigableAutoSuggestFromHistory, None] | |
|
323 | ||
|
324 | ||
|
325 | def _swap_autosuggestion( | |
|
326 | buffer: Buffer, | |
|
327 | provider: NavigableAutoSuggestFromHistory, | |
|
328 | direction_method: Callable, | |
|
329 | ): | |
|
330 | """ | |
|
331 | We skip most recent history entry (in either direction) if it equals the | |
|
332 | current autosuggestion because if user cycles when auto-suggestion is shown | |
|
333 | they most likely want something else than what was suggested (otherwise | |
|
334 | they would have accepted the suggestion). | |
|
335 | """ | |
|
336 | suggestion = buffer.suggestion | |
|
337 | if not suggestion: | |
|
338 | return | |
|
339 | ||
|
340 | query = _get_query(buffer.document) | |
|
341 | current = query + suggestion.text | |
|
342 | ||
|
343 | direction_method(query=query, other_than=current, history=buffer.history) | |
|
344 | ||
|
345 | new_suggestion = provider.get_suggestion(buffer, buffer.document) | |
|
346 | buffer.suggestion = new_suggestion | |
|
347 | ||
|
348 | ||
|
349 | def swap_autosuggestion_up(provider: Provider): | |
|
350 | def swap_autosuggestion_up(event: KeyPressEvent): | |
|
351 | """Get next autosuggestion from history.""" | |
|
352 | if not isinstance(provider, NavigableAutoSuggestFromHistory): | |
|
353 | return | |
|
354 | ||
|
355 | return _swap_autosuggestion( | |
|
356 | buffer=event.current_buffer, provider=provider, direction_method=provider.up | |
|
357 | ) | |
|
358 | ||
|
359 | swap_autosuggestion_up.__name__ = "swap_autosuggestion_up" | |
|
360 | return swap_autosuggestion_up | |
|
361 | ||
|
362 | ||
|
363 | def swap_autosuggestion_down( | |
|
364 | provider: Union[AutoSuggestFromHistory, NavigableAutoSuggestFromHistory, None] | |
|
365 | ): | |
|
366 | def swap_autosuggestion_down(event: KeyPressEvent): | |
|
367 | """Get previous autosuggestion from history.""" | |
|
368 | if not isinstance(provider, NavigableAutoSuggestFromHistory): | |
|
369 | return | |
|
370 | ||
|
371 | return _swap_autosuggestion( | |
|
372 | buffer=event.current_buffer, | |
|
373 | provider=provider, | |
|
374 | direction_method=provider.down, | |
|
375 | ) | |
|
376 | ||
|
377 | swap_autosuggestion_down.__name__ = "swap_autosuggestion_down" | |
|
378 | return swap_autosuggestion_down |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644, binary diff hidden |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
@@ -1,39 +1,41 b'' | |||
|
1 | 1 | name: Run MyPy |
|
2 | 2 | |
|
3 | 3 | on: |
|
4 | 4 | push: |
|
5 | 5 | branches: [ main, 7.x] |
|
6 | 6 | pull_request: |
|
7 | 7 | branches: [ main, 7.x] |
|
8 | 8 | |
|
9 | 9 | permissions: |
|
10 | 10 | contents: read |
|
11 | 11 | |
|
12 | 12 | jobs: |
|
13 | 13 | build: |
|
14 | 14 | |
|
15 | 15 | runs-on: ubuntu-latest |
|
16 | 16 | strategy: |
|
17 | 17 | matrix: |
|
18 | 18 | python-version: ["3.x"] |
|
19 | 19 | |
|
20 | 20 | steps: |
|
21 | 21 | - uses: actions/checkout@v3 |
|
22 | 22 | - name: Set up Python ${{ matrix.python-version }} |
|
23 | 23 | uses: actions/setup-python@v4 |
|
24 | 24 | with: |
|
25 | 25 | python-version: ${{ matrix.python-version }} |
|
26 | 26 | - name: Install dependencies |
|
27 | 27 | run: | |
|
28 | 28 | python -m pip install --upgrade pip |
|
29 | 29 | pip install mypy pyflakes flake8 |
|
30 | 30 | - name: Lint with mypy |
|
31 | 31 | run: | |
|
32 | set -e | |
|
32 | 33 | mypy -p IPython.terminal |
|
33 | 34 | mypy -p IPython.core.magics |
|
34 | 35 | mypy -p IPython.core.guarded_eval |
|
35 | 36 | mypy -p IPython.core.completer |
|
36 | 37 | - name: Lint with pyflakes |
|
37 | 38 | run: | |
|
39 | set -e | |
|
38 | 40 | flake8 IPython/core/magics/script.py |
|
39 | 41 | flake8 IPython/core/magics/packaging.py |
@@ -1,489 +1,488 b'' | |||
|
1 | 1 | # encoding: utf-8 |
|
2 | 2 | """ |
|
3 | 3 | An application for IPython. |
|
4 | 4 | |
|
5 | 5 | All top-level applications should use the classes in this module for |
|
6 | 6 | handling configuration and creating configurables. |
|
7 | 7 | |
|
8 | 8 | The job of an :class:`Application` is to create the master configuration |
|
9 | 9 | object and then create the configurable objects, passing the config to them. |
|
10 | 10 | """ |
|
11 | 11 | |
|
12 | 12 | # Copyright (c) IPython Development Team. |
|
13 | 13 | # Distributed under the terms of the Modified BSD License. |
|
14 | 14 | |
|
15 | 15 | import atexit |
|
16 | 16 | from copy import deepcopy |
|
17 | 17 | import logging |
|
18 | 18 | import os |
|
19 | 19 | import shutil |
|
20 | 20 | import sys |
|
21 | 21 | |
|
22 | 22 | from pathlib import Path |
|
23 | 23 | |
|
24 | 24 | from traitlets.config.application import Application, catch_config_error |
|
25 | 25 | from traitlets.config.loader import ConfigFileNotFound, PyFileConfigLoader |
|
26 | 26 | from IPython.core import release, crashhandler |
|
27 | 27 | from IPython.core.profiledir import ProfileDir, ProfileDirError |
|
28 | 28 | from IPython.paths import get_ipython_dir, get_ipython_package_dir |
|
29 | 29 | from IPython.utils.path import ensure_dir_exists |
|
30 | 30 | from traitlets import ( |
|
31 | 31 | List, Unicode, Type, Bool, Set, Instance, Undefined, |
|
32 | 32 | default, observe, |
|
33 | 33 | ) |
|
34 | 34 | |
|
35 | 35 | if os.name == "nt": |
|
36 | 36 | programdata = os.environ.get("PROGRAMDATA", None) |
|
37 | 37 | if programdata is not None: |
|
38 | 38 | SYSTEM_CONFIG_DIRS = [str(Path(programdata) / "ipython")] |
|
39 | 39 | else: # PROGRAMDATA is not defined by default on XP. |
|
40 | 40 | SYSTEM_CONFIG_DIRS = [] |
|
41 | 41 | else: |
|
42 | 42 | SYSTEM_CONFIG_DIRS = [ |
|
43 | 43 | "/usr/local/etc/ipython", |
|
44 | 44 | "/etc/ipython", |
|
45 | 45 | ] |
|
46 | 46 | |
|
47 | 47 | |
|
48 | 48 | ENV_CONFIG_DIRS = [] |
|
49 | 49 | _env_config_dir = os.path.join(sys.prefix, 'etc', 'ipython') |
|
50 | 50 | if _env_config_dir not in SYSTEM_CONFIG_DIRS: |
|
51 | 51 | # only add ENV_CONFIG if sys.prefix is not already included |
|
52 | 52 | ENV_CONFIG_DIRS.append(_env_config_dir) |
|
53 | 53 | |
|
54 | 54 | |
|
55 | 55 | _envvar = os.environ.get('IPYTHON_SUPPRESS_CONFIG_ERRORS') |
|
56 | 56 | if _envvar in {None, ''}: |
|
57 | 57 | IPYTHON_SUPPRESS_CONFIG_ERRORS = None |
|
58 | 58 | else: |
|
59 | 59 | if _envvar.lower() in {'1','true'}: |
|
60 | 60 | IPYTHON_SUPPRESS_CONFIG_ERRORS = True |
|
61 | 61 | elif _envvar.lower() in {'0','false'} : |
|
62 | 62 | IPYTHON_SUPPRESS_CONFIG_ERRORS = False |
|
63 | 63 | else: |
|
64 | 64 | sys.exit("Unsupported value for environment variable: 'IPYTHON_SUPPRESS_CONFIG_ERRORS' is set to '%s' which is none of {'0', '1', 'false', 'true', ''}."% _envvar ) |
|
65 | 65 | |
|
66 | 66 | # aliases and flags |
|
67 | 67 | |
|
68 | 68 | base_aliases = {} |
|
69 | 69 | if isinstance(Application.aliases, dict): |
|
70 | 70 | # traitlets 5 |
|
71 | 71 | base_aliases.update(Application.aliases) |
|
72 | 72 | base_aliases.update( |
|
73 | 73 | { |
|
74 | 74 | "profile-dir": "ProfileDir.location", |
|
75 | 75 | "profile": "BaseIPythonApplication.profile", |
|
76 | 76 | "ipython-dir": "BaseIPythonApplication.ipython_dir", |
|
77 | 77 | "log-level": "Application.log_level", |
|
78 | 78 | "config": "BaseIPythonApplication.extra_config_file", |
|
79 | 79 | } |
|
80 | 80 | ) |
|
81 | 81 | |
|
82 | 82 | base_flags = dict() |
|
83 | 83 | if isinstance(Application.flags, dict): |
|
84 | 84 | # traitlets 5 |
|
85 | 85 | base_flags.update(Application.flags) |
|
86 | 86 | base_flags.update( |
|
87 | 87 | dict( |
|
88 | 88 | debug=( |
|
89 | 89 | {"Application": {"log_level": logging.DEBUG}}, |
|
90 | 90 | "set log level to logging.DEBUG (maximize logging output)", |
|
91 | 91 | ), |
|
92 | 92 | quiet=( |
|
93 | 93 | {"Application": {"log_level": logging.CRITICAL}}, |
|
94 | 94 | "set log level to logging.CRITICAL (minimize logging output)", |
|
95 | 95 | ), |
|
96 | 96 | init=( |
|
97 | 97 | { |
|
98 | 98 | "BaseIPythonApplication": { |
|
99 | 99 | "copy_config_files": True, |
|
100 | 100 | "auto_create": True, |
|
101 | 101 | } |
|
102 | 102 | }, |
|
103 | 103 | """Initialize profile with default config files. This is equivalent |
|
104 | 104 | to running `ipython profile create <profile>` prior to startup. |
|
105 | 105 | """, |
|
106 | 106 | ), |
|
107 | 107 | ) |
|
108 | 108 | ) |
|
109 | 109 | |
|
110 | 110 | |
|
111 | 111 | class ProfileAwareConfigLoader(PyFileConfigLoader): |
|
112 | 112 | """A Python file config loader that is aware of IPython profiles.""" |
|
113 | 113 | def load_subconfig(self, fname, path=None, profile=None): |
|
114 | 114 | if profile is not None: |
|
115 | 115 | try: |
|
116 | 116 | profile_dir = ProfileDir.find_profile_dir_by_name( |
|
117 | 117 | get_ipython_dir(), |
|
118 | 118 | profile, |
|
119 | 119 | ) |
|
120 | 120 | except ProfileDirError: |
|
121 | 121 | return |
|
122 | 122 | path = profile_dir.location |
|
123 | 123 | return super(ProfileAwareConfigLoader, self).load_subconfig(fname, path=path) |
|
124 | 124 | |
|
125 | 125 | class BaseIPythonApplication(Application): |
|
126 | ||
|
127 | name = u'ipython' | |
|
128 | description = Unicode(u'IPython: an enhanced interactive Python shell.') | |
|
126 | name = "ipython" | |
|
127 | description = "IPython: an enhanced interactive Python shell." | |
|
129 | 128 | version = Unicode(release.version) |
|
130 | 129 | |
|
131 | 130 | aliases = base_aliases |
|
132 | 131 | flags = base_flags |
|
133 | 132 | classes = List([ProfileDir]) |
|
134 | 133 | |
|
135 | 134 | # enable `load_subconfig('cfg.py', profile='name')` |
|
136 | 135 | python_config_loader_class = ProfileAwareConfigLoader |
|
137 | 136 | |
|
138 | 137 | # Track whether the config_file has changed, |
|
139 | 138 | # because some logic happens only if we aren't using the default. |
|
140 | 139 | config_file_specified = Set() |
|
141 | 140 | |
|
142 | 141 | config_file_name = Unicode() |
|
143 | 142 | @default('config_file_name') |
|
144 | 143 | def _config_file_name_default(self): |
|
145 | 144 | return self.name.replace('-','_') + u'_config.py' |
|
146 | 145 | @observe('config_file_name') |
|
147 | 146 | def _config_file_name_changed(self, change): |
|
148 | 147 | if change['new'] != change['old']: |
|
149 | 148 | self.config_file_specified.add(change['new']) |
|
150 | 149 | |
|
151 | 150 | # The directory that contains IPython's builtin profiles. |
|
152 | 151 | builtin_profile_dir = Unicode( |
|
153 | 152 | os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default') |
|
154 | 153 | ) |
|
155 | 154 | |
|
156 | 155 | config_file_paths = List(Unicode()) |
|
157 | 156 | @default('config_file_paths') |
|
158 | 157 | def _config_file_paths_default(self): |
|
159 | 158 | return [] |
|
160 | 159 | |
|
161 | 160 | extra_config_file = Unicode( |
|
162 | 161 | help="""Path to an extra config file to load. |
|
163 | 162 | |
|
164 | 163 | If specified, load this config file in addition to any other IPython config. |
|
165 | 164 | """).tag(config=True) |
|
166 | 165 | @observe('extra_config_file') |
|
167 | 166 | def _extra_config_file_changed(self, change): |
|
168 | 167 | old = change['old'] |
|
169 | 168 | new = change['new'] |
|
170 | 169 | try: |
|
171 | 170 | self.config_files.remove(old) |
|
172 | 171 | except ValueError: |
|
173 | 172 | pass |
|
174 | 173 | self.config_file_specified.add(new) |
|
175 | 174 | self.config_files.append(new) |
|
176 | 175 | |
|
177 | 176 | profile = Unicode(u'default', |
|
178 | 177 | help="""The IPython profile to use.""" |
|
179 | 178 | ).tag(config=True) |
|
180 | 179 | |
|
181 | 180 | @observe('profile') |
|
182 | 181 | def _profile_changed(self, change): |
|
183 | 182 | self.builtin_profile_dir = os.path.join( |
|
184 | 183 | get_ipython_package_dir(), u'config', u'profile', change['new'] |
|
185 | 184 | ) |
|
186 | 185 | |
|
187 | 186 | add_ipython_dir_to_sys_path = Bool( |
|
188 | 187 | False, |
|
189 | 188 | """Should the IPython profile directory be added to sys path ? |
|
190 | 189 | |
|
191 | 190 | This option was non-existing before IPython 8.0, and ipython_dir was added to |
|
192 | 191 | sys path to allow import of extensions present there. This was historical |
|
193 | 192 | baggage from when pip did not exist. This now default to false, |
|
194 | 193 | but can be set to true for legacy reasons. |
|
195 | 194 | """, |
|
196 | 195 | ).tag(config=True) |
|
197 | 196 | |
|
198 | 197 | ipython_dir = Unicode( |
|
199 | 198 | help=""" |
|
200 | 199 | The name of the IPython directory. This directory is used for logging |
|
201 | 200 | configuration (through profiles), history storage, etc. The default |
|
202 | 201 | is usually $HOME/.ipython. This option can also be specified through |
|
203 | 202 | the environment variable IPYTHONDIR. |
|
204 | 203 | """ |
|
205 | 204 | ).tag(config=True) |
|
206 | 205 | @default('ipython_dir') |
|
207 | 206 | def _ipython_dir_default(self): |
|
208 | 207 | d = get_ipython_dir() |
|
209 | 208 | self._ipython_dir_changed({ |
|
210 | 209 | 'name': 'ipython_dir', |
|
211 | 210 | 'old': d, |
|
212 | 211 | 'new': d, |
|
213 | 212 | }) |
|
214 | 213 | return d |
|
215 | 214 | |
|
216 | 215 | _in_init_profile_dir = False |
|
217 | 216 | profile_dir = Instance(ProfileDir, allow_none=True) |
|
218 | 217 | @default('profile_dir') |
|
219 | 218 | def _profile_dir_default(self): |
|
220 | 219 | # avoid recursion |
|
221 | 220 | if self._in_init_profile_dir: |
|
222 | 221 | return |
|
223 | 222 | # profile_dir requested early, force initialization |
|
224 | 223 | self.init_profile_dir() |
|
225 | 224 | return self.profile_dir |
|
226 | 225 | |
|
227 | 226 | overwrite = Bool(False, |
|
228 | 227 | help="""Whether to overwrite existing config files when copying""" |
|
229 | 228 | ).tag(config=True) |
|
230 | 229 | auto_create = Bool(False, |
|
231 | 230 | help="""Whether to create profile dir if it doesn't exist""" |
|
232 | 231 | ).tag(config=True) |
|
233 | 232 | |
|
234 | 233 | config_files = List(Unicode()) |
|
235 | 234 | @default('config_files') |
|
236 | 235 | def _config_files_default(self): |
|
237 | 236 | return [self.config_file_name] |
|
238 | 237 | |
|
239 | 238 | copy_config_files = Bool(False, |
|
240 | 239 | help="""Whether to install the default config files into the profile dir. |
|
241 | 240 | If a new profile is being created, and IPython contains config files for that |
|
242 | 241 | profile, then they will be staged into the new directory. Otherwise, |
|
243 | 242 | default config files will be automatically generated. |
|
244 | 243 | """).tag(config=True) |
|
245 | 244 | |
|
246 | 245 | verbose_crash = Bool(False, |
|
247 | 246 | help="""Create a massive crash report when IPython encounters what may be an |
|
248 | 247 | internal error. The default is to append a short message to the |
|
249 | 248 | usual traceback""").tag(config=True) |
|
250 | 249 | |
|
251 | 250 | # The class to use as the crash handler. |
|
252 | 251 | crash_handler_class = Type(crashhandler.CrashHandler) |
|
253 | 252 | |
|
254 | 253 | @catch_config_error |
|
255 | 254 | def __init__(self, **kwargs): |
|
256 | 255 | super(BaseIPythonApplication, self).__init__(**kwargs) |
|
257 | 256 | # ensure current working directory exists |
|
258 | 257 | try: |
|
259 | 258 | os.getcwd() |
|
260 | 259 | except: |
|
261 | 260 | # exit if cwd doesn't exist |
|
262 | 261 | self.log.error("Current working directory doesn't exist.") |
|
263 | 262 | self.exit(1) |
|
264 | 263 | |
|
265 | 264 | #------------------------------------------------------------------------- |
|
266 | 265 | # Various stages of Application creation |
|
267 | 266 | #------------------------------------------------------------------------- |
|
268 | 267 | |
|
269 | 268 | def init_crash_handler(self): |
|
270 | 269 | """Create a crash handler, typically setting sys.excepthook to it.""" |
|
271 | 270 | self.crash_handler = self.crash_handler_class(self) |
|
272 | 271 | sys.excepthook = self.excepthook |
|
273 | 272 | def unset_crashhandler(): |
|
274 | 273 | sys.excepthook = sys.__excepthook__ |
|
275 | 274 | atexit.register(unset_crashhandler) |
|
276 | 275 | |
|
277 | 276 | def excepthook(self, etype, evalue, tb): |
|
278 | 277 | """this is sys.excepthook after init_crashhandler |
|
279 | 278 | |
|
280 | 279 | set self.verbose_crash=True to use our full crashhandler, instead of |
|
281 | 280 | a regular traceback with a short message (crash_handler_lite) |
|
282 | 281 | """ |
|
283 | 282 | |
|
284 | 283 | if self.verbose_crash: |
|
285 | 284 | return self.crash_handler(etype, evalue, tb) |
|
286 | 285 | else: |
|
287 | 286 | return crashhandler.crash_handler_lite(etype, evalue, tb) |
|
288 | 287 | |
|
289 | 288 | @observe('ipython_dir') |
|
290 | 289 | def _ipython_dir_changed(self, change): |
|
291 | 290 | old = change['old'] |
|
292 | 291 | new = change['new'] |
|
293 | 292 | if old is not Undefined: |
|
294 | 293 | str_old = os.path.abspath(old) |
|
295 | 294 | if str_old in sys.path: |
|
296 | 295 | sys.path.remove(str_old) |
|
297 | 296 | if self.add_ipython_dir_to_sys_path: |
|
298 | 297 | str_path = os.path.abspath(new) |
|
299 | 298 | sys.path.append(str_path) |
|
300 | 299 | ensure_dir_exists(new) |
|
301 | 300 | readme = os.path.join(new, "README") |
|
302 | 301 | readme_src = os.path.join( |
|
303 | 302 | get_ipython_package_dir(), "config", "profile", "README" |
|
304 | 303 | ) |
|
305 | 304 | if not os.path.exists(readme) and os.path.exists(readme_src): |
|
306 | 305 | shutil.copy(readme_src, readme) |
|
307 | 306 | for d in ("extensions", "nbextensions"): |
|
308 | 307 | path = os.path.join(new, d) |
|
309 | 308 | try: |
|
310 | 309 | ensure_dir_exists(path) |
|
311 | 310 | except OSError as e: |
|
312 | 311 | # this will not be EEXIST |
|
313 | 312 | self.log.error("couldn't create path %s: %s", path, e) |
|
314 |
self.log.debug("IPYTHONDIR set to: %s" |
|
|
313 | self.log.debug("IPYTHONDIR set to: %s", new) | |
|
315 | 314 | |
|
316 | 315 | def load_config_file(self, suppress_errors=IPYTHON_SUPPRESS_CONFIG_ERRORS): |
|
317 | 316 | """Load the config file. |
|
318 | 317 | |
|
319 | 318 | By default, errors in loading config are handled, and a warning |
|
320 | 319 | printed on screen. For testing, the suppress_errors option is set |
|
321 | 320 | to False, so errors will make tests fail. |
|
322 | 321 | |
|
323 | 322 | `suppress_errors` default value is to be `None` in which case the |
|
324 | 323 | behavior default to the one of `traitlets.Application`. |
|
325 | 324 | |
|
326 | 325 | The default value can be set : |
|
327 | 326 | - to `False` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '0', or 'false' (case insensitive). |
|
328 | 327 | - to `True` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '1' or 'true' (case insensitive). |
|
329 | 328 | - to `None` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '' (empty string) or leaving it unset. |
|
330 | 329 | |
|
331 | 330 | Any other value are invalid, and will make IPython exit with a non-zero return code. |
|
332 | 331 | """ |
|
333 | 332 | |
|
334 | 333 | |
|
335 | 334 | self.log.debug("Searching path %s for config files", self.config_file_paths) |
|
336 | 335 | base_config = 'ipython_config.py' |
|
337 | 336 | self.log.debug("Attempting to load config file: %s" % |
|
338 | 337 | base_config) |
|
339 | 338 | try: |
|
340 | 339 | if suppress_errors is not None: |
|
341 | 340 | old_value = Application.raise_config_file_errors |
|
342 | 341 | Application.raise_config_file_errors = not suppress_errors; |
|
343 | 342 | Application.load_config_file( |
|
344 | 343 | self, |
|
345 | 344 | base_config, |
|
346 | 345 | path=self.config_file_paths |
|
347 | 346 | ) |
|
348 | 347 | except ConfigFileNotFound: |
|
349 | 348 | # ignore errors loading parent |
|
350 | 349 | self.log.debug("Config file %s not found", base_config) |
|
351 | 350 | pass |
|
352 | 351 | if suppress_errors is not None: |
|
353 | 352 | Application.raise_config_file_errors = old_value |
|
354 | 353 | |
|
355 | 354 | for config_file_name in self.config_files: |
|
356 | 355 | if not config_file_name or config_file_name == base_config: |
|
357 | 356 | continue |
|
358 | 357 | self.log.debug("Attempting to load config file: %s" % |
|
359 | 358 | self.config_file_name) |
|
360 | 359 | try: |
|
361 | 360 | Application.load_config_file( |
|
362 | 361 | self, |
|
363 | 362 | config_file_name, |
|
364 | 363 | path=self.config_file_paths |
|
365 | 364 | ) |
|
366 | 365 | except ConfigFileNotFound: |
|
367 | 366 | # Only warn if the default config file was NOT being used. |
|
368 | 367 | if config_file_name in self.config_file_specified: |
|
369 | 368 | msg = self.log.warning |
|
370 | 369 | else: |
|
371 | 370 | msg = self.log.debug |
|
372 | 371 | msg("Config file not found, skipping: %s", config_file_name) |
|
373 | 372 | except Exception: |
|
374 | 373 | # For testing purposes. |
|
375 | 374 | if not suppress_errors: |
|
376 | 375 | raise |
|
377 | 376 | self.log.warning("Error loading config file: %s" % |
|
378 | 377 | self.config_file_name, exc_info=True) |
|
379 | 378 | |
|
380 | 379 | def init_profile_dir(self): |
|
381 | 380 | """initialize the profile dir""" |
|
382 | 381 | self._in_init_profile_dir = True |
|
383 | 382 | if self.profile_dir is not None: |
|
384 | 383 | # already ran |
|
385 | 384 | return |
|
386 | 385 | if 'ProfileDir.location' not in self.config: |
|
387 | 386 | # location not specified, find by profile name |
|
388 | 387 | try: |
|
389 | 388 | p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config) |
|
390 | 389 | except ProfileDirError: |
|
391 | 390 | # not found, maybe create it (always create default profile) |
|
392 | 391 | if self.auto_create or self.profile == 'default': |
|
393 | 392 | try: |
|
394 | 393 | p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config) |
|
395 | 394 | except ProfileDirError: |
|
396 | 395 | self.log.fatal("Could not create profile: %r"%self.profile) |
|
397 | 396 | self.exit(1) |
|
398 | 397 | else: |
|
399 | 398 | self.log.info("Created profile dir: %r"%p.location) |
|
400 | 399 | else: |
|
401 | 400 | self.log.fatal("Profile %r not found."%self.profile) |
|
402 | 401 | self.exit(1) |
|
403 | 402 | else: |
|
404 |
self.log.debug( |
|
|
403 | self.log.debug("Using existing profile dir: %r", p.location) | |
|
405 | 404 | else: |
|
406 | 405 | location = self.config.ProfileDir.location |
|
407 | 406 | # location is fully specified |
|
408 | 407 | try: |
|
409 | 408 | p = ProfileDir.find_profile_dir(location, self.config) |
|
410 | 409 | except ProfileDirError: |
|
411 | 410 | # not found, maybe create it |
|
412 | 411 | if self.auto_create: |
|
413 | 412 | try: |
|
414 | 413 | p = ProfileDir.create_profile_dir(location, self.config) |
|
415 | 414 | except ProfileDirError: |
|
416 | 415 | self.log.fatal("Could not create profile directory: %r"%location) |
|
417 | 416 | self.exit(1) |
|
418 | 417 | else: |
|
419 | 418 | self.log.debug("Creating new profile dir: %r"%location) |
|
420 | 419 | else: |
|
421 | 420 | self.log.fatal("Profile directory %r not found."%location) |
|
422 | 421 | self.exit(1) |
|
423 | 422 | else: |
|
424 |
self.log.debug( |
|
|
423 | self.log.debug("Using existing profile dir: %r", p.location) | |
|
425 | 424 | # if profile_dir is specified explicitly, set profile name |
|
426 | 425 | dir_name = os.path.basename(p.location) |
|
427 | 426 | if dir_name.startswith('profile_'): |
|
428 | 427 | self.profile = dir_name[8:] |
|
429 | 428 | |
|
430 | 429 | self.profile_dir = p |
|
431 | 430 | self.config_file_paths.append(p.location) |
|
432 | 431 | self._in_init_profile_dir = False |
|
433 | 432 | |
|
434 | 433 | def init_config_files(self): |
|
435 | 434 | """[optionally] copy default config files into profile dir.""" |
|
436 | 435 | self.config_file_paths.extend(ENV_CONFIG_DIRS) |
|
437 | 436 | self.config_file_paths.extend(SYSTEM_CONFIG_DIRS) |
|
438 | 437 | # copy config files |
|
439 | 438 | path = Path(self.builtin_profile_dir) |
|
440 | 439 | if self.copy_config_files: |
|
441 | 440 | src = self.profile |
|
442 | 441 | |
|
443 | 442 | cfg = self.config_file_name |
|
444 | 443 | if path and (path / cfg).exists(): |
|
445 | 444 | self.log.warning( |
|
446 | 445 | "Staging %r from %s into %r [overwrite=%s]" |
|
447 | 446 | % (cfg, src, self.profile_dir.location, self.overwrite) |
|
448 | 447 | ) |
|
449 | 448 | self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite) |
|
450 | 449 | else: |
|
451 | 450 | self.stage_default_config_file() |
|
452 | 451 | else: |
|
453 | 452 | # Still stage *bundled* config files, but not generated ones |
|
454 | 453 | # This is necessary for `ipython profile=sympy` to load the profile |
|
455 | 454 | # on the first go |
|
456 | 455 | files = path.glob("*.py") |
|
457 | 456 | for fullpath in files: |
|
458 | 457 | cfg = fullpath.name |
|
459 | 458 | if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False): |
|
460 | 459 | # file was copied |
|
461 | 460 | self.log.warning("Staging bundled %s from %s into %r"%( |
|
462 | 461 | cfg, self.profile, self.profile_dir.location) |
|
463 | 462 | ) |
|
464 | 463 | |
|
465 | 464 | |
|
466 | 465 | def stage_default_config_file(self): |
|
467 | 466 | """auto generate default config file, and stage it into the profile.""" |
|
468 | 467 | s = self.generate_config_file() |
|
469 | 468 | config_file = Path(self.profile_dir.location) / self.config_file_name |
|
470 | 469 | if self.overwrite or not config_file.exists(): |
|
471 |
self.log.warning("Generating default config file: %r" |
|
|
470 | self.log.warning("Generating default config file: %r", (config_file)) | |
|
472 | 471 | config_file.write_text(s, encoding="utf-8") |
|
473 | 472 | |
|
474 | 473 | @catch_config_error |
|
475 | 474 | def initialize(self, argv=None): |
|
476 | 475 | # don't hook up crash handler before parsing command-line |
|
477 | 476 | self.parse_command_line(argv) |
|
478 | 477 | self.init_crash_handler() |
|
479 | 478 | if self.subapp is not None: |
|
480 | 479 | # stop here if subapp is taking over |
|
481 | 480 | return |
|
482 | 481 | # save a copy of CLI config to re-load after config files |
|
483 | 482 | # so that it has highest priority |
|
484 | 483 | cl_config = deepcopy(self.config) |
|
485 | 484 | self.init_profile_dir() |
|
486 | 485 | self.init_config_files() |
|
487 | 486 | self.load_config_file() |
|
488 | 487 | # enforce cl-opts override configfile opts: |
|
489 | 488 | self.update_config(cl_config) |
@@ -1,325 +1,331 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | """Displayhook for IPython. |
|
3 | 3 | |
|
4 | 4 | This defines a callable class that IPython uses for `sys.displayhook`. |
|
5 | 5 | """ |
|
6 | 6 | |
|
7 | 7 | # Copyright (c) IPython Development Team. |
|
8 | 8 | # Distributed under the terms of the Modified BSD License. |
|
9 | 9 | |
|
10 | 10 | import builtins as builtin_mod |
|
11 | 11 | import sys |
|
12 | 12 | import io as _io |
|
13 | 13 | import tokenize |
|
14 | 14 | |
|
15 | 15 | from traitlets.config.configurable import Configurable |
|
16 | 16 | from traitlets import Instance, Float |
|
17 | 17 | from warnings import warn |
|
18 | 18 | |
|
19 | 19 | # TODO: Move the various attributes (cache_size, [others now moved]). Some |
|
20 | 20 | # of these are also attributes of InteractiveShell. They should be on ONE object |
|
21 | 21 | # only and the other objects should ask that one object for their values. |
|
22 | 22 | |
|
23 | 23 | class DisplayHook(Configurable): |
|
24 | 24 | """The custom IPython displayhook to replace sys.displayhook. |
|
25 | 25 | |
|
26 | 26 | This class does many things, but the basic idea is that it is a callable |
|
27 | 27 | that gets called anytime user code returns a value. |
|
28 | 28 | """ |
|
29 | 29 | |
|
30 | 30 | shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', |
|
31 | 31 | allow_none=True) |
|
32 | 32 | exec_result = Instance('IPython.core.interactiveshell.ExecutionResult', |
|
33 | 33 | allow_none=True) |
|
34 | 34 | cull_fraction = Float(0.2) |
|
35 | 35 | |
|
36 | 36 | def __init__(self, shell=None, cache_size=1000, **kwargs): |
|
37 | 37 | super(DisplayHook, self).__init__(shell=shell, **kwargs) |
|
38 | 38 | cache_size_min = 3 |
|
39 | 39 | if cache_size <= 0: |
|
40 | 40 | self.do_full_cache = 0 |
|
41 | 41 | cache_size = 0 |
|
42 | 42 | elif cache_size < cache_size_min: |
|
43 | 43 | self.do_full_cache = 0 |
|
44 | 44 | cache_size = 0 |
|
45 | 45 | warn('caching was disabled (min value for cache size is %s).' % |
|
46 | 46 | cache_size_min,stacklevel=3) |
|
47 | 47 | else: |
|
48 | 48 | self.do_full_cache = 1 |
|
49 | 49 | |
|
50 | 50 | self.cache_size = cache_size |
|
51 | 51 | |
|
52 | 52 | # we need a reference to the user-level namespace |
|
53 | 53 | self.shell = shell |
|
54 | 54 | |
|
55 | 55 | self._,self.__,self.___ = '','','' |
|
56 | 56 | |
|
57 | 57 | # these are deliberately global: |
|
58 | 58 | to_user_ns = {'_':self._,'__':self.__,'___':self.___} |
|
59 | 59 | self.shell.user_ns.update(to_user_ns) |
|
60 | 60 | |
|
61 | 61 | @property |
|
62 | 62 | def prompt_count(self): |
|
63 | 63 | return self.shell.execution_count |
|
64 | 64 | |
|
65 | 65 | #------------------------------------------------------------------------- |
|
66 | 66 | # Methods used in __call__. Override these methods to modify the behavior |
|
67 | 67 | # of the displayhook. |
|
68 | 68 | #------------------------------------------------------------------------- |
|
69 | 69 | |
|
70 | 70 | def check_for_underscore(self): |
|
71 | 71 | """Check if the user has set the '_' variable by hand.""" |
|
72 | 72 | # If something injected a '_' variable in __builtin__, delete |
|
73 | 73 | # ipython's automatic one so we don't clobber that. gettext() in |
|
74 | 74 | # particular uses _, so we need to stay away from it. |
|
75 | 75 | if '_' in builtin_mod.__dict__: |
|
76 | 76 | try: |
|
77 | 77 | user_value = self.shell.user_ns['_'] |
|
78 | 78 | if user_value is not self._: |
|
79 | 79 | return |
|
80 | 80 | del self.shell.user_ns['_'] |
|
81 | 81 | except KeyError: |
|
82 | 82 | pass |
|
83 | 83 | |
|
84 | 84 | def quiet(self): |
|
85 | 85 | """Should we silence the display hook because of ';'?""" |
|
86 | 86 | # do not print output if input ends in ';' |
|
87 | 87 | |
|
88 | 88 | try: |
|
89 | 89 | cell = self.shell.history_manager.input_hist_parsed[-1] |
|
90 | 90 | except IndexError: |
|
91 | 91 | # some uses of ipshellembed may fail here |
|
92 | 92 | return False |
|
93 | 93 | |
|
94 | sio = _io.StringIO(cell) | |
|
94 | return self.semicolon_at_end_of_expression(cell) | |
|
95 | ||
|
96 | @staticmethod | |
|
97 | def semicolon_at_end_of_expression(expression): | |
|
98 | """Parse Python expression and detects whether last token is ';'""" | |
|
99 | ||
|
100 | sio = _io.StringIO(expression) | |
|
95 | 101 | tokens = list(tokenize.generate_tokens(sio.readline)) |
|
96 | 102 | |
|
97 | 103 | for token in reversed(tokens): |
|
98 | 104 | if token[0] in (tokenize.ENDMARKER, tokenize.NL, tokenize.NEWLINE, tokenize.COMMENT): |
|
99 | 105 | continue |
|
100 | 106 | if (token[0] == tokenize.OP) and (token[1] == ';'): |
|
101 | 107 | return True |
|
102 | 108 | else: |
|
103 | 109 | return False |
|
104 | 110 | |
|
105 | 111 | def start_displayhook(self): |
|
106 | 112 | """Start the displayhook, initializing resources.""" |
|
107 | 113 | pass |
|
108 | 114 | |
|
109 | 115 | def write_output_prompt(self): |
|
110 | 116 | """Write the output prompt. |
|
111 | 117 | |
|
112 | 118 | The default implementation simply writes the prompt to |
|
113 | 119 | ``sys.stdout``. |
|
114 | 120 | """ |
|
115 | 121 | # Use write, not print which adds an extra space. |
|
116 | 122 | sys.stdout.write(self.shell.separate_out) |
|
117 | 123 | outprompt = 'Out[{}]: '.format(self.shell.execution_count) |
|
118 | 124 | if self.do_full_cache: |
|
119 | 125 | sys.stdout.write(outprompt) |
|
120 | 126 | |
|
121 | 127 | def compute_format_data(self, result): |
|
122 | 128 | """Compute format data of the object to be displayed. |
|
123 | 129 | |
|
124 | 130 | The format data is a generalization of the :func:`repr` of an object. |
|
125 | 131 | In the default implementation the format data is a :class:`dict` of |
|
126 | 132 | key value pair where the keys are valid MIME types and the values |
|
127 | 133 | are JSON'able data structure containing the raw data for that MIME |
|
128 | 134 | type. It is up to frontends to determine pick a MIME to to use and |
|
129 | 135 | display that data in an appropriate manner. |
|
130 | 136 | |
|
131 | 137 | This method only computes the format data for the object and should |
|
132 | 138 | NOT actually print or write that to a stream. |
|
133 | 139 | |
|
134 | 140 | Parameters |
|
135 | 141 | ---------- |
|
136 | 142 | result : object |
|
137 | 143 | The Python object passed to the display hook, whose format will be |
|
138 | 144 | computed. |
|
139 | 145 | |
|
140 | 146 | Returns |
|
141 | 147 | ------- |
|
142 | 148 | (format_dict, md_dict) : dict |
|
143 | 149 | format_dict is a :class:`dict` whose keys are valid MIME types and values are |
|
144 | 150 | JSON'able raw data for that MIME type. It is recommended that |
|
145 | 151 | all return values of this should always include the "text/plain" |
|
146 | 152 | MIME type representation of the object. |
|
147 | 153 | md_dict is a :class:`dict` with the same MIME type keys |
|
148 | 154 | of metadata associated with each output. |
|
149 | 155 | |
|
150 | 156 | """ |
|
151 | 157 | return self.shell.display_formatter.format(result) |
|
152 | 158 | |
|
153 | 159 | # This can be set to True by the write_output_prompt method in a subclass |
|
154 | 160 | prompt_end_newline = False |
|
155 | 161 | |
|
156 | 162 | def write_format_data(self, format_dict, md_dict=None) -> None: |
|
157 | 163 | """Write the format data dict to the frontend. |
|
158 | 164 | |
|
159 | 165 | This default version of this method simply writes the plain text |
|
160 | 166 | representation of the object to ``sys.stdout``. Subclasses should |
|
161 | 167 | override this method to send the entire `format_dict` to the |
|
162 | 168 | frontends. |
|
163 | 169 | |
|
164 | 170 | Parameters |
|
165 | 171 | ---------- |
|
166 | 172 | format_dict : dict |
|
167 | 173 | The format dict for the object passed to `sys.displayhook`. |
|
168 | 174 | md_dict : dict (optional) |
|
169 | 175 | The metadata dict to be associated with the display data. |
|
170 | 176 | """ |
|
171 | 177 | if 'text/plain' not in format_dict: |
|
172 | 178 | # nothing to do |
|
173 | 179 | return |
|
174 | 180 | # We want to print because we want to always make sure we have a |
|
175 | 181 | # newline, even if all the prompt separators are ''. This is the |
|
176 | 182 | # standard IPython behavior. |
|
177 | 183 | result_repr = format_dict['text/plain'] |
|
178 | 184 | if '\n' in result_repr: |
|
179 | 185 | # So that multi-line strings line up with the left column of |
|
180 | 186 | # the screen, instead of having the output prompt mess up |
|
181 | 187 | # their first line. |
|
182 | 188 | # We use the prompt template instead of the expanded prompt |
|
183 | 189 | # because the expansion may add ANSI escapes that will interfere |
|
184 | 190 | # with our ability to determine whether or not we should add |
|
185 | 191 | # a newline. |
|
186 | 192 | if not self.prompt_end_newline: |
|
187 | 193 | # But avoid extraneous empty lines. |
|
188 | 194 | result_repr = '\n' + result_repr |
|
189 | 195 | |
|
190 | 196 | try: |
|
191 | 197 | print(result_repr) |
|
192 | 198 | except UnicodeEncodeError: |
|
193 | 199 | # If a character is not supported by the terminal encoding replace |
|
194 | 200 | # it with its \u or \x representation |
|
195 | 201 | print(result_repr.encode(sys.stdout.encoding,'backslashreplace').decode(sys.stdout.encoding)) |
|
196 | 202 | |
|
197 | 203 | def update_user_ns(self, result): |
|
198 | 204 | """Update user_ns with various things like _, __, _1, etc.""" |
|
199 | 205 | |
|
200 | 206 | # Avoid recursive reference when displaying _oh/Out |
|
201 | 207 | if self.cache_size and result is not self.shell.user_ns['_oh']: |
|
202 | 208 | if len(self.shell.user_ns['_oh']) >= self.cache_size and self.do_full_cache: |
|
203 | 209 | self.cull_cache() |
|
204 | 210 | |
|
205 | 211 | # Don't overwrite '_' and friends if '_' is in __builtin__ |
|
206 | 212 | # (otherwise we cause buggy behavior for things like gettext). and |
|
207 | 213 | # do not overwrite _, __ or ___ if one of these has been assigned |
|
208 | 214 | # by the user. |
|
209 | 215 | update_unders = True |
|
210 | 216 | for unders in ['_'*i for i in range(1,4)]: |
|
211 | 217 | if not unders in self.shell.user_ns: |
|
212 | 218 | continue |
|
213 | 219 | if getattr(self, unders) is not self.shell.user_ns.get(unders): |
|
214 | 220 | update_unders = False |
|
215 | 221 | |
|
216 | 222 | self.___ = self.__ |
|
217 | 223 | self.__ = self._ |
|
218 | 224 | self._ = result |
|
219 | 225 | |
|
220 | 226 | if ('_' not in builtin_mod.__dict__) and (update_unders): |
|
221 | 227 | self.shell.push({'_':self._, |
|
222 | 228 | '__':self.__, |
|
223 | 229 | '___':self.___}, interactive=False) |
|
224 | 230 | |
|
225 | 231 | # hackish access to top-level namespace to create _1,_2... dynamically |
|
226 | 232 | to_main = {} |
|
227 | 233 | if self.do_full_cache: |
|
228 | 234 | new_result = '_%s' % self.prompt_count |
|
229 | 235 | to_main[new_result] = result |
|
230 | 236 | self.shell.push(to_main, interactive=False) |
|
231 | 237 | self.shell.user_ns['_oh'][self.prompt_count] = result |
|
232 | 238 | |
|
233 | 239 | def fill_exec_result(self, result): |
|
234 | 240 | if self.exec_result is not None: |
|
235 | 241 | self.exec_result.result = result |
|
236 | 242 | |
|
237 | 243 | def log_output(self, format_dict): |
|
238 | 244 | """Log the output.""" |
|
239 | 245 | if 'text/plain' not in format_dict: |
|
240 | 246 | # nothing to do |
|
241 | 247 | return |
|
242 | 248 | if self.shell.logger.log_output: |
|
243 | 249 | self.shell.logger.log_write(format_dict['text/plain'], 'output') |
|
244 | 250 | self.shell.history_manager.output_hist_reprs[self.prompt_count] = \ |
|
245 | 251 | format_dict['text/plain'] |
|
246 | 252 | |
|
247 | 253 | def finish_displayhook(self): |
|
248 | 254 | """Finish up all displayhook activities.""" |
|
249 | 255 | sys.stdout.write(self.shell.separate_out2) |
|
250 | 256 | sys.stdout.flush() |
|
251 | 257 | |
|
252 | 258 | def __call__(self, result=None): |
|
253 | 259 | """Printing with history cache management. |
|
254 | 260 | |
|
255 | 261 | This is invoked every time the interpreter needs to print, and is |
|
256 | 262 | activated by setting the variable sys.displayhook to it. |
|
257 | 263 | """ |
|
258 | 264 | self.check_for_underscore() |
|
259 | 265 | if result is not None and not self.quiet(): |
|
260 | 266 | self.start_displayhook() |
|
261 | 267 | self.write_output_prompt() |
|
262 | 268 | format_dict, md_dict = self.compute_format_data(result) |
|
263 | 269 | self.update_user_ns(result) |
|
264 | 270 | self.fill_exec_result(result) |
|
265 | 271 | if format_dict: |
|
266 | 272 | self.write_format_data(format_dict, md_dict) |
|
267 | 273 | self.log_output(format_dict) |
|
268 | 274 | self.finish_displayhook() |
|
269 | 275 | |
|
270 | 276 | def cull_cache(self): |
|
271 | 277 | """Output cache is full, cull the oldest entries""" |
|
272 | 278 | oh = self.shell.user_ns.get('_oh', {}) |
|
273 | 279 | sz = len(oh) |
|
274 | 280 | cull_count = max(int(sz * self.cull_fraction), 2) |
|
275 | 281 | warn('Output cache limit (currently {sz} entries) hit.\n' |
|
276 | 282 | 'Flushing oldest {cull_count} entries.'.format(sz=sz, cull_count=cull_count)) |
|
277 | 283 | |
|
278 | 284 | for i, n in enumerate(sorted(oh)): |
|
279 | 285 | if i >= cull_count: |
|
280 | 286 | break |
|
281 | 287 | self.shell.user_ns.pop('_%i' % n, None) |
|
282 | 288 | oh.pop(n, None) |
|
283 | 289 | |
|
284 | 290 | |
|
285 | 291 | def flush(self): |
|
286 | 292 | if not self.do_full_cache: |
|
287 | 293 | raise ValueError("You shouldn't have reached the cache flush " |
|
288 | 294 | "if full caching is not enabled!") |
|
289 | 295 | # delete auto-generated vars from global namespace |
|
290 | 296 | |
|
291 | 297 | for n in range(1,self.prompt_count + 1): |
|
292 | 298 | key = '_'+repr(n) |
|
293 | 299 | try: |
|
294 | 300 | del self.shell.user_ns[key] |
|
295 | 301 | except: pass |
|
296 | 302 | # In some embedded circumstances, the user_ns doesn't have the |
|
297 | 303 | # '_oh' key set up. |
|
298 | 304 | oh = self.shell.user_ns.get('_oh', None) |
|
299 | 305 | if oh is not None: |
|
300 | 306 | oh.clear() |
|
301 | 307 | |
|
302 | 308 | # Release our own references to objects: |
|
303 | 309 | self._, self.__, self.___ = '', '', '' |
|
304 | 310 | |
|
305 | 311 | if '_' not in builtin_mod.__dict__: |
|
306 | 312 | self.shell.user_ns.update({'_':self._,'__':self.__,'___':self.___}) |
|
307 | 313 | import gc |
|
308 | 314 | # TODO: Is this really needed? |
|
309 | 315 | # IronPython blocks here forever |
|
310 | 316 | if sys.platform != "cli": |
|
311 | 317 | gc.collect() |
|
312 | 318 | |
|
313 | 319 | |
|
314 | 320 | class CapturingDisplayHook(object): |
|
315 | 321 | def __init__(self, shell, outputs=None): |
|
316 | 322 | self.shell = shell |
|
317 | 323 | if outputs is None: |
|
318 | 324 | outputs = [] |
|
319 | 325 | self.outputs = outputs |
|
320 | 326 | |
|
321 | 327 | def __call__(self, result=None): |
|
322 | 328 | if result is None: |
|
323 | 329 | return |
|
324 | 330 | format_dict, md_dict = self.shell.display_formatter.format(result) |
|
325 | 331 | self.outputs.append({ 'data': format_dict, 'metadata': md_dict }) |
@@ -1,3844 +1,3861 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | """Main IPython class.""" |
|
3 | 3 | |
|
4 | 4 | #----------------------------------------------------------------------------- |
|
5 | 5 | # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> |
|
6 | 6 | # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu> |
|
7 | 7 | # Copyright (C) 2008-2011 The IPython Development Team |
|
8 | 8 | # |
|
9 | 9 | # Distributed under the terms of the BSD License. The full license is in |
|
10 | 10 | # the file COPYING, distributed as part of this software. |
|
11 | 11 | #----------------------------------------------------------------------------- |
|
12 | 12 | |
|
13 | 13 | |
|
14 | 14 | import abc |
|
15 | 15 | import ast |
|
16 | 16 | import atexit |
|
17 | 17 | import bdb |
|
18 | 18 | import builtins as builtin_mod |
|
19 | 19 | import functools |
|
20 | 20 | import inspect |
|
21 | 21 | import os |
|
22 | 22 | import re |
|
23 | 23 | import runpy |
|
24 | 24 | import subprocess |
|
25 | 25 | import sys |
|
26 | 26 | import tempfile |
|
27 | 27 | import traceback |
|
28 | 28 | import types |
|
29 | 29 | import warnings |
|
30 | 30 | from ast import stmt |
|
31 | 31 | from io import open as io_open |
|
32 | 32 | from logging import error |
|
33 | 33 | from pathlib import Path |
|
34 | 34 | from typing import Callable |
|
35 | 35 | from typing import List as ListType |
|
36 | 36 | from typing import Optional, Tuple |
|
37 | 37 | from warnings import warn |
|
38 | 38 | |
|
39 | 39 | from pickleshare import PickleShareDB |
|
40 | 40 | from tempfile import TemporaryDirectory |
|
41 | 41 | from traitlets import ( |
|
42 | 42 | Any, |
|
43 | 43 | Bool, |
|
44 | 44 | CaselessStrEnum, |
|
45 | 45 | Dict, |
|
46 | 46 | Enum, |
|
47 | 47 | Instance, |
|
48 | 48 | Integer, |
|
49 | 49 | List, |
|
50 | 50 | Type, |
|
51 | 51 | Unicode, |
|
52 | 52 | default, |
|
53 | 53 | observe, |
|
54 | 54 | validate, |
|
55 | 55 | ) |
|
56 | 56 | from traitlets.config.configurable import SingletonConfigurable |
|
57 | 57 | from traitlets.utils.importstring import import_item |
|
58 | 58 | |
|
59 | 59 | import IPython.core.hooks |
|
60 | 60 | from IPython.core import magic, oinspect, page, prefilter, ultratb |
|
61 | 61 | from IPython.core.alias import Alias, AliasManager |
|
62 | 62 | from IPython.core.autocall import ExitAutocall |
|
63 | 63 | from IPython.core.builtin_trap import BuiltinTrap |
|
64 | 64 | from IPython.core.compilerop import CachingCompiler |
|
65 | 65 | from IPython.core.debugger import InterruptiblePdb |
|
66 | 66 | from IPython.core.display_trap import DisplayTrap |
|
67 | 67 | from IPython.core.displayhook import DisplayHook |
|
68 | 68 | from IPython.core.displaypub import DisplayPublisher |
|
69 | 69 | from IPython.core.error import InputRejected, UsageError |
|
70 | 70 | from IPython.core.events import EventManager, available_events |
|
71 | 71 | from IPython.core.extensions import ExtensionManager |
|
72 | 72 | from IPython.core.formatters import DisplayFormatter |
|
73 | 73 | from IPython.core.history import HistoryManager |
|
74 | 74 | from IPython.core.inputtransformer2 import ESC_MAGIC, ESC_MAGIC2 |
|
75 | 75 | from IPython.core.logger import Logger |
|
76 | 76 | from IPython.core.macro import Macro |
|
77 | 77 | from IPython.core.payload import PayloadManager |
|
78 | 78 | from IPython.core.prefilter import PrefilterManager |
|
79 | 79 | from IPython.core.profiledir import ProfileDir |
|
80 | 80 | from IPython.core.usage import default_banner |
|
81 | 81 | from IPython.display import display |
|
82 | 82 | from IPython.paths import get_ipython_dir |
|
83 | 83 | from IPython.testing.skipdoctest import skip_doctest |
|
84 | 84 | from IPython.utils import PyColorize, io, openpy, py3compat |
|
85 | 85 | from IPython.utils.decorators import undoc |
|
86 | 86 | from IPython.utils.io import ask_yes_no |
|
87 | 87 | from IPython.utils.ipstruct import Struct |
|
88 | 88 | from IPython.utils.path import ensure_dir_exists, get_home_dir, get_py_filename |
|
89 | 89 | from IPython.utils.process import getoutput, system |
|
90 | 90 | from IPython.utils.strdispatch import StrDispatch |
|
91 | 91 | from IPython.utils.syspathcontext import prepended_to_syspath |
|
92 | 92 | from IPython.utils.text import DollarFormatter, LSString, SList, format_screen |
|
93 | 93 | |
|
94 | 94 | sphinxify: Optional[Callable] |
|
95 | 95 | |
|
96 | 96 | try: |
|
97 | 97 | import docrepr.sphinxify as sphx |
|
98 | 98 | |
|
99 | 99 | def sphinxify(oinfo): |
|
100 | 100 | wrapped_docstring = sphx.wrap_main_docstring(oinfo) |
|
101 | 101 | |
|
102 | 102 | def sphinxify_docstring(docstring): |
|
103 | 103 | with TemporaryDirectory() as dirname: |
|
104 | 104 | return { |
|
105 | 105 | "text/html": sphx.sphinxify(wrapped_docstring, dirname), |
|
106 | 106 | "text/plain": docstring, |
|
107 | 107 | } |
|
108 | 108 | |
|
109 | 109 | return sphinxify_docstring |
|
110 | 110 | except ImportError: |
|
111 | 111 | sphinxify = None |
|
112 | 112 | |
|
113 | 113 | |
|
114 | 114 | class ProvisionalWarning(DeprecationWarning): |
|
115 | 115 | """ |
|
116 | 116 | Warning class for unstable features |
|
117 | 117 | """ |
|
118 | 118 | pass |
|
119 | 119 | |
|
120 | 120 | from ast import Module |
|
121 | 121 | |
|
122 | 122 | _assign_nodes = (ast.AugAssign, ast.AnnAssign, ast.Assign) |
|
123 | 123 | _single_targets_nodes = (ast.AugAssign, ast.AnnAssign) |
|
124 | 124 | |
|
125 | 125 | #----------------------------------------------------------------------------- |
|
126 | 126 | # Await Helpers |
|
127 | 127 | #----------------------------------------------------------------------------- |
|
128 | 128 | |
|
129 | 129 | # we still need to run things using the asyncio eventloop, but there is no |
|
130 | 130 | # async integration |
|
131 | 131 | from .async_helpers import ( |
|
132 | 132 | _asyncio_runner, |
|
133 | 133 | _curio_runner, |
|
134 | 134 | _pseudo_sync_runner, |
|
135 | 135 | _should_be_async, |
|
136 | 136 | _trio_runner, |
|
137 | 137 | ) |
|
138 | 138 | |
|
139 | 139 | #----------------------------------------------------------------------------- |
|
140 | 140 | # Globals |
|
141 | 141 | #----------------------------------------------------------------------------- |
|
142 | 142 | |
|
143 | 143 | # compiled regexps for autoindent management |
|
144 | 144 | dedent_re = re.compile(r'^\s+raise|^\s+return|^\s+pass') |
|
145 | 145 | |
|
146 | 146 | #----------------------------------------------------------------------------- |
|
147 | 147 | # Utilities |
|
148 | 148 | #----------------------------------------------------------------------------- |
|
149 | 149 | |
|
150 | 150 | |
|
151 | 151 | def is_integer_string(s: str): |
|
152 | 152 | """ |
|
153 | 153 | Variant of "str.isnumeric()" that allow negative values and other ints. |
|
154 | 154 | """ |
|
155 | 155 | try: |
|
156 | 156 | int(s) |
|
157 | 157 | return True |
|
158 | 158 | except ValueError: |
|
159 | 159 | return False |
|
160 | 160 | raise ValueError("Unexpected error") |
|
161 | 161 | |
|
162 | 162 | |
|
163 | 163 | @undoc |
|
164 | 164 | def softspace(file, newvalue): |
|
165 | 165 | """Copied from code.py, to remove the dependency""" |
|
166 | 166 | |
|
167 | 167 | oldvalue = 0 |
|
168 | 168 | try: |
|
169 | 169 | oldvalue = file.softspace |
|
170 | 170 | except AttributeError: |
|
171 | 171 | pass |
|
172 | 172 | try: |
|
173 | 173 | file.softspace = newvalue |
|
174 | 174 | except (AttributeError, TypeError): |
|
175 | 175 | # "attribute-less object" or "read-only attributes" |
|
176 | 176 | pass |
|
177 | 177 | return oldvalue |
|
178 | 178 | |
|
179 | 179 | @undoc |
|
180 | 180 | def no_op(*a, **kw): |
|
181 | 181 | pass |
|
182 | 182 | |
|
183 | 183 | |
|
184 | 184 | class SpaceInInput(Exception): pass |
|
185 | 185 | |
|
186 | 186 | |
|
187 | 187 | class SeparateUnicode(Unicode): |
|
188 | 188 | r"""A Unicode subclass to validate separate_in, separate_out, etc. |
|
189 | 189 | |
|
190 | 190 | This is a Unicode based trait that converts '0'->'' and ``'\\n'->'\n'``. |
|
191 | 191 | """ |
|
192 | 192 | |
|
193 | 193 | def validate(self, obj, value): |
|
194 | 194 | if value == '0': value = '' |
|
195 | 195 | value = value.replace('\\n','\n') |
|
196 | 196 | return super(SeparateUnicode, self).validate(obj, value) |
|
197 | 197 | |
|
198 | 198 | |
|
199 | 199 | @undoc |
|
200 | 200 | class DummyMod(object): |
|
201 | 201 | """A dummy module used for IPython's interactive module when |
|
202 | 202 | a namespace must be assigned to the module's __dict__.""" |
|
203 | 203 | __spec__ = None |
|
204 | 204 | |
|
205 | 205 | |
|
206 | 206 | class ExecutionInfo(object): |
|
207 | 207 | """The arguments used for a call to :meth:`InteractiveShell.run_cell` |
|
208 | 208 | |
|
209 | 209 | Stores information about what is going to happen. |
|
210 | 210 | """ |
|
211 | 211 | raw_cell = None |
|
212 | 212 | store_history = False |
|
213 | 213 | silent = False |
|
214 | 214 | shell_futures = True |
|
215 | 215 | cell_id = None |
|
216 | 216 | |
|
217 | 217 | def __init__(self, raw_cell, store_history, silent, shell_futures, cell_id): |
|
218 | 218 | self.raw_cell = raw_cell |
|
219 | 219 | self.store_history = store_history |
|
220 | 220 | self.silent = silent |
|
221 | 221 | self.shell_futures = shell_futures |
|
222 | 222 | self.cell_id = cell_id |
|
223 | 223 | |
|
224 | 224 | def __repr__(self): |
|
225 | 225 | name = self.__class__.__qualname__ |
|
226 | 226 | raw_cell = ( |
|
227 | 227 | (self.raw_cell[:50] + "..") if len(self.raw_cell) > 50 else self.raw_cell |
|
228 | 228 | ) |
|
229 | 229 | return ( |
|
230 | 230 | '<%s object at %x, raw_cell="%s" store_history=%s silent=%s shell_futures=%s cell_id=%s>' |
|
231 | 231 | % ( |
|
232 | 232 | name, |
|
233 | 233 | id(self), |
|
234 | 234 | raw_cell, |
|
235 | 235 | self.store_history, |
|
236 | 236 | self.silent, |
|
237 | 237 | self.shell_futures, |
|
238 | 238 | self.cell_id, |
|
239 | 239 | ) |
|
240 | 240 | ) |
|
241 | 241 | |
|
242 | 242 | |
|
243 | 243 | class ExecutionResult(object): |
|
244 | 244 | """The result of a call to :meth:`InteractiveShell.run_cell` |
|
245 | 245 | |
|
246 | 246 | Stores information about what took place. |
|
247 | 247 | """ |
|
248 | 248 | execution_count = None |
|
249 | 249 | error_before_exec = None |
|
250 | 250 | error_in_exec: Optional[BaseException] = None |
|
251 | 251 | info = None |
|
252 | 252 | result = None |
|
253 | 253 | |
|
254 | 254 | def __init__(self, info): |
|
255 | 255 | self.info = info |
|
256 | 256 | |
|
257 | 257 | @property |
|
258 | 258 | def success(self): |
|
259 | 259 | return (self.error_before_exec is None) and (self.error_in_exec is None) |
|
260 | 260 | |
|
261 | 261 | def raise_error(self): |
|
262 | 262 | """Reraises error if `success` is `False`, otherwise does nothing""" |
|
263 | 263 | if self.error_before_exec is not None: |
|
264 | 264 | raise self.error_before_exec |
|
265 | 265 | if self.error_in_exec is not None: |
|
266 | 266 | raise self.error_in_exec |
|
267 | 267 | |
|
268 | 268 | def __repr__(self): |
|
269 | 269 | name = self.__class__.__qualname__ |
|
270 | 270 | return '<%s object at %x, execution_count=%s error_before_exec=%s error_in_exec=%s info=%s result=%s>' %\ |
|
271 | 271 | (name, id(self), self.execution_count, self.error_before_exec, self.error_in_exec, repr(self.info), repr(self.result)) |
|
272 | 272 | |
|
273 | 273 | @functools.wraps(io_open) |
|
274 | 274 | def _modified_open(file, *args, **kwargs): |
|
275 | 275 | if file in {0, 1, 2}: |
|
276 | 276 | raise ValueError( |
|
277 | 277 | f"IPython won't let you open fd={file} by default " |
|
278 | 278 | "as it is likely to crash IPython. If you know what you are doing, " |
|
279 | 279 | "you can use builtins' open." |
|
280 | 280 | ) |
|
281 | 281 | |
|
282 | 282 | return io_open(file, *args, **kwargs) |
|
283 | 283 | |
|
284 | 284 | class InteractiveShell(SingletonConfigurable): |
|
285 | 285 | """An enhanced, interactive shell for Python.""" |
|
286 | 286 | |
|
287 | 287 | _instance = None |
|
288 | 288 | |
|
289 | 289 | ast_transformers = List([], help= |
|
290 | 290 | """ |
|
291 | 291 | A list of ast.NodeTransformer subclass instances, which will be applied |
|
292 | 292 | to user input before code is run. |
|
293 | 293 | """ |
|
294 | 294 | ).tag(config=True) |
|
295 | 295 | |
|
296 | 296 | autocall = Enum((0,1,2), default_value=0, help= |
|
297 | 297 | """ |
|
298 | 298 | Make IPython automatically call any callable object even if you didn't |
|
299 | 299 | type explicit parentheses. For example, 'str 43' becomes 'str(43)' |
|
300 | 300 | automatically. The value can be '0' to disable the feature, '1' for |
|
301 | 301 | 'smart' autocall, where it is not applied if there are no more |
|
302 | 302 | arguments on the line, and '2' for 'full' autocall, where all callable |
|
303 | 303 | objects are automatically called (even if no arguments are present). |
|
304 | 304 | """ |
|
305 | 305 | ).tag(config=True) |
|
306 | 306 | |
|
307 | 307 | autoindent = Bool(True, help= |
|
308 | 308 | """ |
|
309 | 309 | Autoindent IPython code entered interactively. |
|
310 | 310 | """ |
|
311 | 311 | ).tag(config=True) |
|
312 | 312 | |
|
313 | 313 | autoawait = Bool(True, help= |
|
314 | 314 | """ |
|
315 | 315 | Automatically run await statement in the top level repl. |
|
316 | 316 | """ |
|
317 | 317 | ).tag(config=True) |
|
318 | 318 | |
|
319 | 319 | loop_runner_map ={ |
|
320 | 320 | 'asyncio':(_asyncio_runner, True), |
|
321 | 321 | 'curio':(_curio_runner, True), |
|
322 | 322 | 'trio':(_trio_runner, True), |
|
323 | 323 | 'sync': (_pseudo_sync_runner, False) |
|
324 | 324 | } |
|
325 | 325 | |
|
326 | 326 | loop_runner = Any(default_value="IPython.core.interactiveshell._asyncio_runner", |
|
327 | 327 | allow_none=True, |
|
328 | 328 | help="""Select the loop runner that will be used to execute top-level asynchronous code""" |
|
329 | 329 | ).tag(config=True) |
|
330 | 330 | |
|
331 | 331 | @default('loop_runner') |
|
332 | 332 | def _default_loop_runner(self): |
|
333 | 333 | return import_item("IPython.core.interactiveshell._asyncio_runner") |
|
334 | 334 | |
|
335 | 335 | @validate('loop_runner') |
|
336 | 336 | def _import_runner(self, proposal): |
|
337 | 337 | if isinstance(proposal.value, str): |
|
338 | 338 | if proposal.value in self.loop_runner_map: |
|
339 | 339 | runner, autoawait = self.loop_runner_map[proposal.value] |
|
340 | 340 | self.autoawait = autoawait |
|
341 | 341 | return runner |
|
342 | 342 | runner = import_item(proposal.value) |
|
343 | 343 | if not callable(runner): |
|
344 | 344 | raise ValueError('loop_runner must be callable') |
|
345 | 345 | return runner |
|
346 | 346 | if not callable(proposal.value): |
|
347 | 347 | raise ValueError('loop_runner must be callable') |
|
348 | 348 | return proposal.value |
|
349 | 349 | |
|
350 | 350 | automagic = Bool(True, help= |
|
351 | 351 | """ |
|
352 | 352 | Enable magic commands to be called without the leading %. |
|
353 | 353 | """ |
|
354 | 354 | ).tag(config=True) |
|
355 | 355 | |
|
356 | 356 | banner1 = Unicode(default_banner, |
|
357 | 357 | help="""The part of the banner to be printed before the profile""" |
|
358 | 358 | ).tag(config=True) |
|
359 | 359 | banner2 = Unicode('', |
|
360 | 360 | help="""The part of the banner to be printed after the profile""" |
|
361 | 361 | ).tag(config=True) |
|
362 | 362 | |
|
363 | 363 | cache_size = Integer(1000, help= |
|
364 | 364 | """ |
|
365 | 365 | Set the size of the output cache. The default is 1000, you can |
|
366 | 366 | change it permanently in your config file. Setting it to 0 completely |
|
367 | 367 | disables the caching system, and the minimum value accepted is 3 (if |
|
368 | 368 | you provide a value less than 3, it is reset to 0 and a warning is |
|
369 | 369 | issued). This limit is defined because otherwise you'll spend more |
|
370 | 370 | time re-flushing a too small cache than working |
|
371 | 371 | """ |
|
372 | 372 | ).tag(config=True) |
|
373 | 373 | color_info = Bool(True, help= |
|
374 | 374 | """ |
|
375 | 375 | Use colors for displaying information about objects. Because this |
|
376 | 376 | information is passed through a pager (like 'less'), and some pagers |
|
377 | 377 | get confused with color codes, this capability can be turned off. |
|
378 | 378 | """ |
|
379 | 379 | ).tag(config=True) |
|
380 | 380 | colors = CaselessStrEnum(('Neutral', 'NoColor','LightBG','Linux'), |
|
381 | 381 | default_value='Neutral', |
|
382 | 382 | help="Set the color scheme (NoColor, Neutral, Linux, or LightBG)." |
|
383 | 383 | ).tag(config=True) |
|
384 | 384 | debug = Bool(False).tag(config=True) |
|
385 | 385 | disable_failing_post_execute = Bool(False, |
|
386 | 386 | help="Don't call post-execute functions that have failed in the past." |
|
387 | 387 | ).tag(config=True) |
|
388 | 388 | display_formatter = Instance(DisplayFormatter, allow_none=True) |
|
389 | 389 | displayhook_class = Type(DisplayHook) |
|
390 | 390 | display_pub_class = Type(DisplayPublisher) |
|
391 | 391 | compiler_class = Type(CachingCompiler) |
|
392 | 392 | inspector_class = Type( |
|
393 | 393 | oinspect.Inspector, help="Class to use to instantiate the shell inspector" |
|
394 | 394 | ).tag(config=True) |
|
395 | 395 | |
|
396 | 396 | sphinxify_docstring = Bool(False, help= |
|
397 | 397 | """ |
|
398 | 398 | Enables rich html representation of docstrings. (This requires the |
|
399 | 399 | docrepr module). |
|
400 | 400 | """).tag(config=True) |
|
401 | 401 | |
|
402 | 402 | @observe("sphinxify_docstring") |
|
403 | 403 | def _sphinxify_docstring_changed(self, change): |
|
404 | 404 | if change['new']: |
|
405 | 405 | warn("`sphinxify_docstring` is provisional since IPython 5.0 and might change in future versions." , ProvisionalWarning) |
|
406 | 406 | |
|
407 | 407 | enable_html_pager = Bool(False, help= |
|
408 | 408 | """ |
|
409 | 409 | (Provisional API) enables html representation in mime bundles sent |
|
410 | 410 | to pagers. |
|
411 | 411 | """).tag(config=True) |
|
412 | 412 | |
|
413 | 413 | @observe("enable_html_pager") |
|
414 | 414 | def _enable_html_pager_changed(self, change): |
|
415 | 415 | if change['new']: |
|
416 | 416 | warn("`enable_html_pager` is provisional since IPython 5.0 and might change in future versions.", ProvisionalWarning) |
|
417 | 417 | |
|
418 | 418 | data_pub_class = None |
|
419 | 419 | |
|
420 | 420 | exit_now = Bool(False) |
|
421 | 421 | exiter = Instance(ExitAutocall) |
|
422 | 422 | @default('exiter') |
|
423 | 423 | def _exiter_default(self): |
|
424 | 424 | return ExitAutocall(self) |
|
425 | 425 | # Monotonically increasing execution counter |
|
426 | 426 | execution_count = Integer(1) |
|
427 | 427 | filename = Unicode("<ipython console>") |
|
428 | 428 | ipython_dir= Unicode('').tag(config=True) # Set to get_ipython_dir() in __init__ |
|
429 | 429 | |
|
430 | 430 | # Used to transform cells before running them, and check whether code is complete |
|
431 | 431 | input_transformer_manager = Instance('IPython.core.inputtransformer2.TransformerManager', |
|
432 | 432 | ()) |
|
433 | 433 | |
|
434 | 434 | @property |
|
435 | 435 | def input_transformers_cleanup(self): |
|
436 | 436 | return self.input_transformer_manager.cleanup_transforms |
|
437 | 437 | |
|
438 | 438 | input_transformers_post = List([], |
|
439 | 439 | help="A list of string input transformers, to be applied after IPython's " |
|
440 | 440 | "own input transformations." |
|
441 | 441 | ) |
|
442 | 442 | |
|
443 | 443 | @property |
|
444 | 444 | def input_splitter(self): |
|
445 | 445 | """Make this available for backward compatibility (pre-7.0 release) with existing code. |
|
446 | 446 | |
|
447 | 447 | For example, ipykernel ipykernel currently uses |
|
448 | 448 | `shell.input_splitter.check_complete` |
|
449 | 449 | """ |
|
450 | 450 | from warnings import warn |
|
451 | 451 | warn("`input_splitter` is deprecated since IPython 7.0, prefer `input_transformer_manager`.", |
|
452 | 452 | DeprecationWarning, stacklevel=2 |
|
453 | 453 | ) |
|
454 | 454 | return self.input_transformer_manager |
|
455 | 455 | |
|
456 | 456 | logstart = Bool(False, help= |
|
457 | 457 | """ |
|
458 | 458 | Start logging to the default log file in overwrite mode. |
|
459 | 459 | Use `logappend` to specify a log file to **append** logs to. |
|
460 | 460 | """ |
|
461 | 461 | ).tag(config=True) |
|
462 | 462 | logfile = Unicode('', help= |
|
463 | 463 | """ |
|
464 | 464 | The name of the logfile to use. |
|
465 | 465 | """ |
|
466 | 466 | ).tag(config=True) |
|
467 | 467 | logappend = Unicode('', help= |
|
468 | 468 | """ |
|
469 | 469 | Start logging to the given file in append mode. |
|
470 | 470 | Use `logfile` to specify a log file to **overwrite** logs to. |
|
471 | 471 | """ |
|
472 | 472 | ).tag(config=True) |
|
473 | 473 | object_info_string_level = Enum((0,1,2), default_value=0, |
|
474 | 474 | ).tag(config=True) |
|
475 | 475 | pdb = Bool(False, help= |
|
476 | 476 | """ |
|
477 | 477 | Automatically call the pdb debugger after every exception. |
|
478 | 478 | """ |
|
479 | 479 | ).tag(config=True) |
|
480 | 480 | display_page = Bool(False, |
|
481 | 481 | help="""If True, anything that would be passed to the pager |
|
482 | 482 | will be displayed as regular output instead.""" |
|
483 | 483 | ).tag(config=True) |
|
484 | 484 | |
|
485 | 485 | |
|
486 | 486 | show_rewritten_input = Bool(True, |
|
487 | 487 | help="Show rewritten input, e.g. for autocall." |
|
488 | 488 | ).tag(config=True) |
|
489 | 489 | |
|
490 | 490 | quiet = Bool(False).tag(config=True) |
|
491 | 491 | |
|
492 | 492 | history_length = Integer(10000, |
|
493 | 493 | help='Total length of command history' |
|
494 | 494 | ).tag(config=True) |
|
495 | 495 | |
|
496 | 496 | history_load_length = Integer(1000, help= |
|
497 | 497 | """ |
|
498 | 498 | The number of saved history entries to be loaded |
|
499 | 499 | into the history buffer at startup. |
|
500 | 500 | """ |
|
501 | 501 | ).tag(config=True) |
|
502 | 502 | |
|
503 | 503 | ast_node_interactivity = Enum(['all', 'last', 'last_expr', 'none', 'last_expr_or_assign'], |
|
504 | 504 | default_value='last_expr', |
|
505 | 505 | help=""" |
|
506 | 506 | 'all', 'last', 'last_expr' or 'none', 'last_expr_or_assign' specifying |
|
507 | 507 | which nodes should be run interactively (displaying output from expressions). |
|
508 | 508 | """ |
|
509 | 509 | ).tag(config=True) |
|
510 | 510 | |
|
511 | 511 | warn_venv = Bool( |
|
512 | 512 | True, |
|
513 | 513 | help="Warn if running in a virtual environment with no IPython installed (so IPython from the global environment is used).", |
|
514 | 514 | ).tag(config=True) |
|
515 | 515 | |
|
516 | 516 | # TODO: this part of prompt management should be moved to the frontends. |
|
517 | 517 | # Use custom TraitTypes that convert '0'->'' and '\\n'->'\n' |
|
518 | 518 | separate_in = SeparateUnicode('\n').tag(config=True) |
|
519 | 519 | separate_out = SeparateUnicode('').tag(config=True) |
|
520 | 520 | separate_out2 = SeparateUnicode('').tag(config=True) |
|
521 | 521 | wildcards_case_sensitive = Bool(True).tag(config=True) |
|
522 | 522 | xmode = CaselessStrEnum(('Context', 'Plain', 'Verbose', 'Minimal'), |
|
523 | 523 | default_value='Context', |
|
524 | 524 | help="Switch modes for the IPython exception handlers." |
|
525 | 525 | ).tag(config=True) |
|
526 | 526 | |
|
527 | 527 | # Subcomponents of InteractiveShell |
|
528 | 528 | alias_manager = Instance('IPython.core.alias.AliasManager', allow_none=True) |
|
529 | 529 | prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager', allow_none=True) |
|
530 | 530 | builtin_trap = Instance('IPython.core.builtin_trap.BuiltinTrap', allow_none=True) |
|
531 | 531 | display_trap = Instance('IPython.core.display_trap.DisplayTrap', allow_none=True) |
|
532 | 532 | extension_manager = Instance('IPython.core.extensions.ExtensionManager', allow_none=True) |
|
533 | 533 | payload_manager = Instance('IPython.core.payload.PayloadManager', allow_none=True) |
|
534 | 534 | history_manager = Instance('IPython.core.history.HistoryAccessorBase', allow_none=True) |
|
535 | 535 | magics_manager = Instance('IPython.core.magic.MagicsManager', allow_none=True) |
|
536 | 536 | |
|
537 | 537 | profile_dir = Instance('IPython.core.application.ProfileDir', allow_none=True) |
|
538 | 538 | @property |
|
539 | 539 | def profile(self): |
|
540 | 540 | if self.profile_dir is not None: |
|
541 | 541 | name = os.path.basename(self.profile_dir.location) |
|
542 | 542 | return name.replace('profile_','') |
|
543 | 543 | |
|
544 | 544 | |
|
545 | 545 | # Private interface |
|
546 | 546 | _post_execute = Dict() |
|
547 | 547 | |
|
548 | 548 | # Tracks any GUI loop loaded for pylab |
|
549 | 549 | pylab_gui_select = None |
|
550 | 550 | |
|
551 | 551 | last_execution_succeeded = Bool(True, help='Did last executed command succeeded') |
|
552 | 552 | |
|
553 | 553 | last_execution_result = Instance('IPython.core.interactiveshell.ExecutionResult', help='Result of executing the last command', allow_none=True) |
|
554 | 554 | |
|
555 | 555 | def __init__(self, ipython_dir=None, profile_dir=None, |
|
556 | 556 | user_module=None, user_ns=None, |
|
557 | 557 | custom_exceptions=((), None), **kwargs): |
|
558 | 558 | # This is where traits with a config_key argument are updated |
|
559 | 559 | # from the values on config. |
|
560 | 560 | super(InteractiveShell, self).__init__(**kwargs) |
|
561 | 561 | if 'PromptManager' in self.config: |
|
562 | 562 | warn('As of IPython 5.0 `PromptManager` config will have no effect' |
|
563 | 563 | ' and has been replaced by TerminalInteractiveShell.prompts_class') |
|
564 | 564 | self.configurables = [self] |
|
565 | 565 | |
|
566 | 566 | # These are relatively independent and stateless |
|
567 | 567 | self.init_ipython_dir(ipython_dir) |
|
568 | 568 | self.init_profile_dir(profile_dir) |
|
569 | 569 | self.init_instance_attrs() |
|
570 | 570 | self.init_environment() |
|
571 | 571 | |
|
572 | 572 | # Check if we're in a virtualenv, and set up sys.path. |
|
573 | 573 | self.init_virtualenv() |
|
574 | 574 | |
|
575 | 575 | # Create namespaces (user_ns, user_global_ns, etc.) |
|
576 | 576 | self.init_create_namespaces(user_module, user_ns) |
|
577 | 577 | # This has to be done after init_create_namespaces because it uses |
|
578 | 578 | # something in self.user_ns, but before init_sys_modules, which |
|
579 | 579 | # is the first thing to modify sys. |
|
580 | 580 | # TODO: When we override sys.stdout and sys.stderr before this class |
|
581 | 581 | # is created, we are saving the overridden ones here. Not sure if this |
|
582 | 582 | # is what we want to do. |
|
583 | 583 | self.save_sys_module_state() |
|
584 | 584 | self.init_sys_modules() |
|
585 | 585 | |
|
586 | 586 | # While we're trying to have each part of the code directly access what |
|
587 | 587 | # it needs without keeping redundant references to objects, we have too |
|
588 | 588 | # much legacy code that expects ip.db to exist. |
|
589 | 589 | self.db = PickleShareDB(os.path.join(self.profile_dir.location, 'db')) |
|
590 | 590 | |
|
591 | 591 | self.init_history() |
|
592 | 592 | self.init_encoding() |
|
593 | 593 | self.init_prefilter() |
|
594 | 594 | |
|
595 | 595 | self.init_syntax_highlighting() |
|
596 | 596 | self.init_hooks() |
|
597 | 597 | self.init_events() |
|
598 | 598 | self.init_pushd_popd_magic() |
|
599 | 599 | self.init_user_ns() |
|
600 | 600 | self.init_logger() |
|
601 | 601 | self.init_builtins() |
|
602 | 602 | |
|
603 | 603 | # The following was in post_config_initialization |
|
604 | 604 | self.init_inspector() |
|
605 | 605 | self.raw_input_original = input |
|
606 | 606 | self.init_completer() |
|
607 | 607 | # TODO: init_io() needs to happen before init_traceback handlers |
|
608 | 608 | # because the traceback handlers hardcode the stdout/stderr streams. |
|
609 | 609 | # This logic in in debugger.Pdb and should eventually be changed. |
|
610 | 610 | self.init_io() |
|
611 | 611 | self.init_traceback_handlers(custom_exceptions) |
|
612 | 612 | self.init_prompts() |
|
613 | 613 | self.init_display_formatter() |
|
614 | 614 | self.init_display_pub() |
|
615 | 615 | self.init_data_pub() |
|
616 | 616 | self.init_displayhook() |
|
617 | 617 | self.init_magics() |
|
618 | 618 | self.init_alias() |
|
619 | 619 | self.init_logstart() |
|
620 | 620 | self.init_pdb() |
|
621 | 621 | self.init_extension_manager() |
|
622 | 622 | self.init_payload() |
|
623 | 623 | self.events.trigger('shell_initialized', self) |
|
624 | 624 | atexit.register(self.atexit_operations) |
|
625 | 625 | |
|
626 | 626 | # The trio runner is used for running Trio in the foreground thread. It |
|
627 | 627 | # is different from `_trio_runner(async_fn)` in `async_helpers.py` |
|
628 | 628 | # which calls `trio.run()` for every cell. This runner runs all cells |
|
629 | 629 | # inside a single Trio event loop. If used, it is set from |
|
630 | 630 | # `ipykernel.kernelapp`. |
|
631 | 631 | self.trio_runner = None |
|
632 | 632 | |
|
633 | 633 | def get_ipython(self): |
|
634 | 634 | """Return the currently running IPython instance.""" |
|
635 | 635 | return self |
|
636 | 636 | |
|
637 | 637 | #------------------------------------------------------------------------- |
|
638 | 638 | # Trait changed handlers |
|
639 | 639 | #------------------------------------------------------------------------- |
|
640 | 640 | @observe('ipython_dir') |
|
641 | 641 | def _ipython_dir_changed(self, change): |
|
642 | 642 | ensure_dir_exists(change['new']) |
|
643 | 643 | |
|
644 | 644 | def set_autoindent(self,value=None): |
|
645 | 645 | """Set the autoindent flag. |
|
646 | 646 | |
|
647 | 647 | If called with no arguments, it acts as a toggle.""" |
|
648 | 648 | if value is None: |
|
649 | 649 | self.autoindent = not self.autoindent |
|
650 | 650 | else: |
|
651 | 651 | self.autoindent = value |
|
652 | 652 | |
|
653 | 653 | def set_trio_runner(self, tr): |
|
654 | 654 | self.trio_runner = tr |
|
655 | 655 | |
|
656 | 656 | #------------------------------------------------------------------------- |
|
657 | 657 | # init_* methods called by __init__ |
|
658 | 658 | #------------------------------------------------------------------------- |
|
659 | 659 | |
|
660 | 660 | def init_ipython_dir(self, ipython_dir): |
|
661 | 661 | if ipython_dir is not None: |
|
662 | 662 | self.ipython_dir = ipython_dir |
|
663 | 663 | return |
|
664 | 664 | |
|
665 | 665 | self.ipython_dir = get_ipython_dir() |
|
666 | 666 | |
|
667 | 667 | def init_profile_dir(self, profile_dir): |
|
668 | 668 | if profile_dir is not None: |
|
669 | 669 | self.profile_dir = profile_dir |
|
670 | 670 | return |
|
671 | 671 | self.profile_dir = ProfileDir.create_profile_dir_by_name( |
|
672 | 672 | self.ipython_dir, "default" |
|
673 | 673 | ) |
|
674 | 674 | |
|
675 | 675 | def init_instance_attrs(self): |
|
676 | 676 | self.more = False |
|
677 | 677 | |
|
678 | 678 | # command compiler |
|
679 | 679 | self.compile = self.compiler_class() |
|
680 | 680 | |
|
681 | 681 | # Make an empty namespace, which extension writers can rely on both |
|
682 | 682 | # existing and NEVER being used by ipython itself. This gives them a |
|
683 | 683 | # convenient location for storing additional information and state |
|
684 | 684 | # their extensions may require, without fear of collisions with other |
|
685 | 685 | # ipython names that may develop later. |
|
686 | 686 | self.meta = Struct() |
|
687 | 687 | |
|
688 | 688 | # Temporary files used for various purposes. Deleted at exit. |
|
689 | 689 | # The files here are stored with Path from Pathlib |
|
690 | 690 | self.tempfiles = [] |
|
691 | 691 | self.tempdirs = [] |
|
692 | 692 | |
|
693 | 693 | # keep track of where we started running (mainly for crash post-mortem) |
|
694 | 694 | # This is not being used anywhere currently. |
|
695 | 695 | self.starting_dir = os.getcwd() |
|
696 | 696 | |
|
697 | 697 | # Indentation management |
|
698 | 698 | self.indent_current_nsp = 0 |
|
699 | 699 | |
|
700 | 700 | # Dict to track post-execution functions that have been registered |
|
701 | 701 | self._post_execute = {} |
|
702 | 702 | |
|
703 | 703 | def init_environment(self): |
|
704 | 704 | """Any changes we need to make to the user's environment.""" |
|
705 | 705 | pass |
|
706 | 706 | |
|
707 | 707 | def init_encoding(self): |
|
708 | 708 | # Get system encoding at startup time. Certain terminals (like Emacs |
|
709 | 709 | # under Win32 have it set to None, and we need to have a known valid |
|
710 | 710 | # encoding to use in the raw_input() method |
|
711 | 711 | try: |
|
712 | 712 | self.stdin_encoding = sys.stdin.encoding or 'ascii' |
|
713 | 713 | except AttributeError: |
|
714 | 714 | self.stdin_encoding = 'ascii' |
|
715 | 715 | |
|
716 | 716 | |
|
717 | 717 | @observe('colors') |
|
718 | 718 | def init_syntax_highlighting(self, changes=None): |
|
719 | 719 | # Python source parser/formatter for syntax highlighting |
|
720 | 720 | pyformat = PyColorize.Parser(style=self.colors, parent=self).format |
|
721 | 721 | self.pycolorize = lambda src: pyformat(src,'str') |
|
722 | 722 | |
|
723 | 723 | def refresh_style(self): |
|
724 | 724 | # No-op here, used in subclass |
|
725 | 725 | pass |
|
726 | 726 | |
|
727 | 727 | def init_pushd_popd_magic(self): |
|
728 | 728 | # for pushd/popd management |
|
729 | 729 | self.home_dir = get_home_dir() |
|
730 | 730 | |
|
731 | 731 | self.dir_stack = [] |
|
732 | 732 | |
|
733 | 733 | def init_logger(self): |
|
734 | 734 | self.logger = Logger(self.home_dir, logfname='ipython_log.py', |
|
735 | 735 | logmode='rotate') |
|
736 | 736 | |
|
737 | 737 | def init_logstart(self): |
|
738 | 738 | """Initialize logging in case it was requested at the command line. |
|
739 | 739 | """ |
|
740 | 740 | if self.logappend: |
|
741 | 741 | self.magic('logstart %s append' % self.logappend) |
|
742 | 742 | elif self.logfile: |
|
743 | 743 | self.magic('logstart %s' % self.logfile) |
|
744 | 744 | elif self.logstart: |
|
745 | 745 | self.magic('logstart') |
|
746 | 746 | |
|
747 | 747 | |
|
748 | 748 | def init_builtins(self): |
|
749 | 749 | # A single, static flag that we set to True. Its presence indicates |
|
750 | 750 | # that an IPython shell has been created, and we make no attempts at |
|
751 | 751 | # removing on exit or representing the existence of more than one |
|
752 | 752 | # IPython at a time. |
|
753 | 753 | builtin_mod.__dict__['__IPYTHON__'] = True |
|
754 | 754 | builtin_mod.__dict__['display'] = display |
|
755 | 755 | |
|
756 | 756 | self.builtin_trap = BuiltinTrap(shell=self) |
|
757 | 757 | |
|
758 | 758 | @observe('colors') |
|
759 | 759 | def init_inspector(self, changes=None): |
|
760 | 760 | # Object inspector |
|
761 | 761 | self.inspector = self.inspector_class( |
|
762 | 762 | oinspect.InspectColors, |
|
763 | 763 | PyColorize.ANSICodeColors, |
|
764 | 764 | self.colors, |
|
765 | 765 | self.object_info_string_level, |
|
766 | 766 | ) |
|
767 | 767 | |
|
768 | 768 | def init_io(self): |
|
769 | 769 | # implemented in subclasses, TerminalInteractiveShell does call |
|
770 | 770 | # colorama.init(). |
|
771 | 771 | pass |
|
772 | 772 | |
|
773 | 773 | def init_prompts(self): |
|
774 | 774 | # Set system prompts, so that scripts can decide if they are running |
|
775 | 775 | # interactively. |
|
776 | 776 | sys.ps1 = 'In : ' |
|
777 | 777 | sys.ps2 = '...: ' |
|
778 | 778 | sys.ps3 = 'Out: ' |
|
779 | 779 | |
|
780 | 780 | def init_display_formatter(self): |
|
781 | 781 | self.display_formatter = DisplayFormatter(parent=self) |
|
782 | 782 | self.configurables.append(self.display_formatter) |
|
783 | 783 | |
|
784 | 784 | def init_display_pub(self): |
|
785 | 785 | self.display_pub = self.display_pub_class(parent=self, shell=self) |
|
786 | 786 | self.configurables.append(self.display_pub) |
|
787 | 787 | |
|
788 | 788 | def init_data_pub(self): |
|
789 | 789 | if not self.data_pub_class: |
|
790 | 790 | self.data_pub = None |
|
791 | 791 | return |
|
792 | 792 | self.data_pub = self.data_pub_class(parent=self) |
|
793 | 793 | self.configurables.append(self.data_pub) |
|
794 | 794 | |
|
795 | 795 | def init_displayhook(self): |
|
796 | 796 | # Initialize displayhook, set in/out prompts and printing system |
|
797 | 797 | self.displayhook = self.displayhook_class( |
|
798 | 798 | parent=self, |
|
799 | 799 | shell=self, |
|
800 | 800 | cache_size=self.cache_size, |
|
801 | 801 | ) |
|
802 | 802 | self.configurables.append(self.displayhook) |
|
803 | 803 | # This is a context manager that installs/revmoes the displayhook at |
|
804 | 804 | # the appropriate time. |
|
805 | 805 | self.display_trap = DisplayTrap(hook=self.displayhook) |
|
806 | 806 | |
|
807 | 807 | @staticmethod |
|
808 | 808 | def get_path_links(p: Path): |
|
809 | 809 | """Gets path links including all symlinks |
|
810 | 810 | |
|
811 | 811 | Examples |
|
812 | 812 | -------- |
|
813 | 813 | In [1]: from IPython.core.interactiveshell import InteractiveShell |
|
814 | 814 | |
|
815 | 815 | In [2]: import sys, pathlib |
|
816 | 816 | |
|
817 | 817 | In [3]: paths = InteractiveShell.get_path_links(pathlib.Path(sys.executable)) |
|
818 | 818 | |
|
819 | 819 | In [4]: len(paths) == len(set(paths)) |
|
820 | 820 | Out[4]: True |
|
821 | 821 | |
|
822 | 822 | In [5]: bool(paths) |
|
823 | 823 | Out[5]: True |
|
824 | 824 | """ |
|
825 | 825 | paths = [p] |
|
826 | 826 | while p.is_symlink(): |
|
827 | 827 | new_path = Path(os.readlink(p)) |
|
828 | 828 | if not new_path.is_absolute(): |
|
829 | 829 | new_path = p.parent / new_path |
|
830 | 830 | p = new_path |
|
831 | 831 | paths.append(p) |
|
832 | 832 | return paths |
|
833 | 833 | |
|
834 | 834 | def init_virtualenv(self): |
|
835 | 835 | """Add the current virtualenv to sys.path so the user can import modules from it. |
|
836 | 836 | This isn't perfect: it doesn't use the Python interpreter with which the |
|
837 | 837 | virtualenv was built, and it ignores the --no-site-packages option. A |
|
838 | 838 | warning will appear suggesting the user installs IPython in the |
|
839 | 839 | virtualenv, but for many cases, it probably works well enough. |
|
840 | 840 | |
|
841 | 841 | Adapted from code snippets online. |
|
842 | 842 | |
|
843 | 843 | http://blog.ufsoft.org/2009/1/29/ipython-and-virtualenv |
|
844 | 844 | """ |
|
845 | 845 | if 'VIRTUAL_ENV' not in os.environ: |
|
846 | 846 | # Not in a virtualenv |
|
847 | 847 | return |
|
848 | 848 | elif os.environ["VIRTUAL_ENV"] == "": |
|
849 | 849 | warn("Virtual env path set to '', please check if this is intended.") |
|
850 | 850 | return |
|
851 | 851 | |
|
852 | 852 | p = Path(sys.executable) |
|
853 | 853 | p_venv = Path(os.environ["VIRTUAL_ENV"]) |
|
854 | 854 | |
|
855 | 855 | # fallback venv detection: |
|
856 | 856 | # stdlib venv may symlink sys.executable, so we can't use realpath. |
|
857 | 857 | # but others can symlink *to* the venv Python, so we can't just use sys.executable. |
|
858 | 858 | # So we just check every item in the symlink tree (generally <= 3) |
|
859 | 859 | paths = self.get_path_links(p) |
|
860 | 860 | |
|
861 | 861 | # In Cygwin paths like "c:\..." and '\cygdrive\c\...' are possible |
|
862 | 862 | if p_venv.parts[1] == "cygdrive": |
|
863 | 863 | drive_name = p_venv.parts[2] |
|
864 | 864 | p_venv = (drive_name + ":/") / Path(*p_venv.parts[3:]) |
|
865 | 865 | |
|
866 | 866 | if any(p_venv == p.parents[1] for p in paths): |
|
867 | 867 | # Our exe is inside or has access to the virtualenv, don't need to do anything. |
|
868 | 868 | return |
|
869 | 869 | |
|
870 | 870 | if sys.platform == "win32": |
|
871 | 871 | virtual_env = str(Path(os.environ["VIRTUAL_ENV"], "Lib", "site-packages")) |
|
872 | 872 | else: |
|
873 | 873 | virtual_env_path = Path( |
|
874 | 874 | os.environ["VIRTUAL_ENV"], "lib", "python{}.{}", "site-packages" |
|
875 | 875 | ) |
|
876 | 876 | p_ver = sys.version_info[:2] |
|
877 | 877 | |
|
878 | 878 | # Predict version from py[thon]-x.x in the $VIRTUAL_ENV |
|
879 | 879 | re_m = re.search(r"\bpy(?:thon)?([23])\.(\d+)\b", os.environ["VIRTUAL_ENV"]) |
|
880 | 880 | if re_m: |
|
881 | 881 | predicted_path = Path(str(virtual_env_path).format(*re_m.groups())) |
|
882 | 882 | if predicted_path.exists(): |
|
883 | 883 | p_ver = re_m.groups() |
|
884 | 884 | |
|
885 | 885 | virtual_env = str(virtual_env_path).format(*p_ver) |
|
886 | 886 | if self.warn_venv: |
|
887 | 887 | warn( |
|
888 | 888 | "Attempting to work in a virtualenv. If you encounter problems, " |
|
889 | 889 | "please install IPython inside the virtualenv." |
|
890 | 890 | ) |
|
891 | 891 | import site |
|
892 | 892 | sys.path.insert(0, virtual_env) |
|
893 | 893 | site.addsitedir(virtual_env) |
|
894 | 894 | |
|
895 | 895 | #------------------------------------------------------------------------- |
|
896 | 896 | # Things related to injections into the sys module |
|
897 | 897 | #------------------------------------------------------------------------- |
|
898 | 898 | |
|
899 | 899 | def save_sys_module_state(self): |
|
900 | 900 | """Save the state of hooks in the sys module. |
|
901 | 901 | |
|
902 | 902 | This has to be called after self.user_module is created. |
|
903 | 903 | """ |
|
904 | 904 | self._orig_sys_module_state = {'stdin': sys.stdin, |
|
905 | 905 | 'stdout': sys.stdout, |
|
906 | 906 | 'stderr': sys.stderr, |
|
907 | 907 | 'excepthook': sys.excepthook} |
|
908 | 908 | self._orig_sys_modules_main_name = self.user_module.__name__ |
|
909 | 909 | self._orig_sys_modules_main_mod = sys.modules.get(self.user_module.__name__) |
|
910 | 910 | |
|
911 | 911 | def restore_sys_module_state(self): |
|
912 | 912 | """Restore the state of the sys module.""" |
|
913 | 913 | try: |
|
914 | 914 | for k, v in self._orig_sys_module_state.items(): |
|
915 | 915 | setattr(sys, k, v) |
|
916 | 916 | except AttributeError: |
|
917 | 917 | pass |
|
918 | 918 | # Reset what what done in self.init_sys_modules |
|
919 | 919 | if self._orig_sys_modules_main_mod is not None: |
|
920 | 920 | sys.modules[self._orig_sys_modules_main_name] = self._orig_sys_modules_main_mod |
|
921 | 921 | |
|
922 | 922 | #------------------------------------------------------------------------- |
|
923 | 923 | # Things related to the banner |
|
924 | 924 | #------------------------------------------------------------------------- |
|
925 | 925 | |
|
926 | 926 | @property |
|
927 | 927 | def banner(self): |
|
928 | 928 | banner = self.banner1 |
|
929 | 929 | if self.profile and self.profile != 'default': |
|
930 | 930 | banner += '\nIPython profile: %s\n' % self.profile |
|
931 | 931 | if self.banner2: |
|
932 | 932 | banner += '\n' + self.banner2 |
|
933 | 933 | return banner |
|
934 | 934 | |
|
935 | 935 | def show_banner(self, banner=None): |
|
936 | 936 | if banner is None: |
|
937 | 937 | banner = self.banner |
|
938 | 938 | sys.stdout.write(banner) |
|
939 | 939 | |
|
940 | 940 | #------------------------------------------------------------------------- |
|
941 | 941 | # Things related to hooks |
|
942 | 942 | #------------------------------------------------------------------------- |
|
943 | 943 | |
|
944 | 944 | def init_hooks(self): |
|
945 | 945 | # hooks holds pointers used for user-side customizations |
|
946 | 946 | self.hooks = Struct() |
|
947 | 947 | |
|
948 | 948 | self.strdispatchers = {} |
|
949 | 949 | |
|
950 | 950 | # Set all default hooks, defined in the IPython.hooks module. |
|
951 | 951 | hooks = IPython.core.hooks |
|
952 | 952 | for hook_name in hooks.__all__: |
|
953 | 953 | # default hooks have priority 100, i.e. low; user hooks should have |
|
954 | 954 | # 0-100 priority |
|
955 | 955 | self.set_hook(hook_name, getattr(hooks, hook_name), 100) |
|
956 | 956 | |
|
957 | 957 | if self.display_page: |
|
958 | 958 | self.set_hook('show_in_pager', page.as_hook(page.display_page), 90) |
|
959 | 959 | |
|
960 | 960 | def set_hook(self, name, hook, priority=50, str_key=None, re_key=None): |
|
961 | 961 | """set_hook(name,hook) -> sets an internal IPython hook. |
|
962 | 962 | |
|
963 | 963 | IPython exposes some of its internal API as user-modifiable hooks. By |
|
964 | 964 | adding your function to one of these hooks, you can modify IPython's |
|
965 | 965 | behavior to call at runtime your own routines.""" |
|
966 | 966 | |
|
967 | 967 | # At some point in the future, this should validate the hook before it |
|
968 | 968 | # accepts it. Probably at least check that the hook takes the number |
|
969 | 969 | # of args it's supposed to. |
|
970 | 970 | |
|
971 | 971 | f = types.MethodType(hook,self) |
|
972 | 972 | |
|
973 | 973 | # check if the hook is for strdispatcher first |
|
974 | 974 | if str_key is not None: |
|
975 | 975 | sdp = self.strdispatchers.get(name, StrDispatch()) |
|
976 | 976 | sdp.add_s(str_key, f, priority ) |
|
977 | 977 | self.strdispatchers[name] = sdp |
|
978 | 978 | return |
|
979 | 979 | if re_key is not None: |
|
980 | 980 | sdp = self.strdispatchers.get(name, StrDispatch()) |
|
981 | 981 | sdp.add_re(re.compile(re_key), f, priority ) |
|
982 | 982 | self.strdispatchers[name] = sdp |
|
983 | 983 | return |
|
984 | 984 | |
|
985 | 985 | dp = getattr(self.hooks, name, None) |
|
986 | 986 | if name not in IPython.core.hooks.__all__: |
|
987 | 987 | print("Warning! Hook '%s' is not one of %s" % \ |
|
988 | 988 | (name, IPython.core.hooks.__all__ )) |
|
989 | 989 | |
|
990 | 990 | if name in IPython.core.hooks.deprecated: |
|
991 | 991 | alternative = IPython.core.hooks.deprecated[name] |
|
992 | 992 | raise ValueError( |
|
993 | 993 | "Hook {} has been deprecated since IPython 5.0. Use {} instead.".format( |
|
994 | 994 | name, alternative |
|
995 | 995 | ) |
|
996 | 996 | ) |
|
997 | 997 | |
|
998 | 998 | if not dp: |
|
999 | 999 | dp = IPython.core.hooks.CommandChainDispatcher() |
|
1000 | 1000 | |
|
1001 | 1001 | try: |
|
1002 | 1002 | dp.add(f,priority) |
|
1003 | 1003 | except AttributeError: |
|
1004 | 1004 | # it was not commandchain, plain old func - replace |
|
1005 | 1005 | dp = f |
|
1006 | 1006 | |
|
1007 | 1007 | setattr(self.hooks,name, dp) |
|
1008 | 1008 | |
|
1009 | 1009 | #------------------------------------------------------------------------- |
|
1010 | 1010 | # Things related to events |
|
1011 | 1011 | #------------------------------------------------------------------------- |
|
1012 | 1012 | |
|
1013 | 1013 | def init_events(self): |
|
1014 | 1014 | self.events = EventManager(self, available_events) |
|
1015 | 1015 | |
|
1016 | 1016 | self.events.register("pre_execute", self._clear_warning_registry) |
|
1017 | 1017 | |
|
1018 | 1018 | def register_post_execute(self, func): |
|
1019 | 1019 | """DEPRECATED: Use ip.events.register('post_run_cell', func) |
|
1020 | 1020 | |
|
1021 | 1021 | Register a function for calling after code execution. |
|
1022 | 1022 | """ |
|
1023 | 1023 | raise ValueError( |
|
1024 | 1024 | "ip.register_post_execute is deprecated since IPython 1.0, use " |
|
1025 | 1025 | "ip.events.register('post_run_cell', func) instead." |
|
1026 | 1026 | ) |
|
1027 | 1027 | |
|
1028 | 1028 | def _clear_warning_registry(self): |
|
1029 | 1029 | # clear the warning registry, so that different code blocks with |
|
1030 | 1030 | # overlapping line number ranges don't cause spurious suppression of |
|
1031 | 1031 | # warnings (see gh-6611 for details) |
|
1032 | 1032 | if "__warningregistry__" in self.user_global_ns: |
|
1033 | 1033 | del self.user_global_ns["__warningregistry__"] |
|
1034 | 1034 | |
|
1035 | 1035 | #------------------------------------------------------------------------- |
|
1036 | 1036 | # Things related to the "main" module |
|
1037 | 1037 | #------------------------------------------------------------------------- |
|
1038 | 1038 | |
|
1039 | 1039 | def new_main_mod(self, filename, modname): |
|
1040 | 1040 | """Return a new 'main' module object for user code execution. |
|
1041 | 1041 | |
|
1042 | 1042 | ``filename`` should be the path of the script which will be run in the |
|
1043 | 1043 | module. Requests with the same filename will get the same module, with |
|
1044 | 1044 | its namespace cleared. |
|
1045 | 1045 | |
|
1046 | 1046 | ``modname`` should be the module name - normally either '__main__' or |
|
1047 | 1047 | the basename of the file without the extension. |
|
1048 | 1048 | |
|
1049 | 1049 | When scripts are executed via %run, we must keep a reference to their |
|
1050 | 1050 | __main__ module around so that Python doesn't |
|
1051 | 1051 | clear it, rendering references to module globals useless. |
|
1052 | 1052 | |
|
1053 | 1053 | This method keeps said reference in a private dict, keyed by the |
|
1054 | 1054 | absolute path of the script. This way, for multiple executions of the |
|
1055 | 1055 | same script we only keep one copy of the namespace (the last one), |
|
1056 | 1056 | thus preventing memory leaks from old references while allowing the |
|
1057 | 1057 | objects from the last execution to be accessible. |
|
1058 | 1058 | """ |
|
1059 | 1059 | filename = os.path.abspath(filename) |
|
1060 | 1060 | try: |
|
1061 | 1061 | main_mod = self._main_mod_cache[filename] |
|
1062 | 1062 | except KeyError: |
|
1063 | 1063 | main_mod = self._main_mod_cache[filename] = types.ModuleType( |
|
1064 | 1064 | modname, |
|
1065 | 1065 | doc="Module created for script run in IPython") |
|
1066 | 1066 | else: |
|
1067 | 1067 | main_mod.__dict__.clear() |
|
1068 | 1068 | main_mod.__name__ = modname |
|
1069 | 1069 | |
|
1070 | 1070 | main_mod.__file__ = filename |
|
1071 | 1071 | # It seems pydoc (and perhaps others) needs any module instance to |
|
1072 | 1072 | # implement a __nonzero__ method |
|
1073 | 1073 | main_mod.__nonzero__ = lambda : True |
|
1074 | 1074 | |
|
1075 | 1075 | return main_mod |
|
1076 | 1076 | |
|
1077 | 1077 | def clear_main_mod_cache(self): |
|
1078 | 1078 | """Clear the cache of main modules. |
|
1079 | 1079 | |
|
1080 | 1080 | Mainly for use by utilities like %reset. |
|
1081 | 1081 | |
|
1082 | 1082 | Examples |
|
1083 | 1083 | -------- |
|
1084 | 1084 | In [15]: import IPython |
|
1085 | 1085 | |
|
1086 | 1086 | In [16]: m = _ip.new_main_mod(IPython.__file__, 'IPython') |
|
1087 | 1087 | |
|
1088 | 1088 | In [17]: len(_ip._main_mod_cache) > 0 |
|
1089 | 1089 | Out[17]: True |
|
1090 | 1090 | |
|
1091 | 1091 | In [18]: _ip.clear_main_mod_cache() |
|
1092 | 1092 | |
|
1093 | 1093 | In [19]: len(_ip._main_mod_cache) == 0 |
|
1094 | 1094 | Out[19]: True |
|
1095 | 1095 | """ |
|
1096 | 1096 | self._main_mod_cache.clear() |
|
1097 | 1097 | |
|
1098 | 1098 | #------------------------------------------------------------------------- |
|
1099 | 1099 | # Things related to debugging |
|
1100 | 1100 | #------------------------------------------------------------------------- |
|
1101 | 1101 | |
|
1102 | 1102 | def init_pdb(self): |
|
1103 | 1103 | # Set calling of pdb on exceptions |
|
1104 | 1104 | # self.call_pdb is a property |
|
1105 | 1105 | self.call_pdb = self.pdb |
|
1106 | 1106 | |
|
1107 | 1107 | def _get_call_pdb(self): |
|
1108 | 1108 | return self._call_pdb |
|
1109 | 1109 | |
|
1110 | 1110 | def _set_call_pdb(self,val): |
|
1111 | 1111 | |
|
1112 | 1112 | if val not in (0,1,False,True): |
|
1113 | 1113 | raise ValueError('new call_pdb value must be boolean') |
|
1114 | 1114 | |
|
1115 | 1115 | # store value in instance |
|
1116 | 1116 | self._call_pdb = val |
|
1117 | 1117 | |
|
1118 | 1118 | # notify the actual exception handlers |
|
1119 | 1119 | self.InteractiveTB.call_pdb = val |
|
1120 | 1120 | |
|
1121 | 1121 | call_pdb = property(_get_call_pdb,_set_call_pdb,None, |
|
1122 | 1122 | 'Control auto-activation of pdb at exceptions') |
|
1123 | 1123 | |
|
1124 | 1124 | def debugger(self,force=False): |
|
1125 | 1125 | """Call the pdb debugger. |
|
1126 | 1126 | |
|
1127 | 1127 | Keywords: |
|
1128 | 1128 | |
|
1129 | 1129 | - force(False): by default, this routine checks the instance call_pdb |
|
1130 | 1130 | flag and does not actually invoke the debugger if the flag is false. |
|
1131 | 1131 | The 'force' option forces the debugger to activate even if the flag |
|
1132 | 1132 | is false. |
|
1133 | 1133 | """ |
|
1134 | 1134 | |
|
1135 | 1135 | if not (force or self.call_pdb): |
|
1136 | 1136 | return |
|
1137 | 1137 | |
|
1138 | 1138 | if not hasattr(sys,'last_traceback'): |
|
1139 | 1139 | error('No traceback has been produced, nothing to debug.') |
|
1140 | 1140 | return |
|
1141 | 1141 | |
|
1142 | 1142 | self.InteractiveTB.debugger(force=True) |
|
1143 | 1143 | |
|
1144 | 1144 | #------------------------------------------------------------------------- |
|
1145 | 1145 | # Things related to IPython's various namespaces |
|
1146 | 1146 | #------------------------------------------------------------------------- |
|
1147 | 1147 | default_user_namespaces = True |
|
1148 | 1148 | |
|
1149 | 1149 | def init_create_namespaces(self, user_module=None, user_ns=None): |
|
1150 | 1150 | # Create the namespace where the user will operate. user_ns is |
|
1151 | 1151 | # normally the only one used, and it is passed to the exec calls as |
|
1152 | 1152 | # the locals argument. But we do carry a user_global_ns namespace |
|
1153 | 1153 | # given as the exec 'globals' argument, This is useful in embedding |
|
1154 | 1154 | # situations where the ipython shell opens in a context where the |
|
1155 | 1155 | # distinction between locals and globals is meaningful. For |
|
1156 | 1156 | # non-embedded contexts, it is just the same object as the user_ns dict. |
|
1157 | 1157 | |
|
1158 | 1158 | # FIXME. For some strange reason, __builtins__ is showing up at user |
|
1159 | 1159 | # level as a dict instead of a module. This is a manual fix, but I |
|
1160 | 1160 | # should really track down where the problem is coming from. Alex |
|
1161 | 1161 | # Schmolck reported this problem first. |
|
1162 | 1162 | |
|
1163 | 1163 | # A useful post by Alex Martelli on this topic: |
|
1164 | 1164 | # Re: inconsistent value from __builtins__ |
|
1165 | 1165 | # Von: Alex Martelli <aleaxit@yahoo.com> |
|
1166 | 1166 | # Datum: Freitag 01 Oktober 2004 04:45:34 nachmittags/abends |
|
1167 | 1167 | # Gruppen: comp.lang.python |
|
1168 | 1168 | |
|
1169 | 1169 | # Michael Hohn <hohn@hooknose.lbl.gov> wrote: |
|
1170 | 1170 | # > >>> print type(builtin_check.get_global_binding('__builtins__')) |
|
1171 | 1171 | # > <type 'dict'> |
|
1172 | 1172 | # > >>> print type(__builtins__) |
|
1173 | 1173 | # > <type 'module'> |
|
1174 | 1174 | # > Is this difference in return value intentional? |
|
1175 | 1175 | |
|
1176 | 1176 | # Well, it's documented that '__builtins__' can be either a dictionary |
|
1177 | 1177 | # or a module, and it's been that way for a long time. Whether it's |
|
1178 | 1178 | # intentional (or sensible), I don't know. In any case, the idea is |
|
1179 | 1179 | # that if you need to access the built-in namespace directly, you |
|
1180 | 1180 | # should start with "import __builtin__" (note, no 's') which will |
|
1181 | 1181 | # definitely give you a module. Yeah, it's somewhat confusing:-(. |
|
1182 | 1182 | |
|
1183 | 1183 | # These routines return a properly built module and dict as needed by |
|
1184 | 1184 | # the rest of the code, and can also be used by extension writers to |
|
1185 | 1185 | # generate properly initialized namespaces. |
|
1186 | 1186 | if (user_ns is not None) or (user_module is not None): |
|
1187 | 1187 | self.default_user_namespaces = False |
|
1188 | 1188 | self.user_module, self.user_ns = self.prepare_user_module(user_module, user_ns) |
|
1189 | 1189 | |
|
1190 | 1190 | # A record of hidden variables we have added to the user namespace, so |
|
1191 | 1191 | # we can list later only variables defined in actual interactive use. |
|
1192 | 1192 | self.user_ns_hidden = {} |
|
1193 | 1193 | |
|
1194 | 1194 | # Now that FakeModule produces a real module, we've run into a nasty |
|
1195 | 1195 | # problem: after script execution (via %run), the module where the user |
|
1196 | 1196 | # code ran is deleted. Now that this object is a true module (needed |
|
1197 | 1197 | # so doctest and other tools work correctly), the Python module |
|
1198 | 1198 | # teardown mechanism runs over it, and sets to None every variable |
|
1199 | 1199 | # present in that module. Top-level references to objects from the |
|
1200 | 1200 | # script survive, because the user_ns is updated with them. However, |
|
1201 | 1201 | # calling functions defined in the script that use other things from |
|
1202 | 1202 | # the script will fail, because the function's closure had references |
|
1203 | 1203 | # to the original objects, which are now all None. So we must protect |
|
1204 | 1204 | # these modules from deletion by keeping a cache. |
|
1205 | 1205 | # |
|
1206 | 1206 | # To avoid keeping stale modules around (we only need the one from the |
|
1207 | 1207 | # last run), we use a dict keyed with the full path to the script, so |
|
1208 | 1208 | # only the last version of the module is held in the cache. Note, |
|
1209 | 1209 | # however, that we must cache the module *namespace contents* (their |
|
1210 | 1210 | # __dict__). Because if we try to cache the actual modules, old ones |
|
1211 | 1211 | # (uncached) could be destroyed while still holding references (such as |
|
1212 | 1212 | # those held by GUI objects that tend to be long-lived)> |
|
1213 | 1213 | # |
|
1214 | 1214 | # The %reset command will flush this cache. See the cache_main_mod() |
|
1215 | 1215 | # and clear_main_mod_cache() methods for details on use. |
|
1216 | 1216 | |
|
1217 | 1217 | # This is the cache used for 'main' namespaces |
|
1218 | 1218 | self._main_mod_cache = {} |
|
1219 | 1219 | |
|
1220 | 1220 | # A table holding all the namespaces IPython deals with, so that |
|
1221 | 1221 | # introspection facilities can search easily. |
|
1222 | 1222 | self.ns_table = {'user_global':self.user_module.__dict__, |
|
1223 | 1223 | 'user_local':self.user_ns, |
|
1224 | 1224 | 'builtin':builtin_mod.__dict__ |
|
1225 | 1225 | } |
|
1226 | 1226 | |
|
1227 | 1227 | @property |
|
1228 | 1228 | def user_global_ns(self): |
|
1229 | 1229 | return self.user_module.__dict__ |
|
1230 | 1230 | |
|
1231 | 1231 | def prepare_user_module(self, user_module=None, user_ns=None): |
|
1232 | 1232 | """Prepare the module and namespace in which user code will be run. |
|
1233 | 1233 | |
|
1234 | 1234 | When IPython is started normally, both parameters are None: a new module |
|
1235 | 1235 | is created automatically, and its __dict__ used as the namespace. |
|
1236 | 1236 | |
|
1237 | 1237 | If only user_module is provided, its __dict__ is used as the namespace. |
|
1238 | 1238 | If only user_ns is provided, a dummy module is created, and user_ns |
|
1239 | 1239 | becomes the global namespace. If both are provided (as they may be |
|
1240 | 1240 | when embedding), user_ns is the local namespace, and user_module |
|
1241 | 1241 | provides the global namespace. |
|
1242 | 1242 | |
|
1243 | 1243 | Parameters |
|
1244 | 1244 | ---------- |
|
1245 | 1245 | user_module : module, optional |
|
1246 | 1246 | The current user module in which IPython is being run. If None, |
|
1247 | 1247 | a clean module will be created. |
|
1248 | 1248 | user_ns : dict, optional |
|
1249 | 1249 | A namespace in which to run interactive commands. |
|
1250 | 1250 | |
|
1251 | 1251 | Returns |
|
1252 | 1252 | ------- |
|
1253 | 1253 | A tuple of user_module and user_ns, each properly initialised. |
|
1254 | 1254 | """ |
|
1255 | 1255 | if user_module is None and user_ns is not None: |
|
1256 | 1256 | user_ns.setdefault("__name__", "__main__") |
|
1257 | 1257 | user_module = DummyMod() |
|
1258 | 1258 | user_module.__dict__ = user_ns |
|
1259 | 1259 | |
|
1260 | 1260 | if user_module is None: |
|
1261 | 1261 | user_module = types.ModuleType("__main__", |
|
1262 | 1262 | doc="Automatically created module for IPython interactive environment") |
|
1263 | 1263 | |
|
1264 | 1264 | # We must ensure that __builtin__ (without the final 's') is always |
|
1265 | 1265 | # available and pointing to the __builtin__ *module*. For more details: |
|
1266 | 1266 | # http://mail.python.org/pipermail/python-dev/2001-April/014068.html |
|
1267 | 1267 | user_module.__dict__.setdefault('__builtin__', builtin_mod) |
|
1268 | 1268 | user_module.__dict__.setdefault('__builtins__', builtin_mod) |
|
1269 | 1269 | |
|
1270 | 1270 | if user_ns is None: |
|
1271 | 1271 | user_ns = user_module.__dict__ |
|
1272 | 1272 | |
|
1273 | 1273 | return user_module, user_ns |
|
1274 | 1274 | |
|
1275 | 1275 | def init_sys_modules(self): |
|
1276 | 1276 | # We need to insert into sys.modules something that looks like a |
|
1277 | 1277 | # module but which accesses the IPython namespace, for shelve and |
|
1278 | 1278 | # pickle to work interactively. Normally they rely on getting |
|
1279 | 1279 | # everything out of __main__, but for embedding purposes each IPython |
|
1280 | 1280 | # instance has its own private namespace, so we can't go shoving |
|
1281 | 1281 | # everything into __main__. |
|
1282 | 1282 | |
|
1283 | 1283 | # note, however, that we should only do this for non-embedded |
|
1284 | 1284 | # ipythons, which really mimic the __main__.__dict__ with their own |
|
1285 | 1285 | # namespace. Embedded instances, on the other hand, should not do |
|
1286 | 1286 | # this because they need to manage the user local/global namespaces |
|
1287 | 1287 | # only, but they live within a 'normal' __main__ (meaning, they |
|
1288 | 1288 | # shouldn't overtake the execution environment of the script they're |
|
1289 | 1289 | # embedded in). |
|
1290 | 1290 | |
|
1291 | 1291 | # This is overridden in the InteractiveShellEmbed subclass to a no-op. |
|
1292 | 1292 | main_name = self.user_module.__name__ |
|
1293 | 1293 | sys.modules[main_name] = self.user_module |
|
1294 | 1294 | |
|
1295 | 1295 | def init_user_ns(self): |
|
1296 | 1296 | """Initialize all user-visible namespaces to their minimum defaults. |
|
1297 | 1297 | |
|
1298 | 1298 | Certain history lists are also initialized here, as they effectively |
|
1299 | 1299 | act as user namespaces. |
|
1300 | 1300 | |
|
1301 | 1301 | Notes |
|
1302 | 1302 | ----- |
|
1303 | 1303 | All data structures here are only filled in, they are NOT reset by this |
|
1304 | 1304 | method. If they were not empty before, data will simply be added to |
|
1305 | 1305 | them. |
|
1306 | 1306 | """ |
|
1307 | 1307 | # This function works in two parts: first we put a few things in |
|
1308 | 1308 | # user_ns, and we sync that contents into user_ns_hidden so that these |
|
1309 | 1309 | # initial variables aren't shown by %who. After the sync, we add the |
|
1310 | 1310 | # rest of what we *do* want the user to see with %who even on a new |
|
1311 | 1311 | # session (probably nothing, so they really only see their own stuff) |
|
1312 | 1312 | |
|
1313 | 1313 | # The user dict must *always* have a __builtin__ reference to the |
|
1314 | 1314 | # Python standard __builtin__ namespace, which must be imported. |
|
1315 | 1315 | # This is so that certain operations in prompt evaluation can be |
|
1316 | 1316 | # reliably executed with builtins. Note that we can NOT use |
|
1317 | 1317 | # __builtins__ (note the 's'), because that can either be a dict or a |
|
1318 | 1318 | # module, and can even mutate at runtime, depending on the context |
|
1319 | 1319 | # (Python makes no guarantees on it). In contrast, __builtin__ is |
|
1320 | 1320 | # always a module object, though it must be explicitly imported. |
|
1321 | 1321 | |
|
1322 | 1322 | # For more details: |
|
1323 | 1323 | # http://mail.python.org/pipermail/python-dev/2001-April/014068.html |
|
1324 | 1324 | ns = {} |
|
1325 | 1325 | |
|
1326 | 1326 | # make global variables for user access to the histories |
|
1327 | 1327 | ns['_ih'] = self.history_manager.input_hist_parsed |
|
1328 | 1328 | ns['_oh'] = self.history_manager.output_hist |
|
1329 | 1329 | ns['_dh'] = self.history_manager.dir_hist |
|
1330 | 1330 | |
|
1331 | 1331 | # user aliases to input and output histories. These shouldn't show up |
|
1332 | 1332 | # in %who, as they can have very large reprs. |
|
1333 | 1333 | ns['In'] = self.history_manager.input_hist_parsed |
|
1334 | 1334 | ns['Out'] = self.history_manager.output_hist |
|
1335 | 1335 | |
|
1336 | 1336 | # Store myself as the public api!!! |
|
1337 | 1337 | ns['get_ipython'] = self.get_ipython |
|
1338 | 1338 | |
|
1339 | 1339 | ns['exit'] = self.exiter |
|
1340 | 1340 | ns['quit'] = self.exiter |
|
1341 | 1341 | ns["open"] = _modified_open |
|
1342 | 1342 | |
|
1343 | 1343 | # Sync what we've added so far to user_ns_hidden so these aren't seen |
|
1344 | 1344 | # by %who |
|
1345 | 1345 | self.user_ns_hidden.update(ns) |
|
1346 | 1346 | |
|
1347 | 1347 | # Anything put into ns now would show up in %who. Think twice before |
|
1348 | 1348 | # putting anything here, as we really want %who to show the user their |
|
1349 | 1349 | # stuff, not our variables. |
|
1350 | 1350 | |
|
1351 | 1351 | # Finally, update the real user's namespace |
|
1352 | 1352 | self.user_ns.update(ns) |
|
1353 | 1353 | |
|
1354 | 1354 | @property |
|
1355 | 1355 | def all_ns_refs(self): |
|
1356 | 1356 | """Get a list of references to all the namespace dictionaries in which |
|
1357 | 1357 | IPython might store a user-created object. |
|
1358 | 1358 | |
|
1359 | 1359 | Note that this does not include the displayhook, which also caches |
|
1360 | 1360 | objects from the output.""" |
|
1361 | 1361 | return [self.user_ns, self.user_global_ns, self.user_ns_hidden] + \ |
|
1362 | 1362 | [m.__dict__ for m in self._main_mod_cache.values()] |
|
1363 | 1363 | |
|
1364 | 1364 | def reset(self, new_session=True, aggressive=False): |
|
1365 | 1365 | """Clear all internal namespaces, and attempt to release references to |
|
1366 | 1366 | user objects. |
|
1367 | 1367 | |
|
1368 | 1368 | If new_session is True, a new history session will be opened. |
|
1369 | 1369 | """ |
|
1370 | 1370 | # Clear histories |
|
1371 | 1371 | self.history_manager.reset(new_session) |
|
1372 | 1372 | # Reset counter used to index all histories |
|
1373 | 1373 | if new_session: |
|
1374 | 1374 | self.execution_count = 1 |
|
1375 | 1375 | |
|
1376 | 1376 | # Reset last execution result |
|
1377 | 1377 | self.last_execution_succeeded = True |
|
1378 | 1378 | self.last_execution_result = None |
|
1379 | 1379 | |
|
1380 | 1380 | # Flush cached output items |
|
1381 | 1381 | if self.displayhook.do_full_cache: |
|
1382 | 1382 | self.displayhook.flush() |
|
1383 | 1383 | |
|
1384 | 1384 | # The main execution namespaces must be cleared very carefully, |
|
1385 | 1385 | # skipping the deletion of the builtin-related keys, because doing so |
|
1386 | 1386 | # would cause errors in many object's __del__ methods. |
|
1387 | 1387 | if self.user_ns is not self.user_global_ns: |
|
1388 | 1388 | self.user_ns.clear() |
|
1389 | 1389 | ns = self.user_global_ns |
|
1390 | 1390 | drop_keys = set(ns.keys()) |
|
1391 | 1391 | drop_keys.discard('__builtin__') |
|
1392 | 1392 | drop_keys.discard('__builtins__') |
|
1393 | 1393 | drop_keys.discard('__name__') |
|
1394 | 1394 | for k in drop_keys: |
|
1395 | 1395 | del ns[k] |
|
1396 | 1396 | |
|
1397 | 1397 | self.user_ns_hidden.clear() |
|
1398 | 1398 | |
|
1399 | 1399 | # Restore the user namespaces to minimal usability |
|
1400 | 1400 | self.init_user_ns() |
|
1401 | 1401 | if aggressive and not hasattr(self, "_sys_modules_keys"): |
|
1402 | 1402 | print("Cannot restore sys.module, no snapshot") |
|
1403 | 1403 | elif aggressive: |
|
1404 | 1404 | print("culling sys module...") |
|
1405 | 1405 | current_keys = set(sys.modules.keys()) |
|
1406 | 1406 | for k in current_keys - self._sys_modules_keys: |
|
1407 | 1407 | if k.startswith("multiprocessing"): |
|
1408 | 1408 | continue |
|
1409 | 1409 | del sys.modules[k] |
|
1410 | 1410 | |
|
1411 | 1411 | # Restore the default and user aliases |
|
1412 | 1412 | self.alias_manager.clear_aliases() |
|
1413 | 1413 | self.alias_manager.init_aliases() |
|
1414 | 1414 | |
|
1415 | 1415 | # Now define aliases that only make sense on the terminal, because they |
|
1416 | 1416 | # need direct access to the console in a way that we can't emulate in |
|
1417 | 1417 | # GUI or web frontend |
|
1418 | 1418 | if os.name == 'posix': |
|
1419 | 1419 | for cmd in ('clear', 'more', 'less', 'man'): |
|
1420 | 1420 | if cmd not in self.magics_manager.magics['line']: |
|
1421 | 1421 | self.alias_manager.soft_define_alias(cmd, cmd) |
|
1422 | 1422 | |
|
1423 | 1423 | # Flush the private list of module references kept for script |
|
1424 | 1424 | # execution protection |
|
1425 | 1425 | self.clear_main_mod_cache() |
|
1426 | 1426 | |
|
1427 | 1427 | def del_var(self, varname, by_name=False): |
|
1428 | 1428 | """Delete a variable from the various namespaces, so that, as |
|
1429 | 1429 | far as possible, we're not keeping any hidden references to it. |
|
1430 | 1430 | |
|
1431 | 1431 | Parameters |
|
1432 | 1432 | ---------- |
|
1433 | 1433 | varname : str |
|
1434 | 1434 | The name of the variable to delete. |
|
1435 | 1435 | by_name : bool |
|
1436 | 1436 | If True, delete variables with the given name in each |
|
1437 | 1437 | namespace. If False (default), find the variable in the user |
|
1438 | 1438 | namespace, and delete references to it. |
|
1439 | 1439 | """ |
|
1440 | 1440 | if varname in ('__builtin__', '__builtins__'): |
|
1441 | 1441 | raise ValueError("Refusing to delete %s" % varname) |
|
1442 | 1442 | |
|
1443 | 1443 | ns_refs = self.all_ns_refs |
|
1444 | 1444 | |
|
1445 | 1445 | if by_name: # Delete by name |
|
1446 | 1446 | for ns in ns_refs: |
|
1447 | 1447 | try: |
|
1448 | 1448 | del ns[varname] |
|
1449 | 1449 | except KeyError: |
|
1450 | 1450 | pass |
|
1451 | 1451 | else: # Delete by object |
|
1452 | 1452 | try: |
|
1453 | 1453 | obj = self.user_ns[varname] |
|
1454 | 1454 | except KeyError as e: |
|
1455 | 1455 | raise NameError("name '%s' is not defined" % varname) from e |
|
1456 | 1456 | # Also check in output history |
|
1457 | 1457 | ns_refs.append(self.history_manager.output_hist) |
|
1458 | 1458 | for ns in ns_refs: |
|
1459 | 1459 | to_delete = [n for n, o in ns.items() if o is obj] |
|
1460 | 1460 | for name in to_delete: |
|
1461 | 1461 | del ns[name] |
|
1462 | 1462 | |
|
1463 | 1463 | # Ensure it is removed from the last execution result |
|
1464 | 1464 | if self.last_execution_result.result is obj: |
|
1465 | 1465 | self.last_execution_result = None |
|
1466 | 1466 | |
|
1467 | 1467 | # displayhook keeps extra references, but not in a dictionary |
|
1468 | 1468 | for name in ('_', '__', '___'): |
|
1469 | 1469 | if getattr(self.displayhook, name) is obj: |
|
1470 | 1470 | setattr(self.displayhook, name, None) |
|
1471 | 1471 | |
|
1472 | 1472 | def reset_selective(self, regex=None): |
|
1473 | 1473 | """Clear selective variables from internal namespaces based on a |
|
1474 | 1474 | specified regular expression. |
|
1475 | 1475 | |
|
1476 | 1476 | Parameters |
|
1477 | 1477 | ---------- |
|
1478 | 1478 | regex : string or compiled pattern, optional |
|
1479 | 1479 | A regular expression pattern that will be used in searching |
|
1480 | 1480 | variable names in the users namespaces. |
|
1481 | 1481 | """ |
|
1482 | 1482 | if regex is not None: |
|
1483 | 1483 | try: |
|
1484 | 1484 | m = re.compile(regex) |
|
1485 | 1485 | except TypeError as e: |
|
1486 | 1486 | raise TypeError('regex must be a string or compiled pattern') from e |
|
1487 | 1487 | # Search for keys in each namespace that match the given regex |
|
1488 | 1488 | # If a match is found, delete the key/value pair. |
|
1489 | 1489 | for ns in self.all_ns_refs: |
|
1490 | 1490 | for var in ns: |
|
1491 | 1491 | if m.search(var): |
|
1492 | 1492 | del ns[var] |
|
1493 | 1493 | |
|
1494 | 1494 | def push(self, variables, interactive=True): |
|
1495 | 1495 | """Inject a group of variables into the IPython user namespace. |
|
1496 | 1496 | |
|
1497 | 1497 | Parameters |
|
1498 | 1498 | ---------- |
|
1499 | 1499 | variables : dict, str or list/tuple of str |
|
1500 | 1500 | The variables to inject into the user's namespace. If a dict, a |
|
1501 | 1501 | simple update is done. If a str, the string is assumed to have |
|
1502 | 1502 | variable names separated by spaces. A list/tuple of str can also |
|
1503 | 1503 | be used to give the variable names. If just the variable names are |
|
1504 | 1504 | give (list/tuple/str) then the variable values looked up in the |
|
1505 | 1505 | callers frame. |
|
1506 | 1506 | interactive : bool |
|
1507 | 1507 | If True (default), the variables will be listed with the ``who`` |
|
1508 | 1508 | magic. |
|
1509 | 1509 | """ |
|
1510 | 1510 | vdict = None |
|
1511 | 1511 | |
|
1512 | 1512 | # We need a dict of name/value pairs to do namespace updates. |
|
1513 | 1513 | if isinstance(variables, dict): |
|
1514 | 1514 | vdict = variables |
|
1515 | 1515 | elif isinstance(variables, (str, list, tuple)): |
|
1516 | 1516 | if isinstance(variables, str): |
|
1517 | 1517 | vlist = variables.split() |
|
1518 | 1518 | else: |
|
1519 | 1519 | vlist = variables |
|
1520 | 1520 | vdict = {} |
|
1521 | 1521 | cf = sys._getframe(1) |
|
1522 | 1522 | for name in vlist: |
|
1523 | 1523 | try: |
|
1524 | 1524 | vdict[name] = eval(name, cf.f_globals, cf.f_locals) |
|
1525 | 1525 | except: |
|
1526 | 1526 | print('Could not get variable %s from %s' % |
|
1527 | 1527 | (name,cf.f_code.co_name)) |
|
1528 | 1528 | else: |
|
1529 | 1529 | raise ValueError('variables must be a dict/str/list/tuple') |
|
1530 | 1530 | |
|
1531 | 1531 | # Propagate variables to user namespace |
|
1532 | 1532 | self.user_ns.update(vdict) |
|
1533 | 1533 | |
|
1534 | 1534 | # And configure interactive visibility |
|
1535 | 1535 | user_ns_hidden = self.user_ns_hidden |
|
1536 | 1536 | if interactive: |
|
1537 | 1537 | for name in vdict: |
|
1538 | 1538 | user_ns_hidden.pop(name, None) |
|
1539 | 1539 | else: |
|
1540 | 1540 | user_ns_hidden.update(vdict) |
|
1541 | 1541 | |
|
1542 | 1542 | def drop_by_id(self, variables): |
|
1543 | 1543 | """Remove a dict of variables from the user namespace, if they are the |
|
1544 | 1544 | same as the values in the dictionary. |
|
1545 | 1545 | |
|
1546 | 1546 | This is intended for use by extensions: variables that they've added can |
|
1547 | 1547 | be taken back out if they are unloaded, without removing any that the |
|
1548 | 1548 | user has overwritten. |
|
1549 | 1549 | |
|
1550 | 1550 | Parameters |
|
1551 | 1551 | ---------- |
|
1552 | 1552 | variables : dict |
|
1553 | 1553 | A dictionary mapping object names (as strings) to the objects. |
|
1554 | 1554 | """ |
|
1555 | 1555 | for name, obj in variables.items(): |
|
1556 | 1556 | if name in self.user_ns and self.user_ns[name] is obj: |
|
1557 | 1557 | del self.user_ns[name] |
|
1558 | 1558 | self.user_ns_hidden.pop(name, None) |
|
1559 | 1559 | |
|
1560 | 1560 | #------------------------------------------------------------------------- |
|
1561 | 1561 | # Things related to object introspection |
|
1562 | 1562 | #------------------------------------------------------------------------- |
|
1563 | 1563 | |
|
1564 | 1564 | def _ofind(self, oname, namespaces=None): |
|
1565 | 1565 | """Find an object in the available namespaces. |
|
1566 | 1566 | |
|
1567 | 1567 | self._ofind(oname) -> dict with keys: found,obj,ospace,ismagic |
|
1568 | 1568 | |
|
1569 | 1569 | Has special code to detect magic functions. |
|
1570 | 1570 | """ |
|
1571 | 1571 | oname = oname.strip() |
|
1572 | 1572 | raw_parts = oname.split(".") |
|
1573 | 1573 | parts = [] |
|
1574 | 1574 | parts_ok = True |
|
1575 | 1575 | for p in raw_parts: |
|
1576 | 1576 | if p.endswith("]"): |
|
1577 | 1577 | var, *indices = p.split("[") |
|
1578 | 1578 | if not var.isidentifier(): |
|
1579 | 1579 | parts_ok = False |
|
1580 | 1580 | break |
|
1581 | 1581 | parts.append(var) |
|
1582 | 1582 | for ind in indices: |
|
1583 | 1583 | if ind[-1] != "]" and not is_integer_string(ind[:-1]): |
|
1584 | 1584 | parts_ok = False |
|
1585 | 1585 | break |
|
1586 | 1586 | parts.append(ind[:-1]) |
|
1587 | 1587 | continue |
|
1588 | 1588 | |
|
1589 | 1589 | if not p.isidentifier(): |
|
1590 | 1590 | parts_ok = False |
|
1591 | 1591 | parts.append(p) |
|
1592 | 1592 | |
|
1593 | 1593 | if ( |
|
1594 | 1594 | not oname.startswith(ESC_MAGIC) |
|
1595 | 1595 | and not oname.startswith(ESC_MAGIC2) |
|
1596 | 1596 | and not parts_ok |
|
1597 | 1597 | ): |
|
1598 | 1598 | return {"found": False} |
|
1599 | 1599 | |
|
1600 | 1600 | if namespaces is None: |
|
1601 | 1601 | # Namespaces to search in: |
|
1602 | 1602 | # Put them in a list. The order is important so that we |
|
1603 | 1603 | # find things in the same order that Python finds them. |
|
1604 | 1604 | namespaces = [ ('Interactive', self.user_ns), |
|
1605 | 1605 | ('Interactive (global)', self.user_global_ns), |
|
1606 | 1606 | ('Python builtin', builtin_mod.__dict__), |
|
1607 | 1607 | ] |
|
1608 | 1608 | |
|
1609 | 1609 | ismagic = False |
|
1610 | 1610 | isalias = False |
|
1611 | 1611 | found = False |
|
1612 | 1612 | ospace = None |
|
1613 | 1613 | parent = None |
|
1614 | 1614 | obj = None |
|
1615 | 1615 | |
|
1616 | 1616 | |
|
1617 | 1617 | # Look for the given name by splitting it in parts. If the head is |
|
1618 | 1618 | # found, then we look for all the remaining parts as members, and only |
|
1619 | 1619 | # declare success if we can find them all. |
|
1620 | 1620 | oname_parts = parts |
|
1621 | 1621 | oname_head, oname_rest = oname_parts[0],oname_parts[1:] |
|
1622 | 1622 | for nsname,ns in namespaces: |
|
1623 | 1623 | try: |
|
1624 | 1624 | obj = ns[oname_head] |
|
1625 | 1625 | except KeyError: |
|
1626 | 1626 | continue |
|
1627 | 1627 | else: |
|
1628 | 1628 | for idx, part in enumerate(oname_rest): |
|
1629 | 1629 | try: |
|
1630 | 1630 | parent = obj |
|
1631 | 1631 | # The last part is looked up in a special way to avoid |
|
1632 | 1632 | # descriptor invocation as it may raise or have side |
|
1633 | 1633 | # effects. |
|
1634 | 1634 | if idx == len(oname_rest) - 1: |
|
1635 | 1635 | obj = self._getattr_property(obj, part) |
|
1636 | 1636 | else: |
|
1637 | 1637 | if is_integer_string(part): |
|
1638 | 1638 | obj = obj[int(part)] |
|
1639 | 1639 | else: |
|
1640 | 1640 | obj = getattr(obj, part) |
|
1641 | 1641 | except: |
|
1642 | 1642 | # Blanket except b/c some badly implemented objects |
|
1643 | 1643 | # allow __getattr__ to raise exceptions other than |
|
1644 | 1644 | # AttributeError, which then crashes IPython. |
|
1645 | 1645 | break |
|
1646 | 1646 | else: |
|
1647 | 1647 | # If we finish the for loop (no break), we got all members |
|
1648 | 1648 | found = True |
|
1649 | 1649 | ospace = nsname |
|
1650 | 1650 | break # namespace loop |
|
1651 | 1651 | |
|
1652 | 1652 | # Try to see if it's magic |
|
1653 | 1653 | if not found: |
|
1654 | 1654 | obj = None |
|
1655 | 1655 | if oname.startswith(ESC_MAGIC2): |
|
1656 | 1656 | oname = oname.lstrip(ESC_MAGIC2) |
|
1657 | 1657 | obj = self.find_cell_magic(oname) |
|
1658 | 1658 | elif oname.startswith(ESC_MAGIC): |
|
1659 | 1659 | oname = oname.lstrip(ESC_MAGIC) |
|
1660 | 1660 | obj = self.find_line_magic(oname) |
|
1661 | 1661 | else: |
|
1662 | 1662 | # search without prefix, so run? will find %run? |
|
1663 | 1663 | obj = self.find_line_magic(oname) |
|
1664 | 1664 | if obj is None: |
|
1665 | 1665 | obj = self.find_cell_magic(oname) |
|
1666 | 1666 | if obj is not None: |
|
1667 | 1667 | found = True |
|
1668 | 1668 | ospace = 'IPython internal' |
|
1669 | 1669 | ismagic = True |
|
1670 | 1670 | isalias = isinstance(obj, Alias) |
|
1671 | 1671 | |
|
1672 | 1672 | # Last try: special-case some literals like '', [], {}, etc: |
|
1673 | 1673 | if not found and oname_head in ["''",'""','[]','{}','()']: |
|
1674 | 1674 | obj = eval(oname_head) |
|
1675 | 1675 | found = True |
|
1676 | 1676 | ospace = 'Interactive' |
|
1677 | 1677 | |
|
1678 | 1678 | return { |
|
1679 | 1679 | 'obj':obj, |
|
1680 | 1680 | 'found':found, |
|
1681 | 1681 | 'parent':parent, |
|
1682 | 1682 | 'ismagic':ismagic, |
|
1683 | 1683 | 'isalias':isalias, |
|
1684 | 1684 | 'namespace':ospace |
|
1685 | 1685 | } |
|
1686 | 1686 | |
|
1687 | 1687 | @staticmethod |
|
1688 | 1688 | def _getattr_property(obj, attrname): |
|
1689 | 1689 | """Property-aware getattr to use in object finding. |
|
1690 | 1690 | |
|
1691 | 1691 | If attrname represents a property, return it unevaluated (in case it has |
|
1692 | 1692 | side effects or raises an error. |
|
1693 | 1693 | |
|
1694 | 1694 | """ |
|
1695 | 1695 | if not isinstance(obj, type): |
|
1696 | 1696 | try: |
|
1697 | 1697 | # `getattr(type(obj), attrname)` is not guaranteed to return |
|
1698 | 1698 | # `obj`, but does so for property: |
|
1699 | 1699 | # |
|
1700 | 1700 | # property.__get__(self, None, cls) -> self |
|
1701 | 1701 | # |
|
1702 | 1702 | # The universal alternative is to traverse the mro manually |
|
1703 | 1703 | # searching for attrname in class dicts. |
|
1704 | 1704 | if is_integer_string(attrname): |
|
1705 | 1705 | return obj[int(attrname)] |
|
1706 | 1706 | else: |
|
1707 | 1707 | attr = getattr(type(obj), attrname) |
|
1708 | 1708 | except AttributeError: |
|
1709 | 1709 | pass |
|
1710 | 1710 | else: |
|
1711 | 1711 | # This relies on the fact that data descriptors (with both |
|
1712 | 1712 | # __get__ & __set__ magic methods) take precedence over |
|
1713 | 1713 | # instance-level attributes: |
|
1714 | 1714 | # |
|
1715 | 1715 | # class A(object): |
|
1716 | 1716 | # @property |
|
1717 | 1717 | # def foobar(self): return 123 |
|
1718 | 1718 | # a = A() |
|
1719 | 1719 | # a.__dict__['foobar'] = 345 |
|
1720 | 1720 | # a.foobar # == 123 |
|
1721 | 1721 | # |
|
1722 | 1722 | # So, a property may be returned right away. |
|
1723 | 1723 | if isinstance(attr, property): |
|
1724 | 1724 | return attr |
|
1725 | 1725 | |
|
1726 | 1726 | # Nothing helped, fall back. |
|
1727 | 1727 | return getattr(obj, attrname) |
|
1728 | 1728 | |
|
1729 | 1729 | def _object_find(self, oname, namespaces=None): |
|
1730 | 1730 | """Find an object and return a struct with info about it.""" |
|
1731 | 1731 | return Struct(self._ofind(oname, namespaces)) |
|
1732 | 1732 | |
|
1733 | 1733 | def _inspect(self, meth, oname, namespaces=None, **kw): |
|
1734 | 1734 | """Generic interface to the inspector system. |
|
1735 | 1735 | |
|
1736 | 1736 | This function is meant to be called by pdef, pdoc & friends. |
|
1737 | 1737 | """ |
|
1738 | 1738 | info = self._object_find(oname, namespaces) |
|
1739 | 1739 | docformat = ( |
|
1740 | 1740 | sphinxify(self.object_inspect(oname)) if self.sphinxify_docstring else None |
|
1741 | 1741 | ) |
|
1742 | 1742 | if info.found: |
|
1743 | 1743 | pmethod = getattr(self.inspector, meth) |
|
1744 | 1744 | # TODO: only apply format_screen to the plain/text repr of the mime |
|
1745 | 1745 | # bundle. |
|
1746 | 1746 | formatter = format_screen if info.ismagic else docformat |
|
1747 | 1747 | if meth == 'pdoc': |
|
1748 | 1748 | pmethod(info.obj, oname, formatter) |
|
1749 | 1749 | elif meth == 'pinfo': |
|
1750 | 1750 | pmethod( |
|
1751 | 1751 | info.obj, |
|
1752 | 1752 | oname, |
|
1753 | 1753 | formatter, |
|
1754 | 1754 | info, |
|
1755 | 1755 | enable_html_pager=self.enable_html_pager, |
|
1756 | 1756 | **kw, |
|
1757 | 1757 | ) |
|
1758 | 1758 | else: |
|
1759 | 1759 | pmethod(info.obj, oname) |
|
1760 | 1760 | else: |
|
1761 | 1761 | print('Object `%s` not found.' % oname) |
|
1762 | 1762 | return 'not found' # so callers can take other action |
|
1763 | 1763 | |
|
1764 | 1764 | def object_inspect(self, oname, detail_level=0): |
|
1765 | 1765 | """Get object info about oname""" |
|
1766 | 1766 | with self.builtin_trap: |
|
1767 | 1767 | info = self._object_find(oname) |
|
1768 | 1768 | if info.found: |
|
1769 | 1769 | return self.inspector.info(info.obj, oname, info=info, |
|
1770 | 1770 | detail_level=detail_level |
|
1771 | 1771 | ) |
|
1772 | 1772 | else: |
|
1773 | 1773 | return oinspect.object_info(name=oname, found=False) |
|
1774 | 1774 | |
|
1775 | 1775 | def object_inspect_text(self, oname, detail_level=0): |
|
1776 | 1776 | """Get object info as formatted text""" |
|
1777 | 1777 | return self.object_inspect_mime(oname, detail_level)['text/plain'] |
|
1778 | 1778 | |
|
1779 | 1779 | def object_inspect_mime(self, oname, detail_level=0, omit_sections=()): |
|
1780 | 1780 | """Get object info as a mimebundle of formatted representations. |
|
1781 | 1781 | |
|
1782 | 1782 | A mimebundle is a dictionary, keyed by mime-type. |
|
1783 | 1783 | It must always have the key `'text/plain'`. |
|
1784 | 1784 | """ |
|
1785 | 1785 | with self.builtin_trap: |
|
1786 | 1786 | info = self._object_find(oname) |
|
1787 | 1787 | if info.found: |
|
1788 | 1788 | docformat = ( |
|
1789 | 1789 | sphinxify(self.object_inspect(oname)) |
|
1790 | 1790 | if self.sphinxify_docstring |
|
1791 | 1791 | else None |
|
1792 | 1792 | ) |
|
1793 | 1793 | return self.inspector._get_info( |
|
1794 | 1794 | info.obj, |
|
1795 | 1795 | oname, |
|
1796 | 1796 | info=info, |
|
1797 | 1797 | detail_level=detail_level, |
|
1798 | 1798 | formatter=docformat, |
|
1799 | 1799 | omit_sections=omit_sections, |
|
1800 | 1800 | ) |
|
1801 | 1801 | else: |
|
1802 | 1802 | raise KeyError(oname) |
|
1803 | 1803 | |
|
1804 | 1804 | #------------------------------------------------------------------------- |
|
1805 | 1805 | # Things related to history management |
|
1806 | 1806 | #------------------------------------------------------------------------- |
|
1807 | 1807 | |
|
1808 | 1808 | def init_history(self): |
|
1809 | 1809 | """Sets up the command history, and starts regular autosaves.""" |
|
1810 | 1810 | self.history_manager = HistoryManager(shell=self, parent=self) |
|
1811 | 1811 | self.configurables.append(self.history_manager) |
|
1812 | 1812 | |
|
1813 | 1813 | #------------------------------------------------------------------------- |
|
1814 | 1814 | # Things related to exception handling and tracebacks (not debugging) |
|
1815 | 1815 | #------------------------------------------------------------------------- |
|
1816 | 1816 | |
|
1817 | 1817 | debugger_cls = InterruptiblePdb |
|
1818 | 1818 | |
|
1819 | 1819 | def init_traceback_handlers(self, custom_exceptions): |
|
1820 | 1820 | # Syntax error handler. |
|
1821 | 1821 | self.SyntaxTB = ultratb.SyntaxTB(color_scheme='NoColor', parent=self) |
|
1822 | 1822 | |
|
1823 | 1823 | # The interactive one is initialized with an offset, meaning we always |
|
1824 | 1824 | # want to remove the topmost item in the traceback, which is our own |
|
1825 | 1825 | # internal code. Valid modes: ['Plain','Context','Verbose','Minimal'] |
|
1826 | 1826 | self.InteractiveTB = ultratb.AutoFormattedTB(mode = 'Plain', |
|
1827 | 1827 | color_scheme='NoColor', |
|
1828 | 1828 | tb_offset = 1, |
|
1829 | 1829 | debugger_cls=self.debugger_cls, parent=self) |
|
1830 | 1830 | |
|
1831 | 1831 | # The instance will store a pointer to the system-wide exception hook, |
|
1832 | 1832 | # so that runtime code (such as magics) can access it. This is because |
|
1833 | 1833 | # during the read-eval loop, it may get temporarily overwritten. |
|
1834 | 1834 | self.sys_excepthook = sys.excepthook |
|
1835 | 1835 | |
|
1836 | 1836 | # and add any custom exception handlers the user may have specified |
|
1837 | 1837 | self.set_custom_exc(*custom_exceptions) |
|
1838 | 1838 | |
|
1839 | 1839 | # Set the exception mode |
|
1840 | 1840 | self.InteractiveTB.set_mode(mode=self.xmode) |
|
1841 | 1841 | |
|
1842 | 1842 | def set_custom_exc(self, exc_tuple, handler): |
|
1843 | 1843 | """set_custom_exc(exc_tuple, handler) |
|
1844 | 1844 | |
|
1845 | 1845 | Set a custom exception handler, which will be called if any of the |
|
1846 | 1846 | exceptions in exc_tuple occur in the mainloop (specifically, in the |
|
1847 | 1847 | run_code() method). |
|
1848 | 1848 | |
|
1849 | 1849 | Parameters |
|
1850 | 1850 | ---------- |
|
1851 | 1851 | exc_tuple : tuple of exception classes |
|
1852 | 1852 | A *tuple* of exception classes, for which to call the defined |
|
1853 | 1853 | handler. It is very important that you use a tuple, and NOT A |
|
1854 | 1854 | LIST here, because of the way Python's except statement works. If |
|
1855 | 1855 | you only want to trap a single exception, use a singleton tuple:: |
|
1856 | 1856 | |
|
1857 | 1857 | exc_tuple == (MyCustomException,) |
|
1858 | 1858 | |
|
1859 | 1859 | handler : callable |
|
1860 | 1860 | handler must have the following signature:: |
|
1861 | 1861 | |
|
1862 | 1862 | def my_handler(self, etype, value, tb, tb_offset=None): |
|
1863 | 1863 | ... |
|
1864 | 1864 | return structured_traceback |
|
1865 | 1865 | |
|
1866 | 1866 | Your handler must return a structured traceback (a list of strings), |
|
1867 | 1867 | or None. |
|
1868 | 1868 | |
|
1869 | 1869 | This will be made into an instance method (via types.MethodType) |
|
1870 | 1870 | of IPython itself, and it will be called if any of the exceptions |
|
1871 | 1871 | listed in the exc_tuple are caught. If the handler is None, an |
|
1872 | 1872 | internal basic one is used, which just prints basic info. |
|
1873 | 1873 | |
|
1874 | 1874 | To protect IPython from crashes, if your handler ever raises an |
|
1875 | 1875 | exception or returns an invalid result, it will be immediately |
|
1876 | 1876 | disabled. |
|
1877 | 1877 | |
|
1878 | 1878 | Notes |
|
1879 | 1879 | ----- |
|
1880 | 1880 | WARNING: by putting in your own exception handler into IPython's main |
|
1881 | 1881 | execution loop, you run a very good chance of nasty crashes. This |
|
1882 | 1882 | facility should only be used if you really know what you are doing. |
|
1883 | 1883 | """ |
|
1884 | 1884 | |
|
1885 | 1885 | if not isinstance(exc_tuple, tuple): |
|
1886 | 1886 | raise TypeError("The custom exceptions must be given as a tuple.") |
|
1887 | 1887 | |
|
1888 | 1888 | def dummy_handler(self, etype, value, tb, tb_offset=None): |
|
1889 | 1889 | print('*** Simple custom exception handler ***') |
|
1890 | 1890 | print('Exception type :', etype) |
|
1891 | 1891 | print('Exception value:', value) |
|
1892 | 1892 | print('Traceback :', tb) |
|
1893 | 1893 | |
|
1894 | 1894 | def validate_stb(stb): |
|
1895 | 1895 | """validate structured traceback return type |
|
1896 | 1896 | |
|
1897 | 1897 | return type of CustomTB *should* be a list of strings, but allow |
|
1898 | 1898 | single strings or None, which are harmless. |
|
1899 | 1899 | |
|
1900 | 1900 | This function will *always* return a list of strings, |
|
1901 | 1901 | and will raise a TypeError if stb is inappropriate. |
|
1902 | 1902 | """ |
|
1903 | 1903 | msg = "CustomTB must return list of strings, not %r" % stb |
|
1904 | 1904 | if stb is None: |
|
1905 | 1905 | return [] |
|
1906 | 1906 | elif isinstance(stb, str): |
|
1907 | 1907 | return [stb] |
|
1908 | 1908 | elif not isinstance(stb, list): |
|
1909 | 1909 | raise TypeError(msg) |
|
1910 | 1910 | # it's a list |
|
1911 | 1911 | for line in stb: |
|
1912 | 1912 | # check every element |
|
1913 | 1913 | if not isinstance(line, str): |
|
1914 | 1914 | raise TypeError(msg) |
|
1915 | 1915 | return stb |
|
1916 | 1916 | |
|
1917 | 1917 | if handler is None: |
|
1918 | 1918 | wrapped = dummy_handler |
|
1919 | 1919 | else: |
|
1920 | 1920 | def wrapped(self,etype,value,tb,tb_offset=None): |
|
1921 | 1921 | """wrap CustomTB handler, to protect IPython from user code |
|
1922 | 1922 | |
|
1923 | 1923 | This makes it harder (but not impossible) for custom exception |
|
1924 | 1924 | handlers to crash IPython. |
|
1925 | 1925 | """ |
|
1926 | 1926 | try: |
|
1927 | 1927 | stb = handler(self,etype,value,tb,tb_offset=tb_offset) |
|
1928 | 1928 | return validate_stb(stb) |
|
1929 | 1929 | except: |
|
1930 | 1930 | # clear custom handler immediately |
|
1931 | 1931 | self.set_custom_exc((), None) |
|
1932 | 1932 | print("Custom TB Handler failed, unregistering", file=sys.stderr) |
|
1933 | 1933 | # show the exception in handler first |
|
1934 | 1934 | stb = self.InteractiveTB.structured_traceback(*sys.exc_info()) |
|
1935 | 1935 | print(self.InteractiveTB.stb2text(stb)) |
|
1936 | 1936 | print("The original exception:") |
|
1937 | 1937 | stb = self.InteractiveTB.structured_traceback( |
|
1938 | 1938 | (etype,value,tb), tb_offset=tb_offset |
|
1939 | 1939 | ) |
|
1940 | 1940 | return stb |
|
1941 | 1941 | |
|
1942 | 1942 | self.CustomTB = types.MethodType(wrapped,self) |
|
1943 | 1943 | self.custom_exceptions = exc_tuple |
|
1944 | 1944 | |
|
1945 | 1945 | def excepthook(self, etype, value, tb): |
|
1946 | 1946 | """One more defense for GUI apps that call sys.excepthook. |
|
1947 | 1947 | |
|
1948 | 1948 | GUI frameworks like wxPython trap exceptions and call |
|
1949 | 1949 | sys.excepthook themselves. I guess this is a feature that |
|
1950 | 1950 | enables them to keep running after exceptions that would |
|
1951 | 1951 | otherwise kill their mainloop. This is a bother for IPython |
|
1952 | 1952 | which expects to catch all of the program exceptions with a try: |
|
1953 | 1953 | except: statement. |
|
1954 | 1954 | |
|
1955 | 1955 | Normally, IPython sets sys.excepthook to a CrashHandler instance, so if |
|
1956 | 1956 | any app directly invokes sys.excepthook, it will look to the user like |
|
1957 | 1957 | IPython crashed. In order to work around this, we can disable the |
|
1958 | 1958 | CrashHandler and replace it with this excepthook instead, which prints a |
|
1959 | 1959 | regular traceback using our InteractiveTB. In this fashion, apps which |
|
1960 | 1960 | call sys.excepthook will generate a regular-looking exception from |
|
1961 | 1961 | IPython, and the CrashHandler will only be triggered by real IPython |
|
1962 | 1962 | crashes. |
|
1963 | 1963 | |
|
1964 | 1964 | This hook should be used sparingly, only in places which are not likely |
|
1965 | 1965 | to be true IPython errors. |
|
1966 | 1966 | """ |
|
1967 | 1967 | self.showtraceback((etype, value, tb), tb_offset=0) |
|
1968 | 1968 | |
|
1969 | 1969 | def _get_exc_info(self, exc_tuple=None): |
|
1970 | 1970 | """get exc_info from a given tuple, sys.exc_info() or sys.last_type etc. |
|
1971 | 1971 | |
|
1972 | 1972 | Ensures sys.last_type,value,traceback hold the exc_info we found, |
|
1973 | 1973 | from whichever source. |
|
1974 | 1974 | |
|
1975 | 1975 | raises ValueError if none of these contain any information |
|
1976 | 1976 | """ |
|
1977 | 1977 | if exc_tuple is None: |
|
1978 | 1978 | etype, value, tb = sys.exc_info() |
|
1979 | 1979 | else: |
|
1980 | 1980 | etype, value, tb = exc_tuple |
|
1981 | 1981 | |
|
1982 | 1982 | if etype is None: |
|
1983 | 1983 | if hasattr(sys, 'last_type'): |
|
1984 | 1984 | etype, value, tb = sys.last_type, sys.last_value, \ |
|
1985 | 1985 | sys.last_traceback |
|
1986 | 1986 | |
|
1987 | 1987 | if etype is None: |
|
1988 | 1988 | raise ValueError("No exception to find") |
|
1989 | 1989 | |
|
1990 | 1990 | # Now store the exception info in sys.last_type etc. |
|
1991 | 1991 | # WARNING: these variables are somewhat deprecated and not |
|
1992 | 1992 | # necessarily safe to use in a threaded environment, but tools |
|
1993 | 1993 | # like pdb depend on their existence, so let's set them. If we |
|
1994 | 1994 | # find problems in the field, we'll need to revisit their use. |
|
1995 | 1995 | sys.last_type = etype |
|
1996 | 1996 | sys.last_value = value |
|
1997 | 1997 | sys.last_traceback = tb |
|
1998 | 1998 | |
|
1999 | 1999 | return etype, value, tb |
|
2000 | 2000 | |
|
2001 | 2001 | def show_usage_error(self, exc): |
|
2002 | 2002 | """Show a short message for UsageErrors |
|
2003 | 2003 | |
|
2004 | 2004 | These are special exceptions that shouldn't show a traceback. |
|
2005 | 2005 | """ |
|
2006 | 2006 | print("UsageError: %s" % exc, file=sys.stderr) |
|
2007 | 2007 | |
|
2008 | 2008 | def get_exception_only(self, exc_tuple=None): |
|
2009 | 2009 | """ |
|
2010 | 2010 | Return as a string (ending with a newline) the exception that |
|
2011 | 2011 | just occurred, without any traceback. |
|
2012 | 2012 | """ |
|
2013 | 2013 | etype, value, tb = self._get_exc_info(exc_tuple) |
|
2014 | 2014 | msg = traceback.format_exception_only(etype, value) |
|
2015 | 2015 | return ''.join(msg) |
|
2016 | 2016 | |
|
2017 | 2017 | def showtraceback(self, exc_tuple=None, filename=None, tb_offset=None, |
|
2018 | 2018 | exception_only=False, running_compiled_code=False): |
|
2019 | 2019 | """Display the exception that just occurred. |
|
2020 | 2020 | |
|
2021 | 2021 | If nothing is known about the exception, this is the method which |
|
2022 | 2022 | should be used throughout the code for presenting user tracebacks, |
|
2023 | 2023 | rather than directly invoking the InteractiveTB object. |
|
2024 | 2024 | |
|
2025 | 2025 | A specific showsyntaxerror() also exists, but this method can take |
|
2026 | 2026 | care of calling it if needed, so unless you are explicitly catching a |
|
2027 | 2027 | SyntaxError exception, don't try to analyze the stack manually and |
|
2028 | 2028 | simply call this method.""" |
|
2029 | 2029 | |
|
2030 | 2030 | try: |
|
2031 | 2031 | try: |
|
2032 | 2032 | etype, value, tb = self._get_exc_info(exc_tuple) |
|
2033 | 2033 | except ValueError: |
|
2034 | 2034 | print('No traceback available to show.', file=sys.stderr) |
|
2035 | 2035 | return |
|
2036 | 2036 | |
|
2037 | 2037 | if issubclass(etype, SyntaxError): |
|
2038 | 2038 | # Though this won't be called by syntax errors in the input |
|
2039 | 2039 | # line, there may be SyntaxError cases with imported code. |
|
2040 | 2040 | self.showsyntaxerror(filename, running_compiled_code) |
|
2041 | 2041 | elif etype is UsageError: |
|
2042 | 2042 | self.show_usage_error(value) |
|
2043 | 2043 | else: |
|
2044 | 2044 | if exception_only: |
|
2045 | 2045 | stb = ['An exception has occurred, use %tb to see ' |
|
2046 | 2046 | 'the full traceback.\n'] |
|
2047 | 2047 | stb.extend(self.InteractiveTB.get_exception_only(etype, |
|
2048 | 2048 | value)) |
|
2049 | 2049 | else: |
|
2050 | 2050 | try: |
|
2051 | 2051 | # Exception classes can customise their traceback - we |
|
2052 | 2052 | # use this in IPython.parallel for exceptions occurring |
|
2053 | 2053 | # in the engines. This should return a list of strings. |
|
2054 | 2054 | if hasattr(value, "_render_traceback_"): |
|
2055 | 2055 | stb = value._render_traceback_() |
|
2056 | 2056 | else: |
|
2057 | 2057 | stb = self.InteractiveTB.structured_traceback( |
|
2058 | 2058 | etype, value, tb, tb_offset=tb_offset |
|
2059 | 2059 | ) |
|
2060 | 2060 | |
|
2061 | 2061 | except Exception: |
|
2062 | 2062 | print( |
|
2063 | 2063 | "Unexpected exception formatting exception. Falling back to standard exception" |
|
2064 | 2064 | ) |
|
2065 | 2065 | traceback.print_exc() |
|
2066 | 2066 | return None |
|
2067 | 2067 | |
|
2068 | 2068 | self._showtraceback(etype, value, stb) |
|
2069 | 2069 | if self.call_pdb: |
|
2070 | 2070 | # drop into debugger |
|
2071 | 2071 | self.debugger(force=True) |
|
2072 | 2072 | return |
|
2073 | 2073 | |
|
2074 | 2074 | # Actually show the traceback |
|
2075 | 2075 | self._showtraceback(etype, value, stb) |
|
2076 | 2076 | |
|
2077 | 2077 | except KeyboardInterrupt: |
|
2078 | 2078 | print('\n' + self.get_exception_only(), file=sys.stderr) |
|
2079 | 2079 | |
|
2080 | 2080 | def _showtraceback(self, etype, evalue, stb: str): |
|
2081 | 2081 | """Actually show a traceback. |
|
2082 | 2082 | |
|
2083 | 2083 | Subclasses may override this method to put the traceback on a different |
|
2084 | 2084 | place, like a side channel. |
|
2085 | 2085 | """ |
|
2086 | 2086 | val = self.InteractiveTB.stb2text(stb) |
|
2087 | 2087 | try: |
|
2088 | 2088 | print(val) |
|
2089 | 2089 | except UnicodeEncodeError: |
|
2090 | 2090 | print(val.encode("utf-8", "backslashreplace").decode()) |
|
2091 | 2091 | |
|
2092 | 2092 | def showsyntaxerror(self, filename=None, running_compiled_code=False): |
|
2093 | 2093 | """Display the syntax error that just occurred. |
|
2094 | 2094 | |
|
2095 | 2095 | This doesn't display a stack trace because there isn't one. |
|
2096 | 2096 | |
|
2097 | 2097 | If a filename is given, it is stuffed in the exception instead |
|
2098 | 2098 | of what was there before (because Python's parser always uses |
|
2099 | 2099 | "<string>" when reading from a string). |
|
2100 | 2100 | |
|
2101 | 2101 | If the syntax error occurred when running a compiled code (i.e. running_compile_code=True), |
|
2102 | 2102 | longer stack trace will be displayed. |
|
2103 | 2103 | """ |
|
2104 | 2104 | etype, value, last_traceback = self._get_exc_info() |
|
2105 | 2105 | |
|
2106 | 2106 | if filename and issubclass(etype, SyntaxError): |
|
2107 | 2107 | try: |
|
2108 | 2108 | value.filename = filename |
|
2109 | 2109 | except: |
|
2110 | 2110 | # Not the format we expect; leave it alone |
|
2111 | 2111 | pass |
|
2112 | 2112 | |
|
2113 | 2113 | # If the error occurred when executing compiled code, we should provide full stacktrace. |
|
2114 | 2114 | elist = traceback.extract_tb(last_traceback) if running_compiled_code else [] |
|
2115 | 2115 | stb = self.SyntaxTB.structured_traceback(etype, value, elist) |
|
2116 | 2116 | self._showtraceback(etype, value, stb) |
|
2117 | 2117 | |
|
2118 | 2118 | # This is overridden in TerminalInteractiveShell to show a message about |
|
2119 | 2119 | # the %paste magic. |
|
2120 | 2120 | def showindentationerror(self): |
|
2121 | 2121 | """Called by _run_cell when there's an IndentationError in code entered |
|
2122 | 2122 | at the prompt. |
|
2123 | 2123 | |
|
2124 | 2124 | This is overridden in TerminalInteractiveShell to show a message about |
|
2125 | 2125 | the %paste magic.""" |
|
2126 | 2126 | self.showsyntaxerror() |
|
2127 | 2127 | |
|
2128 | 2128 | @skip_doctest |
|
2129 | 2129 | def set_next_input(self, s, replace=False): |
|
2130 | 2130 | """ Sets the 'default' input string for the next command line. |
|
2131 | 2131 | |
|
2132 | 2132 | Example:: |
|
2133 | 2133 | |
|
2134 | 2134 | In [1]: _ip.set_next_input("Hello Word") |
|
2135 | 2135 | In [2]: Hello Word_ # cursor is here |
|
2136 | 2136 | """ |
|
2137 | 2137 | self.rl_next_input = s |
|
2138 | 2138 | |
|
2139 | 2139 | def _indent_current_str(self): |
|
2140 | 2140 | """return the current level of indentation as a string""" |
|
2141 | 2141 | return self.input_splitter.get_indent_spaces() * ' ' |
|
2142 | 2142 | |
|
2143 | 2143 | #------------------------------------------------------------------------- |
|
2144 | 2144 | # Things related to text completion |
|
2145 | 2145 | #------------------------------------------------------------------------- |
|
2146 | 2146 | |
|
2147 | 2147 | def init_completer(self): |
|
2148 | 2148 | """Initialize the completion machinery. |
|
2149 | 2149 | |
|
2150 | 2150 | This creates completion machinery that can be used by client code, |
|
2151 | 2151 | either interactively in-process (typically triggered by the readline |
|
2152 | 2152 | library), programmatically (such as in test suites) or out-of-process |
|
2153 | 2153 | (typically over the network by remote frontends). |
|
2154 | 2154 | """ |
|
2155 | 2155 | from IPython.core.completer import IPCompleter |
|
2156 | 2156 | from IPython.core.completerlib import ( |
|
2157 | 2157 | cd_completer, |
|
2158 | 2158 | magic_run_completer, |
|
2159 | 2159 | module_completer, |
|
2160 | 2160 | reset_completer, |
|
2161 | 2161 | ) |
|
2162 | 2162 | |
|
2163 | 2163 | self.Completer = IPCompleter(shell=self, |
|
2164 | 2164 | namespace=self.user_ns, |
|
2165 | 2165 | global_namespace=self.user_global_ns, |
|
2166 | 2166 | parent=self, |
|
2167 | 2167 | ) |
|
2168 | 2168 | self.configurables.append(self.Completer) |
|
2169 | 2169 | |
|
2170 | 2170 | # Add custom completers to the basic ones built into IPCompleter |
|
2171 | 2171 | sdisp = self.strdispatchers.get('complete_command', StrDispatch()) |
|
2172 | 2172 | self.strdispatchers['complete_command'] = sdisp |
|
2173 | 2173 | self.Completer.custom_completers = sdisp |
|
2174 | 2174 | |
|
2175 | 2175 | self.set_hook('complete_command', module_completer, str_key = 'import') |
|
2176 | 2176 | self.set_hook('complete_command', module_completer, str_key = 'from') |
|
2177 | 2177 | self.set_hook('complete_command', module_completer, str_key = '%aimport') |
|
2178 | 2178 | self.set_hook('complete_command', magic_run_completer, str_key = '%run') |
|
2179 | 2179 | self.set_hook('complete_command', cd_completer, str_key = '%cd') |
|
2180 | 2180 | self.set_hook('complete_command', reset_completer, str_key = '%reset') |
|
2181 | 2181 | |
|
2182 | 2182 | @skip_doctest |
|
2183 | 2183 | def complete(self, text, line=None, cursor_pos=None): |
|
2184 | 2184 | """Return the completed text and a list of completions. |
|
2185 | 2185 | |
|
2186 | 2186 | Parameters |
|
2187 | 2187 | ---------- |
|
2188 | 2188 | text : string |
|
2189 | 2189 | A string of text to be completed on. It can be given as empty and |
|
2190 | 2190 | instead a line/position pair are given. In this case, the |
|
2191 | 2191 | completer itself will split the line like readline does. |
|
2192 | 2192 | line : string, optional |
|
2193 | 2193 | The complete line that text is part of. |
|
2194 | 2194 | cursor_pos : int, optional |
|
2195 | 2195 | The position of the cursor on the input line. |
|
2196 | 2196 | |
|
2197 | 2197 | Returns |
|
2198 | 2198 | ------- |
|
2199 | 2199 | text : string |
|
2200 | 2200 | The actual text that was completed. |
|
2201 | 2201 | matches : list |
|
2202 | 2202 | A sorted list with all possible completions. |
|
2203 | 2203 | |
|
2204 | 2204 | Notes |
|
2205 | 2205 | ----- |
|
2206 | 2206 | The optional arguments allow the completion to take more context into |
|
2207 | 2207 | account, and are part of the low-level completion API. |
|
2208 | 2208 | |
|
2209 | 2209 | This is a wrapper around the completion mechanism, similar to what |
|
2210 | 2210 | readline does at the command line when the TAB key is hit. By |
|
2211 | 2211 | exposing it as a method, it can be used by other non-readline |
|
2212 | 2212 | environments (such as GUIs) for text completion. |
|
2213 | 2213 | |
|
2214 | 2214 | Examples |
|
2215 | 2215 | -------- |
|
2216 | 2216 | In [1]: x = 'hello' |
|
2217 | 2217 | |
|
2218 | 2218 | In [2]: _ip.complete('x.l') |
|
2219 | 2219 | Out[2]: ('x.l', ['x.ljust', 'x.lower', 'x.lstrip']) |
|
2220 | 2220 | """ |
|
2221 | 2221 | |
|
2222 | 2222 | # Inject names into __builtin__ so we can complete on the added names. |
|
2223 | 2223 | with self.builtin_trap: |
|
2224 | 2224 | return self.Completer.complete(text, line, cursor_pos) |
|
2225 | 2225 | |
|
2226 | 2226 | def set_custom_completer(self, completer, pos=0) -> None: |
|
2227 | 2227 | """Adds a new custom completer function. |
|
2228 | 2228 | |
|
2229 | 2229 | The position argument (defaults to 0) is the index in the completers |
|
2230 | 2230 | list where you want the completer to be inserted. |
|
2231 | 2231 | |
|
2232 | 2232 | `completer` should have the following signature:: |
|
2233 | 2233 | |
|
2234 | 2234 | def completion(self: Completer, text: string) -> List[str]: |
|
2235 | 2235 | raise NotImplementedError |
|
2236 | 2236 | |
|
2237 | 2237 | It will be bound to the current Completer instance and pass some text |
|
2238 | 2238 | and return a list with current completions to suggest to the user. |
|
2239 | 2239 | """ |
|
2240 | 2240 | |
|
2241 | 2241 | newcomp = types.MethodType(completer, self.Completer) |
|
2242 | 2242 | self.Completer.custom_matchers.insert(pos,newcomp) |
|
2243 | 2243 | |
|
2244 | 2244 | def set_completer_frame(self, frame=None): |
|
2245 | 2245 | """Set the frame of the completer.""" |
|
2246 | 2246 | if frame: |
|
2247 | 2247 | self.Completer.namespace = frame.f_locals |
|
2248 | 2248 | self.Completer.global_namespace = frame.f_globals |
|
2249 | 2249 | else: |
|
2250 | 2250 | self.Completer.namespace = self.user_ns |
|
2251 | 2251 | self.Completer.global_namespace = self.user_global_ns |
|
2252 | 2252 | |
|
2253 | 2253 | #------------------------------------------------------------------------- |
|
2254 | 2254 | # Things related to magics |
|
2255 | 2255 | #------------------------------------------------------------------------- |
|
2256 | 2256 | |
|
2257 | 2257 | def init_magics(self): |
|
2258 | 2258 | from IPython.core import magics as m |
|
2259 | 2259 | self.magics_manager = magic.MagicsManager(shell=self, |
|
2260 | 2260 | parent=self, |
|
2261 | 2261 | user_magics=m.UserMagics(self)) |
|
2262 | 2262 | self.configurables.append(self.magics_manager) |
|
2263 | 2263 | |
|
2264 | 2264 | # Expose as public API from the magics manager |
|
2265 | 2265 | self.register_magics = self.magics_manager.register |
|
2266 | 2266 | |
|
2267 | 2267 | self.register_magics(m.AutoMagics, m.BasicMagics, m.CodeMagics, |
|
2268 | 2268 | m.ConfigMagics, m.DisplayMagics, m.ExecutionMagics, |
|
2269 | 2269 | m.ExtensionMagics, m.HistoryMagics, m.LoggingMagics, |
|
2270 | 2270 | m.NamespaceMagics, m.OSMagics, m.PackagingMagics, |
|
2271 | 2271 | m.PylabMagics, m.ScriptMagics, |
|
2272 | 2272 | ) |
|
2273 | 2273 | self.register_magics(m.AsyncMagics) |
|
2274 | 2274 | |
|
2275 | 2275 | # Register Magic Aliases |
|
2276 | 2276 | mman = self.magics_manager |
|
2277 | 2277 | # FIXME: magic aliases should be defined by the Magics classes |
|
2278 | 2278 | # or in MagicsManager, not here |
|
2279 | 2279 | mman.register_alias('ed', 'edit') |
|
2280 | 2280 | mman.register_alias('hist', 'history') |
|
2281 | 2281 | mman.register_alias('rep', 'recall') |
|
2282 | 2282 | mman.register_alias('SVG', 'svg', 'cell') |
|
2283 | 2283 | mman.register_alias('HTML', 'html', 'cell') |
|
2284 | 2284 | mman.register_alias('file', 'writefile', 'cell') |
|
2285 | 2285 | |
|
2286 | 2286 | # FIXME: Move the color initialization to the DisplayHook, which |
|
2287 | 2287 | # should be split into a prompt manager and displayhook. We probably |
|
2288 | 2288 | # even need a centralize colors management object. |
|
2289 | 2289 | self.run_line_magic('colors', self.colors) |
|
2290 | 2290 | |
|
2291 | 2291 | # Defined here so that it's included in the documentation |
|
2292 | 2292 | @functools.wraps(magic.MagicsManager.register_function) |
|
2293 | 2293 | def register_magic_function(self, func, magic_kind='line', magic_name=None): |
|
2294 | 2294 | self.magics_manager.register_function( |
|
2295 | 2295 | func, magic_kind=magic_kind, magic_name=magic_name |
|
2296 | 2296 | ) |
|
2297 | 2297 | |
|
2298 | 2298 | def _find_with_lazy_load(self, /, type_, magic_name: str): |
|
2299 | 2299 | """ |
|
2300 | 2300 | Try to find a magic potentially lazy-loading it. |
|
2301 | 2301 | |
|
2302 | 2302 | Parameters |
|
2303 | 2303 | ---------- |
|
2304 | 2304 | |
|
2305 | 2305 | type_: "line"|"cell" |
|
2306 | 2306 | the type of magics we are trying to find/lazy load. |
|
2307 | 2307 | magic_name: str |
|
2308 | 2308 | The name of the magic we are trying to find/lazy load |
|
2309 | 2309 | |
|
2310 | 2310 | |
|
2311 | 2311 | Note that this may have any side effects |
|
2312 | 2312 | """ |
|
2313 | 2313 | finder = {"line": self.find_line_magic, "cell": self.find_cell_magic}[type_] |
|
2314 | 2314 | fn = finder(magic_name) |
|
2315 | 2315 | if fn is not None: |
|
2316 | 2316 | return fn |
|
2317 | 2317 | lazy = self.magics_manager.lazy_magics.get(magic_name) |
|
2318 | 2318 | if lazy is None: |
|
2319 | 2319 | return None |
|
2320 | 2320 | |
|
2321 | 2321 | self.run_line_magic("load_ext", lazy) |
|
2322 | 2322 | res = finder(magic_name) |
|
2323 | 2323 | return res |
|
2324 | 2324 | |
|
2325 | 2325 | def run_line_magic(self, magic_name: str, line, _stack_depth=1): |
|
2326 | 2326 | """Execute the given line magic. |
|
2327 | 2327 | |
|
2328 | 2328 | Parameters |
|
2329 | 2329 | ---------- |
|
2330 | 2330 | magic_name : str |
|
2331 | 2331 | Name of the desired magic function, without '%' prefix. |
|
2332 | 2332 | line : str |
|
2333 | 2333 | The rest of the input line as a single string. |
|
2334 | 2334 | _stack_depth : int |
|
2335 | 2335 | If run_line_magic() is called from magic() then _stack_depth=2. |
|
2336 | 2336 | This is added to ensure backward compatibility for use of 'get_ipython().magic()' |
|
2337 | 2337 | """ |
|
2338 | 2338 | fn = self._find_with_lazy_load("line", magic_name) |
|
2339 | 2339 | if fn is None: |
|
2340 | 2340 | lazy = self.magics_manager.lazy_magics.get(magic_name) |
|
2341 | 2341 | if lazy: |
|
2342 | 2342 | self.run_line_magic("load_ext", lazy) |
|
2343 | 2343 | fn = self.find_line_magic(magic_name) |
|
2344 | 2344 | if fn is None: |
|
2345 | 2345 | cm = self.find_cell_magic(magic_name) |
|
2346 | 2346 | etpl = "Line magic function `%%%s` not found%s." |
|
2347 | 2347 | extra = '' if cm is None else (' (But cell magic `%%%%%s` exists, ' |
|
2348 | 2348 | 'did you mean that instead?)' % magic_name ) |
|
2349 | 2349 | raise UsageError(etpl % (magic_name, extra)) |
|
2350 | 2350 | else: |
|
2351 | 2351 | # Note: this is the distance in the stack to the user's frame. |
|
2352 | 2352 | # This will need to be updated if the internal calling logic gets |
|
2353 | 2353 | # refactored, or else we'll be expanding the wrong variables. |
|
2354 | 2354 | |
|
2355 | 2355 | # Determine stack_depth depending on where run_line_magic() has been called |
|
2356 | 2356 | stack_depth = _stack_depth |
|
2357 | 2357 | if getattr(fn, magic.MAGIC_NO_VAR_EXPAND_ATTR, False): |
|
2358 | 2358 | # magic has opted out of var_expand |
|
2359 | 2359 | magic_arg_s = line |
|
2360 | 2360 | else: |
|
2361 | 2361 | magic_arg_s = self.var_expand(line, stack_depth) |
|
2362 | 2362 | # Put magic args in a list so we can call with f(*a) syntax |
|
2363 | 2363 | args = [magic_arg_s] |
|
2364 | 2364 | kwargs = {} |
|
2365 | 2365 | # Grab local namespace if we need it: |
|
2366 | 2366 | if getattr(fn, "needs_local_scope", False): |
|
2367 | 2367 | kwargs['local_ns'] = self.get_local_scope(stack_depth) |
|
2368 | 2368 | with self.builtin_trap: |
|
2369 | 2369 | result = fn(*args, **kwargs) |
|
2370 | ||
|
2371 | # The code below prevents the output from being displayed | |
|
2372 | # when using magics with decodator @output_can_be_silenced | |
|
2373 | # when the last Python token in the expression is a ';'. | |
|
2374 | if getattr(fn, magic.MAGIC_OUTPUT_CAN_BE_SILENCED, False): | |
|
2375 | if DisplayHook.semicolon_at_end_of_expression(magic_arg_s): | |
|
2376 | return None | |
|
2377 | ||
|
2370 | 2378 | return result |
|
2371 | 2379 | |
|
2372 | 2380 | def get_local_scope(self, stack_depth): |
|
2373 | 2381 | """Get local scope at given stack depth. |
|
2374 | 2382 | |
|
2375 | 2383 | Parameters |
|
2376 | 2384 | ---------- |
|
2377 | 2385 | stack_depth : int |
|
2378 | 2386 | Depth relative to calling frame |
|
2379 | 2387 | """ |
|
2380 | 2388 | return sys._getframe(stack_depth + 1).f_locals |
|
2381 | 2389 | |
|
2382 | 2390 | def run_cell_magic(self, magic_name, line, cell): |
|
2383 | 2391 | """Execute the given cell magic. |
|
2384 | 2392 | |
|
2385 | 2393 | Parameters |
|
2386 | 2394 | ---------- |
|
2387 | 2395 | magic_name : str |
|
2388 | 2396 | Name of the desired magic function, without '%' prefix. |
|
2389 | 2397 | line : str |
|
2390 | 2398 | The rest of the first input line as a single string. |
|
2391 | 2399 | cell : str |
|
2392 | 2400 | The body of the cell as a (possibly multiline) string. |
|
2393 | 2401 | """ |
|
2394 | 2402 | fn = self._find_with_lazy_load("cell", magic_name) |
|
2395 | 2403 | if fn is None: |
|
2396 | 2404 | lm = self.find_line_magic(magic_name) |
|
2397 | 2405 | etpl = "Cell magic `%%{0}` not found{1}." |
|
2398 | 2406 | extra = '' if lm is None else (' (But line magic `%{0}` exists, ' |
|
2399 | 2407 | 'did you mean that instead?)'.format(magic_name)) |
|
2400 | 2408 | raise UsageError(etpl.format(magic_name, extra)) |
|
2401 | 2409 | elif cell == '': |
|
2402 | 2410 | message = '%%{0} is a cell magic, but the cell body is empty.'.format(magic_name) |
|
2403 | 2411 | if self.find_line_magic(magic_name) is not None: |
|
2404 | 2412 | message += ' Did you mean the line magic %{0} (single %)?'.format(magic_name) |
|
2405 | 2413 | raise UsageError(message) |
|
2406 | 2414 | else: |
|
2407 | 2415 | # Note: this is the distance in the stack to the user's frame. |
|
2408 | 2416 | # This will need to be updated if the internal calling logic gets |
|
2409 | 2417 | # refactored, or else we'll be expanding the wrong variables. |
|
2410 | 2418 | stack_depth = 2 |
|
2411 | 2419 | if getattr(fn, magic.MAGIC_NO_VAR_EXPAND_ATTR, False): |
|
2412 | 2420 | # magic has opted out of var_expand |
|
2413 | 2421 | magic_arg_s = line |
|
2414 | 2422 | else: |
|
2415 | 2423 | magic_arg_s = self.var_expand(line, stack_depth) |
|
2416 | 2424 | kwargs = {} |
|
2417 | 2425 | if getattr(fn, "needs_local_scope", False): |
|
2418 | 2426 | kwargs['local_ns'] = self.user_ns |
|
2419 | 2427 | |
|
2420 | 2428 | with self.builtin_trap: |
|
2421 | 2429 | args = (magic_arg_s, cell) |
|
2422 | 2430 | result = fn(*args, **kwargs) |
|
2431 | ||
|
2432 | # The code below prevents the output from being displayed | |
|
2433 | # when using magics with decodator @output_can_be_silenced | |
|
2434 | # when the last Python token in the expression is a ';'. | |
|
2435 | if getattr(fn, magic.MAGIC_OUTPUT_CAN_BE_SILENCED, False): | |
|
2436 | if DisplayHook.semicolon_at_end_of_expression(cell): | |
|
2437 | return None | |
|
2438 | ||
|
2423 | 2439 | return result |
|
2424 | 2440 | |
|
2425 | 2441 | def find_line_magic(self, magic_name): |
|
2426 | 2442 | """Find and return a line magic by name. |
|
2427 | 2443 | |
|
2428 | 2444 | Returns None if the magic isn't found.""" |
|
2429 | 2445 | return self.magics_manager.magics['line'].get(magic_name) |
|
2430 | 2446 | |
|
2431 | 2447 | def find_cell_magic(self, magic_name): |
|
2432 | 2448 | """Find and return a cell magic by name. |
|
2433 | 2449 | |
|
2434 | 2450 | Returns None if the magic isn't found.""" |
|
2435 | 2451 | return self.magics_manager.magics['cell'].get(magic_name) |
|
2436 | 2452 | |
|
2437 | 2453 | def find_magic(self, magic_name, magic_kind='line'): |
|
2438 | 2454 | """Find and return a magic of the given type by name. |
|
2439 | 2455 | |
|
2440 | 2456 | Returns None if the magic isn't found.""" |
|
2441 | 2457 | return self.magics_manager.magics[magic_kind].get(magic_name) |
|
2442 | 2458 | |
|
2443 | 2459 | def magic(self, arg_s): |
|
2444 | 2460 | """ |
|
2445 | 2461 | DEPRECATED |
|
2446 | 2462 | |
|
2447 | 2463 | Deprecated since IPython 0.13 (warning added in |
|
2448 | 2464 | 8.1), use run_line_magic(magic_name, parameter_s). |
|
2449 | 2465 | |
|
2450 | 2466 | Call a magic function by name. |
|
2451 | 2467 | |
|
2452 | 2468 | Input: a string containing the name of the magic function to call and |
|
2453 | 2469 | any additional arguments to be passed to the magic. |
|
2454 | 2470 | |
|
2455 | 2471 | magic('name -opt foo bar') is equivalent to typing at the ipython |
|
2456 | 2472 | prompt: |
|
2457 | 2473 | |
|
2458 | 2474 | In[1]: %name -opt foo bar |
|
2459 | 2475 | |
|
2460 | 2476 | To call a magic without arguments, simply use magic('name'). |
|
2461 | 2477 | |
|
2462 | 2478 | This provides a proper Python function to call IPython's magics in any |
|
2463 | 2479 | valid Python code you can type at the interpreter, including loops and |
|
2464 | 2480 | compound statements. |
|
2465 | 2481 | """ |
|
2466 | 2482 | warnings.warn( |
|
2467 | 2483 | "`magic(...)` is deprecated since IPython 0.13 (warning added in " |
|
2468 | 2484 | "8.1), use run_line_magic(magic_name, parameter_s).", |
|
2469 | 2485 | DeprecationWarning, |
|
2470 | 2486 | stacklevel=2, |
|
2471 | 2487 | ) |
|
2472 | 2488 | # TODO: should we issue a loud deprecation warning here? |
|
2473 | 2489 | magic_name, _, magic_arg_s = arg_s.partition(' ') |
|
2474 | 2490 | magic_name = magic_name.lstrip(prefilter.ESC_MAGIC) |
|
2475 | 2491 | return self.run_line_magic(magic_name, magic_arg_s, _stack_depth=2) |
|
2476 | 2492 | |
|
2477 | 2493 | #------------------------------------------------------------------------- |
|
2478 | 2494 | # Things related to macros |
|
2479 | 2495 | #------------------------------------------------------------------------- |
|
2480 | 2496 | |
|
2481 | 2497 | def define_macro(self, name, themacro): |
|
2482 | 2498 | """Define a new macro |
|
2483 | 2499 | |
|
2484 | 2500 | Parameters |
|
2485 | 2501 | ---------- |
|
2486 | 2502 | name : str |
|
2487 | 2503 | The name of the macro. |
|
2488 | 2504 | themacro : str or Macro |
|
2489 | 2505 | The action to do upon invoking the macro. If a string, a new |
|
2490 | 2506 | Macro object is created by passing the string to it. |
|
2491 | 2507 | """ |
|
2492 | 2508 | |
|
2493 | 2509 | from IPython.core import macro |
|
2494 | 2510 | |
|
2495 | 2511 | if isinstance(themacro, str): |
|
2496 | 2512 | themacro = macro.Macro(themacro) |
|
2497 | 2513 | if not isinstance(themacro, macro.Macro): |
|
2498 | 2514 | raise ValueError('A macro must be a string or a Macro instance.') |
|
2499 | 2515 | self.user_ns[name] = themacro |
|
2500 | 2516 | |
|
2501 | 2517 | #------------------------------------------------------------------------- |
|
2502 | 2518 | # Things related to the running of system commands |
|
2503 | 2519 | #------------------------------------------------------------------------- |
|
2504 | 2520 | |
|
2505 | 2521 | def system_piped(self, cmd): |
|
2506 | 2522 | """Call the given cmd in a subprocess, piping stdout/err |
|
2507 | 2523 | |
|
2508 | 2524 | Parameters |
|
2509 | 2525 | ---------- |
|
2510 | 2526 | cmd : str |
|
2511 | 2527 | Command to execute (can not end in '&', as background processes are |
|
2512 | 2528 | not supported. Should not be a command that expects input |
|
2513 | 2529 | other than simple text. |
|
2514 | 2530 | """ |
|
2515 | 2531 | if cmd.rstrip().endswith('&'): |
|
2516 | 2532 | # this is *far* from a rigorous test |
|
2517 | 2533 | # We do not support backgrounding processes because we either use |
|
2518 | 2534 | # pexpect or pipes to read from. Users can always just call |
|
2519 | 2535 | # os.system() or use ip.system=ip.system_raw |
|
2520 | 2536 | # if they really want a background process. |
|
2521 | 2537 | raise OSError("Background processes not supported.") |
|
2522 | 2538 | |
|
2523 | 2539 | # we explicitly do NOT return the subprocess status code, because |
|
2524 | 2540 | # a non-None value would trigger :func:`sys.displayhook` calls. |
|
2525 | 2541 | # Instead, we store the exit_code in user_ns. |
|
2526 | 2542 | self.user_ns['_exit_code'] = system(self.var_expand(cmd, depth=1)) |
|
2527 | 2543 | |
|
2528 | 2544 | def system_raw(self, cmd): |
|
2529 | 2545 | """Call the given cmd in a subprocess using os.system on Windows or |
|
2530 | 2546 | subprocess.call using the system shell on other platforms. |
|
2531 | 2547 | |
|
2532 | 2548 | Parameters |
|
2533 | 2549 | ---------- |
|
2534 | 2550 | cmd : str |
|
2535 | 2551 | Command to execute. |
|
2536 | 2552 | """ |
|
2537 | 2553 | cmd = self.var_expand(cmd, depth=1) |
|
2538 | 2554 | # warn if there is an IPython magic alternative. |
|
2539 | 2555 | main_cmd = cmd.split()[0] |
|
2540 | 2556 | has_magic_alternatives = ("pip", "conda", "cd") |
|
2541 | 2557 | |
|
2542 | 2558 | if main_cmd in has_magic_alternatives: |
|
2543 | 2559 | warnings.warn( |
|
2544 | 2560 | ( |
|
2545 | 2561 | "You executed the system command !{0} which may not work " |
|
2546 | 2562 | "as expected. Try the IPython magic %{0} instead." |
|
2547 | 2563 | ).format(main_cmd) |
|
2548 | 2564 | ) |
|
2549 | 2565 | |
|
2550 | 2566 | # protect os.system from UNC paths on Windows, which it can't handle: |
|
2551 | 2567 | if sys.platform == 'win32': |
|
2552 | 2568 | from IPython.utils._process_win32 import AvoidUNCPath |
|
2553 | 2569 | with AvoidUNCPath() as path: |
|
2554 | 2570 | if path is not None: |
|
2555 | 2571 | cmd = '"pushd %s &&"%s' % (path, cmd) |
|
2556 | 2572 | try: |
|
2557 | 2573 | ec = os.system(cmd) |
|
2558 | 2574 | except KeyboardInterrupt: |
|
2559 | 2575 | print('\n' + self.get_exception_only(), file=sys.stderr) |
|
2560 | 2576 | ec = -2 |
|
2561 | 2577 | else: |
|
2562 | 2578 | # For posix the result of the subprocess.call() below is an exit |
|
2563 | 2579 | # code, which by convention is zero for success, positive for |
|
2564 | 2580 | # program failure. Exit codes above 128 are reserved for signals, |
|
2565 | 2581 | # and the formula for converting a signal to an exit code is usually |
|
2566 | 2582 | # signal_number+128. To more easily differentiate between exit |
|
2567 | 2583 | # codes and signals, ipython uses negative numbers. For instance |
|
2568 | 2584 | # since control-c is signal 2 but exit code 130, ipython's |
|
2569 | 2585 | # _exit_code variable will read -2. Note that some shells like |
|
2570 | 2586 | # csh and fish don't follow sh/bash conventions for exit codes. |
|
2571 | 2587 | executable = os.environ.get('SHELL', None) |
|
2572 | 2588 | try: |
|
2573 | 2589 | # Use env shell instead of default /bin/sh |
|
2574 | 2590 | ec = subprocess.call(cmd, shell=True, executable=executable) |
|
2575 | 2591 | except KeyboardInterrupt: |
|
2576 | 2592 | # intercept control-C; a long traceback is not useful here |
|
2577 | 2593 | print('\n' + self.get_exception_only(), file=sys.stderr) |
|
2578 | 2594 | ec = 130 |
|
2579 | 2595 | if ec > 128: |
|
2580 | 2596 | ec = -(ec - 128) |
|
2581 | 2597 | |
|
2582 | 2598 | # We explicitly do NOT return the subprocess status code, because |
|
2583 | 2599 | # a non-None value would trigger :func:`sys.displayhook` calls. |
|
2584 | 2600 | # Instead, we store the exit_code in user_ns. Note the semantics |
|
2585 | 2601 | # of _exit_code: for control-c, _exit_code == -signal.SIGNIT, |
|
2586 | 2602 | # but raising SystemExit(_exit_code) will give status 254! |
|
2587 | 2603 | self.user_ns['_exit_code'] = ec |
|
2588 | 2604 | |
|
2589 | 2605 | # use piped system by default, because it is better behaved |
|
2590 | 2606 | system = system_piped |
|
2591 | 2607 | |
|
2592 | 2608 | def getoutput(self, cmd, split=True, depth=0): |
|
2593 | 2609 | """Get output (possibly including stderr) from a subprocess. |
|
2594 | 2610 | |
|
2595 | 2611 | Parameters |
|
2596 | 2612 | ---------- |
|
2597 | 2613 | cmd : str |
|
2598 | 2614 | Command to execute (can not end in '&', as background processes are |
|
2599 | 2615 | not supported. |
|
2600 | 2616 | split : bool, optional |
|
2601 | 2617 | If True, split the output into an IPython SList. Otherwise, an |
|
2602 | 2618 | IPython LSString is returned. These are objects similar to normal |
|
2603 | 2619 | lists and strings, with a few convenience attributes for easier |
|
2604 | 2620 | manipulation of line-based output. You can use '?' on them for |
|
2605 | 2621 | details. |
|
2606 | 2622 | depth : int, optional |
|
2607 | 2623 | How many frames above the caller are the local variables which should |
|
2608 | 2624 | be expanded in the command string? The default (0) assumes that the |
|
2609 | 2625 | expansion variables are in the stack frame calling this function. |
|
2610 | 2626 | """ |
|
2611 | 2627 | if cmd.rstrip().endswith('&'): |
|
2612 | 2628 | # this is *far* from a rigorous test |
|
2613 | 2629 | raise OSError("Background processes not supported.") |
|
2614 | 2630 | out = getoutput(self.var_expand(cmd, depth=depth+1)) |
|
2615 | 2631 | if split: |
|
2616 | 2632 | out = SList(out.splitlines()) |
|
2617 | 2633 | else: |
|
2618 | 2634 | out = LSString(out) |
|
2619 | 2635 | return out |
|
2620 | 2636 | |
|
2621 | 2637 | #------------------------------------------------------------------------- |
|
2622 | 2638 | # Things related to aliases |
|
2623 | 2639 | #------------------------------------------------------------------------- |
|
2624 | 2640 | |
|
2625 | 2641 | def init_alias(self): |
|
2626 | 2642 | self.alias_manager = AliasManager(shell=self, parent=self) |
|
2627 | 2643 | self.configurables.append(self.alias_manager) |
|
2628 | 2644 | |
|
2629 | 2645 | #------------------------------------------------------------------------- |
|
2630 | 2646 | # Things related to extensions |
|
2631 | 2647 | #------------------------------------------------------------------------- |
|
2632 | 2648 | |
|
2633 | 2649 | def init_extension_manager(self): |
|
2634 | 2650 | self.extension_manager = ExtensionManager(shell=self, parent=self) |
|
2635 | 2651 | self.configurables.append(self.extension_manager) |
|
2636 | 2652 | |
|
2637 | 2653 | #------------------------------------------------------------------------- |
|
2638 | 2654 | # Things related to payloads |
|
2639 | 2655 | #------------------------------------------------------------------------- |
|
2640 | 2656 | |
|
2641 | 2657 | def init_payload(self): |
|
2642 | 2658 | self.payload_manager = PayloadManager(parent=self) |
|
2643 | 2659 | self.configurables.append(self.payload_manager) |
|
2644 | 2660 | |
|
2645 | 2661 | #------------------------------------------------------------------------- |
|
2646 | 2662 | # Things related to the prefilter |
|
2647 | 2663 | #------------------------------------------------------------------------- |
|
2648 | 2664 | |
|
2649 | 2665 | def init_prefilter(self): |
|
2650 | 2666 | self.prefilter_manager = PrefilterManager(shell=self, parent=self) |
|
2651 | 2667 | self.configurables.append(self.prefilter_manager) |
|
2652 | 2668 | # Ultimately this will be refactored in the new interpreter code, but |
|
2653 | 2669 | # for now, we should expose the main prefilter method (there's legacy |
|
2654 | 2670 | # code out there that may rely on this). |
|
2655 | 2671 | self.prefilter = self.prefilter_manager.prefilter_lines |
|
2656 | 2672 | |
|
2657 | 2673 | def auto_rewrite_input(self, cmd): |
|
2658 | 2674 | """Print to the screen the rewritten form of the user's command. |
|
2659 | 2675 | |
|
2660 | 2676 | This shows visual feedback by rewriting input lines that cause |
|
2661 | 2677 | automatic calling to kick in, like:: |
|
2662 | 2678 | |
|
2663 | 2679 | /f x |
|
2664 | 2680 | |
|
2665 | 2681 | into:: |
|
2666 | 2682 | |
|
2667 | 2683 | ------> f(x) |
|
2668 | 2684 | |
|
2669 | 2685 | after the user's input prompt. This helps the user understand that the |
|
2670 | 2686 | input line was transformed automatically by IPython. |
|
2671 | 2687 | """ |
|
2672 | 2688 | if not self.show_rewritten_input: |
|
2673 | 2689 | return |
|
2674 | 2690 | |
|
2675 | 2691 | # This is overridden in TerminalInteractiveShell to use fancy prompts |
|
2676 | 2692 | print("------> " + cmd) |
|
2677 | 2693 | |
|
2678 | 2694 | #------------------------------------------------------------------------- |
|
2679 | 2695 | # Things related to extracting values/expressions from kernel and user_ns |
|
2680 | 2696 | #------------------------------------------------------------------------- |
|
2681 | 2697 | |
|
2682 | 2698 | def _user_obj_error(self): |
|
2683 | 2699 | """return simple exception dict |
|
2684 | 2700 | |
|
2685 | 2701 | for use in user_expressions |
|
2686 | 2702 | """ |
|
2687 | 2703 | |
|
2688 | 2704 | etype, evalue, tb = self._get_exc_info() |
|
2689 | 2705 | stb = self.InteractiveTB.get_exception_only(etype, evalue) |
|
2690 | 2706 | |
|
2691 | 2707 | exc_info = { |
|
2692 | 2708 | "status": "error", |
|
2693 | 2709 | "traceback": stb, |
|
2694 | 2710 | "ename": etype.__name__, |
|
2695 | 2711 | "evalue": py3compat.safe_unicode(evalue), |
|
2696 | 2712 | } |
|
2697 | 2713 | |
|
2698 | 2714 | return exc_info |
|
2699 | 2715 | |
|
2700 | 2716 | def _format_user_obj(self, obj): |
|
2701 | 2717 | """format a user object to display dict |
|
2702 | 2718 | |
|
2703 | 2719 | for use in user_expressions |
|
2704 | 2720 | """ |
|
2705 | 2721 | |
|
2706 | 2722 | data, md = self.display_formatter.format(obj) |
|
2707 | 2723 | value = { |
|
2708 | 2724 | 'status' : 'ok', |
|
2709 | 2725 | 'data' : data, |
|
2710 | 2726 | 'metadata' : md, |
|
2711 | 2727 | } |
|
2712 | 2728 | return value |
|
2713 | 2729 | |
|
2714 | 2730 | def user_expressions(self, expressions): |
|
2715 | 2731 | """Evaluate a dict of expressions in the user's namespace. |
|
2716 | 2732 | |
|
2717 | 2733 | Parameters |
|
2718 | 2734 | ---------- |
|
2719 | 2735 | expressions : dict |
|
2720 | 2736 | A dict with string keys and string values. The expression values |
|
2721 | 2737 | should be valid Python expressions, each of which will be evaluated |
|
2722 | 2738 | in the user namespace. |
|
2723 | 2739 | |
|
2724 | 2740 | Returns |
|
2725 | 2741 | ------- |
|
2726 | 2742 | A dict, keyed like the input expressions dict, with the rich mime-typed |
|
2727 | 2743 | display_data of each value. |
|
2728 | 2744 | """ |
|
2729 | 2745 | out = {} |
|
2730 | 2746 | user_ns = self.user_ns |
|
2731 | 2747 | global_ns = self.user_global_ns |
|
2732 | 2748 | |
|
2733 | 2749 | for key, expr in expressions.items(): |
|
2734 | 2750 | try: |
|
2735 | 2751 | value = self._format_user_obj(eval(expr, global_ns, user_ns)) |
|
2736 | 2752 | except: |
|
2737 | 2753 | value = self._user_obj_error() |
|
2738 | 2754 | out[key] = value |
|
2739 | 2755 | return out |
|
2740 | 2756 | |
|
2741 | 2757 | #------------------------------------------------------------------------- |
|
2742 | 2758 | # Things related to the running of code |
|
2743 | 2759 | #------------------------------------------------------------------------- |
|
2744 | 2760 | |
|
2745 | 2761 | def ex(self, cmd): |
|
2746 | 2762 | """Execute a normal python statement in user namespace.""" |
|
2747 | 2763 | with self.builtin_trap: |
|
2748 | 2764 | exec(cmd, self.user_global_ns, self.user_ns) |
|
2749 | 2765 | |
|
2750 | 2766 | def ev(self, expr): |
|
2751 | 2767 | """Evaluate python expression expr in user namespace. |
|
2752 | 2768 | |
|
2753 | 2769 | Returns the result of evaluation |
|
2754 | 2770 | """ |
|
2755 | 2771 | with self.builtin_trap: |
|
2756 | 2772 | return eval(expr, self.user_global_ns, self.user_ns) |
|
2757 | 2773 | |
|
2758 | 2774 | def safe_execfile(self, fname, *where, exit_ignore=False, raise_exceptions=False, shell_futures=False): |
|
2759 | 2775 | """A safe version of the builtin execfile(). |
|
2760 | 2776 | |
|
2761 | 2777 | This version will never throw an exception, but instead print |
|
2762 | 2778 | helpful error messages to the screen. This only works on pure |
|
2763 | 2779 | Python files with the .py extension. |
|
2764 | 2780 | |
|
2765 | 2781 | Parameters |
|
2766 | 2782 | ---------- |
|
2767 | 2783 | fname : string |
|
2768 | 2784 | The name of the file to be executed. |
|
2769 | 2785 | *where : tuple |
|
2770 | 2786 | One or two namespaces, passed to execfile() as (globals,locals). |
|
2771 | 2787 | If only one is given, it is passed as both. |
|
2772 | 2788 | exit_ignore : bool (False) |
|
2773 | 2789 | If True, then silence SystemExit for non-zero status (it is always |
|
2774 | 2790 | silenced for zero status, as it is so common). |
|
2775 | 2791 | raise_exceptions : bool (False) |
|
2776 | 2792 | If True raise exceptions everywhere. Meant for testing. |
|
2777 | 2793 | shell_futures : bool (False) |
|
2778 | 2794 | If True, the code will share future statements with the interactive |
|
2779 | 2795 | shell. It will both be affected by previous __future__ imports, and |
|
2780 | 2796 | any __future__ imports in the code will affect the shell. If False, |
|
2781 | 2797 | __future__ imports are not shared in either direction. |
|
2782 | 2798 | |
|
2783 | 2799 | """ |
|
2784 | 2800 | fname = Path(fname).expanduser().resolve() |
|
2785 | 2801 | |
|
2786 | 2802 | # Make sure we can open the file |
|
2787 | 2803 | try: |
|
2788 | 2804 | with fname.open("rb"): |
|
2789 | 2805 | pass |
|
2790 | 2806 | except: |
|
2791 | 2807 | warn('Could not open file <%s> for safe execution.' % fname) |
|
2792 | 2808 | return |
|
2793 | 2809 | |
|
2794 | 2810 | # Find things also in current directory. This is needed to mimic the |
|
2795 | 2811 | # behavior of running a script from the system command line, where |
|
2796 | 2812 | # Python inserts the script's directory into sys.path |
|
2797 | 2813 | dname = str(fname.parent) |
|
2798 | 2814 | |
|
2799 | 2815 | with prepended_to_syspath(dname), self.builtin_trap: |
|
2800 | 2816 | try: |
|
2801 | 2817 | glob, loc = (where + (None, ))[:2] |
|
2802 | 2818 | py3compat.execfile( |
|
2803 | 2819 | fname, glob, loc, |
|
2804 | 2820 | self.compile if shell_futures else None) |
|
2805 | 2821 | except SystemExit as status: |
|
2806 | 2822 | # If the call was made with 0 or None exit status (sys.exit(0) |
|
2807 | 2823 | # or sys.exit() ), don't bother showing a traceback, as both of |
|
2808 | 2824 | # these are considered normal by the OS: |
|
2809 | 2825 | # > python -c'import sys;sys.exit(0)'; echo $? |
|
2810 | 2826 | # 0 |
|
2811 | 2827 | # > python -c'import sys;sys.exit()'; echo $? |
|
2812 | 2828 | # 0 |
|
2813 | 2829 | # For other exit status, we show the exception unless |
|
2814 | 2830 | # explicitly silenced, but only in short form. |
|
2815 | 2831 | if status.code: |
|
2816 | 2832 | if raise_exceptions: |
|
2817 | 2833 | raise |
|
2818 | 2834 | if not exit_ignore: |
|
2819 | 2835 | self.showtraceback(exception_only=True) |
|
2820 | 2836 | except: |
|
2821 | 2837 | if raise_exceptions: |
|
2822 | 2838 | raise |
|
2823 | 2839 | # tb offset is 2 because we wrap execfile |
|
2824 | 2840 | self.showtraceback(tb_offset=2) |
|
2825 | 2841 | |
|
2826 | 2842 | def safe_execfile_ipy(self, fname, shell_futures=False, raise_exceptions=False): |
|
2827 | 2843 | """Like safe_execfile, but for .ipy or .ipynb files with IPython syntax. |
|
2828 | 2844 | |
|
2829 | 2845 | Parameters |
|
2830 | 2846 | ---------- |
|
2831 | 2847 | fname : str |
|
2832 | 2848 | The name of the file to execute. The filename must have a |
|
2833 | 2849 | .ipy or .ipynb extension. |
|
2834 | 2850 | shell_futures : bool (False) |
|
2835 | 2851 | If True, the code will share future statements with the interactive |
|
2836 | 2852 | shell. It will both be affected by previous __future__ imports, and |
|
2837 | 2853 | any __future__ imports in the code will affect the shell. If False, |
|
2838 | 2854 | __future__ imports are not shared in either direction. |
|
2839 | 2855 | raise_exceptions : bool (False) |
|
2840 | 2856 | If True raise exceptions everywhere. Meant for testing. |
|
2841 | 2857 | """ |
|
2842 | 2858 | fname = Path(fname).expanduser().resolve() |
|
2843 | 2859 | |
|
2844 | 2860 | # Make sure we can open the file |
|
2845 | 2861 | try: |
|
2846 | 2862 | with fname.open("rb"): |
|
2847 | 2863 | pass |
|
2848 | 2864 | except: |
|
2849 | 2865 | warn('Could not open file <%s> for safe execution.' % fname) |
|
2850 | 2866 | return |
|
2851 | 2867 | |
|
2852 | 2868 | # Find things also in current directory. This is needed to mimic the |
|
2853 | 2869 | # behavior of running a script from the system command line, where |
|
2854 | 2870 | # Python inserts the script's directory into sys.path |
|
2855 | 2871 | dname = str(fname.parent) |
|
2856 | 2872 | |
|
2857 | 2873 | def get_cells(): |
|
2858 | 2874 | """generator for sequence of code blocks to run""" |
|
2859 | 2875 | if fname.suffix == ".ipynb": |
|
2860 | 2876 | from nbformat import read |
|
2861 | 2877 | nb = read(fname, as_version=4) |
|
2862 | 2878 | if not nb.cells: |
|
2863 | 2879 | return |
|
2864 | 2880 | for cell in nb.cells: |
|
2865 | 2881 | if cell.cell_type == 'code': |
|
2866 | 2882 | yield cell.source |
|
2867 | 2883 | else: |
|
2868 | 2884 | yield fname.read_text(encoding="utf-8") |
|
2869 | 2885 | |
|
2870 | 2886 | with prepended_to_syspath(dname): |
|
2871 | 2887 | try: |
|
2872 | 2888 | for cell in get_cells(): |
|
2873 | 2889 | result = self.run_cell(cell, silent=True, shell_futures=shell_futures) |
|
2874 | 2890 | if raise_exceptions: |
|
2875 | 2891 | result.raise_error() |
|
2876 | 2892 | elif not result.success: |
|
2877 | 2893 | break |
|
2878 | 2894 | except: |
|
2879 | 2895 | if raise_exceptions: |
|
2880 | 2896 | raise |
|
2881 | 2897 | self.showtraceback() |
|
2882 | 2898 | warn('Unknown failure executing file: <%s>' % fname) |
|
2883 | 2899 | |
|
2884 | 2900 | def safe_run_module(self, mod_name, where): |
|
2885 | 2901 | """A safe version of runpy.run_module(). |
|
2886 | 2902 | |
|
2887 | 2903 | This version will never throw an exception, but instead print |
|
2888 | 2904 | helpful error messages to the screen. |
|
2889 | 2905 | |
|
2890 | 2906 | `SystemExit` exceptions with status code 0 or None are ignored. |
|
2891 | 2907 | |
|
2892 | 2908 | Parameters |
|
2893 | 2909 | ---------- |
|
2894 | 2910 | mod_name : string |
|
2895 | 2911 | The name of the module to be executed. |
|
2896 | 2912 | where : dict |
|
2897 | 2913 | The globals namespace. |
|
2898 | 2914 | """ |
|
2899 | 2915 | try: |
|
2900 | 2916 | try: |
|
2901 | 2917 | where.update( |
|
2902 | 2918 | runpy.run_module(str(mod_name), run_name="__main__", |
|
2903 | 2919 | alter_sys=True) |
|
2904 | 2920 | ) |
|
2905 | 2921 | except SystemExit as status: |
|
2906 | 2922 | if status.code: |
|
2907 | 2923 | raise |
|
2908 | 2924 | except: |
|
2909 | 2925 | self.showtraceback() |
|
2910 | 2926 | warn('Unknown failure executing module: <%s>' % mod_name) |
|
2911 | 2927 | |
|
2912 | 2928 | def run_cell( |
|
2913 | 2929 | self, |
|
2914 | 2930 | raw_cell, |
|
2915 | 2931 | store_history=False, |
|
2916 | 2932 | silent=False, |
|
2917 | 2933 | shell_futures=True, |
|
2918 | 2934 | cell_id=None, |
|
2919 | 2935 | ): |
|
2920 | 2936 | """Run a complete IPython cell. |
|
2921 | 2937 | |
|
2922 | 2938 | Parameters |
|
2923 | 2939 | ---------- |
|
2924 | 2940 | raw_cell : str |
|
2925 | 2941 | The code (including IPython code such as %magic functions) to run. |
|
2926 | 2942 | store_history : bool |
|
2927 | 2943 | If True, the raw and translated cell will be stored in IPython's |
|
2928 | 2944 | history. For user code calling back into IPython's machinery, this |
|
2929 | 2945 | should be set to False. |
|
2930 | 2946 | silent : bool |
|
2931 | 2947 | If True, avoid side-effects, such as implicit displayhooks and |
|
2932 | 2948 | and logging. silent=True forces store_history=False. |
|
2933 | 2949 | shell_futures : bool |
|
2934 | 2950 | If True, the code will share future statements with the interactive |
|
2935 | 2951 | shell. It will both be affected by previous __future__ imports, and |
|
2936 | 2952 | any __future__ imports in the code will affect the shell. If False, |
|
2937 | 2953 | __future__ imports are not shared in either direction. |
|
2938 | 2954 | |
|
2939 | 2955 | Returns |
|
2940 | 2956 | ------- |
|
2941 | 2957 | result : :class:`ExecutionResult` |
|
2942 | 2958 | """ |
|
2943 | 2959 | result = None |
|
2944 | 2960 | try: |
|
2945 | 2961 | result = self._run_cell( |
|
2946 | 2962 | raw_cell, store_history, silent, shell_futures, cell_id |
|
2947 | 2963 | ) |
|
2948 | 2964 | finally: |
|
2949 | 2965 | self.events.trigger('post_execute') |
|
2950 | 2966 | if not silent: |
|
2951 | 2967 | self.events.trigger('post_run_cell', result) |
|
2952 | 2968 | return result |
|
2953 | 2969 | |
|
2954 | 2970 | def _run_cell( |
|
2955 | 2971 | self, |
|
2956 | 2972 | raw_cell: str, |
|
2957 | 2973 | store_history: bool, |
|
2958 | 2974 | silent: bool, |
|
2959 | 2975 | shell_futures: bool, |
|
2960 | 2976 | cell_id: str, |
|
2961 | 2977 | ) -> ExecutionResult: |
|
2962 | 2978 | """Internal method to run a complete IPython cell.""" |
|
2963 | 2979 | |
|
2964 | 2980 | # we need to avoid calling self.transform_cell multiple time on the same thing |
|
2965 | 2981 | # so we need to store some results: |
|
2966 | 2982 | preprocessing_exc_tuple = None |
|
2967 | 2983 | try: |
|
2968 | 2984 | transformed_cell = self.transform_cell(raw_cell) |
|
2969 | 2985 | except Exception: |
|
2970 | 2986 | transformed_cell = raw_cell |
|
2971 | 2987 | preprocessing_exc_tuple = sys.exc_info() |
|
2972 | 2988 | |
|
2973 | 2989 | assert transformed_cell is not None |
|
2974 | 2990 | coro = self.run_cell_async( |
|
2975 | 2991 | raw_cell, |
|
2976 | 2992 | store_history=store_history, |
|
2977 | 2993 | silent=silent, |
|
2978 | 2994 | shell_futures=shell_futures, |
|
2979 | 2995 | transformed_cell=transformed_cell, |
|
2980 | 2996 | preprocessing_exc_tuple=preprocessing_exc_tuple, |
|
2981 | 2997 | cell_id=cell_id, |
|
2982 | 2998 | ) |
|
2983 | 2999 | |
|
2984 | 3000 | # run_cell_async is async, but may not actually need an eventloop. |
|
2985 | 3001 | # when this is the case, we want to run it using the pseudo_sync_runner |
|
2986 | 3002 | # so that code can invoke eventloops (for example via the %run , and |
|
2987 | 3003 | # `%paste` magic. |
|
2988 | 3004 | if self.trio_runner: |
|
2989 | 3005 | runner = self.trio_runner |
|
2990 | 3006 | elif self.should_run_async( |
|
2991 | 3007 | raw_cell, |
|
2992 | 3008 | transformed_cell=transformed_cell, |
|
2993 | 3009 | preprocessing_exc_tuple=preprocessing_exc_tuple, |
|
2994 | 3010 | ): |
|
2995 | 3011 | runner = self.loop_runner |
|
2996 | 3012 | else: |
|
2997 | 3013 | runner = _pseudo_sync_runner |
|
2998 | 3014 | |
|
2999 | 3015 | try: |
|
3000 | 3016 | return runner(coro) |
|
3001 | 3017 | except BaseException as e: |
|
3002 | 3018 | info = ExecutionInfo( |
|
3003 | 3019 | raw_cell, store_history, silent, shell_futures, cell_id |
|
3004 | 3020 | ) |
|
3005 | 3021 | result = ExecutionResult(info) |
|
3006 | 3022 | result.error_in_exec = e |
|
3007 | 3023 | self.showtraceback(running_compiled_code=True) |
|
3008 | 3024 | return result |
|
3009 | 3025 | |
|
3010 | 3026 | def should_run_async( |
|
3011 | 3027 | self, raw_cell: str, *, transformed_cell=None, preprocessing_exc_tuple=None |
|
3012 | 3028 | ) -> bool: |
|
3013 | 3029 | """Return whether a cell should be run asynchronously via a coroutine runner |
|
3014 | 3030 | |
|
3015 | 3031 | Parameters |
|
3016 | 3032 | ---------- |
|
3017 | 3033 | raw_cell : str |
|
3018 | 3034 | The code to be executed |
|
3019 | 3035 | |
|
3020 | 3036 | Returns |
|
3021 | 3037 | ------- |
|
3022 | 3038 | result: bool |
|
3023 | 3039 | Whether the code needs to be run with a coroutine runner or not |
|
3024 | 3040 | .. versionadded:: 7.0 |
|
3025 | 3041 | """ |
|
3026 | 3042 | if not self.autoawait: |
|
3027 | 3043 | return False |
|
3028 | 3044 | if preprocessing_exc_tuple is not None: |
|
3029 | 3045 | return False |
|
3030 | 3046 | assert preprocessing_exc_tuple is None |
|
3031 | 3047 | if transformed_cell is None: |
|
3032 | 3048 | warnings.warn( |
|
3033 | 3049 | "`should_run_async` will not call `transform_cell`" |
|
3034 | 3050 | " automatically in the future. Please pass the result to" |
|
3035 | 3051 | " `transformed_cell` argument and any exception that happen" |
|
3036 | 3052 | " during the" |
|
3037 | 3053 | "transform in `preprocessing_exc_tuple` in" |
|
3038 | 3054 | " IPython 7.17 and above.", |
|
3039 | 3055 | DeprecationWarning, |
|
3040 | 3056 | stacklevel=2, |
|
3041 | 3057 | ) |
|
3042 | 3058 | try: |
|
3043 | 3059 | cell = self.transform_cell(raw_cell) |
|
3044 | 3060 | except Exception: |
|
3045 | 3061 | # any exception during transform will be raised |
|
3046 | 3062 | # prior to execution |
|
3047 | 3063 | return False |
|
3048 | 3064 | else: |
|
3049 | 3065 | cell = transformed_cell |
|
3050 | 3066 | return _should_be_async(cell) |
|
3051 | 3067 | |
|
3052 | 3068 | async def run_cell_async( |
|
3053 | 3069 | self, |
|
3054 | 3070 | raw_cell: str, |
|
3055 | 3071 | store_history=False, |
|
3056 | 3072 | silent=False, |
|
3057 | 3073 | shell_futures=True, |
|
3058 | 3074 | *, |
|
3059 | 3075 | transformed_cell: Optional[str] = None, |
|
3060 | 3076 | preprocessing_exc_tuple: Optional[Any] = None, |
|
3061 | 3077 | cell_id=None, |
|
3062 | 3078 | ) -> ExecutionResult: |
|
3063 | 3079 | """Run a complete IPython cell asynchronously. |
|
3064 | 3080 | |
|
3065 | 3081 | Parameters |
|
3066 | 3082 | ---------- |
|
3067 | 3083 | raw_cell : str |
|
3068 | 3084 | The code (including IPython code such as %magic functions) to run. |
|
3069 | 3085 | store_history : bool |
|
3070 | 3086 | If True, the raw and translated cell will be stored in IPython's |
|
3071 | 3087 | history. For user code calling back into IPython's machinery, this |
|
3072 | 3088 | should be set to False. |
|
3073 | 3089 | silent : bool |
|
3074 | 3090 | If True, avoid side-effects, such as implicit displayhooks and |
|
3075 | 3091 | and logging. silent=True forces store_history=False. |
|
3076 | 3092 | shell_futures : bool |
|
3077 | 3093 | If True, the code will share future statements with the interactive |
|
3078 | 3094 | shell. It will both be affected by previous __future__ imports, and |
|
3079 | 3095 | any __future__ imports in the code will affect the shell. If False, |
|
3080 | 3096 | __future__ imports are not shared in either direction. |
|
3081 | 3097 | transformed_cell: str |
|
3082 | 3098 | cell that was passed through transformers |
|
3083 | 3099 | preprocessing_exc_tuple: |
|
3084 | 3100 | trace if the transformation failed. |
|
3085 | 3101 | |
|
3086 | 3102 | Returns |
|
3087 | 3103 | ------- |
|
3088 | 3104 | result : :class:`ExecutionResult` |
|
3089 | 3105 | |
|
3090 | 3106 | .. versionadded:: 7.0 |
|
3091 | 3107 | """ |
|
3092 | 3108 | info = ExecutionInfo(raw_cell, store_history, silent, shell_futures, cell_id) |
|
3093 | 3109 | result = ExecutionResult(info) |
|
3094 | 3110 | |
|
3095 | 3111 | if (not raw_cell) or raw_cell.isspace(): |
|
3096 | 3112 | self.last_execution_succeeded = True |
|
3097 | 3113 | self.last_execution_result = result |
|
3098 | 3114 | return result |
|
3099 | 3115 | |
|
3100 | 3116 | if silent: |
|
3101 | 3117 | store_history = False |
|
3102 | 3118 | |
|
3103 | 3119 | if store_history: |
|
3104 | 3120 | result.execution_count = self.execution_count |
|
3105 | 3121 | |
|
3106 | 3122 | def error_before_exec(value): |
|
3107 | 3123 | if store_history: |
|
3108 | 3124 | self.execution_count += 1 |
|
3109 | 3125 | result.error_before_exec = value |
|
3110 | 3126 | self.last_execution_succeeded = False |
|
3111 | 3127 | self.last_execution_result = result |
|
3112 | 3128 | return result |
|
3113 | 3129 | |
|
3114 | 3130 | self.events.trigger('pre_execute') |
|
3115 | 3131 | if not silent: |
|
3116 | 3132 | self.events.trigger('pre_run_cell', info) |
|
3117 | 3133 | |
|
3118 | 3134 | if transformed_cell is None: |
|
3119 | 3135 | warnings.warn( |
|
3120 | 3136 | "`run_cell_async` will not call `transform_cell`" |
|
3121 | 3137 | " automatically in the future. Please pass the result to" |
|
3122 | 3138 | " `transformed_cell` argument and any exception that happen" |
|
3123 | 3139 | " during the" |
|
3124 | 3140 | "transform in `preprocessing_exc_tuple` in" |
|
3125 | 3141 | " IPython 7.17 and above.", |
|
3126 | 3142 | DeprecationWarning, |
|
3127 | 3143 | stacklevel=2, |
|
3128 | 3144 | ) |
|
3129 | 3145 | # If any of our input transformation (input_transformer_manager or |
|
3130 | 3146 | # prefilter_manager) raises an exception, we store it in this variable |
|
3131 | 3147 | # so that we can display the error after logging the input and storing |
|
3132 | 3148 | # it in the history. |
|
3133 | 3149 | try: |
|
3134 | 3150 | cell = self.transform_cell(raw_cell) |
|
3135 | 3151 | except Exception: |
|
3136 | 3152 | preprocessing_exc_tuple = sys.exc_info() |
|
3137 | 3153 | cell = raw_cell # cell has to exist so it can be stored/logged |
|
3138 | 3154 | else: |
|
3139 | 3155 | preprocessing_exc_tuple = None |
|
3140 | 3156 | else: |
|
3141 | 3157 | if preprocessing_exc_tuple is None: |
|
3142 | 3158 | cell = transformed_cell |
|
3143 | 3159 | else: |
|
3144 | 3160 | cell = raw_cell |
|
3145 | 3161 | |
|
3146 | 3162 | # Do NOT store paste/cpaste magic history |
|
3147 | 3163 | if "get_ipython().run_line_magic(" in cell and "paste" in cell: |
|
3148 | 3164 | store_history = False |
|
3149 | 3165 | |
|
3150 | 3166 | # Store raw and processed history |
|
3151 | 3167 | if store_history: |
|
3152 | 3168 | self.history_manager.store_inputs(self.execution_count, cell, raw_cell) |
|
3153 | 3169 | if not silent: |
|
3154 | 3170 | self.logger.log(cell, raw_cell) |
|
3155 | 3171 | |
|
3156 | 3172 | # Display the exception if input processing failed. |
|
3157 | 3173 | if preprocessing_exc_tuple is not None: |
|
3158 | 3174 | self.showtraceback(preprocessing_exc_tuple) |
|
3159 | 3175 | if store_history: |
|
3160 | 3176 | self.execution_count += 1 |
|
3161 | 3177 | return error_before_exec(preprocessing_exc_tuple[1]) |
|
3162 | 3178 | |
|
3163 | 3179 | # Our own compiler remembers the __future__ environment. If we want to |
|
3164 | 3180 | # run code with a separate __future__ environment, use the default |
|
3165 | 3181 | # compiler |
|
3166 | 3182 | compiler = self.compile if shell_futures else self.compiler_class() |
|
3167 | 3183 | |
|
3168 | 3184 | _run_async = False |
|
3169 | 3185 | |
|
3170 | 3186 | with self.builtin_trap: |
|
3171 | 3187 | cell_name = compiler.cache(cell, self.execution_count, raw_code=raw_cell) |
|
3172 | 3188 | |
|
3173 | 3189 | with self.display_trap: |
|
3174 | 3190 | # Compile to bytecode |
|
3175 | 3191 | try: |
|
3176 | 3192 | code_ast = compiler.ast_parse(cell, filename=cell_name) |
|
3177 | 3193 | except self.custom_exceptions as e: |
|
3178 | 3194 | etype, value, tb = sys.exc_info() |
|
3179 | 3195 | self.CustomTB(etype, value, tb) |
|
3180 | 3196 | return error_before_exec(e) |
|
3181 | 3197 | except IndentationError as e: |
|
3182 | 3198 | self.showindentationerror() |
|
3183 | 3199 | return error_before_exec(e) |
|
3184 | 3200 | except (OverflowError, SyntaxError, ValueError, TypeError, |
|
3185 | 3201 | MemoryError) as e: |
|
3186 | 3202 | self.showsyntaxerror() |
|
3187 | 3203 | return error_before_exec(e) |
|
3188 | 3204 | |
|
3189 | 3205 | # Apply AST transformations |
|
3190 | 3206 | try: |
|
3191 | 3207 | code_ast = self.transform_ast(code_ast) |
|
3192 | 3208 | except InputRejected as e: |
|
3193 | 3209 | self.showtraceback() |
|
3194 | 3210 | return error_before_exec(e) |
|
3195 | 3211 | |
|
3196 | 3212 | # Give the displayhook a reference to our ExecutionResult so it |
|
3197 | 3213 | # can fill in the output value. |
|
3198 | 3214 | self.displayhook.exec_result = result |
|
3199 | 3215 | |
|
3200 | 3216 | # Execute the user code |
|
3201 | 3217 | interactivity = "none" if silent else self.ast_node_interactivity |
|
3202 | 3218 | |
|
3219 | ||
|
3203 | 3220 | has_raised = await self.run_ast_nodes(code_ast.body, cell_name, |
|
3204 | 3221 | interactivity=interactivity, compiler=compiler, result=result) |
|
3205 | 3222 | |
|
3206 | 3223 | self.last_execution_succeeded = not has_raised |
|
3207 | 3224 | self.last_execution_result = result |
|
3208 | 3225 | |
|
3209 | 3226 | # Reset this so later displayed values do not modify the |
|
3210 | 3227 | # ExecutionResult |
|
3211 | 3228 | self.displayhook.exec_result = None |
|
3212 | 3229 | |
|
3213 | 3230 | if store_history: |
|
3214 | 3231 | # Write output to the database. Does nothing unless |
|
3215 | 3232 | # history output logging is enabled. |
|
3216 | 3233 | self.history_manager.store_output(self.execution_count) |
|
3217 | 3234 | # Each cell is a *single* input, regardless of how many lines it has |
|
3218 | 3235 | self.execution_count += 1 |
|
3219 | 3236 | |
|
3220 | 3237 | return result |
|
3221 | 3238 | |
|
3222 | 3239 | def transform_cell(self, raw_cell): |
|
3223 | 3240 | """Transform an input cell before parsing it. |
|
3224 | 3241 | |
|
3225 | 3242 | Static transformations, implemented in IPython.core.inputtransformer2, |
|
3226 | 3243 | deal with things like ``%magic`` and ``!system`` commands. |
|
3227 | 3244 | These run on all input. |
|
3228 | 3245 | Dynamic transformations, for things like unescaped magics and the exit |
|
3229 | 3246 | autocall, depend on the state of the interpreter. |
|
3230 | 3247 | These only apply to single line inputs. |
|
3231 | 3248 | |
|
3232 | 3249 | These string-based transformations are followed by AST transformations; |
|
3233 | 3250 | see :meth:`transform_ast`. |
|
3234 | 3251 | """ |
|
3235 | 3252 | # Static input transformations |
|
3236 | 3253 | cell = self.input_transformer_manager.transform_cell(raw_cell) |
|
3237 | 3254 | |
|
3238 | 3255 | if len(cell.splitlines()) == 1: |
|
3239 | 3256 | # Dynamic transformations - only applied for single line commands |
|
3240 | 3257 | with self.builtin_trap: |
|
3241 | 3258 | # use prefilter_lines to handle trailing newlines |
|
3242 | 3259 | # restore trailing newline for ast.parse |
|
3243 | 3260 | cell = self.prefilter_manager.prefilter_lines(cell) + '\n' |
|
3244 | 3261 | |
|
3245 | 3262 | lines = cell.splitlines(keepends=True) |
|
3246 | 3263 | for transform in self.input_transformers_post: |
|
3247 | 3264 | lines = transform(lines) |
|
3248 | 3265 | cell = ''.join(lines) |
|
3249 | 3266 | |
|
3250 | 3267 | return cell |
|
3251 | 3268 | |
|
3252 | 3269 | def transform_ast(self, node): |
|
3253 | 3270 | """Apply the AST transformations from self.ast_transformers |
|
3254 | 3271 | |
|
3255 | 3272 | Parameters |
|
3256 | 3273 | ---------- |
|
3257 | 3274 | node : ast.Node |
|
3258 | 3275 | The root node to be transformed. Typically called with the ast.Module |
|
3259 | 3276 | produced by parsing user input. |
|
3260 | 3277 | |
|
3261 | 3278 | Returns |
|
3262 | 3279 | ------- |
|
3263 | 3280 | An ast.Node corresponding to the node it was called with. Note that it |
|
3264 | 3281 | may also modify the passed object, so don't rely on references to the |
|
3265 | 3282 | original AST. |
|
3266 | 3283 | """ |
|
3267 | 3284 | for transformer in self.ast_transformers: |
|
3268 | 3285 | try: |
|
3269 | 3286 | node = transformer.visit(node) |
|
3270 | 3287 | except InputRejected: |
|
3271 | 3288 | # User-supplied AST transformers can reject an input by raising |
|
3272 | 3289 | # an InputRejected. Short-circuit in this case so that we |
|
3273 | 3290 | # don't unregister the transform. |
|
3274 | 3291 | raise |
|
3275 | 3292 | except Exception: |
|
3276 | 3293 | warn("AST transformer %r threw an error. It will be unregistered." % transformer) |
|
3277 | 3294 | self.ast_transformers.remove(transformer) |
|
3278 | 3295 | |
|
3279 | 3296 | if self.ast_transformers: |
|
3280 | 3297 | ast.fix_missing_locations(node) |
|
3281 | 3298 | return node |
|
3282 | 3299 | |
|
3283 | 3300 | async def run_ast_nodes( |
|
3284 | 3301 | self, |
|
3285 | 3302 | nodelist: ListType[stmt], |
|
3286 | 3303 | cell_name: str, |
|
3287 | 3304 | interactivity="last_expr", |
|
3288 | 3305 | compiler=compile, |
|
3289 | 3306 | result=None, |
|
3290 | 3307 | ): |
|
3291 | 3308 | """Run a sequence of AST nodes. The execution mode depends on the |
|
3292 | 3309 | interactivity parameter. |
|
3293 | 3310 | |
|
3294 | 3311 | Parameters |
|
3295 | 3312 | ---------- |
|
3296 | 3313 | nodelist : list |
|
3297 | 3314 | A sequence of AST nodes to run. |
|
3298 | 3315 | cell_name : str |
|
3299 | 3316 | Will be passed to the compiler as the filename of the cell. Typically |
|
3300 | 3317 | the value returned by ip.compile.cache(cell). |
|
3301 | 3318 | interactivity : str |
|
3302 | 3319 | 'all', 'last', 'last_expr' , 'last_expr_or_assign' or 'none', |
|
3303 | 3320 | specifying which nodes should be run interactively (displaying output |
|
3304 | 3321 | from expressions). 'last_expr' will run the last node interactively |
|
3305 | 3322 | only if it is an expression (i.e. expressions in loops or other blocks |
|
3306 | 3323 | are not displayed) 'last_expr_or_assign' will run the last expression |
|
3307 | 3324 | or the last assignment. Other values for this parameter will raise a |
|
3308 | 3325 | ValueError. |
|
3309 | 3326 | |
|
3310 | 3327 | compiler : callable |
|
3311 | 3328 | A function with the same interface as the built-in compile(), to turn |
|
3312 | 3329 | the AST nodes into code objects. Default is the built-in compile(). |
|
3313 | 3330 | result : ExecutionResult, optional |
|
3314 | 3331 | An object to store exceptions that occur during execution. |
|
3315 | 3332 | |
|
3316 | 3333 | Returns |
|
3317 | 3334 | ------- |
|
3318 | 3335 | True if an exception occurred while running code, False if it finished |
|
3319 | 3336 | running. |
|
3320 | 3337 | """ |
|
3321 | 3338 | if not nodelist: |
|
3322 | 3339 | return |
|
3323 | 3340 | |
|
3324 | 3341 | |
|
3325 | 3342 | if interactivity == 'last_expr_or_assign': |
|
3326 | 3343 | if isinstance(nodelist[-1], _assign_nodes): |
|
3327 | 3344 | asg = nodelist[-1] |
|
3328 | 3345 | if isinstance(asg, ast.Assign) and len(asg.targets) == 1: |
|
3329 | 3346 | target = asg.targets[0] |
|
3330 | 3347 | elif isinstance(asg, _single_targets_nodes): |
|
3331 | 3348 | target = asg.target |
|
3332 | 3349 | else: |
|
3333 | 3350 | target = None |
|
3334 | 3351 | if isinstance(target, ast.Name): |
|
3335 | 3352 | nnode = ast.Expr(ast.Name(target.id, ast.Load())) |
|
3336 | 3353 | ast.fix_missing_locations(nnode) |
|
3337 | 3354 | nodelist.append(nnode) |
|
3338 | 3355 | interactivity = 'last_expr' |
|
3339 | 3356 | |
|
3340 | 3357 | _async = False |
|
3341 | 3358 | if interactivity == 'last_expr': |
|
3342 | 3359 | if isinstance(nodelist[-1], ast.Expr): |
|
3343 | 3360 | interactivity = "last" |
|
3344 | 3361 | else: |
|
3345 | 3362 | interactivity = "none" |
|
3346 | 3363 | |
|
3347 | 3364 | if interactivity == 'none': |
|
3348 | 3365 | to_run_exec, to_run_interactive = nodelist, [] |
|
3349 | 3366 | elif interactivity == 'last': |
|
3350 | 3367 | to_run_exec, to_run_interactive = nodelist[:-1], nodelist[-1:] |
|
3351 | 3368 | elif interactivity == 'all': |
|
3352 | 3369 | to_run_exec, to_run_interactive = [], nodelist |
|
3353 | 3370 | else: |
|
3354 | 3371 | raise ValueError("Interactivity was %r" % interactivity) |
|
3355 | 3372 | |
|
3356 | 3373 | try: |
|
3357 | 3374 | |
|
3358 | 3375 | def compare(code): |
|
3359 | 3376 | is_async = inspect.CO_COROUTINE & code.co_flags == inspect.CO_COROUTINE |
|
3360 | 3377 | return is_async |
|
3361 | 3378 | |
|
3362 | 3379 | # refactor that to just change the mod constructor. |
|
3363 | 3380 | to_run = [] |
|
3364 | 3381 | for node in to_run_exec: |
|
3365 | 3382 | to_run.append((node, "exec")) |
|
3366 | 3383 | |
|
3367 | 3384 | for node in to_run_interactive: |
|
3368 | 3385 | to_run.append((node, "single")) |
|
3369 | 3386 | |
|
3370 | 3387 | for node, mode in to_run: |
|
3371 | 3388 | if mode == "exec": |
|
3372 | 3389 | mod = Module([node], []) |
|
3373 | 3390 | elif mode == "single": |
|
3374 | 3391 | mod = ast.Interactive([node]) |
|
3375 | 3392 | with compiler.extra_flags( |
|
3376 | 3393 | getattr(ast, "PyCF_ALLOW_TOP_LEVEL_AWAIT", 0x0) |
|
3377 | 3394 | if self.autoawait |
|
3378 | 3395 | else 0x0 |
|
3379 | 3396 | ): |
|
3380 | 3397 | code = compiler(mod, cell_name, mode) |
|
3381 | 3398 | asy = compare(code) |
|
3382 | 3399 | if await self.run_code(code, result, async_=asy): |
|
3383 | 3400 | return True |
|
3384 | 3401 | |
|
3385 | 3402 | # Flush softspace |
|
3386 | 3403 | if softspace(sys.stdout, 0): |
|
3387 | 3404 | print() |
|
3388 | 3405 | |
|
3389 | 3406 | except: |
|
3390 | 3407 | # It's possible to have exceptions raised here, typically by |
|
3391 | 3408 | # compilation of odd code (such as a naked 'return' outside a |
|
3392 | 3409 | # function) that did parse but isn't valid. Typically the exception |
|
3393 | 3410 | # is a SyntaxError, but it's safest just to catch anything and show |
|
3394 | 3411 | # the user a traceback. |
|
3395 | 3412 | |
|
3396 | 3413 | # We do only one try/except outside the loop to minimize the impact |
|
3397 | 3414 | # on runtime, and also because if any node in the node list is |
|
3398 | 3415 | # broken, we should stop execution completely. |
|
3399 | 3416 | if result: |
|
3400 | 3417 | result.error_before_exec = sys.exc_info()[1] |
|
3401 | 3418 | self.showtraceback() |
|
3402 | 3419 | return True |
|
3403 | 3420 | |
|
3404 | 3421 | return False |
|
3405 | 3422 | |
|
3406 | 3423 | async def run_code(self, code_obj, result=None, *, async_=False): |
|
3407 | 3424 | """Execute a code object. |
|
3408 | 3425 | |
|
3409 | 3426 | When an exception occurs, self.showtraceback() is called to display a |
|
3410 | 3427 | traceback. |
|
3411 | 3428 | |
|
3412 | 3429 | Parameters |
|
3413 | 3430 | ---------- |
|
3414 | 3431 | code_obj : code object |
|
3415 | 3432 | A compiled code object, to be executed |
|
3416 | 3433 | result : ExecutionResult, optional |
|
3417 | 3434 | An object to store exceptions that occur during execution. |
|
3418 | 3435 | async_ : Bool (Experimental) |
|
3419 | 3436 | Attempt to run top-level asynchronous code in a default loop. |
|
3420 | 3437 | |
|
3421 | 3438 | Returns |
|
3422 | 3439 | ------- |
|
3423 | 3440 | False : successful execution. |
|
3424 | 3441 | True : an error occurred. |
|
3425 | 3442 | """ |
|
3426 | 3443 | # special value to say that anything above is IPython and should be |
|
3427 | 3444 | # hidden. |
|
3428 | 3445 | __tracebackhide__ = "__ipython_bottom__" |
|
3429 | 3446 | # Set our own excepthook in case the user code tries to call it |
|
3430 | 3447 | # directly, so that the IPython crash handler doesn't get triggered |
|
3431 | 3448 | old_excepthook, sys.excepthook = sys.excepthook, self.excepthook |
|
3432 | 3449 | |
|
3433 | 3450 | # we save the original sys.excepthook in the instance, in case config |
|
3434 | 3451 | # code (such as magics) needs access to it. |
|
3435 | 3452 | self.sys_excepthook = old_excepthook |
|
3436 | 3453 | outflag = True # happens in more places, so it's easier as default |
|
3437 | 3454 | try: |
|
3438 | 3455 | try: |
|
3439 | 3456 | if async_: |
|
3440 | 3457 | await eval(code_obj, self.user_global_ns, self.user_ns) |
|
3441 | 3458 | else: |
|
3442 | 3459 | exec(code_obj, self.user_global_ns, self.user_ns) |
|
3443 | 3460 | finally: |
|
3444 | 3461 | # Reset our crash handler in place |
|
3445 | 3462 | sys.excepthook = old_excepthook |
|
3446 | 3463 | except SystemExit as e: |
|
3447 | 3464 | if result is not None: |
|
3448 | 3465 | result.error_in_exec = e |
|
3449 | 3466 | self.showtraceback(exception_only=True) |
|
3450 | 3467 | warn("To exit: use 'exit', 'quit', or Ctrl-D.", stacklevel=1) |
|
3451 | 3468 | except bdb.BdbQuit: |
|
3452 | 3469 | etype, value, tb = sys.exc_info() |
|
3453 | 3470 | if result is not None: |
|
3454 | 3471 | result.error_in_exec = value |
|
3455 | 3472 | # the BdbQuit stops here |
|
3456 | 3473 | except self.custom_exceptions: |
|
3457 | 3474 | etype, value, tb = sys.exc_info() |
|
3458 | 3475 | if result is not None: |
|
3459 | 3476 | result.error_in_exec = value |
|
3460 | 3477 | self.CustomTB(etype, value, tb) |
|
3461 | 3478 | except: |
|
3462 | 3479 | if result is not None: |
|
3463 | 3480 | result.error_in_exec = sys.exc_info()[1] |
|
3464 | 3481 | self.showtraceback(running_compiled_code=True) |
|
3465 | 3482 | else: |
|
3466 | 3483 | outflag = False |
|
3467 | 3484 | return outflag |
|
3468 | 3485 | |
|
3469 | 3486 | # For backwards compatibility |
|
3470 | 3487 | runcode = run_code |
|
3471 | 3488 | |
|
3472 | 3489 | def check_complete(self, code: str) -> Tuple[str, str]: |
|
3473 | 3490 | """Return whether a block of code is ready to execute, or should be continued |
|
3474 | 3491 | |
|
3475 | 3492 | Parameters |
|
3476 | 3493 | ---------- |
|
3477 | 3494 | code : string |
|
3478 | 3495 | Python input code, which can be multiline. |
|
3479 | 3496 | |
|
3480 | 3497 | Returns |
|
3481 | 3498 | ------- |
|
3482 | 3499 | status : str |
|
3483 | 3500 | One of 'complete', 'incomplete', or 'invalid' if source is not a |
|
3484 | 3501 | prefix of valid code. |
|
3485 | 3502 | indent : str |
|
3486 | 3503 | When status is 'incomplete', this is some whitespace to insert on |
|
3487 | 3504 | the next line of the prompt. |
|
3488 | 3505 | """ |
|
3489 | 3506 | status, nspaces = self.input_transformer_manager.check_complete(code) |
|
3490 | 3507 | return status, ' ' * (nspaces or 0) |
|
3491 | 3508 | |
|
3492 | 3509 | #------------------------------------------------------------------------- |
|
3493 | 3510 | # Things related to GUI support and pylab |
|
3494 | 3511 | #------------------------------------------------------------------------- |
|
3495 | 3512 | |
|
3496 | 3513 | active_eventloop = None |
|
3497 | 3514 | |
|
3498 | 3515 | def enable_gui(self, gui=None): |
|
3499 | 3516 | raise NotImplementedError('Implement enable_gui in a subclass') |
|
3500 | 3517 | |
|
3501 | 3518 | def enable_matplotlib(self, gui=None): |
|
3502 | 3519 | """Enable interactive matplotlib and inline figure support. |
|
3503 | 3520 | |
|
3504 | 3521 | This takes the following steps: |
|
3505 | 3522 | |
|
3506 | 3523 | 1. select the appropriate eventloop and matplotlib backend |
|
3507 | 3524 | 2. set up matplotlib for interactive use with that backend |
|
3508 | 3525 | 3. configure formatters for inline figure display |
|
3509 | 3526 | 4. enable the selected gui eventloop |
|
3510 | 3527 | |
|
3511 | 3528 | Parameters |
|
3512 | 3529 | ---------- |
|
3513 | 3530 | gui : optional, string |
|
3514 | 3531 | If given, dictates the choice of matplotlib GUI backend to use |
|
3515 | 3532 | (should be one of IPython's supported backends, 'qt', 'osx', 'tk', |
|
3516 | 3533 | 'gtk', 'wx' or 'inline'), otherwise we use the default chosen by |
|
3517 | 3534 | matplotlib (as dictated by the matplotlib build-time options plus the |
|
3518 | 3535 | user's matplotlibrc configuration file). Note that not all backends |
|
3519 | 3536 | make sense in all contexts, for example a terminal ipython can't |
|
3520 | 3537 | display figures inline. |
|
3521 | 3538 | """ |
|
3522 | 3539 | from matplotlib_inline.backend_inline import configure_inline_support |
|
3523 | 3540 | |
|
3524 | 3541 | from IPython.core import pylabtools as pt |
|
3525 | 3542 | gui, backend = pt.find_gui_and_backend(gui, self.pylab_gui_select) |
|
3526 | 3543 | |
|
3527 | 3544 | if gui != 'inline': |
|
3528 | 3545 | # If we have our first gui selection, store it |
|
3529 | 3546 | if self.pylab_gui_select is None: |
|
3530 | 3547 | self.pylab_gui_select = gui |
|
3531 | 3548 | # Otherwise if they are different |
|
3532 | 3549 | elif gui != self.pylab_gui_select: |
|
3533 | 3550 | print('Warning: Cannot change to a different GUI toolkit: %s.' |
|
3534 | 3551 | ' Using %s instead.' % (gui, self.pylab_gui_select)) |
|
3535 | 3552 | gui, backend = pt.find_gui_and_backend(self.pylab_gui_select) |
|
3536 | 3553 | |
|
3537 | 3554 | pt.activate_matplotlib(backend) |
|
3538 | 3555 | configure_inline_support(self, backend) |
|
3539 | 3556 | |
|
3540 | 3557 | # Now we must activate the gui pylab wants to use, and fix %run to take |
|
3541 | 3558 | # plot updates into account |
|
3542 | 3559 | self.enable_gui(gui) |
|
3543 | 3560 | self.magics_manager.registry['ExecutionMagics'].default_runner = \ |
|
3544 | 3561 | pt.mpl_runner(self.safe_execfile) |
|
3545 | 3562 | |
|
3546 | 3563 | return gui, backend |
|
3547 | 3564 | |
|
3548 | 3565 | def enable_pylab(self, gui=None, import_all=True, welcome_message=False): |
|
3549 | 3566 | """Activate pylab support at runtime. |
|
3550 | 3567 | |
|
3551 | 3568 | This turns on support for matplotlib, preloads into the interactive |
|
3552 | 3569 | namespace all of numpy and pylab, and configures IPython to correctly |
|
3553 | 3570 | interact with the GUI event loop. The GUI backend to be used can be |
|
3554 | 3571 | optionally selected with the optional ``gui`` argument. |
|
3555 | 3572 | |
|
3556 | 3573 | This method only adds preloading the namespace to InteractiveShell.enable_matplotlib. |
|
3557 | 3574 | |
|
3558 | 3575 | Parameters |
|
3559 | 3576 | ---------- |
|
3560 | 3577 | gui : optional, string |
|
3561 | 3578 | If given, dictates the choice of matplotlib GUI backend to use |
|
3562 | 3579 | (should be one of IPython's supported backends, 'qt', 'osx', 'tk', |
|
3563 | 3580 | 'gtk', 'wx' or 'inline'), otherwise we use the default chosen by |
|
3564 | 3581 | matplotlib (as dictated by the matplotlib build-time options plus the |
|
3565 | 3582 | user's matplotlibrc configuration file). Note that not all backends |
|
3566 | 3583 | make sense in all contexts, for example a terminal ipython can't |
|
3567 | 3584 | display figures inline. |
|
3568 | 3585 | import_all : optional, bool, default: True |
|
3569 | 3586 | Whether to do `from numpy import *` and `from pylab import *` |
|
3570 | 3587 | in addition to module imports. |
|
3571 | 3588 | welcome_message : deprecated |
|
3572 | 3589 | This argument is ignored, no welcome message will be displayed. |
|
3573 | 3590 | """ |
|
3574 | 3591 | from IPython.core.pylabtools import import_pylab |
|
3575 | 3592 | |
|
3576 | 3593 | gui, backend = self.enable_matplotlib(gui) |
|
3577 | 3594 | |
|
3578 | 3595 | # We want to prevent the loading of pylab to pollute the user's |
|
3579 | 3596 | # namespace as shown by the %who* magics, so we execute the activation |
|
3580 | 3597 | # code in an empty namespace, and we update *both* user_ns and |
|
3581 | 3598 | # user_ns_hidden with this information. |
|
3582 | 3599 | ns = {} |
|
3583 | 3600 | import_pylab(ns, import_all) |
|
3584 | 3601 | # warn about clobbered names |
|
3585 | 3602 | ignored = {"__builtins__"} |
|
3586 | 3603 | both = set(ns).intersection(self.user_ns).difference(ignored) |
|
3587 | 3604 | clobbered = [ name for name in both if self.user_ns[name] is not ns[name] ] |
|
3588 | 3605 | self.user_ns.update(ns) |
|
3589 | 3606 | self.user_ns_hidden.update(ns) |
|
3590 | 3607 | return gui, backend, clobbered |
|
3591 | 3608 | |
|
3592 | 3609 | #------------------------------------------------------------------------- |
|
3593 | 3610 | # Utilities |
|
3594 | 3611 | #------------------------------------------------------------------------- |
|
3595 | 3612 | |
|
3596 | 3613 | def var_expand(self, cmd, depth=0, formatter=DollarFormatter()): |
|
3597 | 3614 | """Expand python variables in a string. |
|
3598 | 3615 | |
|
3599 | 3616 | The depth argument indicates how many frames above the caller should |
|
3600 | 3617 | be walked to look for the local namespace where to expand variables. |
|
3601 | 3618 | |
|
3602 | 3619 | The global namespace for expansion is always the user's interactive |
|
3603 | 3620 | namespace. |
|
3604 | 3621 | """ |
|
3605 | 3622 | ns = self.user_ns.copy() |
|
3606 | 3623 | try: |
|
3607 | 3624 | frame = sys._getframe(depth+1) |
|
3608 | 3625 | except ValueError: |
|
3609 | 3626 | # This is thrown if there aren't that many frames on the stack, |
|
3610 | 3627 | # e.g. if a script called run_line_magic() directly. |
|
3611 | 3628 | pass |
|
3612 | 3629 | else: |
|
3613 | 3630 | ns.update(frame.f_locals) |
|
3614 | 3631 | |
|
3615 | 3632 | try: |
|
3616 | 3633 | # We have to use .vformat() here, because 'self' is a valid and common |
|
3617 | 3634 | # name, and expanding **ns for .format() would make it collide with |
|
3618 | 3635 | # the 'self' argument of the method. |
|
3619 | 3636 | cmd = formatter.vformat(cmd, args=[], kwargs=ns) |
|
3620 | 3637 | except Exception: |
|
3621 | 3638 | # if formatter couldn't format, just let it go untransformed |
|
3622 | 3639 | pass |
|
3623 | 3640 | return cmd |
|
3624 | 3641 | |
|
3625 | 3642 | def mktempfile(self, data=None, prefix='ipython_edit_'): |
|
3626 | 3643 | """Make a new tempfile and return its filename. |
|
3627 | 3644 | |
|
3628 | 3645 | This makes a call to tempfile.mkstemp (created in a tempfile.mkdtemp), |
|
3629 | 3646 | but it registers the created filename internally so ipython cleans it up |
|
3630 | 3647 | at exit time. |
|
3631 | 3648 | |
|
3632 | 3649 | Optional inputs: |
|
3633 | 3650 | |
|
3634 | 3651 | - data(None): if data is given, it gets written out to the temp file |
|
3635 | 3652 | immediately, and the file is closed again.""" |
|
3636 | 3653 | |
|
3637 | 3654 | dir_path = Path(tempfile.mkdtemp(prefix=prefix)) |
|
3638 | 3655 | self.tempdirs.append(dir_path) |
|
3639 | 3656 | |
|
3640 | 3657 | handle, filename = tempfile.mkstemp(".py", prefix, dir=str(dir_path)) |
|
3641 | 3658 | os.close(handle) # On Windows, there can only be one open handle on a file |
|
3642 | 3659 | |
|
3643 | 3660 | file_path = Path(filename) |
|
3644 | 3661 | self.tempfiles.append(file_path) |
|
3645 | 3662 | |
|
3646 | 3663 | if data: |
|
3647 | 3664 | file_path.write_text(data, encoding="utf-8") |
|
3648 | 3665 | return filename |
|
3649 | 3666 | |
|
3650 | 3667 | def ask_yes_no(self, prompt, default=None, interrupt=None): |
|
3651 | 3668 | if self.quiet: |
|
3652 | 3669 | return True |
|
3653 | 3670 | return ask_yes_no(prompt,default,interrupt) |
|
3654 | 3671 | |
|
3655 | 3672 | def show_usage(self): |
|
3656 | 3673 | """Show a usage message""" |
|
3657 | 3674 | page.page(IPython.core.usage.interactive_usage) |
|
3658 | 3675 | |
|
3659 | 3676 | def extract_input_lines(self, range_str, raw=False): |
|
3660 | 3677 | """Return as a string a set of input history slices. |
|
3661 | 3678 | |
|
3662 | 3679 | Parameters |
|
3663 | 3680 | ---------- |
|
3664 | 3681 | range_str : str |
|
3665 | 3682 | The set of slices is given as a string, like "~5/6-~4/2 4:8 9", |
|
3666 | 3683 | since this function is for use by magic functions which get their |
|
3667 | 3684 | arguments as strings. The number before the / is the session |
|
3668 | 3685 | number: ~n goes n back from the current session. |
|
3669 | 3686 | |
|
3670 | 3687 | If empty string is given, returns history of current session |
|
3671 | 3688 | without the last input. |
|
3672 | 3689 | |
|
3673 | 3690 | raw : bool, optional |
|
3674 | 3691 | By default, the processed input is used. If this is true, the raw |
|
3675 | 3692 | input history is used instead. |
|
3676 | 3693 | |
|
3677 | 3694 | Notes |
|
3678 | 3695 | ----- |
|
3679 | 3696 | Slices can be described with two notations: |
|
3680 | 3697 | |
|
3681 | 3698 | * ``N:M`` -> standard python form, means including items N...(M-1). |
|
3682 | 3699 | * ``N-M`` -> include items N..M (closed endpoint). |
|
3683 | 3700 | """ |
|
3684 | 3701 | lines = self.history_manager.get_range_by_str(range_str, raw=raw) |
|
3685 | 3702 | text = "\n".join(x for _, _, x in lines) |
|
3686 | 3703 | |
|
3687 | 3704 | # Skip the last line, as it's probably the magic that called this |
|
3688 | 3705 | if not range_str: |
|
3689 | 3706 | if "\n" not in text: |
|
3690 | 3707 | text = "" |
|
3691 | 3708 | else: |
|
3692 | 3709 | text = text[: text.rfind("\n")] |
|
3693 | 3710 | |
|
3694 | 3711 | return text |
|
3695 | 3712 | |
|
3696 | 3713 | def find_user_code(self, target, raw=True, py_only=False, skip_encoding_cookie=True, search_ns=False): |
|
3697 | 3714 | """Get a code string from history, file, url, or a string or macro. |
|
3698 | 3715 | |
|
3699 | 3716 | This is mainly used by magic functions. |
|
3700 | 3717 | |
|
3701 | 3718 | Parameters |
|
3702 | 3719 | ---------- |
|
3703 | 3720 | target : str |
|
3704 | 3721 | A string specifying code to retrieve. This will be tried respectively |
|
3705 | 3722 | as: ranges of input history (see %history for syntax), url, |
|
3706 | 3723 | corresponding .py file, filename, or an expression evaluating to a |
|
3707 | 3724 | string or Macro in the user namespace. |
|
3708 | 3725 | |
|
3709 | 3726 | If empty string is given, returns complete history of current |
|
3710 | 3727 | session, without the last line. |
|
3711 | 3728 | |
|
3712 | 3729 | raw : bool |
|
3713 | 3730 | If true (default), retrieve raw history. Has no effect on the other |
|
3714 | 3731 | retrieval mechanisms. |
|
3715 | 3732 | |
|
3716 | 3733 | py_only : bool (default False) |
|
3717 | 3734 | Only try to fetch python code, do not try alternative methods to decode file |
|
3718 | 3735 | if unicode fails. |
|
3719 | 3736 | |
|
3720 | 3737 | Returns |
|
3721 | 3738 | ------- |
|
3722 | 3739 | A string of code. |
|
3723 | 3740 | ValueError is raised if nothing is found, and TypeError if it evaluates |
|
3724 | 3741 | to an object of another type. In each case, .args[0] is a printable |
|
3725 | 3742 | message. |
|
3726 | 3743 | """ |
|
3727 | 3744 | code = self.extract_input_lines(target, raw=raw) # Grab history |
|
3728 | 3745 | if code: |
|
3729 | 3746 | return code |
|
3730 | 3747 | try: |
|
3731 | 3748 | if target.startswith(('http://', 'https://')): |
|
3732 | 3749 | return openpy.read_py_url(target, skip_encoding_cookie=skip_encoding_cookie) |
|
3733 | 3750 | except UnicodeDecodeError as e: |
|
3734 | 3751 | if not py_only : |
|
3735 | 3752 | # Deferred import |
|
3736 | 3753 | from urllib.request import urlopen |
|
3737 | 3754 | response = urlopen(target) |
|
3738 | 3755 | return response.read().decode('latin1') |
|
3739 | 3756 | raise ValueError(("'%s' seem to be unreadable.") % target) from e |
|
3740 | 3757 | |
|
3741 | 3758 | potential_target = [target] |
|
3742 | 3759 | try : |
|
3743 | 3760 | potential_target.insert(0,get_py_filename(target)) |
|
3744 | 3761 | except IOError: |
|
3745 | 3762 | pass |
|
3746 | 3763 | |
|
3747 | 3764 | for tgt in potential_target : |
|
3748 | 3765 | if os.path.isfile(tgt): # Read file |
|
3749 | 3766 | try : |
|
3750 | 3767 | return openpy.read_py_file(tgt, skip_encoding_cookie=skip_encoding_cookie) |
|
3751 | 3768 | except UnicodeDecodeError as e: |
|
3752 | 3769 | if not py_only : |
|
3753 | 3770 | with io_open(tgt,'r', encoding='latin1') as f : |
|
3754 | 3771 | return f.read() |
|
3755 | 3772 | raise ValueError(("'%s' seem to be unreadable.") % target) from e |
|
3756 | 3773 | elif os.path.isdir(os.path.expanduser(tgt)): |
|
3757 | 3774 | raise ValueError("'%s' is a directory, not a regular file." % target) |
|
3758 | 3775 | |
|
3759 | 3776 | if search_ns: |
|
3760 | 3777 | # Inspect namespace to load object source |
|
3761 | 3778 | object_info = self.object_inspect(target, detail_level=1) |
|
3762 | 3779 | if object_info['found'] and object_info['source']: |
|
3763 | 3780 | return object_info['source'] |
|
3764 | 3781 | |
|
3765 | 3782 | try: # User namespace |
|
3766 | 3783 | codeobj = eval(target, self.user_ns) |
|
3767 | 3784 | except Exception as e: |
|
3768 | 3785 | raise ValueError(("'%s' was not found in history, as a file, url, " |
|
3769 | 3786 | "nor in the user namespace.") % target) from e |
|
3770 | 3787 | |
|
3771 | 3788 | if isinstance(codeobj, str): |
|
3772 | 3789 | return codeobj |
|
3773 | 3790 | elif isinstance(codeobj, Macro): |
|
3774 | 3791 | return codeobj.value |
|
3775 | 3792 | |
|
3776 | 3793 | raise TypeError("%s is neither a string nor a macro." % target, |
|
3777 | 3794 | codeobj) |
|
3778 | 3795 | |
|
3779 | 3796 | def _atexit_once(self): |
|
3780 | 3797 | """ |
|
3781 | 3798 | At exist operation that need to be called at most once. |
|
3782 | 3799 | Second call to this function per instance will do nothing. |
|
3783 | 3800 | """ |
|
3784 | 3801 | |
|
3785 | 3802 | if not getattr(self, "_atexit_once_called", False): |
|
3786 | 3803 | self._atexit_once_called = True |
|
3787 | 3804 | # Clear all user namespaces to release all references cleanly. |
|
3788 | 3805 | self.reset(new_session=False) |
|
3789 | 3806 | # Close the history session (this stores the end time and line count) |
|
3790 | 3807 | # this must be *before* the tempfile cleanup, in case of temporary |
|
3791 | 3808 | # history db |
|
3792 | 3809 | self.history_manager.end_session() |
|
3793 | 3810 | self.history_manager = None |
|
3794 | 3811 | |
|
3795 | 3812 | #------------------------------------------------------------------------- |
|
3796 | 3813 | # Things related to IPython exiting |
|
3797 | 3814 | #------------------------------------------------------------------------- |
|
3798 | 3815 | def atexit_operations(self): |
|
3799 | 3816 | """This will be executed at the time of exit. |
|
3800 | 3817 | |
|
3801 | 3818 | Cleanup operations and saving of persistent data that is done |
|
3802 | 3819 | unconditionally by IPython should be performed here. |
|
3803 | 3820 | |
|
3804 | 3821 | For things that may depend on startup flags or platform specifics (such |
|
3805 | 3822 | as having readline or not), register a separate atexit function in the |
|
3806 | 3823 | code that has the appropriate information, rather than trying to |
|
3807 | 3824 | clutter |
|
3808 | 3825 | """ |
|
3809 | 3826 | self._atexit_once() |
|
3810 | 3827 | |
|
3811 | 3828 | # Cleanup all tempfiles and folders left around |
|
3812 | 3829 | for tfile in self.tempfiles: |
|
3813 | 3830 | try: |
|
3814 | 3831 | tfile.unlink() |
|
3815 | 3832 | self.tempfiles.remove(tfile) |
|
3816 | 3833 | except FileNotFoundError: |
|
3817 | 3834 | pass |
|
3818 | 3835 | del self.tempfiles |
|
3819 | 3836 | for tdir in self.tempdirs: |
|
3820 | 3837 | try: |
|
3821 | 3838 | tdir.rmdir() |
|
3822 | 3839 | self.tempdirs.remove(tdir) |
|
3823 | 3840 | except FileNotFoundError: |
|
3824 | 3841 | pass |
|
3825 | 3842 | del self.tempdirs |
|
3826 | 3843 | |
|
3827 | 3844 | # Restore user's cursor |
|
3828 | 3845 | if hasattr(self, "editing_mode") and self.editing_mode == "vi": |
|
3829 | 3846 | sys.stdout.write("\x1b[0 q") |
|
3830 | 3847 | sys.stdout.flush() |
|
3831 | 3848 | |
|
3832 | 3849 | def cleanup(self): |
|
3833 | 3850 | self.restore_sys_module_state() |
|
3834 | 3851 | |
|
3835 | 3852 | |
|
3836 | 3853 | # Overridden in terminal subclass to change prompts |
|
3837 | 3854 | def switch_doctest_mode(self, mode): |
|
3838 | 3855 | pass |
|
3839 | 3856 | |
|
3840 | 3857 | |
|
3841 | 3858 | class InteractiveShellABC(metaclass=abc.ABCMeta): |
|
3842 | 3859 | """An abstract base class for InteractiveShell.""" |
|
3843 | 3860 | |
|
3844 | 3861 | InteractiveShellABC.register(InteractiveShell) |
@@ -1,218 +1,227 b'' | |||
|
1 | 1 | """Logger class for IPython's logging facilities. |
|
2 | 2 | """ |
|
3 | 3 | |
|
4 | 4 | #***************************************************************************** |
|
5 | 5 | # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and |
|
6 | 6 | # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu> |
|
7 | 7 | # |
|
8 | 8 | # Distributed under the terms of the BSD License. The full license is in |
|
9 | 9 | # the file COPYING, distributed as part of this software. |
|
10 | 10 | #***************************************************************************** |
|
11 | 11 | |
|
12 | 12 | #**************************************************************************** |
|
13 | 13 | # Modules and globals |
|
14 | 14 | |
|
15 | 15 | # Python standard modules |
|
16 | 16 | import glob |
|
17 | 17 | import io |
|
18 | 18 | import os |
|
19 | 19 | import time |
|
20 | 20 | |
|
21 | 21 | |
|
22 | 22 | #**************************************************************************** |
|
23 | 23 | # FIXME: This class isn't a mixin anymore, but it still needs attributes from |
|
24 | 24 | # ipython and does input cache management. Finish cleanup later... |
|
25 | 25 | |
|
26 | 26 | class Logger(object): |
|
27 | 27 | """A Logfile class with different policies for file creation""" |
|
28 | 28 | |
|
29 | 29 | def __init__(self, home_dir, logfname='Logger.log', loghead=u'', |
|
30 | 30 | logmode='over'): |
|
31 | 31 | |
|
32 | 32 | # this is the full ipython instance, we need some attributes from it |
|
33 | 33 | # which won't exist until later. What a mess, clean up later... |
|
34 | 34 | self.home_dir = home_dir |
|
35 | 35 | |
|
36 | 36 | self.logfname = logfname |
|
37 | 37 | self.loghead = loghead |
|
38 | 38 | self.logmode = logmode |
|
39 | 39 | self.logfile = None |
|
40 | 40 | |
|
41 | 41 | # Whether to log raw or processed input |
|
42 | 42 | self.log_raw_input = False |
|
43 | 43 | |
|
44 | 44 | # whether to also log output |
|
45 | 45 | self.log_output = False |
|
46 | 46 | |
|
47 | 47 | # whether to put timestamps before each log entry |
|
48 | 48 | self.timestamp = False |
|
49 | 49 | |
|
50 | 50 | # activity control flags |
|
51 | 51 | self.log_active = False |
|
52 | 52 | |
|
53 | 53 | # logmode is a validated property |
|
54 | 54 | def _set_mode(self,mode): |
|
55 | 55 | if mode not in ['append','backup','global','over','rotate']: |
|
56 | 56 | raise ValueError('invalid log mode %s given' % mode) |
|
57 | 57 | self._logmode = mode |
|
58 | 58 | |
|
59 | 59 | def _get_mode(self): |
|
60 | 60 | return self._logmode |
|
61 | 61 | |
|
62 | 62 | logmode = property(_get_mode,_set_mode) |
|
63 | 63 | |
|
64 | 64 | def logstart(self, logfname=None, loghead=None, logmode=None, |
|
65 | 65 | log_output=False, timestamp=False, log_raw_input=False): |
|
66 | 66 | """Generate a new log-file with a default header. |
|
67 | 67 | |
|
68 | 68 | Raises RuntimeError if the log has already been started""" |
|
69 | 69 | |
|
70 | 70 | if self.logfile is not None: |
|
71 | 71 | raise RuntimeError('Log file is already active: %s' % |
|
72 | 72 | self.logfname) |
|
73 | 73 | |
|
74 | 74 | # The parameters can override constructor defaults |
|
75 | 75 | if logfname is not None: self.logfname = logfname |
|
76 | 76 | if loghead is not None: self.loghead = loghead |
|
77 | 77 | if logmode is not None: self.logmode = logmode |
|
78 | 78 | |
|
79 | 79 | # Parameters not part of the constructor |
|
80 | 80 | self.timestamp = timestamp |
|
81 | 81 | self.log_output = log_output |
|
82 | 82 | self.log_raw_input = log_raw_input |
|
83 | 83 | |
|
84 | 84 | # init depending on the log mode requested |
|
85 | 85 | isfile = os.path.isfile |
|
86 | 86 | logmode = self.logmode |
|
87 | 87 | |
|
88 | 88 | if logmode == 'append': |
|
89 | 89 | self.logfile = io.open(self.logfname, 'a', encoding='utf-8') |
|
90 | 90 | |
|
91 | 91 | elif logmode == 'backup': |
|
92 | 92 | if isfile(self.logfname): |
|
93 | 93 | backup_logname = self.logfname+'~' |
|
94 | 94 | # Manually remove any old backup, since os.rename may fail |
|
95 | 95 | # under Windows. |
|
96 | 96 | if isfile(backup_logname): |
|
97 | 97 | os.remove(backup_logname) |
|
98 | 98 | os.rename(self.logfname,backup_logname) |
|
99 | 99 | self.logfile = io.open(self.logfname, 'w', encoding='utf-8') |
|
100 | 100 | |
|
101 | 101 | elif logmode == 'global': |
|
102 | 102 | self.logfname = os.path.join(self.home_dir,self.logfname) |
|
103 | 103 | self.logfile = io.open(self.logfname, 'a', encoding='utf-8') |
|
104 | 104 | |
|
105 | 105 | elif logmode == 'over': |
|
106 | 106 | if isfile(self.logfname): |
|
107 | 107 | os.remove(self.logfname) |
|
108 | 108 | self.logfile = io.open(self.logfname,'w', encoding='utf-8') |
|
109 | 109 | |
|
110 | 110 | elif logmode == 'rotate': |
|
111 | 111 | if isfile(self.logfname): |
|
112 | 112 | if isfile(self.logfname+'.001~'): |
|
113 | 113 | old = glob.glob(self.logfname+'.*~') |
|
114 | 114 | old.sort() |
|
115 | 115 | old.reverse() |
|
116 | 116 | for f in old: |
|
117 | 117 | root, ext = os.path.splitext(f) |
|
118 | 118 | num = int(ext[1:-1])+1 |
|
119 | 119 | os.rename(f, root+'.'+repr(num).zfill(3)+'~') |
|
120 | 120 | os.rename(self.logfname, self.logfname+'.001~') |
|
121 | 121 | self.logfile = io.open(self.logfname, 'w', encoding='utf-8') |
|
122 | 122 | |
|
123 | 123 | if logmode != 'append': |
|
124 | 124 | self.logfile.write(self.loghead) |
|
125 | 125 | |
|
126 | 126 | self.logfile.flush() |
|
127 | 127 | self.log_active = True |
|
128 | 128 | |
|
129 | 129 | def switch_log(self,val): |
|
130 | 130 | """Switch logging on/off. val should be ONLY a boolean.""" |
|
131 | 131 | |
|
132 | 132 | if val not in [False,True,0,1]: |
|
133 | 133 | raise ValueError('Call switch_log ONLY with a boolean argument, ' |
|
134 | 134 | 'not with: %s' % val) |
|
135 | 135 | |
|
136 | 136 | label = {0:'OFF',1:'ON',False:'OFF',True:'ON'} |
|
137 | 137 | |
|
138 | 138 | if self.logfile is None: |
|
139 | 139 | print(""" |
|
140 | 140 | Logging hasn't been started yet (use logstart for that). |
|
141 | 141 | |
|
142 | 142 | %logon/%logoff are for temporarily starting and stopping logging for a logfile |
|
143 | 143 | which already exists. But you must first start the logging process with |
|
144 | 144 | %logstart (optionally giving a logfile name).""") |
|
145 | 145 | |
|
146 | 146 | else: |
|
147 | 147 | if self.log_active == val: |
|
148 | 148 | print('Logging is already',label[val]) |
|
149 | 149 | else: |
|
150 | 150 | print('Switching logging',label[val]) |
|
151 | 151 | self.log_active = not self.log_active |
|
152 | 152 | self.log_active_out = self.log_active |
|
153 | 153 | |
|
154 | 154 | def logstate(self): |
|
155 | 155 | """Print a status message about the logger.""" |
|
156 | 156 | if self.logfile is None: |
|
157 | 157 | print('Logging has not been activated.') |
|
158 | 158 | else: |
|
159 | 159 | state = self.log_active and 'active' or 'temporarily suspended' |
|
160 | 160 | print('Filename :', self.logfname) |
|
161 | 161 | print('Mode :', self.logmode) |
|
162 | 162 | print('Output logging :', self.log_output) |
|
163 | 163 | print('Raw input log :', self.log_raw_input) |
|
164 | 164 | print('Timestamping :', self.timestamp) |
|
165 | 165 | print('State :', state) |
|
166 | 166 | |
|
167 | 167 | def log(self, line_mod, line_ori): |
|
168 | 168 | """Write the sources to a log. |
|
169 | 169 | |
|
170 | 170 | Inputs: |
|
171 | 171 | |
|
172 | 172 | - line_mod: possibly modified input, such as the transformations made |
|
173 | 173 | by input prefilters or input handlers of various kinds. This should |
|
174 | 174 | always be valid Python. |
|
175 | 175 | |
|
176 | 176 | - line_ori: unmodified input line from the user. This is not |
|
177 | 177 | necessarily valid Python. |
|
178 | 178 | """ |
|
179 | 179 | |
|
180 | 180 | # Write the log line, but decide which one according to the |
|
181 | 181 | # log_raw_input flag, set when the log is started. |
|
182 | 182 | if self.log_raw_input: |
|
183 | 183 | self.log_write(line_ori) |
|
184 | 184 | else: |
|
185 | 185 | self.log_write(line_mod) |
|
186 | 186 | |
|
187 | 187 | def log_write(self, data, kind='input'): |
|
188 | 188 | """Write data to the log file, if active""" |
|
189 | 189 | |
|
190 | 190 | #print 'data: %r' % data # dbg |
|
191 | 191 | if self.log_active and data: |
|
192 | 192 | write = self.logfile.write |
|
193 | 193 | if kind=='input': |
|
194 | 194 | if self.timestamp: |
|
195 | 195 | write(time.strftime('# %a, %d %b %Y %H:%M:%S\n', time.localtime())) |
|
196 | 196 | write(data) |
|
197 | 197 | elif kind=='output' and self.log_output: |
|
198 | 198 | odata = u'\n'.join([u'#[Out]# %s' % s |
|
199 | 199 | for s in data.splitlines()]) |
|
200 | 200 | write(u'%s\n' % odata) |
|
201 | try: | |
|
201 | 202 | self.logfile.flush() |
|
203 | except OSError: | |
|
204 | print("Failed to flush the log file.") | |
|
205 | print( | |
|
206 | f"Please check that {self.logfname} exists and have the right permissions." | |
|
207 | ) | |
|
208 | print( | |
|
209 | "Also consider turning off the log with `%logstop` to avoid this warning." | |
|
210 | ) | |
|
202 | 211 | |
|
203 | 212 | def logstop(self): |
|
204 | 213 | """Fully stop logging and close log file. |
|
205 | 214 | |
|
206 | 215 | In order to start logging again, a new logstart() call needs to be |
|
207 | 216 | made, possibly (though not necessarily) with a new filename, mode and |
|
208 | 217 | other options.""" |
|
209 | 218 | |
|
210 | 219 | if self.logfile is not None: |
|
211 | 220 | self.logfile.close() |
|
212 | 221 | self.logfile = None |
|
213 | 222 | else: |
|
214 | 223 | print("Logging hadn't been started.") |
|
215 | 224 | self.log_active = False |
|
216 | 225 | |
|
217 | 226 | # For backwards compatibility, in case anyone was using this. |
|
218 | 227 | close_log = logstop |
@@ -1,746 +1,757 b'' | |||
|
1 | 1 | # encoding: utf-8 |
|
2 | 2 | """Magic functions for InteractiveShell. |
|
3 | 3 | """ |
|
4 | 4 | |
|
5 | 5 | #----------------------------------------------------------------------------- |
|
6 | 6 | # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and |
|
7 | 7 | # Copyright (C) 2001 Fernando Perez <fperez@colorado.edu> |
|
8 | 8 | # Copyright (C) 2008 The IPython Development Team |
|
9 | 9 | |
|
10 | 10 | # Distributed under the terms of the BSD License. The full license is in |
|
11 | 11 | # the file COPYING, distributed as part of this software. |
|
12 | 12 | #----------------------------------------------------------------------------- |
|
13 | 13 | |
|
14 | 14 | import os |
|
15 | 15 | import re |
|
16 | 16 | import sys |
|
17 | 17 | from getopt import getopt, GetoptError |
|
18 | 18 | |
|
19 | 19 | from traitlets.config.configurable import Configurable |
|
20 | 20 | from . import oinspect |
|
21 | 21 | from .error import UsageError |
|
22 | 22 | from .inputtransformer2 import ESC_MAGIC, ESC_MAGIC2 |
|
23 | 23 | from ..utils.ipstruct import Struct |
|
24 | 24 | from ..utils.process import arg_split |
|
25 | 25 | from ..utils.text import dedent |
|
26 | 26 | from traitlets import Bool, Dict, Instance, observe |
|
27 | 27 | from logging import error |
|
28 | 28 | |
|
29 | 29 | #----------------------------------------------------------------------------- |
|
30 | 30 | # Globals |
|
31 | 31 | #----------------------------------------------------------------------------- |
|
32 | 32 | |
|
33 | 33 | # A dict we'll use for each class that has magics, used as temporary storage to |
|
34 | 34 | # pass information between the @line/cell_magic method decorators and the |
|
35 | 35 | # @magics_class class decorator, because the method decorators have no |
|
36 | 36 | # access to the class when they run. See for more details: |
|
37 | 37 | # http://stackoverflow.com/questions/2366713/can-a-python-decorator-of-an-instance-method-access-the-class |
|
38 | 38 | |
|
39 | 39 | magics = dict(line={}, cell={}) |
|
40 | 40 | |
|
41 | 41 | magic_kinds = ('line', 'cell') |
|
42 | 42 | magic_spec = ('line', 'cell', 'line_cell') |
|
43 | 43 | magic_escapes = dict(line=ESC_MAGIC, cell=ESC_MAGIC2) |
|
44 | 44 | |
|
45 | 45 | #----------------------------------------------------------------------------- |
|
46 | 46 | # Utility classes and functions |
|
47 | 47 | #----------------------------------------------------------------------------- |
|
48 | 48 | |
|
49 | 49 | class Bunch: pass |
|
50 | 50 | |
|
51 | 51 | |
|
52 | 52 | def on_off(tag): |
|
53 | 53 | """Return an ON/OFF string for a 1/0 input. Simple utility function.""" |
|
54 | 54 | return ['OFF','ON'][tag] |
|
55 | 55 | |
|
56 | 56 | |
|
57 | 57 | def compress_dhist(dh): |
|
58 | 58 | """Compress a directory history into a new one with at most 20 entries. |
|
59 | 59 | |
|
60 | 60 | Return a new list made from the first and last 10 elements of dhist after |
|
61 | 61 | removal of duplicates. |
|
62 | 62 | """ |
|
63 | 63 | head, tail = dh[:-10], dh[-10:] |
|
64 | 64 | |
|
65 | 65 | newhead = [] |
|
66 | 66 | done = set() |
|
67 | 67 | for h in head: |
|
68 | 68 | if h in done: |
|
69 | 69 | continue |
|
70 | 70 | newhead.append(h) |
|
71 | 71 | done.add(h) |
|
72 | 72 | |
|
73 | 73 | return newhead + tail |
|
74 | 74 | |
|
75 | 75 | |
|
76 | 76 | def needs_local_scope(func): |
|
77 | 77 | """Decorator to mark magic functions which need to local scope to run.""" |
|
78 | 78 | func.needs_local_scope = True |
|
79 | 79 | return func |
|
80 | 80 | |
|
81 | 81 | #----------------------------------------------------------------------------- |
|
82 | 82 | # Class and method decorators for registering magics |
|
83 | 83 | #----------------------------------------------------------------------------- |
|
84 | 84 | |
|
85 | 85 | def magics_class(cls): |
|
86 | 86 | """Class decorator for all subclasses of the main Magics class. |
|
87 | 87 | |
|
88 | 88 | Any class that subclasses Magics *must* also apply this decorator, to |
|
89 | 89 | ensure that all the methods that have been decorated as line/cell magics |
|
90 | 90 | get correctly registered in the class instance. This is necessary because |
|
91 | 91 | when method decorators run, the class does not exist yet, so they |
|
92 | 92 | temporarily store their information into a module global. Application of |
|
93 | 93 | this class decorator copies that global data to the class instance and |
|
94 | 94 | clears the global. |
|
95 | 95 | |
|
96 | 96 | Obviously, this mechanism is not thread-safe, which means that the |
|
97 | 97 | *creation* of subclasses of Magic should only be done in a single-thread |
|
98 | 98 | context. Instantiation of the classes has no restrictions. Given that |
|
99 | 99 | these classes are typically created at IPython startup time and before user |
|
100 | 100 | application code becomes active, in practice this should not pose any |
|
101 | 101 | problems. |
|
102 | 102 | """ |
|
103 | 103 | cls.registered = True |
|
104 | 104 | cls.magics = dict(line = magics['line'], |
|
105 | 105 | cell = magics['cell']) |
|
106 | 106 | magics['line'] = {} |
|
107 | 107 | magics['cell'] = {} |
|
108 | 108 | return cls |
|
109 | 109 | |
|
110 | 110 | |
|
111 | 111 | def record_magic(dct, magic_kind, magic_name, func): |
|
112 | 112 | """Utility function to store a function as a magic of a specific kind. |
|
113 | 113 | |
|
114 | 114 | Parameters |
|
115 | 115 | ---------- |
|
116 | 116 | dct : dict |
|
117 | 117 | A dictionary with 'line' and 'cell' subdicts. |
|
118 | 118 | magic_kind : str |
|
119 | 119 | Kind of magic to be stored. |
|
120 | 120 | magic_name : str |
|
121 | 121 | Key to store the magic as. |
|
122 | 122 | func : function |
|
123 | 123 | Callable object to store. |
|
124 | 124 | """ |
|
125 | 125 | if magic_kind == 'line_cell': |
|
126 | 126 | dct['line'][magic_name] = dct['cell'][magic_name] = func |
|
127 | 127 | else: |
|
128 | 128 | dct[magic_kind][magic_name] = func |
|
129 | 129 | |
|
130 | 130 | |
|
131 | 131 | def validate_type(magic_kind): |
|
132 | 132 | """Ensure that the given magic_kind is valid. |
|
133 | 133 | |
|
134 | 134 | Check that the given magic_kind is one of the accepted spec types (stored |
|
135 | 135 | in the global `magic_spec`), raise ValueError otherwise. |
|
136 | 136 | """ |
|
137 | 137 | if magic_kind not in magic_spec: |
|
138 | 138 | raise ValueError('magic_kind must be one of %s, %s given' % |
|
139 | 139 | magic_kinds, magic_kind) |
|
140 | 140 | |
|
141 | 141 | |
|
142 | 142 | # The docstrings for the decorator below will be fairly similar for the two |
|
143 | 143 | # types (method and function), so we generate them here once and reuse the |
|
144 | 144 | # templates below. |
|
145 | 145 | _docstring_template = \ |
|
146 | 146 | """Decorate the given {0} as {1} magic. |
|
147 | 147 | |
|
148 | 148 | The decorator can be used with or without arguments, as follows. |
|
149 | 149 | |
|
150 | 150 | i) without arguments: it will create a {1} magic named as the {0} being |
|
151 | 151 | decorated:: |
|
152 | 152 | |
|
153 | 153 | @deco |
|
154 | 154 | def foo(...) |
|
155 | 155 | |
|
156 | 156 | will create a {1} magic named `foo`. |
|
157 | 157 | |
|
158 | 158 | ii) with one string argument: which will be used as the actual name of the |
|
159 | 159 | resulting magic:: |
|
160 | 160 | |
|
161 | 161 | @deco('bar') |
|
162 | 162 | def foo(...) |
|
163 | 163 | |
|
164 | 164 | will create a {1} magic named `bar`. |
|
165 | 165 | |
|
166 | 166 | To register a class magic use ``Interactiveshell.register_magic(class or instance)``. |
|
167 | 167 | """ |
|
168 | 168 | |
|
169 | 169 | # These two are decorator factories. While they are conceptually very similar, |
|
170 | 170 | # there are enough differences in the details that it's simpler to have them |
|
171 | 171 | # written as completely standalone functions rather than trying to share code |
|
172 | 172 | # and make a single one with convoluted logic. |
|
173 | 173 | |
|
174 | 174 | def _method_magic_marker(magic_kind): |
|
175 | 175 | """Decorator factory for methods in Magics subclasses. |
|
176 | 176 | """ |
|
177 | 177 | |
|
178 | 178 | validate_type(magic_kind) |
|
179 | 179 | |
|
180 | 180 | # This is a closure to capture the magic_kind. We could also use a class, |
|
181 | 181 | # but it's overkill for just that one bit of state. |
|
182 | 182 | def magic_deco(arg): |
|
183 | 183 | if callable(arg): |
|
184 | 184 | # "Naked" decorator call (just @foo, no args) |
|
185 | 185 | func = arg |
|
186 | 186 | name = func.__name__ |
|
187 | 187 | retval = arg |
|
188 | 188 | record_magic(magics, magic_kind, name, name) |
|
189 | 189 | elif isinstance(arg, str): |
|
190 | 190 | # Decorator called with arguments (@foo('bar')) |
|
191 | 191 | name = arg |
|
192 | 192 | def mark(func, *a, **kw): |
|
193 | 193 | record_magic(magics, magic_kind, name, func.__name__) |
|
194 | 194 | return func |
|
195 | 195 | retval = mark |
|
196 | 196 | else: |
|
197 | 197 | raise TypeError("Decorator can only be called with " |
|
198 | 198 | "string or function") |
|
199 | 199 | return retval |
|
200 | 200 | |
|
201 | 201 | # Ensure the resulting decorator has a usable docstring |
|
202 | 202 | magic_deco.__doc__ = _docstring_template.format('method', magic_kind) |
|
203 | 203 | return magic_deco |
|
204 | 204 | |
|
205 | 205 | |
|
206 | 206 | def _function_magic_marker(magic_kind): |
|
207 | 207 | """Decorator factory for standalone functions. |
|
208 | 208 | """ |
|
209 | 209 | validate_type(magic_kind) |
|
210 | 210 | |
|
211 | 211 | # This is a closure to capture the magic_kind. We could also use a class, |
|
212 | 212 | # but it's overkill for just that one bit of state. |
|
213 | 213 | def magic_deco(arg): |
|
214 | 214 | # Find get_ipython() in the caller's namespace |
|
215 | 215 | caller = sys._getframe(1) |
|
216 | 216 | for ns in ['f_locals', 'f_globals', 'f_builtins']: |
|
217 | 217 | get_ipython = getattr(caller, ns).get('get_ipython') |
|
218 | 218 | if get_ipython is not None: |
|
219 | 219 | break |
|
220 | 220 | else: |
|
221 | 221 | raise NameError('Decorator can only run in context where ' |
|
222 | 222 | '`get_ipython` exists') |
|
223 | 223 | |
|
224 | 224 | ip = get_ipython() |
|
225 | 225 | |
|
226 | 226 | if callable(arg): |
|
227 | 227 | # "Naked" decorator call (just @foo, no args) |
|
228 | 228 | func = arg |
|
229 | 229 | name = func.__name__ |
|
230 | 230 | ip.register_magic_function(func, magic_kind, name) |
|
231 | 231 | retval = arg |
|
232 | 232 | elif isinstance(arg, str): |
|
233 | 233 | # Decorator called with arguments (@foo('bar')) |
|
234 | 234 | name = arg |
|
235 | 235 | def mark(func, *a, **kw): |
|
236 | 236 | ip.register_magic_function(func, magic_kind, name) |
|
237 | 237 | return func |
|
238 | 238 | retval = mark |
|
239 | 239 | else: |
|
240 | 240 | raise TypeError("Decorator can only be called with " |
|
241 | 241 | "string or function") |
|
242 | 242 | return retval |
|
243 | 243 | |
|
244 | 244 | # Ensure the resulting decorator has a usable docstring |
|
245 | 245 | ds = _docstring_template.format('function', magic_kind) |
|
246 | 246 | |
|
247 | 247 | ds += dedent(""" |
|
248 | 248 | Note: this decorator can only be used in a context where IPython is already |
|
249 | 249 | active, so that the `get_ipython()` call succeeds. You can therefore use |
|
250 | 250 | it in your startup files loaded after IPython initializes, but *not* in the |
|
251 | 251 | IPython configuration file itself, which is executed before IPython is |
|
252 | 252 | fully up and running. Any file located in the `startup` subdirectory of |
|
253 | 253 | your configuration profile will be OK in this sense. |
|
254 | 254 | """) |
|
255 | 255 | |
|
256 | 256 | magic_deco.__doc__ = ds |
|
257 | 257 | return magic_deco |
|
258 | 258 | |
|
259 | 259 | |
|
260 |
MAGIC_NO_VAR_EXPAND_ATTR = |
|
|
260 | MAGIC_NO_VAR_EXPAND_ATTR = "_ipython_magic_no_var_expand" | |
|
261 | MAGIC_OUTPUT_CAN_BE_SILENCED = "_ipython_magic_output_can_be_silenced" | |
|
261 | 262 | |
|
262 | 263 | |
|
263 | 264 | def no_var_expand(magic_func): |
|
264 | 265 | """Mark a magic function as not needing variable expansion |
|
265 | 266 | |
|
266 | 267 | By default, IPython interprets `{a}` or `$a` in the line passed to magics |
|
267 | 268 | as variables that should be interpolated from the interactive namespace |
|
268 | 269 | before passing the line to the magic function. |
|
269 | 270 | This is not always desirable, e.g. when the magic executes Python code |
|
270 | 271 | (%timeit, %time, etc.). |
|
271 | 272 | Decorate magics with `@no_var_expand` to opt-out of variable expansion. |
|
272 | 273 | |
|
273 | 274 | .. versionadded:: 7.3 |
|
274 | 275 | """ |
|
275 | 276 | setattr(magic_func, MAGIC_NO_VAR_EXPAND_ATTR, True) |
|
276 | 277 | return magic_func |
|
277 | 278 | |
|
278 | 279 | |
|
280 | def output_can_be_silenced(magic_func): | |
|
281 | """Mark a magic function so its output may be silenced. | |
|
282 | ||
|
283 | The output is silenced if the Python code used as a parameter of | |
|
284 | the magic ends in a semicolon, not counting a Python comment that can | |
|
285 | follow it. | |
|
286 | """ | |
|
287 | setattr(magic_func, MAGIC_OUTPUT_CAN_BE_SILENCED, True) | |
|
288 | return magic_func | |
|
289 | ||
|
279 | 290 | # Create the actual decorators for public use |
|
280 | 291 | |
|
281 | 292 | # These three are used to decorate methods in class definitions |
|
282 | 293 | line_magic = _method_magic_marker('line') |
|
283 | 294 | cell_magic = _method_magic_marker('cell') |
|
284 | 295 | line_cell_magic = _method_magic_marker('line_cell') |
|
285 | 296 | |
|
286 | 297 | # These three decorate standalone functions and perform the decoration |
|
287 | 298 | # immediately. They can only run where get_ipython() works |
|
288 | 299 | register_line_magic = _function_magic_marker('line') |
|
289 | 300 | register_cell_magic = _function_magic_marker('cell') |
|
290 | 301 | register_line_cell_magic = _function_magic_marker('line_cell') |
|
291 | 302 | |
|
292 | 303 | #----------------------------------------------------------------------------- |
|
293 | 304 | # Core Magic classes |
|
294 | 305 | #----------------------------------------------------------------------------- |
|
295 | 306 | |
|
296 | 307 | class MagicsManager(Configurable): |
|
297 | 308 | """Object that handles all magic-related functionality for IPython. |
|
298 | 309 | """ |
|
299 | 310 | # Non-configurable class attributes |
|
300 | 311 | |
|
301 | 312 | # A two-level dict, first keyed by magic type, then by magic function, and |
|
302 | 313 | # holding the actual callable object as value. This is the dict used for |
|
303 | 314 | # magic function dispatch |
|
304 | 315 | magics = Dict() |
|
305 | 316 | lazy_magics = Dict( |
|
306 | 317 | help=""" |
|
307 | 318 | Mapping from magic names to modules to load. |
|
308 | 319 | |
|
309 | 320 | This can be used in IPython/IPykernel configuration to declare lazy magics |
|
310 | 321 | that will only be imported/registered on first use. |
|
311 | 322 | |
|
312 | 323 | For example:: |
|
313 | 324 | |
|
314 | 325 | c.MagicsManager.lazy_magics = { |
|
315 | 326 | "my_magic": "slow.to.import", |
|
316 | 327 | "my_other_magic": "also.slow", |
|
317 | 328 | } |
|
318 | 329 | |
|
319 | 330 | On first invocation of `%my_magic`, `%%my_magic`, `%%my_other_magic` or |
|
320 | 331 | `%%my_other_magic`, the corresponding module will be loaded as an ipython |
|
321 | 332 | extensions as if you had previously done `%load_ext ipython`. |
|
322 | 333 | |
|
323 | 334 | Magics names should be without percent(s) as magics can be both cell |
|
324 | 335 | and line magics. |
|
325 | 336 | |
|
326 | 337 | Lazy loading happen relatively late in execution process, and |
|
327 | 338 | complex extensions that manipulate Python/IPython internal state or global state |
|
328 | 339 | might not support lazy loading. |
|
329 | 340 | """ |
|
330 | 341 | ).tag( |
|
331 | 342 | config=True, |
|
332 | 343 | ) |
|
333 | 344 | |
|
334 | 345 | # A registry of the original objects that we've been given holding magics. |
|
335 | 346 | registry = Dict() |
|
336 | 347 | |
|
337 | 348 | shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True) |
|
338 | 349 | |
|
339 | 350 | auto_magic = Bool(True, help= |
|
340 | 351 | "Automatically call line magics without requiring explicit % prefix" |
|
341 | 352 | ).tag(config=True) |
|
342 | 353 | @observe('auto_magic') |
|
343 | 354 | def _auto_magic_changed(self, change): |
|
344 | 355 | self.shell.automagic = change['new'] |
|
345 | 356 | |
|
346 | 357 | _auto_status = [ |
|
347 | 358 | 'Automagic is OFF, % prefix IS needed for line magics.', |
|
348 | 359 | 'Automagic is ON, % prefix IS NOT needed for line magics.'] |
|
349 | 360 | |
|
350 | 361 | user_magics = Instance('IPython.core.magics.UserMagics', allow_none=True) |
|
351 | 362 | |
|
352 | 363 | def __init__(self, shell=None, config=None, user_magics=None, **traits): |
|
353 | 364 | |
|
354 | 365 | super(MagicsManager, self).__init__(shell=shell, config=config, |
|
355 | 366 | user_magics=user_magics, **traits) |
|
356 | 367 | self.magics = dict(line={}, cell={}) |
|
357 | 368 | # Let's add the user_magics to the registry for uniformity, so *all* |
|
358 | 369 | # registered magic containers can be found there. |
|
359 | 370 | self.registry[user_magics.__class__.__name__] = user_magics |
|
360 | 371 | |
|
361 | 372 | def auto_status(self): |
|
362 | 373 | """Return descriptive string with automagic status.""" |
|
363 | 374 | return self._auto_status[self.auto_magic] |
|
364 | 375 | |
|
365 | 376 | def lsmagic(self): |
|
366 | 377 | """Return a dict of currently available magic functions. |
|
367 | 378 | |
|
368 | 379 | The return dict has the keys 'line' and 'cell', corresponding to the |
|
369 | 380 | two types of magics we support. Each value is a list of names. |
|
370 | 381 | """ |
|
371 | 382 | return self.magics |
|
372 | 383 | |
|
373 | 384 | def lsmagic_docs(self, brief=False, missing=''): |
|
374 | 385 | """Return dict of documentation of magic functions. |
|
375 | 386 | |
|
376 | 387 | The return dict has the keys 'line' and 'cell', corresponding to the |
|
377 | 388 | two types of magics we support. Each value is a dict keyed by magic |
|
378 | 389 | name whose value is the function docstring. If a docstring is |
|
379 | 390 | unavailable, the value of `missing` is used instead. |
|
380 | 391 | |
|
381 | 392 | If brief is True, only the first line of each docstring will be returned. |
|
382 | 393 | """ |
|
383 | 394 | docs = {} |
|
384 | 395 | for m_type in self.magics: |
|
385 | 396 | m_docs = {} |
|
386 | 397 | for m_name, m_func in self.magics[m_type].items(): |
|
387 | 398 | if m_func.__doc__: |
|
388 | 399 | if brief: |
|
389 | 400 | m_docs[m_name] = m_func.__doc__.split('\n', 1)[0] |
|
390 | 401 | else: |
|
391 | 402 | m_docs[m_name] = m_func.__doc__.rstrip() |
|
392 | 403 | else: |
|
393 | 404 | m_docs[m_name] = missing |
|
394 | 405 | docs[m_type] = m_docs |
|
395 | 406 | return docs |
|
396 | 407 | |
|
397 | 408 | def register_lazy(self, name: str, fully_qualified_name: str): |
|
398 | 409 | """ |
|
399 | 410 | Lazily register a magic via an extension. |
|
400 | 411 | |
|
401 | 412 | |
|
402 | 413 | Parameters |
|
403 | 414 | ---------- |
|
404 | 415 | name : str |
|
405 | 416 | Name of the magic you wish to register. |
|
406 | 417 | fully_qualified_name : |
|
407 | 418 | Fully qualified name of the module/submodule that should be loaded |
|
408 | 419 | as an extensions when the magic is first called. |
|
409 | 420 | It is assumed that loading this extensions will register the given |
|
410 | 421 | magic. |
|
411 | 422 | """ |
|
412 | 423 | |
|
413 | 424 | self.lazy_magics[name] = fully_qualified_name |
|
414 | 425 | |
|
415 | 426 | def register(self, *magic_objects): |
|
416 | 427 | """Register one or more instances of Magics. |
|
417 | 428 | |
|
418 | 429 | Take one or more classes or instances of classes that subclass the main |
|
419 | 430 | `core.Magic` class, and register them with IPython to use the magic |
|
420 | 431 | functions they provide. The registration process will then ensure that |
|
421 | 432 | any methods that have decorated to provide line and/or cell magics will |
|
422 | 433 | be recognized with the `%x`/`%%x` syntax as a line/cell magic |
|
423 | 434 | respectively. |
|
424 | 435 | |
|
425 | 436 | If classes are given, they will be instantiated with the default |
|
426 | 437 | constructor. If your classes need a custom constructor, you should |
|
427 | 438 | instanitate them first and pass the instance. |
|
428 | 439 | |
|
429 | 440 | The provided arguments can be an arbitrary mix of classes and instances. |
|
430 | 441 | |
|
431 | 442 | Parameters |
|
432 | 443 | ---------- |
|
433 | 444 | *magic_objects : one or more classes or instances |
|
434 | 445 | """ |
|
435 | 446 | # Start by validating them to ensure they have all had their magic |
|
436 | 447 | # methods registered at the instance level |
|
437 | 448 | for m in magic_objects: |
|
438 | 449 | if not m.registered: |
|
439 | 450 | raise ValueError("Class of magics %r was constructed without " |
|
440 | 451 | "the @register_magics class decorator") |
|
441 | 452 | if isinstance(m, type): |
|
442 | 453 | # If we're given an uninstantiated class |
|
443 | 454 | m = m(shell=self.shell) |
|
444 | 455 | |
|
445 | 456 | # Now that we have an instance, we can register it and update the |
|
446 | 457 | # table of callables |
|
447 | 458 | self.registry[m.__class__.__name__] = m |
|
448 | 459 | for mtype in magic_kinds: |
|
449 | 460 | self.magics[mtype].update(m.magics[mtype]) |
|
450 | 461 | |
|
451 | 462 | def register_function(self, func, magic_kind='line', magic_name=None): |
|
452 | 463 | """Expose a standalone function as magic function for IPython. |
|
453 | 464 | |
|
454 | 465 | This will create an IPython magic (line, cell or both) from a |
|
455 | 466 | standalone function. The functions should have the following |
|
456 | 467 | signatures: |
|
457 | 468 | |
|
458 | 469 | * For line magics: `def f(line)` |
|
459 | 470 | * For cell magics: `def f(line, cell)` |
|
460 | 471 | * For a function that does both: `def f(line, cell=None)` |
|
461 | 472 | |
|
462 | 473 | In the latter case, the function will be called with `cell==None` when |
|
463 | 474 | invoked as `%f`, and with cell as a string when invoked as `%%f`. |
|
464 | 475 | |
|
465 | 476 | Parameters |
|
466 | 477 | ---------- |
|
467 | 478 | func : callable |
|
468 | 479 | Function to be registered as a magic. |
|
469 | 480 | magic_kind : str |
|
470 | 481 | Kind of magic, one of 'line', 'cell' or 'line_cell' |
|
471 | 482 | magic_name : optional str |
|
472 | 483 | If given, the name the magic will have in the IPython namespace. By |
|
473 | 484 | default, the name of the function itself is used. |
|
474 | 485 | """ |
|
475 | 486 | |
|
476 | 487 | # Create the new method in the user_magics and register it in the |
|
477 | 488 | # global table |
|
478 | 489 | validate_type(magic_kind) |
|
479 | 490 | magic_name = func.__name__ if magic_name is None else magic_name |
|
480 | 491 | setattr(self.user_magics, magic_name, func) |
|
481 | 492 | record_magic(self.magics, magic_kind, magic_name, func) |
|
482 | 493 | |
|
483 | 494 | def register_alias(self, alias_name, magic_name, magic_kind='line', magic_params=None): |
|
484 | 495 | """Register an alias to a magic function. |
|
485 | 496 | |
|
486 | 497 | The alias is an instance of :class:`MagicAlias`, which holds the |
|
487 | 498 | name and kind of the magic it should call. Binding is done at |
|
488 | 499 | call time, so if the underlying magic function is changed the alias |
|
489 | 500 | will call the new function. |
|
490 | 501 | |
|
491 | 502 | Parameters |
|
492 | 503 | ---------- |
|
493 | 504 | alias_name : str |
|
494 | 505 | The name of the magic to be registered. |
|
495 | 506 | magic_name : str |
|
496 | 507 | The name of an existing magic. |
|
497 | 508 | magic_kind : str |
|
498 | 509 | Kind of magic, one of 'line' or 'cell' |
|
499 | 510 | """ |
|
500 | 511 | |
|
501 | 512 | # `validate_type` is too permissive, as it allows 'line_cell' |
|
502 | 513 | # which we do not handle. |
|
503 | 514 | if magic_kind not in magic_kinds: |
|
504 | 515 | raise ValueError('magic_kind must be one of %s, %s given' % |
|
505 | 516 | magic_kinds, magic_kind) |
|
506 | 517 | |
|
507 | 518 | alias = MagicAlias(self.shell, magic_name, magic_kind, magic_params) |
|
508 | 519 | setattr(self.user_magics, alias_name, alias) |
|
509 | 520 | record_magic(self.magics, magic_kind, alias_name, alias) |
|
510 | 521 | |
|
511 | 522 | # Key base class that provides the central functionality for magics. |
|
512 | 523 | |
|
513 | 524 | |
|
514 | 525 | class Magics(Configurable): |
|
515 | 526 | """Base class for implementing magic functions. |
|
516 | 527 | |
|
517 | 528 | Shell functions which can be reached as %function_name. All magic |
|
518 | 529 | functions should accept a string, which they can parse for their own |
|
519 | 530 | needs. This can make some functions easier to type, eg `%cd ../` |
|
520 | 531 | vs. `%cd("../")` |
|
521 | 532 | |
|
522 | 533 | Classes providing magic functions need to subclass this class, and they |
|
523 | 534 | MUST: |
|
524 | 535 | |
|
525 | 536 | - Use the method decorators `@line_magic` and `@cell_magic` to decorate |
|
526 | 537 | individual methods as magic functions, AND |
|
527 | 538 | |
|
528 | 539 | - Use the class decorator `@magics_class` to ensure that the magic |
|
529 | 540 | methods are properly registered at the instance level upon instance |
|
530 | 541 | initialization. |
|
531 | 542 | |
|
532 | 543 | See :mod:`magic_functions` for examples of actual implementation classes. |
|
533 | 544 | """ |
|
534 | 545 | # Dict holding all command-line options for each magic. |
|
535 | 546 | options_table = None |
|
536 | 547 | # Dict for the mapping of magic names to methods, set by class decorator |
|
537 | 548 | magics = None |
|
538 | 549 | # Flag to check that the class decorator was properly applied |
|
539 | 550 | registered = False |
|
540 | 551 | # Instance of IPython shell |
|
541 | 552 | shell = None |
|
542 | 553 | |
|
543 | 554 | def __init__(self, shell=None, **kwargs): |
|
544 | 555 | if not(self.__class__.registered): |
|
545 | 556 | raise ValueError('Magics subclass without registration - ' |
|
546 | 557 | 'did you forget to apply @magics_class?') |
|
547 | 558 | if shell is not None: |
|
548 | 559 | if hasattr(shell, 'configurables'): |
|
549 | 560 | shell.configurables.append(self) |
|
550 | 561 | if hasattr(shell, 'config'): |
|
551 | 562 | kwargs.setdefault('parent', shell) |
|
552 | 563 | |
|
553 | 564 | self.shell = shell |
|
554 | 565 | self.options_table = {} |
|
555 | 566 | # The method decorators are run when the instance doesn't exist yet, so |
|
556 | 567 | # they can only record the names of the methods they are supposed to |
|
557 | 568 | # grab. Only now, that the instance exists, can we create the proper |
|
558 | 569 | # mapping to bound methods. So we read the info off the original names |
|
559 | 570 | # table and replace each method name by the actual bound method. |
|
560 | 571 | # But we mustn't clobber the *class* mapping, in case of multiple instances. |
|
561 | 572 | class_magics = self.magics |
|
562 | 573 | self.magics = {} |
|
563 | 574 | for mtype in magic_kinds: |
|
564 | 575 | tab = self.magics[mtype] = {} |
|
565 | 576 | cls_tab = class_magics[mtype] |
|
566 | 577 | for magic_name, meth_name in cls_tab.items(): |
|
567 | 578 | if isinstance(meth_name, str): |
|
568 | 579 | # it's a method name, grab it |
|
569 | 580 | tab[magic_name] = getattr(self, meth_name) |
|
570 | 581 | else: |
|
571 | 582 | # it's the real thing |
|
572 | 583 | tab[magic_name] = meth_name |
|
573 | 584 | # Configurable **needs** to be initiated at the end or the config |
|
574 | 585 | # magics get screwed up. |
|
575 | 586 | super(Magics, self).__init__(**kwargs) |
|
576 | 587 | |
|
577 | 588 | def arg_err(self,func): |
|
578 | 589 | """Print docstring if incorrect arguments were passed""" |
|
579 | 590 | print('Error in arguments:') |
|
580 | 591 | print(oinspect.getdoc(func)) |
|
581 | 592 | |
|
582 | 593 | def format_latex(self, strng): |
|
583 | 594 | """Format a string for latex inclusion.""" |
|
584 | 595 | |
|
585 | 596 | # Characters that need to be escaped for latex: |
|
586 | 597 | escape_re = re.compile(r'(%|_|\$|#|&)',re.MULTILINE) |
|
587 | 598 | # Magic command names as headers: |
|
588 | 599 | cmd_name_re = re.compile(r'^(%s.*?):' % ESC_MAGIC, |
|
589 | 600 | re.MULTILINE) |
|
590 | 601 | # Magic commands |
|
591 | 602 | cmd_re = re.compile(r'(?P<cmd>%s.+?\b)(?!\}\}:)' % ESC_MAGIC, |
|
592 | 603 | re.MULTILINE) |
|
593 | 604 | # Paragraph continue |
|
594 | 605 | par_re = re.compile(r'\\$',re.MULTILINE) |
|
595 | 606 | |
|
596 | 607 | # The "\n" symbol |
|
597 | 608 | newline_re = re.compile(r'\\n') |
|
598 | 609 | |
|
599 | 610 | # Now build the string for output: |
|
600 | 611 | #strng = cmd_name_re.sub(r'\n\\texttt{\\textsl{\\large \1}}:',strng) |
|
601 | 612 | strng = cmd_name_re.sub(r'\n\\bigskip\n\\texttt{\\textbf{ \1}}:', |
|
602 | 613 | strng) |
|
603 | 614 | strng = cmd_re.sub(r'\\texttt{\g<cmd>}',strng) |
|
604 | 615 | strng = par_re.sub(r'\\\\',strng) |
|
605 | 616 | strng = escape_re.sub(r'\\\1',strng) |
|
606 | 617 | strng = newline_re.sub(r'\\textbackslash{}n',strng) |
|
607 | 618 | return strng |
|
608 | 619 | |
|
609 | 620 | def parse_options(self, arg_str, opt_str, *long_opts, **kw): |
|
610 | 621 | """Parse options passed to an argument string. |
|
611 | 622 | |
|
612 | 623 | The interface is similar to that of :func:`getopt.getopt`, but it |
|
613 | 624 | returns a :class:`~IPython.utils.struct.Struct` with the options as keys |
|
614 | 625 | and the stripped argument string still as a string. |
|
615 | 626 | |
|
616 | 627 | arg_str is quoted as a true sys.argv vector by using shlex.split. |
|
617 | 628 | This allows us to easily expand variables, glob files, quote |
|
618 | 629 | arguments, etc. |
|
619 | 630 | |
|
620 | 631 | Parameters |
|
621 | 632 | ---------- |
|
622 | 633 | arg_str : str |
|
623 | 634 | The arguments to parse. |
|
624 | 635 | opt_str : str |
|
625 | 636 | The options specification. |
|
626 | 637 | mode : str, default 'string' |
|
627 | 638 | If given as 'list', the argument string is returned as a list (split |
|
628 | 639 | on whitespace) instead of a string. |
|
629 | 640 | list_all : bool, default False |
|
630 | 641 | Put all option values in lists. Normally only options |
|
631 | 642 | appearing more than once are put in a list. |
|
632 | 643 | posix : bool, default True |
|
633 | 644 | Whether to split the input line in POSIX mode or not, as per the |
|
634 | 645 | conventions outlined in the :mod:`shlex` module from the standard |
|
635 | 646 | library. |
|
636 | 647 | """ |
|
637 | 648 | |
|
638 | 649 | # inject default options at the beginning of the input line |
|
639 | 650 | caller = sys._getframe(1).f_code.co_name |
|
640 | 651 | arg_str = '%s %s' % (self.options_table.get(caller,''),arg_str) |
|
641 | 652 | |
|
642 | 653 | mode = kw.get('mode','string') |
|
643 | 654 | if mode not in ['string','list']: |
|
644 | 655 | raise ValueError('incorrect mode given: %s' % mode) |
|
645 | 656 | # Get options |
|
646 | 657 | list_all = kw.get('list_all',0) |
|
647 | 658 | posix = kw.get('posix', os.name == 'posix') |
|
648 | 659 | strict = kw.get('strict', True) |
|
649 | 660 | |
|
650 | 661 | preserve_non_opts = kw.get("preserve_non_opts", False) |
|
651 | 662 | remainder_arg_str = arg_str |
|
652 | 663 | |
|
653 | 664 | # Check if we have more than one argument to warrant extra processing: |
|
654 | 665 | odict = {} # Dictionary with options |
|
655 | 666 | args = arg_str.split() |
|
656 | 667 | if len(args) >= 1: |
|
657 | 668 | # If the list of inputs only has 0 or 1 thing in it, there's no |
|
658 | 669 | # need to look for options |
|
659 | 670 | argv = arg_split(arg_str, posix, strict) |
|
660 | 671 | # Do regular option processing |
|
661 | 672 | try: |
|
662 | 673 | opts,args = getopt(argv, opt_str, long_opts) |
|
663 | 674 | except GetoptError as e: |
|
664 | 675 | raise UsageError( |
|
665 | 676 | '%s ( allowed: "%s" %s)' % (e.msg, opt_str, " ".join(long_opts)) |
|
666 | 677 | ) from e |
|
667 | 678 | for o, a in opts: |
|
668 | 679 | if mode == "string" and preserve_non_opts: |
|
669 | 680 | # remove option-parts from the original args-string and preserve remaining-part. |
|
670 | 681 | # This relies on the arg_split(...) and getopt(...)'s impl spec, that the parsed options are |
|
671 | 682 | # returned in the original order. |
|
672 | 683 | remainder_arg_str = remainder_arg_str.replace(o, "", 1).replace( |
|
673 | 684 | a, "", 1 |
|
674 | 685 | ) |
|
675 | 686 | if o.startswith("--"): |
|
676 | 687 | o = o[2:] |
|
677 | 688 | else: |
|
678 | 689 | o = o[1:] |
|
679 | 690 | try: |
|
680 | 691 | odict[o].append(a) |
|
681 | 692 | except AttributeError: |
|
682 | 693 | odict[o] = [odict[o],a] |
|
683 | 694 | except KeyError: |
|
684 | 695 | if list_all: |
|
685 | 696 | odict[o] = [a] |
|
686 | 697 | else: |
|
687 | 698 | odict[o] = a |
|
688 | 699 | |
|
689 | 700 | # Prepare opts,args for return |
|
690 | 701 | opts = Struct(odict) |
|
691 | 702 | if mode == 'string': |
|
692 | 703 | if preserve_non_opts: |
|
693 | 704 | args = remainder_arg_str.lstrip() |
|
694 | 705 | else: |
|
695 | 706 | args = " ".join(args) |
|
696 | 707 | |
|
697 | 708 | return opts,args |
|
698 | 709 | |
|
699 | 710 | def default_option(self, fn, optstr): |
|
700 | 711 | """Make an entry in the options_table for fn, with value optstr""" |
|
701 | 712 | |
|
702 | 713 | if fn not in self.lsmagic(): |
|
703 | 714 | error("%s is not a magic function" % fn) |
|
704 | 715 | self.options_table[fn] = optstr |
|
705 | 716 | |
|
706 | 717 | |
|
707 | 718 | class MagicAlias(object): |
|
708 | 719 | """An alias to another magic function. |
|
709 | 720 | |
|
710 | 721 | An alias is determined by its magic name and magic kind. Lookup |
|
711 | 722 | is done at call time, so if the underlying magic changes the alias |
|
712 | 723 | will call the new function. |
|
713 | 724 | |
|
714 | 725 | Use the :meth:`MagicsManager.register_alias` method or the |
|
715 | 726 | `%alias_magic` magic function to create and register a new alias. |
|
716 | 727 | """ |
|
717 | 728 | def __init__(self, shell, magic_name, magic_kind, magic_params=None): |
|
718 | 729 | self.shell = shell |
|
719 | 730 | self.magic_name = magic_name |
|
720 | 731 | self.magic_params = magic_params |
|
721 | 732 | self.magic_kind = magic_kind |
|
722 | 733 | |
|
723 | 734 | self.pretty_target = '%s%s' % (magic_escapes[self.magic_kind], self.magic_name) |
|
724 | 735 | self.__doc__ = "Alias for `%s`." % self.pretty_target |
|
725 | 736 | |
|
726 | 737 | self._in_call = False |
|
727 | 738 | |
|
728 | 739 | def __call__(self, *args, **kwargs): |
|
729 | 740 | """Call the magic alias.""" |
|
730 | 741 | fn = self.shell.find_magic(self.magic_name, self.magic_kind) |
|
731 | 742 | if fn is None: |
|
732 | 743 | raise UsageError("Magic `%s` not found." % self.pretty_target) |
|
733 | 744 | |
|
734 | 745 | # Protect against infinite recursion. |
|
735 | 746 | if self._in_call: |
|
736 | 747 | raise UsageError("Infinite recursion detected; " |
|
737 | 748 | "magic aliases cannot call themselves.") |
|
738 | 749 | self._in_call = True |
|
739 | 750 | try: |
|
740 | 751 | if self.magic_params: |
|
741 | 752 | args_list = list(args) |
|
742 | 753 | args_list[0] = self.magic_params + " " + args[0] |
|
743 | 754 | args = tuple(args_list) |
|
744 | 755 | return fn(*args, **kwargs) |
|
745 | 756 | finally: |
|
746 | 757 | self._in_call = False |
@@ -1,1510 +1,1512 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | """Implementation of execution-related magic functions.""" |
|
3 | 3 | |
|
4 | 4 | # Copyright (c) IPython Development Team. |
|
5 | 5 | # Distributed under the terms of the Modified BSD License. |
|
6 | 6 | |
|
7 | 7 | |
|
8 | 8 | import ast |
|
9 | 9 | import bdb |
|
10 | 10 | import builtins as builtin_mod |
|
11 | 11 | import cProfile as profile |
|
12 | 12 | import gc |
|
13 | 13 | import itertools |
|
14 | 14 | import math |
|
15 | 15 | import os |
|
16 | 16 | import pstats |
|
17 | 17 | import re |
|
18 | 18 | import shlex |
|
19 | 19 | import sys |
|
20 | 20 | import time |
|
21 | 21 | import timeit |
|
22 | 22 | from ast import Module |
|
23 | 23 | from io import StringIO |
|
24 | 24 | from logging import error |
|
25 | 25 | from pathlib import Path |
|
26 | 26 | from pdb import Restart |
|
27 | 27 | from warnings import warn |
|
28 | 28 | |
|
29 | 29 | from IPython.core import magic_arguments, oinspect, page |
|
30 | 30 | from IPython.core.error import UsageError |
|
31 | 31 | from IPython.core.macro import Macro |
|
32 | 32 | from IPython.core.magic import ( |
|
33 | 33 | Magics, |
|
34 | 34 | cell_magic, |
|
35 | 35 | line_cell_magic, |
|
36 | 36 | line_magic, |
|
37 | 37 | magics_class, |
|
38 | 38 | needs_local_scope, |
|
39 | 39 | no_var_expand, |
|
40 | output_can_be_silenced, | |
|
40 | 41 | on_off, |
|
41 | 42 | ) |
|
42 | 43 | from IPython.testing.skipdoctest import skip_doctest |
|
43 | 44 | from IPython.utils.capture import capture_output |
|
44 | 45 | from IPython.utils.contexts import preserve_keys |
|
45 | 46 | from IPython.utils.ipstruct import Struct |
|
46 | 47 | from IPython.utils.module_paths import find_mod |
|
47 | 48 | from IPython.utils.path import get_py_filename, shellglob |
|
48 | 49 | from IPython.utils.timing import clock, clock2 |
|
49 | 50 | |
|
50 | 51 | #----------------------------------------------------------------------------- |
|
51 | 52 | # Magic implementation classes |
|
52 | 53 | #----------------------------------------------------------------------------- |
|
53 | 54 | |
|
54 | 55 | |
|
55 | 56 | class TimeitResult(object): |
|
56 | 57 | """ |
|
57 | 58 | Object returned by the timeit magic with info about the run. |
|
58 | 59 | |
|
59 | 60 | Contains the following attributes : |
|
60 | 61 | |
|
61 | 62 | loops: (int) number of loops done per measurement |
|
62 | 63 | repeat: (int) number of times the measurement has been repeated |
|
63 | 64 | best: (float) best execution time / number |
|
64 | 65 | all_runs: (list of float) execution time of each run (in s) |
|
65 | 66 | compile_time: (float) time of statement compilation (s) |
|
66 | 67 | |
|
67 | 68 | """ |
|
68 | 69 | def __init__(self, loops, repeat, best, worst, all_runs, compile_time, precision): |
|
69 | 70 | self.loops = loops |
|
70 | 71 | self.repeat = repeat |
|
71 | 72 | self.best = best |
|
72 | 73 | self.worst = worst |
|
73 | 74 | self.all_runs = all_runs |
|
74 | 75 | self.compile_time = compile_time |
|
75 | 76 | self._precision = precision |
|
76 | 77 | self.timings = [ dt / self.loops for dt in all_runs] |
|
77 | 78 | |
|
78 | 79 | @property |
|
79 | 80 | def average(self): |
|
80 | 81 | return math.fsum(self.timings) / len(self.timings) |
|
81 | 82 | |
|
82 | 83 | @property |
|
83 | 84 | def stdev(self): |
|
84 | 85 | mean = self.average |
|
85 | 86 | return (math.fsum([(x - mean) ** 2 for x in self.timings]) / len(self.timings)) ** 0.5 |
|
86 | 87 | |
|
87 | 88 | def __str__(self): |
|
88 | 89 | pm = '+-' |
|
89 | 90 | if hasattr(sys.stdout, 'encoding') and sys.stdout.encoding: |
|
90 | 91 | try: |
|
91 | 92 | u'\xb1'.encode(sys.stdout.encoding) |
|
92 | 93 | pm = u'\xb1' |
|
93 | 94 | except: |
|
94 | 95 | pass |
|
95 | 96 | return "{mean} {pm} {std} per loop (mean {pm} std. dev. of {runs} run{run_plural}, {loops:,} loop{loop_plural} each)".format( |
|
96 | 97 | pm=pm, |
|
97 | 98 | runs=self.repeat, |
|
98 | 99 | loops=self.loops, |
|
99 | 100 | loop_plural="" if self.loops == 1 else "s", |
|
100 | 101 | run_plural="" if self.repeat == 1 else "s", |
|
101 | 102 | mean=_format_time(self.average, self._precision), |
|
102 | 103 | std=_format_time(self.stdev, self._precision), |
|
103 | 104 | ) |
|
104 | 105 | |
|
105 | 106 | def _repr_pretty_(self, p , cycle): |
|
106 | 107 | unic = self.__str__() |
|
107 | 108 | p.text(u'<TimeitResult : '+unic+u'>') |
|
108 | 109 | |
|
109 | 110 | |
|
110 | 111 | class TimeitTemplateFiller(ast.NodeTransformer): |
|
111 | 112 | """Fill in the AST template for timing execution. |
|
112 | 113 | |
|
113 | 114 | This is quite closely tied to the template definition, which is in |
|
114 | 115 | :meth:`ExecutionMagics.timeit`. |
|
115 | 116 | """ |
|
116 | 117 | def __init__(self, ast_setup, ast_stmt): |
|
117 | 118 | self.ast_setup = ast_setup |
|
118 | 119 | self.ast_stmt = ast_stmt |
|
119 | 120 | |
|
120 | 121 | def visit_FunctionDef(self, node): |
|
121 | 122 | "Fill in the setup statement" |
|
122 | 123 | self.generic_visit(node) |
|
123 | 124 | if node.name == "inner": |
|
124 | 125 | node.body[:1] = self.ast_setup.body |
|
125 | 126 | |
|
126 | 127 | return node |
|
127 | 128 | |
|
128 | 129 | def visit_For(self, node): |
|
129 | 130 | "Fill in the statement to be timed" |
|
130 | 131 | if getattr(getattr(node.body[0], 'value', None), 'id', None) == 'stmt': |
|
131 | 132 | node.body = self.ast_stmt.body |
|
132 | 133 | return node |
|
133 | 134 | |
|
134 | 135 | |
|
135 | 136 | class Timer(timeit.Timer): |
|
136 | 137 | """Timer class that explicitly uses self.inner |
|
137 | 138 | |
|
138 | 139 | which is an undocumented implementation detail of CPython, |
|
139 | 140 | not shared by PyPy. |
|
140 | 141 | """ |
|
141 | 142 | # Timer.timeit copied from CPython 3.4.2 |
|
142 | 143 | def timeit(self, number=timeit.default_number): |
|
143 | 144 | """Time 'number' executions of the main statement. |
|
144 | 145 | |
|
145 | 146 | To be precise, this executes the setup statement once, and |
|
146 | 147 | then returns the time it takes to execute the main statement |
|
147 | 148 | a number of times, as a float measured in seconds. The |
|
148 | 149 | argument is the number of times through the loop, defaulting |
|
149 | 150 | to one million. The main statement, the setup statement and |
|
150 | 151 | the timer function to be used are passed to the constructor. |
|
151 | 152 | """ |
|
152 | 153 | it = itertools.repeat(None, number) |
|
153 | 154 | gcold = gc.isenabled() |
|
154 | 155 | gc.disable() |
|
155 | 156 | try: |
|
156 | 157 | timing = self.inner(it, self.timer) |
|
157 | 158 | finally: |
|
158 | 159 | if gcold: |
|
159 | 160 | gc.enable() |
|
160 | 161 | return timing |
|
161 | 162 | |
|
162 | 163 | |
|
163 | 164 | @magics_class |
|
164 | 165 | class ExecutionMagics(Magics): |
|
165 | 166 | """Magics related to code execution, debugging, profiling, etc. |
|
166 | 167 | |
|
167 | 168 | """ |
|
168 | 169 | |
|
169 | 170 | def __init__(self, shell): |
|
170 | 171 | super(ExecutionMagics, self).__init__(shell) |
|
171 | 172 | # Default execution function used to actually run user code. |
|
172 | 173 | self.default_runner = None |
|
173 | 174 | |
|
174 | 175 | @skip_doctest |
|
175 | 176 | @no_var_expand |
|
176 | 177 | @line_cell_magic |
|
177 | 178 | def prun(self, parameter_s='', cell=None): |
|
178 | 179 | |
|
179 | 180 | """Run a statement through the python code profiler. |
|
180 | 181 | |
|
181 | 182 | Usage, in line mode: |
|
182 | 183 | %prun [options] statement |
|
183 | 184 | |
|
184 | 185 | Usage, in cell mode: |
|
185 | 186 | %%prun [options] [statement] |
|
186 | 187 | code... |
|
187 | 188 | code... |
|
188 | 189 | |
|
189 | 190 | In cell mode, the additional code lines are appended to the (possibly |
|
190 | 191 | empty) statement in the first line. Cell mode allows you to easily |
|
191 | 192 | profile multiline blocks without having to put them in a separate |
|
192 | 193 | function. |
|
193 | 194 | |
|
194 | 195 | The given statement (which doesn't require quote marks) is run via the |
|
195 | 196 | python profiler in a manner similar to the profile.run() function. |
|
196 | 197 | Namespaces are internally managed to work correctly; profile.run |
|
197 | 198 | cannot be used in IPython because it makes certain assumptions about |
|
198 | 199 | namespaces which do not hold under IPython. |
|
199 | 200 | |
|
200 | 201 | Options: |
|
201 | 202 | |
|
202 | 203 | -l <limit> |
|
203 | 204 | you can place restrictions on what or how much of the |
|
204 | 205 | profile gets printed. The limit value can be: |
|
205 | 206 | |
|
206 | 207 | * A string: only information for function names containing this string |
|
207 | 208 | is printed. |
|
208 | 209 | |
|
209 | 210 | * An integer: only these many lines are printed. |
|
210 | 211 | |
|
211 | 212 | * A float (between 0 and 1): this fraction of the report is printed |
|
212 | 213 | (for example, use a limit of 0.4 to see the topmost 40% only). |
|
213 | 214 | |
|
214 | 215 | You can combine several limits with repeated use of the option. For |
|
215 | 216 | example, ``-l __init__ -l 5`` will print only the topmost 5 lines of |
|
216 | 217 | information about class constructors. |
|
217 | 218 | |
|
218 | 219 | -r |
|
219 | 220 | return the pstats.Stats object generated by the profiling. This |
|
220 | 221 | object has all the information about the profile in it, and you can |
|
221 | 222 | later use it for further analysis or in other functions. |
|
222 | 223 | |
|
223 | 224 | -s <key> |
|
224 | 225 | sort profile by given key. You can provide more than one key |
|
225 | 226 | by using the option several times: '-s key1 -s key2 -s key3...'. The |
|
226 | 227 | default sorting key is 'time'. |
|
227 | 228 | |
|
228 | 229 | The following is copied verbatim from the profile documentation |
|
229 | 230 | referenced below: |
|
230 | 231 | |
|
231 | 232 | When more than one key is provided, additional keys are used as |
|
232 | 233 | secondary criteria when the there is equality in all keys selected |
|
233 | 234 | before them. |
|
234 | 235 | |
|
235 | 236 | Abbreviations can be used for any key names, as long as the |
|
236 | 237 | abbreviation is unambiguous. The following are the keys currently |
|
237 | 238 | defined: |
|
238 | 239 | |
|
239 | 240 | ============ ===================== |
|
240 | 241 | Valid Arg Meaning |
|
241 | 242 | ============ ===================== |
|
242 | 243 | "calls" call count |
|
243 | 244 | "cumulative" cumulative time |
|
244 | 245 | "file" file name |
|
245 | 246 | "module" file name |
|
246 | 247 | "pcalls" primitive call count |
|
247 | 248 | "line" line number |
|
248 | 249 | "name" function name |
|
249 | 250 | "nfl" name/file/line |
|
250 | 251 | "stdname" standard name |
|
251 | 252 | "time" internal time |
|
252 | 253 | ============ ===================== |
|
253 | 254 | |
|
254 | 255 | Note that all sorts on statistics are in descending order (placing |
|
255 | 256 | most time consuming items first), where as name, file, and line number |
|
256 | 257 | searches are in ascending order (i.e., alphabetical). The subtle |
|
257 | 258 | distinction between "nfl" and "stdname" is that the standard name is a |
|
258 | 259 | sort of the name as printed, which means that the embedded line |
|
259 | 260 | numbers get compared in an odd way. For example, lines 3, 20, and 40 |
|
260 | 261 | would (if the file names were the same) appear in the string order |
|
261 | 262 | "20" "3" and "40". In contrast, "nfl" does a numeric compare of the |
|
262 | 263 | line numbers. In fact, sort_stats("nfl") is the same as |
|
263 | 264 | sort_stats("name", "file", "line"). |
|
264 | 265 | |
|
265 | 266 | -T <filename> |
|
266 | 267 | save profile results as shown on screen to a text |
|
267 | 268 | file. The profile is still shown on screen. |
|
268 | 269 | |
|
269 | 270 | -D <filename> |
|
270 | 271 | save (via dump_stats) profile statistics to given |
|
271 | 272 | filename. This data is in a format understood by the pstats module, and |
|
272 | 273 | is generated by a call to the dump_stats() method of profile |
|
273 | 274 | objects. The profile is still shown on screen. |
|
274 | 275 | |
|
275 | 276 | -q |
|
276 | 277 | suppress output to the pager. Best used with -T and/or -D above. |
|
277 | 278 | |
|
278 | 279 | If you want to run complete programs under the profiler's control, use |
|
279 | 280 | ``%run -p [prof_opts] filename.py [args to program]`` where prof_opts |
|
280 | 281 | contains profiler specific options as described here. |
|
281 | 282 | |
|
282 | 283 | You can read the complete documentation for the profile module with:: |
|
283 | 284 | |
|
284 | 285 | In [1]: import profile; profile.help() |
|
285 | 286 | |
|
286 | 287 | .. versionchanged:: 7.3 |
|
287 | 288 | User variables are no longer expanded, |
|
288 | 289 | the magic line is always left unmodified. |
|
289 | 290 | |
|
290 | 291 | """ |
|
291 | 292 | opts, arg_str = self.parse_options(parameter_s, 'D:l:rs:T:q', |
|
292 | 293 | list_all=True, posix=False) |
|
293 | 294 | if cell is not None: |
|
294 | 295 | arg_str += '\n' + cell |
|
295 | 296 | arg_str = self.shell.transform_cell(arg_str) |
|
296 | 297 | return self._run_with_profiler(arg_str, opts, self.shell.user_ns) |
|
297 | 298 | |
|
298 | 299 | def _run_with_profiler(self, code, opts, namespace): |
|
299 | 300 | """ |
|
300 | 301 | Run `code` with profiler. Used by ``%prun`` and ``%run -p``. |
|
301 | 302 | |
|
302 | 303 | Parameters |
|
303 | 304 | ---------- |
|
304 | 305 | code : str |
|
305 | 306 | Code to be executed. |
|
306 | 307 | opts : Struct |
|
307 | 308 | Options parsed by `self.parse_options`. |
|
308 | 309 | namespace : dict |
|
309 | 310 | A dictionary for Python namespace (e.g., `self.shell.user_ns`). |
|
310 | 311 | |
|
311 | 312 | """ |
|
312 | 313 | |
|
313 | 314 | # Fill default values for unspecified options: |
|
314 | 315 | opts.merge(Struct(D=[''], l=[], s=['time'], T=[''])) |
|
315 | 316 | |
|
316 | 317 | prof = profile.Profile() |
|
317 | 318 | try: |
|
318 | 319 | prof = prof.runctx(code, namespace, namespace) |
|
319 | 320 | sys_exit = '' |
|
320 | 321 | except SystemExit: |
|
321 | 322 | sys_exit = """*** SystemExit exception caught in code being profiled.""" |
|
322 | 323 | |
|
323 | 324 | stats = pstats.Stats(prof).strip_dirs().sort_stats(*opts.s) |
|
324 | 325 | |
|
325 | 326 | lims = opts.l |
|
326 | 327 | if lims: |
|
327 | 328 | lims = [] # rebuild lims with ints/floats/strings |
|
328 | 329 | for lim in opts.l: |
|
329 | 330 | try: |
|
330 | 331 | lims.append(int(lim)) |
|
331 | 332 | except ValueError: |
|
332 | 333 | try: |
|
333 | 334 | lims.append(float(lim)) |
|
334 | 335 | except ValueError: |
|
335 | 336 | lims.append(lim) |
|
336 | 337 | |
|
337 | 338 | # Trap output. |
|
338 | 339 | stdout_trap = StringIO() |
|
339 | 340 | stats_stream = stats.stream |
|
340 | 341 | try: |
|
341 | 342 | stats.stream = stdout_trap |
|
342 | 343 | stats.print_stats(*lims) |
|
343 | 344 | finally: |
|
344 | 345 | stats.stream = stats_stream |
|
345 | 346 | |
|
346 | 347 | output = stdout_trap.getvalue() |
|
347 | 348 | output = output.rstrip() |
|
348 | 349 | |
|
349 | 350 | if 'q' not in opts: |
|
350 | 351 | page.page(output) |
|
351 | 352 | print(sys_exit, end=' ') |
|
352 | 353 | |
|
353 | 354 | dump_file = opts.D[0] |
|
354 | 355 | text_file = opts.T[0] |
|
355 | 356 | if dump_file: |
|
356 | 357 | prof.dump_stats(dump_file) |
|
357 | 358 | print( |
|
358 | 359 | f"\n*** Profile stats marshalled to file {repr(dump_file)}.{sys_exit}" |
|
359 | 360 | ) |
|
360 | 361 | if text_file: |
|
361 | 362 | pfile = Path(text_file) |
|
362 | 363 | pfile.touch(exist_ok=True) |
|
363 | 364 | pfile.write_text(output, encoding="utf-8") |
|
364 | 365 | |
|
365 | 366 | print( |
|
366 | 367 | f"\n*** Profile printout saved to text file {repr(text_file)}.{sys_exit}" |
|
367 | 368 | ) |
|
368 | 369 | |
|
369 | 370 | if 'r' in opts: |
|
370 | 371 | return stats |
|
371 | 372 | |
|
372 | 373 | return None |
|
373 | 374 | |
|
374 | 375 | @line_magic |
|
375 | 376 | def pdb(self, parameter_s=''): |
|
376 | 377 | """Control the automatic calling of the pdb interactive debugger. |
|
377 | 378 | |
|
378 | 379 | Call as '%pdb on', '%pdb 1', '%pdb off' or '%pdb 0'. If called without |
|
379 | 380 | argument it works as a toggle. |
|
380 | 381 | |
|
381 | 382 | When an exception is triggered, IPython can optionally call the |
|
382 | 383 | interactive pdb debugger after the traceback printout. %pdb toggles |
|
383 | 384 | this feature on and off. |
|
384 | 385 | |
|
385 | 386 | The initial state of this feature is set in your configuration |
|
386 | 387 | file (the option is ``InteractiveShell.pdb``). |
|
387 | 388 | |
|
388 | 389 | If you want to just activate the debugger AFTER an exception has fired, |
|
389 | 390 | without having to type '%pdb on' and rerunning your code, you can use |
|
390 | 391 | the %debug magic.""" |
|
391 | 392 | |
|
392 | 393 | par = parameter_s.strip().lower() |
|
393 | 394 | |
|
394 | 395 | if par: |
|
395 | 396 | try: |
|
396 | 397 | new_pdb = {'off':0,'0':0,'on':1,'1':1}[par] |
|
397 | 398 | except KeyError: |
|
398 | 399 | print ('Incorrect argument. Use on/1, off/0, ' |
|
399 | 400 | 'or nothing for a toggle.') |
|
400 | 401 | return |
|
401 | 402 | else: |
|
402 | 403 | # toggle |
|
403 | 404 | new_pdb = not self.shell.call_pdb |
|
404 | 405 | |
|
405 | 406 | # set on the shell |
|
406 | 407 | self.shell.call_pdb = new_pdb |
|
407 | 408 | print('Automatic pdb calling has been turned',on_off(new_pdb)) |
|
408 | 409 | |
|
409 | 410 | @magic_arguments.magic_arguments() |
|
410 | 411 | @magic_arguments.argument('--breakpoint', '-b', metavar='FILE:LINE', |
|
411 | 412 | help=""" |
|
412 | 413 | Set break point at LINE in FILE. |
|
413 | 414 | """ |
|
414 | 415 | ) |
|
415 | 416 | @magic_arguments.argument('statement', nargs='*', |
|
416 | 417 | help=""" |
|
417 | 418 | Code to run in debugger. |
|
418 | 419 | You can omit this in cell magic mode. |
|
419 | 420 | """ |
|
420 | 421 | ) |
|
421 | 422 | @no_var_expand |
|
422 | 423 | @line_cell_magic |
|
423 | 424 | def debug(self, line='', cell=None): |
|
424 | 425 | """Activate the interactive debugger. |
|
425 | 426 | |
|
426 | 427 | This magic command support two ways of activating debugger. |
|
427 | 428 | One is to activate debugger before executing code. This way, you |
|
428 | 429 | can set a break point, to step through the code from the point. |
|
429 | 430 | You can use this mode by giving statements to execute and optionally |
|
430 | 431 | a breakpoint. |
|
431 | 432 | |
|
432 | 433 | The other one is to activate debugger in post-mortem mode. You can |
|
433 | 434 | activate this mode simply running %debug without any argument. |
|
434 | 435 | If an exception has just occurred, this lets you inspect its stack |
|
435 | 436 | frames interactively. Note that this will always work only on the last |
|
436 | 437 | traceback that occurred, so you must call this quickly after an |
|
437 | 438 | exception that you wish to inspect has fired, because if another one |
|
438 | 439 | occurs, it clobbers the previous one. |
|
439 | 440 | |
|
440 | 441 | If you want IPython to automatically do this on every exception, see |
|
441 | 442 | the %pdb magic for more details. |
|
442 | 443 | |
|
443 | 444 | .. versionchanged:: 7.3 |
|
444 | 445 | When running code, user variables are no longer expanded, |
|
445 | 446 | the magic line is always left unmodified. |
|
446 | 447 | |
|
447 | 448 | """ |
|
448 | 449 | args = magic_arguments.parse_argstring(self.debug, line) |
|
449 | 450 | |
|
450 | 451 | if not (args.breakpoint or args.statement or cell): |
|
451 | 452 | self._debug_post_mortem() |
|
452 | 453 | elif not (args.breakpoint or cell): |
|
453 | 454 | # If there is no breakpoints, the line is just code to execute |
|
454 | 455 | self._debug_exec(line, None) |
|
455 | 456 | else: |
|
456 | 457 | # Here we try to reconstruct the code from the output of |
|
457 | 458 | # parse_argstring. This might not work if the code has spaces |
|
458 | 459 | # For example this fails for `print("a b")` |
|
459 | 460 | code = "\n".join(args.statement) |
|
460 | 461 | if cell: |
|
461 | 462 | code += "\n" + cell |
|
462 | 463 | self._debug_exec(code, args.breakpoint) |
|
463 | 464 | |
|
464 | 465 | def _debug_post_mortem(self): |
|
465 | 466 | self.shell.debugger(force=True) |
|
466 | 467 | |
|
467 | 468 | def _debug_exec(self, code, breakpoint): |
|
468 | 469 | if breakpoint: |
|
469 | 470 | (filename, bp_line) = breakpoint.rsplit(':', 1) |
|
470 | 471 | bp_line = int(bp_line) |
|
471 | 472 | else: |
|
472 | 473 | (filename, bp_line) = (None, None) |
|
473 | 474 | self._run_with_debugger(code, self.shell.user_ns, filename, bp_line) |
|
474 | 475 | |
|
475 | 476 | @line_magic |
|
476 | 477 | def tb(self, s): |
|
477 | 478 | """Print the last traceback. |
|
478 | 479 | |
|
479 | 480 | Optionally, specify an exception reporting mode, tuning the |
|
480 | 481 | verbosity of the traceback. By default the currently-active exception |
|
481 | 482 | mode is used. See %xmode for changing exception reporting modes. |
|
482 | 483 | |
|
483 | 484 | Valid modes: Plain, Context, Verbose, and Minimal. |
|
484 | 485 | """ |
|
485 | 486 | interactive_tb = self.shell.InteractiveTB |
|
486 | 487 | if s: |
|
487 | 488 | # Switch exception reporting mode for this one call. |
|
488 | 489 | # Ensure it is switched back. |
|
489 | 490 | def xmode_switch_err(name): |
|
490 | 491 | warn('Error changing %s exception modes.\n%s' % |
|
491 | 492 | (name,sys.exc_info()[1])) |
|
492 | 493 | |
|
493 | 494 | new_mode = s.strip().capitalize() |
|
494 | 495 | original_mode = interactive_tb.mode |
|
495 | 496 | try: |
|
496 | 497 | try: |
|
497 | 498 | interactive_tb.set_mode(mode=new_mode) |
|
498 | 499 | except Exception: |
|
499 | 500 | xmode_switch_err('user') |
|
500 | 501 | else: |
|
501 | 502 | self.shell.showtraceback() |
|
502 | 503 | finally: |
|
503 | 504 | interactive_tb.set_mode(mode=original_mode) |
|
504 | 505 | else: |
|
505 | 506 | self.shell.showtraceback() |
|
506 | 507 | |
|
507 | 508 | @skip_doctest |
|
508 | 509 | @line_magic |
|
509 | 510 | def run(self, parameter_s='', runner=None, |
|
510 | 511 | file_finder=get_py_filename): |
|
511 | 512 | """Run the named file inside IPython as a program. |
|
512 | 513 | |
|
513 | 514 | Usage:: |
|
514 | 515 | |
|
515 | 516 | %run [-n -i -e -G] |
|
516 | 517 | [( -t [-N<N>] | -d [-b<N>] | -p [profile options] )] |
|
517 | 518 | ( -m mod | filename ) [args] |
|
518 | 519 | |
|
519 | 520 | The filename argument should be either a pure Python script (with |
|
520 | 521 | extension ``.py``), or a file with custom IPython syntax (such as |
|
521 | 522 | magics). If the latter, the file can be either a script with ``.ipy`` |
|
522 | 523 | extension, or a Jupyter notebook with ``.ipynb`` extension. When running |
|
523 | 524 | a Jupyter notebook, the output from print statements and other |
|
524 | 525 | displayed objects will appear in the terminal (even matplotlib figures |
|
525 | 526 | will open, if a terminal-compliant backend is being used). Note that, |
|
526 | 527 | at the system command line, the ``jupyter run`` command offers similar |
|
527 | 528 | functionality for executing notebooks (albeit currently with some |
|
528 | 529 | differences in supported options). |
|
529 | 530 | |
|
530 | 531 | Parameters after the filename are passed as command-line arguments to |
|
531 | 532 | the program (put in sys.argv). Then, control returns to IPython's |
|
532 | 533 | prompt. |
|
533 | 534 | |
|
534 | 535 | This is similar to running at a system prompt ``python file args``, |
|
535 | 536 | but with the advantage of giving you IPython's tracebacks, and of |
|
536 | 537 | loading all variables into your interactive namespace for further use |
|
537 | 538 | (unless -p is used, see below). |
|
538 | 539 | |
|
539 | 540 | The file is executed in a namespace initially consisting only of |
|
540 | 541 | ``__name__=='__main__'`` and sys.argv constructed as indicated. It thus |
|
541 | 542 | sees its environment as if it were being run as a stand-alone program |
|
542 | 543 | (except for sharing global objects such as previously imported |
|
543 | 544 | modules). But after execution, the IPython interactive namespace gets |
|
544 | 545 | updated with all variables defined in the program (except for __name__ |
|
545 | 546 | and sys.argv). This allows for very convenient loading of code for |
|
546 | 547 | interactive work, while giving each program a 'clean sheet' to run in. |
|
547 | 548 | |
|
548 | 549 | Arguments are expanded using shell-like glob match. Patterns |
|
549 | 550 | '*', '?', '[seq]' and '[!seq]' can be used. Additionally, |
|
550 | 551 | tilde '~' will be expanded into user's home directory. Unlike |
|
551 | 552 | real shells, quotation does not suppress expansions. Use |
|
552 | 553 | *two* back slashes (e.g. ``\\\\*``) to suppress expansions. |
|
553 | 554 | To completely disable these expansions, you can use -G flag. |
|
554 | 555 | |
|
555 | 556 | On Windows systems, the use of single quotes `'` when specifying |
|
556 | 557 | a file is not supported. Use double quotes `"`. |
|
557 | 558 | |
|
558 | 559 | Options: |
|
559 | 560 | |
|
560 | 561 | -n |
|
561 | 562 | __name__ is NOT set to '__main__', but to the running file's name |
|
562 | 563 | without extension (as python does under import). This allows running |
|
563 | 564 | scripts and reloading the definitions in them without calling code |
|
564 | 565 | protected by an ``if __name__ == "__main__"`` clause. |
|
565 | 566 | |
|
566 | 567 | -i |
|
567 | 568 | run the file in IPython's namespace instead of an empty one. This |
|
568 | 569 | is useful if you are experimenting with code written in a text editor |
|
569 | 570 | which depends on variables defined interactively. |
|
570 | 571 | |
|
571 | 572 | -e |
|
572 | 573 | ignore sys.exit() calls or SystemExit exceptions in the script |
|
573 | 574 | being run. This is particularly useful if IPython is being used to |
|
574 | 575 | run unittests, which always exit with a sys.exit() call. In such |
|
575 | 576 | cases you are interested in the output of the test results, not in |
|
576 | 577 | seeing a traceback of the unittest module. |
|
577 | 578 | |
|
578 | 579 | -t |
|
579 | 580 | print timing information at the end of the run. IPython will give |
|
580 | 581 | you an estimated CPU time consumption for your script, which under |
|
581 | 582 | Unix uses the resource module to avoid the wraparound problems of |
|
582 | 583 | time.clock(). Under Unix, an estimate of time spent on system tasks |
|
583 | 584 | is also given (for Windows platforms this is reported as 0.0). |
|
584 | 585 | |
|
585 | 586 | If -t is given, an additional ``-N<N>`` option can be given, where <N> |
|
586 | 587 | must be an integer indicating how many times you want the script to |
|
587 | 588 | run. The final timing report will include total and per run results. |
|
588 | 589 | |
|
589 | 590 | For example (testing the script uniq_stable.py):: |
|
590 | 591 | |
|
591 | 592 | In [1]: run -t uniq_stable |
|
592 | 593 | |
|
593 | 594 | IPython CPU timings (estimated): |
|
594 | 595 | User : 0.19597 s. |
|
595 | 596 | System: 0.0 s. |
|
596 | 597 | |
|
597 | 598 | In [2]: run -t -N5 uniq_stable |
|
598 | 599 | |
|
599 | 600 | IPython CPU timings (estimated): |
|
600 | 601 | Total runs performed: 5 |
|
601 | 602 | Times : Total Per run |
|
602 | 603 | User : 0.910862 s, 0.1821724 s. |
|
603 | 604 | System: 0.0 s, 0.0 s. |
|
604 | 605 | |
|
605 | 606 | -d |
|
606 | 607 | run your program under the control of pdb, the Python debugger. |
|
607 | 608 | This allows you to execute your program step by step, watch variables, |
|
608 | 609 | etc. Internally, what IPython does is similar to calling:: |
|
609 | 610 | |
|
610 | 611 | pdb.run('execfile("YOURFILENAME")') |
|
611 | 612 | |
|
612 | 613 | with a breakpoint set on line 1 of your file. You can change the line |
|
613 | 614 | number for this automatic breakpoint to be <N> by using the -bN option |
|
614 | 615 | (where N must be an integer). For example:: |
|
615 | 616 | |
|
616 | 617 | %run -d -b40 myscript |
|
617 | 618 | |
|
618 | 619 | will set the first breakpoint at line 40 in myscript.py. Note that |
|
619 | 620 | the first breakpoint must be set on a line which actually does |
|
620 | 621 | something (not a comment or docstring) for it to stop execution. |
|
621 | 622 | |
|
622 | 623 | Or you can specify a breakpoint in a different file:: |
|
623 | 624 | |
|
624 | 625 | %run -d -b myotherfile.py:20 myscript |
|
625 | 626 | |
|
626 | 627 | When the pdb debugger starts, you will see a (Pdb) prompt. You must |
|
627 | 628 | first enter 'c' (without quotes) to start execution up to the first |
|
628 | 629 | breakpoint. |
|
629 | 630 | |
|
630 | 631 | Entering 'help' gives information about the use of the debugger. You |
|
631 | 632 | can easily see pdb's full documentation with "import pdb;pdb.help()" |
|
632 | 633 | at a prompt. |
|
633 | 634 | |
|
634 | 635 | -p |
|
635 | 636 | run program under the control of the Python profiler module (which |
|
636 | 637 | prints a detailed report of execution times, function calls, etc). |
|
637 | 638 | |
|
638 | 639 | You can pass other options after -p which affect the behavior of the |
|
639 | 640 | profiler itself. See the docs for %prun for details. |
|
640 | 641 | |
|
641 | 642 | In this mode, the program's variables do NOT propagate back to the |
|
642 | 643 | IPython interactive namespace (because they remain in the namespace |
|
643 | 644 | where the profiler executes them). |
|
644 | 645 | |
|
645 | 646 | Internally this triggers a call to %prun, see its documentation for |
|
646 | 647 | details on the options available specifically for profiling. |
|
647 | 648 | |
|
648 | 649 | There is one special usage for which the text above doesn't apply: |
|
649 | 650 | if the filename ends with .ipy[nb], the file is run as ipython script, |
|
650 | 651 | just as if the commands were written on IPython prompt. |
|
651 | 652 | |
|
652 | 653 | -m |
|
653 | 654 | specify module name to load instead of script path. Similar to |
|
654 | 655 | the -m option for the python interpreter. Use this option last if you |
|
655 | 656 | want to combine with other %run options. Unlike the python interpreter |
|
656 | 657 | only source modules are allowed no .pyc or .pyo files. |
|
657 | 658 | For example:: |
|
658 | 659 | |
|
659 | 660 | %run -m example |
|
660 | 661 | |
|
661 | 662 | will run the example module. |
|
662 | 663 | |
|
663 | 664 | -G |
|
664 | 665 | disable shell-like glob expansion of arguments. |
|
665 | 666 | |
|
666 | 667 | """ |
|
667 | 668 | |
|
668 | 669 | # Logic to handle issue #3664 |
|
669 | 670 | # Add '--' after '-m <module_name>' to ignore additional args passed to a module. |
|
670 | 671 | if '-m' in parameter_s and '--' not in parameter_s: |
|
671 | 672 | argv = shlex.split(parameter_s, posix=(os.name == 'posix')) |
|
672 | 673 | for idx, arg in enumerate(argv): |
|
673 | 674 | if arg and arg.startswith('-') and arg != '-': |
|
674 | 675 | if arg == '-m': |
|
675 | 676 | argv.insert(idx + 2, '--') |
|
676 | 677 | break |
|
677 | 678 | else: |
|
678 | 679 | # Positional arg, break |
|
679 | 680 | break |
|
680 | 681 | parameter_s = ' '.join(shlex.quote(arg) for arg in argv) |
|
681 | 682 | |
|
682 | 683 | # get arguments and set sys.argv for program to be run. |
|
683 | 684 | opts, arg_lst = self.parse_options(parameter_s, |
|
684 | 685 | 'nidtN:b:pD:l:rs:T:em:G', |
|
685 | 686 | mode='list', list_all=1) |
|
686 | 687 | if "m" in opts: |
|
687 | 688 | modulename = opts["m"][0] |
|
688 | 689 | modpath = find_mod(modulename) |
|
689 | 690 | if modpath is None: |
|
690 | 691 | msg = '%r is not a valid modulename on sys.path'%modulename |
|
691 | 692 | raise Exception(msg) |
|
692 | 693 | arg_lst = [modpath] + arg_lst |
|
693 | 694 | try: |
|
694 | 695 | fpath = None # initialize to make sure fpath is in scope later |
|
695 | 696 | fpath = arg_lst[0] |
|
696 | 697 | filename = file_finder(fpath) |
|
697 | 698 | except IndexError as e: |
|
698 | 699 | msg = 'you must provide at least a filename.' |
|
699 | 700 | raise Exception(msg) from e |
|
700 | 701 | except IOError as e: |
|
701 | 702 | try: |
|
702 | 703 | msg = str(e) |
|
703 | 704 | except UnicodeError: |
|
704 | 705 | msg = e.message |
|
705 | 706 | if os.name == 'nt' and re.match(r"^'.*'$",fpath): |
|
706 | 707 | warn('For Windows, use double quotes to wrap a filename: %run "mypath\\myfile.py"') |
|
707 | 708 | raise Exception(msg) from e |
|
708 | 709 | except TypeError: |
|
709 | 710 | if fpath in sys.meta_path: |
|
710 | 711 | filename = "" |
|
711 | 712 | else: |
|
712 | 713 | raise |
|
713 | 714 | |
|
714 | 715 | if filename.lower().endswith(('.ipy', '.ipynb')): |
|
715 | 716 | with preserve_keys(self.shell.user_ns, '__file__'): |
|
716 | 717 | self.shell.user_ns['__file__'] = filename |
|
717 | 718 | self.shell.safe_execfile_ipy(filename, raise_exceptions=True) |
|
718 | 719 | return |
|
719 | 720 | |
|
720 | 721 | # Control the response to exit() calls made by the script being run |
|
721 | 722 | exit_ignore = 'e' in opts |
|
722 | 723 | |
|
723 | 724 | # Make sure that the running script gets a proper sys.argv as if it |
|
724 | 725 | # were run from a system shell. |
|
725 | 726 | save_argv = sys.argv # save it for later restoring |
|
726 | 727 | |
|
727 | 728 | if 'G' in opts: |
|
728 | 729 | args = arg_lst[1:] |
|
729 | 730 | else: |
|
730 | 731 | # tilde and glob expansion |
|
731 | 732 | args = shellglob(map(os.path.expanduser, arg_lst[1:])) |
|
732 | 733 | |
|
733 | 734 | sys.argv = [filename] + args # put in the proper filename |
|
734 | 735 | |
|
735 | 736 | if 'n' in opts: |
|
736 | 737 | name = Path(filename).stem |
|
737 | 738 | else: |
|
738 | 739 | name = '__main__' |
|
739 | 740 | |
|
740 | 741 | if 'i' in opts: |
|
741 | 742 | # Run in user's interactive namespace |
|
742 | 743 | prog_ns = self.shell.user_ns |
|
743 | 744 | __name__save = self.shell.user_ns['__name__'] |
|
744 | 745 | prog_ns['__name__'] = name |
|
745 | 746 | main_mod = self.shell.user_module |
|
746 | 747 | |
|
747 | 748 | # Since '%run foo' emulates 'python foo.py' at the cmd line, we must |
|
748 | 749 | # set the __file__ global in the script's namespace |
|
749 | 750 | # TK: Is this necessary in interactive mode? |
|
750 | 751 | prog_ns['__file__'] = filename |
|
751 | 752 | else: |
|
752 | 753 | # Run in a fresh, empty namespace |
|
753 | 754 | |
|
754 | 755 | # The shell MUST hold a reference to prog_ns so after %run |
|
755 | 756 | # exits, the python deletion mechanism doesn't zero it out |
|
756 | 757 | # (leaving dangling references). See interactiveshell for details |
|
757 | 758 | main_mod = self.shell.new_main_mod(filename, name) |
|
758 | 759 | prog_ns = main_mod.__dict__ |
|
759 | 760 | |
|
760 | 761 | # pickle fix. See interactiveshell for an explanation. But we need to |
|
761 | 762 | # make sure that, if we overwrite __main__, we replace it at the end |
|
762 | 763 | main_mod_name = prog_ns['__name__'] |
|
763 | 764 | |
|
764 | 765 | if main_mod_name == '__main__': |
|
765 | 766 | restore_main = sys.modules['__main__'] |
|
766 | 767 | else: |
|
767 | 768 | restore_main = False |
|
768 | 769 | |
|
769 | 770 | # This needs to be undone at the end to prevent holding references to |
|
770 | 771 | # every single object ever created. |
|
771 | 772 | sys.modules[main_mod_name] = main_mod |
|
772 | 773 | |
|
773 | 774 | if 'p' in opts or 'd' in opts: |
|
774 | 775 | if 'm' in opts: |
|
775 | 776 | code = 'run_module(modulename, prog_ns)' |
|
776 | 777 | code_ns = { |
|
777 | 778 | 'run_module': self.shell.safe_run_module, |
|
778 | 779 | 'prog_ns': prog_ns, |
|
779 | 780 | 'modulename': modulename, |
|
780 | 781 | } |
|
781 | 782 | else: |
|
782 | 783 | if 'd' in opts: |
|
783 | 784 | # allow exceptions to raise in debug mode |
|
784 | 785 | code = 'execfile(filename, prog_ns, raise_exceptions=True)' |
|
785 | 786 | else: |
|
786 | 787 | code = 'execfile(filename, prog_ns)' |
|
787 | 788 | code_ns = { |
|
788 | 789 | 'execfile': self.shell.safe_execfile, |
|
789 | 790 | 'prog_ns': prog_ns, |
|
790 | 791 | 'filename': get_py_filename(filename), |
|
791 | 792 | } |
|
792 | 793 | |
|
793 | 794 | try: |
|
794 | 795 | stats = None |
|
795 | 796 | if 'p' in opts: |
|
796 | 797 | stats = self._run_with_profiler(code, opts, code_ns) |
|
797 | 798 | else: |
|
798 | 799 | if 'd' in opts: |
|
799 | 800 | bp_file, bp_line = parse_breakpoint( |
|
800 | 801 | opts.get('b', ['1'])[0], filename) |
|
801 | 802 | self._run_with_debugger( |
|
802 | 803 | code, code_ns, filename, bp_line, bp_file) |
|
803 | 804 | else: |
|
804 | 805 | if 'm' in opts: |
|
805 | 806 | def run(): |
|
806 | 807 | self.shell.safe_run_module(modulename, prog_ns) |
|
807 | 808 | else: |
|
808 | 809 | if runner is None: |
|
809 | 810 | runner = self.default_runner |
|
810 | 811 | if runner is None: |
|
811 | 812 | runner = self.shell.safe_execfile |
|
812 | 813 | |
|
813 | 814 | def run(): |
|
814 | 815 | runner(filename, prog_ns, prog_ns, |
|
815 | 816 | exit_ignore=exit_ignore) |
|
816 | 817 | |
|
817 | 818 | if 't' in opts: |
|
818 | 819 | # timed execution |
|
819 | 820 | try: |
|
820 | 821 | nruns = int(opts['N'][0]) |
|
821 | 822 | if nruns < 1: |
|
822 | 823 | error('Number of runs must be >=1') |
|
823 | 824 | return |
|
824 | 825 | except (KeyError): |
|
825 | 826 | nruns = 1 |
|
826 | 827 | self._run_with_timing(run, nruns) |
|
827 | 828 | else: |
|
828 | 829 | # regular execution |
|
829 | 830 | run() |
|
830 | 831 | |
|
831 | 832 | if 'i' in opts: |
|
832 | 833 | self.shell.user_ns['__name__'] = __name__save |
|
833 | 834 | else: |
|
834 | 835 | # update IPython interactive namespace |
|
835 | 836 | |
|
836 | 837 | # Some forms of read errors on the file may mean the |
|
837 | 838 | # __name__ key was never set; using pop we don't have to |
|
838 | 839 | # worry about a possible KeyError. |
|
839 | 840 | prog_ns.pop('__name__', None) |
|
840 | 841 | |
|
841 | 842 | with preserve_keys(self.shell.user_ns, '__file__'): |
|
842 | 843 | self.shell.user_ns.update(prog_ns) |
|
843 | 844 | finally: |
|
844 | 845 | # It's a bit of a mystery why, but __builtins__ can change from |
|
845 | 846 | # being a module to becoming a dict missing some key data after |
|
846 | 847 | # %run. As best I can see, this is NOT something IPython is doing |
|
847 | 848 | # at all, and similar problems have been reported before: |
|
848 | 849 | # http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-10/0188.html |
|
849 | 850 | # Since this seems to be done by the interpreter itself, the best |
|
850 | 851 | # we can do is to at least restore __builtins__ for the user on |
|
851 | 852 | # exit. |
|
852 | 853 | self.shell.user_ns['__builtins__'] = builtin_mod |
|
853 | 854 | |
|
854 | 855 | # Ensure key global structures are restored |
|
855 | 856 | sys.argv = save_argv |
|
856 | 857 | if restore_main: |
|
857 | 858 | sys.modules['__main__'] = restore_main |
|
858 | 859 | if '__mp_main__' in sys.modules: |
|
859 | 860 | sys.modules['__mp_main__'] = restore_main |
|
860 | 861 | else: |
|
861 | 862 | # Remove from sys.modules the reference to main_mod we'd |
|
862 | 863 | # added. Otherwise it will trap references to objects |
|
863 | 864 | # contained therein. |
|
864 | 865 | del sys.modules[main_mod_name] |
|
865 | 866 | |
|
866 | 867 | return stats |
|
867 | 868 | |
|
868 | 869 | def _run_with_debugger(self, code, code_ns, filename=None, |
|
869 | 870 | bp_line=None, bp_file=None): |
|
870 | 871 | """ |
|
871 | 872 | Run `code` in debugger with a break point. |
|
872 | 873 | |
|
873 | 874 | Parameters |
|
874 | 875 | ---------- |
|
875 | 876 | code : str |
|
876 | 877 | Code to execute. |
|
877 | 878 | code_ns : dict |
|
878 | 879 | A namespace in which `code` is executed. |
|
879 | 880 | filename : str |
|
880 | 881 | `code` is ran as if it is in `filename`. |
|
881 | 882 | bp_line : int, optional |
|
882 | 883 | Line number of the break point. |
|
883 | 884 | bp_file : str, optional |
|
884 | 885 | Path to the file in which break point is specified. |
|
885 | 886 | `filename` is used if not given. |
|
886 | 887 | |
|
887 | 888 | Raises |
|
888 | 889 | ------ |
|
889 | 890 | UsageError |
|
890 | 891 | If the break point given by `bp_line` is not valid. |
|
891 | 892 | |
|
892 | 893 | """ |
|
893 | 894 | deb = self.shell.InteractiveTB.pdb |
|
894 | 895 | if not deb: |
|
895 | 896 | self.shell.InteractiveTB.pdb = self.shell.InteractiveTB.debugger_cls() |
|
896 | 897 | deb = self.shell.InteractiveTB.pdb |
|
897 | 898 | |
|
898 | 899 | # deb.checkline() fails if deb.curframe exists but is None; it can |
|
899 | 900 | # handle it not existing. https://github.com/ipython/ipython/issues/10028 |
|
900 | 901 | if hasattr(deb, 'curframe'): |
|
901 | 902 | del deb.curframe |
|
902 | 903 | |
|
903 | 904 | # reset Breakpoint state, which is moronically kept |
|
904 | 905 | # in a class |
|
905 | 906 | bdb.Breakpoint.next = 1 |
|
906 | 907 | bdb.Breakpoint.bplist = {} |
|
907 | 908 | bdb.Breakpoint.bpbynumber = [None] |
|
908 | 909 | deb.clear_all_breaks() |
|
909 | 910 | if bp_line is not None: |
|
910 | 911 | # Set an initial breakpoint to stop execution |
|
911 | 912 | maxtries = 10 |
|
912 | 913 | bp_file = bp_file or filename |
|
913 | 914 | checkline = deb.checkline(bp_file, bp_line) |
|
914 | 915 | if not checkline: |
|
915 | 916 | for bp in range(bp_line + 1, bp_line + maxtries + 1): |
|
916 | 917 | if deb.checkline(bp_file, bp): |
|
917 | 918 | break |
|
918 | 919 | else: |
|
919 | 920 | msg = ("\nI failed to find a valid line to set " |
|
920 | 921 | "a breakpoint\n" |
|
921 | 922 | "after trying up to line: %s.\n" |
|
922 | 923 | "Please set a valid breakpoint manually " |
|
923 | 924 | "with the -b option." % bp) |
|
924 | 925 | raise UsageError(msg) |
|
925 | 926 | # if we find a good linenumber, set the breakpoint |
|
926 | 927 | deb.do_break('%s:%s' % (bp_file, bp_line)) |
|
927 | 928 | |
|
928 | 929 | if filename: |
|
929 | 930 | # Mimic Pdb._runscript(...) |
|
930 | 931 | deb._wait_for_mainpyfile = True |
|
931 | 932 | deb.mainpyfile = deb.canonic(filename) |
|
932 | 933 | |
|
933 | 934 | # Start file run |
|
934 | 935 | print("NOTE: Enter 'c' at the %s prompt to continue execution." % deb.prompt) |
|
935 | 936 | try: |
|
936 | 937 | if filename: |
|
937 | 938 | # save filename so it can be used by methods on the deb object |
|
938 | 939 | deb._exec_filename = filename |
|
939 | 940 | while True: |
|
940 | 941 | try: |
|
941 | 942 | trace = sys.gettrace() |
|
942 | 943 | deb.run(code, code_ns) |
|
943 | 944 | except Restart: |
|
944 | 945 | print("Restarting") |
|
945 | 946 | if filename: |
|
946 | 947 | deb._wait_for_mainpyfile = True |
|
947 | 948 | deb.mainpyfile = deb.canonic(filename) |
|
948 | 949 | continue |
|
949 | 950 | else: |
|
950 | 951 | break |
|
951 | 952 | finally: |
|
952 | 953 | sys.settrace(trace) |
|
953 | 954 | |
|
954 | 955 | |
|
955 | 956 | except: |
|
956 | 957 | etype, value, tb = sys.exc_info() |
|
957 | 958 | # Skip three frames in the traceback: the %run one, |
|
958 | 959 | # one inside bdb.py, and the command-line typed by the |
|
959 | 960 | # user (run by exec in pdb itself). |
|
960 | 961 | self.shell.InteractiveTB(etype, value, tb, tb_offset=3) |
|
961 | 962 | |
|
962 | 963 | @staticmethod |
|
963 | 964 | def _run_with_timing(run, nruns): |
|
964 | 965 | """ |
|
965 | 966 | Run function `run` and print timing information. |
|
966 | 967 | |
|
967 | 968 | Parameters |
|
968 | 969 | ---------- |
|
969 | 970 | run : callable |
|
970 | 971 | Any callable object which takes no argument. |
|
971 | 972 | nruns : int |
|
972 | 973 | Number of times to execute `run`. |
|
973 | 974 | |
|
974 | 975 | """ |
|
975 | 976 | twall0 = time.perf_counter() |
|
976 | 977 | if nruns == 1: |
|
977 | 978 | t0 = clock2() |
|
978 | 979 | run() |
|
979 | 980 | t1 = clock2() |
|
980 | 981 | t_usr = t1[0] - t0[0] |
|
981 | 982 | t_sys = t1[1] - t0[1] |
|
982 | 983 | print("\nIPython CPU timings (estimated):") |
|
983 | 984 | print(" User : %10.2f s." % t_usr) |
|
984 | 985 | print(" System : %10.2f s." % t_sys) |
|
985 | 986 | else: |
|
986 | 987 | runs = range(nruns) |
|
987 | 988 | t0 = clock2() |
|
988 | 989 | for nr in runs: |
|
989 | 990 | run() |
|
990 | 991 | t1 = clock2() |
|
991 | 992 | t_usr = t1[0] - t0[0] |
|
992 | 993 | t_sys = t1[1] - t0[1] |
|
993 | 994 | print("\nIPython CPU timings (estimated):") |
|
994 | 995 | print("Total runs performed:", nruns) |
|
995 | 996 | print(" Times : %10s %10s" % ('Total', 'Per run')) |
|
996 | 997 | print(" User : %10.2f s, %10.2f s." % (t_usr, t_usr / nruns)) |
|
997 | 998 | print(" System : %10.2f s, %10.2f s." % (t_sys, t_sys / nruns)) |
|
998 | 999 | twall1 = time.perf_counter() |
|
999 | 1000 | print("Wall time: %10.2f s." % (twall1 - twall0)) |
|
1000 | 1001 | |
|
1001 | 1002 | @skip_doctest |
|
1002 | 1003 | @no_var_expand |
|
1003 | 1004 | @line_cell_magic |
|
1004 | 1005 | @needs_local_scope |
|
1005 | 1006 | def timeit(self, line='', cell=None, local_ns=None): |
|
1006 | 1007 | """Time execution of a Python statement or expression |
|
1007 | 1008 | |
|
1008 | 1009 | Usage, in line mode: |
|
1009 | 1010 | %timeit [-n<N> -r<R> [-t|-c] -q -p<P> -o] statement |
|
1010 | 1011 | or in cell mode: |
|
1011 | 1012 | %%timeit [-n<N> -r<R> [-t|-c] -q -p<P> -o] setup_code |
|
1012 | 1013 | code |
|
1013 | 1014 | code... |
|
1014 | 1015 | |
|
1015 | 1016 | Time execution of a Python statement or expression using the timeit |
|
1016 | 1017 | module. This function can be used both as a line and cell magic: |
|
1017 | 1018 | |
|
1018 | 1019 | - In line mode you can time a single-line statement (though multiple |
|
1019 | 1020 | ones can be chained with using semicolons). |
|
1020 | 1021 | |
|
1021 | 1022 | - In cell mode, the statement in the first line is used as setup code |
|
1022 | 1023 | (executed but not timed) and the body of the cell is timed. The cell |
|
1023 | 1024 | body has access to any variables created in the setup code. |
|
1024 | 1025 | |
|
1025 | 1026 | Options: |
|
1026 | 1027 | -n<N>: execute the given statement <N> times in a loop. If <N> is not |
|
1027 | 1028 | provided, <N> is determined so as to get sufficient accuracy. |
|
1028 | 1029 | |
|
1029 | 1030 | -r<R>: number of repeats <R>, each consisting of <N> loops, and take the |
|
1030 | 1031 | best result. |
|
1031 | 1032 | Default: 7 |
|
1032 | 1033 | |
|
1033 | 1034 | -t: use time.time to measure the time, which is the default on Unix. |
|
1034 | 1035 | This function measures wall time. |
|
1035 | 1036 | |
|
1036 | 1037 | -c: use time.clock to measure the time, which is the default on |
|
1037 | 1038 | Windows and measures wall time. On Unix, resource.getrusage is used |
|
1038 | 1039 | instead and returns the CPU user time. |
|
1039 | 1040 | |
|
1040 | 1041 | -p<P>: use a precision of <P> digits to display the timing result. |
|
1041 | 1042 | Default: 3 |
|
1042 | 1043 | |
|
1043 | 1044 | -q: Quiet, do not print result. |
|
1044 | 1045 | |
|
1045 | 1046 | -o: return a TimeitResult that can be stored in a variable to inspect |
|
1046 | 1047 | the result in more details. |
|
1047 | 1048 | |
|
1048 | 1049 | .. versionchanged:: 7.3 |
|
1049 | 1050 | User variables are no longer expanded, |
|
1050 | 1051 | the magic line is always left unmodified. |
|
1051 | 1052 | |
|
1052 | 1053 | Examples |
|
1053 | 1054 | -------- |
|
1054 | 1055 | :: |
|
1055 | 1056 | |
|
1056 | 1057 | In [1]: %timeit pass |
|
1057 | 1058 | 8.26 ns ± 0.12 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each) |
|
1058 | 1059 | |
|
1059 | 1060 | In [2]: u = None |
|
1060 | 1061 | |
|
1061 | 1062 | In [3]: %timeit u is None |
|
1062 | 1063 | 29.9 ns ± 0.643 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each) |
|
1063 | 1064 | |
|
1064 | 1065 | In [4]: %timeit -r 4 u == None |
|
1065 | 1066 | |
|
1066 | 1067 | In [5]: import time |
|
1067 | 1068 | |
|
1068 | 1069 | In [6]: %timeit -n1 time.sleep(2) |
|
1069 | 1070 | |
|
1070 | 1071 | The times reported by %timeit will be slightly higher than those |
|
1071 | 1072 | reported by the timeit.py script when variables are accessed. This is |
|
1072 | 1073 | due to the fact that %timeit executes the statement in the namespace |
|
1073 | 1074 | of the shell, compared with timeit.py, which uses a single setup |
|
1074 | 1075 | statement to import function or create variables. Generally, the bias |
|
1075 | 1076 | does not matter as long as results from timeit.py are not mixed with |
|
1076 | 1077 | those from %timeit.""" |
|
1077 | 1078 | |
|
1078 | 1079 | opts, stmt = self.parse_options( |
|
1079 | 1080 | line, "n:r:tcp:qo", posix=False, strict=False, preserve_non_opts=True |
|
1080 | 1081 | ) |
|
1081 | 1082 | if stmt == "" and cell is None: |
|
1082 | 1083 | return |
|
1083 | 1084 | |
|
1084 | 1085 | timefunc = timeit.default_timer |
|
1085 | 1086 | number = int(getattr(opts, "n", 0)) |
|
1086 | 1087 | default_repeat = 7 if timeit.default_repeat < 7 else timeit.default_repeat |
|
1087 | 1088 | repeat = int(getattr(opts, "r", default_repeat)) |
|
1088 | 1089 | precision = int(getattr(opts, "p", 3)) |
|
1089 | 1090 | quiet = 'q' in opts |
|
1090 | 1091 | return_result = 'o' in opts |
|
1091 | 1092 | if hasattr(opts, "t"): |
|
1092 | 1093 | timefunc = time.time |
|
1093 | 1094 | if hasattr(opts, "c"): |
|
1094 | 1095 | timefunc = clock |
|
1095 | 1096 | |
|
1096 | 1097 | timer = Timer(timer=timefunc) |
|
1097 | 1098 | # this code has tight coupling to the inner workings of timeit.Timer, |
|
1098 | 1099 | # but is there a better way to achieve that the code stmt has access |
|
1099 | 1100 | # to the shell namespace? |
|
1100 | 1101 | transform = self.shell.transform_cell |
|
1101 | 1102 | |
|
1102 | 1103 | if cell is None: |
|
1103 | 1104 | # called as line magic |
|
1104 | 1105 | ast_setup = self.shell.compile.ast_parse("pass") |
|
1105 | 1106 | ast_stmt = self.shell.compile.ast_parse(transform(stmt)) |
|
1106 | 1107 | else: |
|
1107 | 1108 | ast_setup = self.shell.compile.ast_parse(transform(stmt)) |
|
1108 | 1109 | ast_stmt = self.shell.compile.ast_parse(transform(cell)) |
|
1109 | 1110 | |
|
1110 | 1111 | ast_setup = self.shell.transform_ast(ast_setup) |
|
1111 | 1112 | ast_stmt = self.shell.transform_ast(ast_stmt) |
|
1112 | 1113 | |
|
1113 | 1114 | # Check that these compile to valid Python code *outside* the timer func |
|
1114 | 1115 | # Invalid code may become valid when put inside the function & loop, |
|
1115 | 1116 | # which messes up error messages. |
|
1116 | 1117 | # https://github.com/ipython/ipython/issues/10636 |
|
1117 | 1118 | self.shell.compile(ast_setup, "<magic-timeit-setup>", "exec") |
|
1118 | 1119 | self.shell.compile(ast_stmt, "<magic-timeit-stmt>", "exec") |
|
1119 | 1120 | |
|
1120 | 1121 | # This codestring is taken from timeit.template - we fill it in as an |
|
1121 | 1122 | # AST, so that we can apply our AST transformations to the user code |
|
1122 | 1123 | # without affecting the timing code. |
|
1123 | 1124 | timeit_ast_template = ast.parse('def inner(_it, _timer):\n' |
|
1124 | 1125 | ' setup\n' |
|
1125 | 1126 | ' _t0 = _timer()\n' |
|
1126 | 1127 | ' for _i in _it:\n' |
|
1127 | 1128 | ' stmt\n' |
|
1128 | 1129 | ' _t1 = _timer()\n' |
|
1129 | 1130 | ' return _t1 - _t0\n') |
|
1130 | 1131 | |
|
1131 | 1132 | timeit_ast = TimeitTemplateFiller(ast_setup, ast_stmt).visit(timeit_ast_template) |
|
1132 | 1133 | timeit_ast = ast.fix_missing_locations(timeit_ast) |
|
1133 | 1134 | |
|
1134 | 1135 | # Track compilation time so it can be reported if too long |
|
1135 | 1136 | # Minimum time above which compilation time will be reported |
|
1136 | 1137 | tc_min = 0.1 |
|
1137 | 1138 | |
|
1138 | 1139 | t0 = clock() |
|
1139 | 1140 | code = self.shell.compile(timeit_ast, "<magic-timeit>", "exec") |
|
1140 | 1141 | tc = clock()-t0 |
|
1141 | 1142 | |
|
1142 | 1143 | ns = {} |
|
1143 | 1144 | glob = self.shell.user_ns |
|
1144 | 1145 | # handles global vars with same name as local vars. We store them in conflict_globs. |
|
1145 | 1146 | conflict_globs = {} |
|
1146 | 1147 | if local_ns and cell is None: |
|
1147 | 1148 | for var_name, var_val in glob.items(): |
|
1148 | 1149 | if var_name in local_ns: |
|
1149 | 1150 | conflict_globs[var_name] = var_val |
|
1150 | 1151 | glob.update(local_ns) |
|
1151 | 1152 | |
|
1152 | 1153 | exec(code, glob, ns) |
|
1153 | 1154 | timer.inner = ns["inner"] |
|
1154 | 1155 | |
|
1155 | 1156 | # This is used to check if there is a huge difference between the |
|
1156 | 1157 | # best and worst timings. |
|
1157 | 1158 | # Issue: https://github.com/ipython/ipython/issues/6471 |
|
1158 | 1159 | if number == 0: |
|
1159 | 1160 | # determine number so that 0.2 <= total time < 2.0 |
|
1160 | 1161 | for index in range(0, 10): |
|
1161 | 1162 | number = 10 ** index |
|
1162 | 1163 | time_number = timer.timeit(number) |
|
1163 | 1164 | if time_number >= 0.2: |
|
1164 | 1165 | break |
|
1165 | 1166 | |
|
1166 | 1167 | all_runs = timer.repeat(repeat, number) |
|
1167 | 1168 | best = min(all_runs) / number |
|
1168 | 1169 | worst = max(all_runs) / number |
|
1169 | 1170 | timeit_result = TimeitResult(number, repeat, best, worst, all_runs, tc, precision) |
|
1170 | 1171 | |
|
1171 | 1172 | # Restore global vars from conflict_globs |
|
1172 | 1173 | if conflict_globs: |
|
1173 | 1174 | glob.update(conflict_globs) |
|
1174 | 1175 | |
|
1175 | 1176 | if not quiet : |
|
1176 | 1177 | # Check best timing is greater than zero to avoid a |
|
1177 | 1178 | # ZeroDivisionError. |
|
1178 | 1179 | # In cases where the slowest timing is lesser than a microsecond |
|
1179 | 1180 | # we assume that it does not really matter if the fastest |
|
1180 | 1181 | # timing is 4 times faster than the slowest timing or not. |
|
1181 | 1182 | if worst > 4 * best and best > 0 and worst > 1e-6: |
|
1182 | 1183 | print("The slowest run took %0.2f times longer than the " |
|
1183 | 1184 | "fastest. This could mean that an intermediate result " |
|
1184 | 1185 | "is being cached." % (worst / best)) |
|
1185 | 1186 | |
|
1186 | 1187 | print( timeit_result ) |
|
1187 | 1188 | |
|
1188 | 1189 | if tc > tc_min: |
|
1189 | 1190 | print("Compiler time: %.2f s" % tc) |
|
1190 | 1191 | if return_result: |
|
1191 | 1192 | return timeit_result |
|
1192 | 1193 | |
|
1193 | 1194 | @skip_doctest |
|
1194 | 1195 | @no_var_expand |
|
1195 | 1196 | @needs_local_scope |
|
1196 | 1197 | @line_cell_magic |
|
1198 | @output_can_be_silenced | |
|
1197 | 1199 | def time(self,line='', cell=None, local_ns=None): |
|
1198 | 1200 | """Time execution of a Python statement or expression. |
|
1199 | 1201 | |
|
1200 | 1202 | The CPU and wall clock times are printed, and the value of the |
|
1201 | 1203 | expression (if any) is returned. Note that under Win32, system time |
|
1202 | 1204 | is always reported as 0, since it can not be measured. |
|
1203 | 1205 | |
|
1204 | 1206 | This function can be used both as a line and cell magic: |
|
1205 | 1207 | |
|
1206 | 1208 | - In line mode you can time a single-line statement (though multiple |
|
1207 | 1209 | ones can be chained with using semicolons). |
|
1208 | 1210 | |
|
1209 | 1211 | - In cell mode, you can time the cell body (a directly |
|
1210 | 1212 | following statement raises an error). |
|
1211 | 1213 | |
|
1212 | 1214 | This function provides very basic timing functionality. Use the timeit |
|
1213 | 1215 | magic for more control over the measurement. |
|
1214 | 1216 | |
|
1215 | 1217 | .. versionchanged:: 7.3 |
|
1216 | 1218 | User variables are no longer expanded, |
|
1217 | 1219 | the magic line is always left unmodified. |
|
1218 | 1220 | |
|
1219 | 1221 | Examples |
|
1220 | 1222 | -------- |
|
1221 | 1223 | :: |
|
1222 | 1224 | |
|
1223 | 1225 | In [1]: %time 2**128 |
|
1224 | 1226 | CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s |
|
1225 | 1227 | Wall time: 0.00 |
|
1226 | 1228 | Out[1]: 340282366920938463463374607431768211456L |
|
1227 | 1229 | |
|
1228 | 1230 | In [2]: n = 1000000 |
|
1229 | 1231 | |
|
1230 | 1232 | In [3]: %time sum(range(n)) |
|
1231 | 1233 | CPU times: user 1.20 s, sys: 0.05 s, total: 1.25 s |
|
1232 | 1234 | Wall time: 1.37 |
|
1233 | 1235 | Out[3]: 499999500000L |
|
1234 | 1236 | |
|
1235 | 1237 | In [4]: %time print 'hello world' |
|
1236 | 1238 | hello world |
|
1237 | 1239 | CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s |
|
1238 | 1240 | Wall time: 0.00 |
|
1239 | 1241 | |
|
1240 | 1242 | .. note:: |
|
1241 | 1243 | The time needed by Python to compile the given expression will be |
|
1242 | 1244 | reported if it is more than 0.1s. |
|
1243 | 1245 | |
|
1244 | 1246 | In the example below, the actual exponentiation is done by Python |
|
1245 | 1247 | at compilation time, so while the expression can take a noticeable |
|
1246 | 1248 | amount of time to compute, that time is purely due to the |
|
1247 | 1249 | compilation:: |
|
1248 | 1250 | |
|
1249 | 1251 | In [5]: %time 3**9999; |
|
1250 | 1252 | CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s |
|
1251 | 1253 | Wall time: 0.00 s |
|
1252 | 1254 | |
|
1253 | 1255 | In [6]: %time 3**999999; |
|
1254 | 1256 | CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s |
|
1255 | 1257 | Wall time: 0.00 s |
|
1256 | 1258 | Compiler : 0.78 s |
|
1257 | 1259 | """ |
|
1258 | 1260 | # fail immediately if the given expression can't be compiled |
|
1259 | 1261 | |
|
1260 | 1262 | if line and cell: |
|
1261 | 1263 | raise UsageError("Can't use statement directly after '%%time'!") |
|
1262 | 1264 | |
|
1263 | 1265 | if cell: |
|
1264 | 1266 | expr = self.shell.transform_cell(cell) |
|
1265 | 1267 | else: |
|
1266 | 1268 | expr = self.shell.transform_cell(line) |
|
1267 | 1269 | |
|
1268 | 1270 | # Minimum time above which parse time will be reported |
|
1269 | 1271 | tp_min = 0.1 |
|
1270 | 1272 | |
|
1271 | 1273 | t0 = clock() |
|
1272 | 1274 | expr_ast = self.shell.compile.ast_parse(expr) |
|
1273 | 1275 | tp = clock()-t0 |
|
1274 | 1276 | |
|
1275 | 1277 | # Apply AST transformations |
|
1276 | 1278 | expr_ast = self.shell.transform_ast(expr_ast) |
|
1277 | 1279 | |
|
1278 | 1280 | # Minimum time above which compilation time will be reported |
|
1279 | 1281 | tc_min = 0.1 |
|
1280 | 1282 | |
|
1281 | 1283 | expr_val=None |
|
1282 | 1284 | if len(expr_ast.body)==1 and isinstance(expr_ast.body[0], ast.Expr): |
|
1283 | 1285 | mode = 'eval' |
|
1284 | 1286 | source = '<timed eval>' |
|
1285 | 1287 | expr_ast = ast.Expression(expr_ast.body[0].value) |
|
1286 | 1288 | else: |
|
1287 | 1289 | mode = 'exec' |
|
1288 | 1290 | source = '<timed exec>' |
|
1289 | 1291 | # multi-line %%time case |
|
1290 | 1292 | if len(expr_ast.body) > 1 and isinstance(expr_ast.body[-1], ast.Expr): |
|
1291 | 1293 | expr_val= expr_ast.body[-1] |
|
1292 | 1294 | expr_ast = expr_ast.body[:-1] |
|
1293 | 1295 | expr_ast = Module(expr_ast, []) |
|
1294 | 1296 | expr_val = ast.Expression(expr_val.value) |
|
1295 | 1297 | |
|
1296 | 1298 | t0 = clock() |
|
1297 | 1299 | code = self.shell.compile(expr_ast, source, mode) |
|
1298 | 1300 | tc = clock()-t0 |
|
1299 | 1301 | |
|
1300 | 1302 | # skew measurement as little as possible |
|
1301 | 1303 | glob = self.shell.user_ns |
|
1302 | 1304 | wtime = time.time |
|
1303 | 1305 | # time execution |
|
1304 | 1306 | wall_st = wtime() |
|
1305 | 1307 | if mode=='eval': |
|
1306 | 1308 | st = clock2() |
|
1307 | 1309 | try: |
|
1308 | 1310 | out = eval(code, glob, local_ns) |
|
1309 | 1311 | except: |
|
1310 | 1312 | self.shell.showtraceback() |
|
1311 | 1313 | return |
|
1312 | 1314 | end = clock2() |
|
1313 | 1315 | else: |
|
1314 | 1316 | st = clock2() |
|
1315 | 1317 | try: |
|
1316 | 1318 | exec(code, glob, local_ns) |
|
1317 | 1319 | out=None |
|
1318 | 1320 | # multi-line %%time case |
|
1319 | 1321 | if expr_val is not None: |
|
1320 | 1322 | code_2 = self.shell.compile(expr_val, source, 'eval') |
|
1321 | 1323 | out = eval(code_2, glob, local_ns) |
|
1322 | 1324 | except: |
|
1323 | 1325 | self.shell.showtraceback() |
|
1324 | 1326 | return |
|
1325 | 1327 | end = clock2() |
|
1326 | 1328 | |
|
1327 | 1329 | wall_end = wtime() |
|
1328 | 1330 | # Compute actual times and report |
|
1329 | 1331 | wall_time = wall_end - wall_st |
|
1330 | 1332 | cpu_user = end[0] - st[0] |
|
1331 | 1333 | cpu_sys = end[1] - st[1] |
|
1332 | 1334 | cpu_tot = cpu_user + cpu_sys |
|
1333 | 1335 | # On windows cpu_sys is always zero, so only total is displayed |
|
1334 | 1336 | if sys.platform != "win32": |
|
1335 | 1337 | print( |
|
1336 | 1338 | f"CPU times: user {_format_time(cpu_user)}, sys: {_format_time(cpu_sys)}, total: {_format_time(cpu_tot)}" |
|
1337 | 1339 | ) |
|
1338 | 1340 | else: |
|
1339 | 1341 | print(f"CPU times: total: {_format_time(cpu_tot)}") |
|
1340 | 1342 | print(f"Wall time: {_format_time(wall_time)}") |
|
1341 | 1343 | if tc > tc_min: |
|
1342 | 1344 | print(f"Compiler : {_format_time(tc)}") |
|
1343 | 1345 | if tp > tp_min: |
|
1344 | 1346 | print(f"Parser : {_format_time(tp)}") |
|
1345 | 1347 | return out |
|
1346 | 1348 | |
|
1347 | 1349 | @skip_doctest |
|
1348 | 1350 | @line_magic |
|
1349 | 1351 | def macro(self, parameter_s=''): |
|
1350 | 1352 | """Define a macro for future re-execution. It accepts ranges of history, |
|
1351 | 1353 | filenames or string objects. |
|
1352 | 1354 | |
|
1353 | 1355 | Usage:\\ |
|
1354 | 1356 | %macro [options] name n1-n2 n3-n4 ... n5 .. n6 ... |
|
1355 | 1357 | |
|
1356 | 1358 | Options: |
|
1357 | 1359 | |
|
1358 | 1360 | -r: use 'raw' input. By default, the 'processed' history is used, |
|
1359 | 1361 | so that magics are loaded in their transformed version to valid |
|
1360 | 1362 | Python. If this option is given, the raw input as typed at the |
|
1361 | 1363 | command line is used instead. |
|
1362 | 1364 | |
|
1363 | 1365 | -q: quiet macro definition. By default, a tag line is printed |
|
1364 | 1366 | to indicate the macro has been created, and then the contents of |
|
1365 | 1367 | the macro are printed. If this option is given, then no printout |
|
1366 | 1368 | is produced once the macro is created. |
|
1367 | 1369 | |
|
1368 | 1370 | This will define a global variable called `name` which is a string |
|
1369 | 1371 | made of joining the slices and lines you specify (n1,n2,... numbers |
|
1370 | 1372 | above) from your input history into a single string. This variable |
|
1371 | 1373 | acts like an automatic function which re-executes those lines as if |
|
1372 | 1374 | you had typed them. You just type 'name' at the prompt and the code |
|
1373 | 1375 | executes. |
|
1374 | 1376 | |
|
1375 | 1377 | The syntax for indicating input ranges is described in %history. |
|
1376 | 1378 | |
|
1377 | 1379 | Note: as a 'hidden' feature, you can also use traditional python slice |
|
1378 | 1380 | notation, where N:M means numbers N through M-1. |
|
1379 | 1381 | |
|
1380 | 1382 | For example, if your history contains (print using %hist -n ):: |
|
1381 | 1383 | |
|
1382 | 1384 | 44: x=1 |
|
1383 | 1385 | 45: y=3 |
|
1384 | 1386 | 46: z=x+y |
|
1385 | 1387 | 47: print x |
|
1386 | 1388 | 48: a=5 |
|
1387 | 1389 | 49: print 'x',x,'y',y |
|
1388 | 1390 | |
|
1389 | 1391 | you can create a macro with lines 44 through 47 (included) and line 49 |
|
1390 | 1392 | called my_macro with:: |
|
1391 | 1393 | |
|
1392 | 1394 | In [55]: %macro my_macro 44-47 49 |
|
1393 | 1395 | |
|
1394 | 1396 | Now, typing `my_macro` (without quotes) will re-execute all this code |
|
1395 | 1397 | in one pass. |
|
1396 | 1398 | |
|
1397 | 1399 | You don't need to give the line-numbers in order, and any given line |
|
1398 | 1400 | number can appear multiple times. You can assemble macros with any |
|
1399 | 1401 | lines from your input history in any order. |
|
1400 | 1402 | |
|
1401 | 1403 | The macro is a simple object which holds its value in an attribute, |
|
1402 | 1404 | but IPython's display system checks for macros and executes them as |
|
1403 | 1405 | code instead of printing them when you type their name. |
|
1404 | 1406 | |
|
1405 | 1407 | You can view a macro's contents by explicitly printing it with:: |
|
1406 | 1408 | |
|
1407 | 1409 | print macro_name |
|
1408 | 1410 | |
|
1409 | 1411 | """ |
|
1410 | 1412 | opts,args = self.parse_options(parameter_s,'rq',mode='list') |
|
1411 | 1413 | if not args: # List existing macros |
|
1412 | 1414 | return sorted(k for k,v in self.shell.user_ns.items() if isinstance(v, Macro)) |
|
1413 | 1415 | if len(args) == 1: |
|
1414 | 1416 | raise UsageError( |
|
1415 | 1417 | "%macro insufficient args; usage '%macro name n1-n2 n3-4...") |
|
1416 | 1418 | name, codefrom = args[0], " ".join(args[1:]) |
|
1417 | 1419 | |
|
1418 | 1420 | #print 'rng',ranges # dbg |
|
1419 | 1421 | try: |
|
1420 | 1422 | lines = self.shell.find_user_code(codefrom, 'r' in opts) |
|
1421 | 1423 | except (ValueError, TypeError) as e: |
|
1422 | 1424 | print(e.args[0]) |
|
1423 | 1425 | return |
|
1424 | 1426 | macro = Macro(lines) |
|
1425 | 1427 | self.shell.define_macro(name, macro) |
|
1426 | 1428 | if not ( 'q' in opts) : |
|
1427 | 1429 | print('Macro `%s` created. To execute, type its name (without quotes).' % name) |
|
1428 | 1430 | print('=== Macro contents: ===') |
|
1429 | 1431 | print(macro, end=' ') |
|
1430 | 1432 | |
|
1431 | 1433 | @magic_arguments.magic_arguments() |
|
1432 | 1434 | @magic_arguments.argument('output', type=str, default='', nargs='?', |
|
1433 | 1435 | help="""The name of the variable in which to store output. |
|
1434 | 1436 | This is a utils.io.CapturedIO object with stdout/err attributes |
|
1435 | 1437 | for the text of the captured output. |
|
1436 | 1438 | |
|
1437 | 1439 | CapturedOutput also has a show() method for displaying the output, |
|
1438 | 1440 | and __call__ as well, so you can use that to quickly display the |
|
1439 | 1441 | output. |
|
1440 | 1442 | |
|
1441 | 1443 | If unspecified, captured output is discarded. |
|
1442 | 1444 | """ |
|
1443 | 1445 | ) |
|
1444 | 1446 | @magic_arguments.argument('--no-stderr', action="store_true", |
|
1445 | 1447 | help="""Don't capture stderr.""" |
|
1446 | 1448 | ) |
|
1447 | 1449 | @magic_arguments.argument('--no-stdout', action="store_true", |
|
1448 | 1450 | help="""Don't capture stdout.""" |
|
1449 | 1451 | ) |
|
1450 | 1452 | @magic_arguments.argument('--no-display', action="store_true", |
|
1451 | 1453 | help="""Don't capture IPython's rich display.""" |
|
1452 | 1454 | ) |
|
1453 | 1455 | @cell_magic |
|
1454 | 1456 | def capture(self, line, cell): |
|
1455 | 1457 | """run the cell, capturing stdout, stderr, and IPython's rich display() calls.""" |
|
1456 | 1458 | args = magic_arguments.parse_argstring(self.capture, line) |
|
1457 | 1459 | out = not args.no_stdout |
|
1458 | 1460 | err = not args.no_stderr |
|
1459 | 1461 | disp = not args.no_display |
|
1460 | 1462 | with capture_output(out, err, disp) as io: |
|
1461 | 1463 | self.shell.run_cell(cell) |
|
1462 | 1464 | if args.output: |
|
1463 | 1465 | self.shell.user_ns[args.output] = io |
|
1464 | 1466 | |
|
1465 | 1467 | def parse_breakpoint(text, current_file): |
|
1466 | 1468 | '''Returns (file, line) for file:line and (current_file, line) for line''' |
|
1467 | 1469 | colon = text.find(':') |
|
1468 | 1470 | if colon == -1: |
|
1469 | 1471 | return current_file, int(text) |
|
1470 | 1472 | else: |
|
1471 | 1473 | return text[:colon], int(text[colon+1:]) |
|
1472 | 1474 | |
|
1473 | 1475 | def _format_time(timespan, precision=3): |
|
1474 | 1476 | """Formats the timespan in a human readable form""" |
|
1475 | 1477 | |
|
1476 | 1478 | if timespan >= 60.0: |
|
1477 | 1479 | # we have more than a minute, format that in a human readable form |
|
1478 | 1480 | # Idea from http://snipplr.com/view/5713/ |
|
1479 | 1481 | parts = [("d", 60*60*24),("h", 60*60),("min", 60), ("s", 1)] |
|
1480 | 1482 | time = [] |
|
1481 | 1483 | leftover = timespan |
|
1482 | 1484 | for suffix, length in parts: |
|
1483 | 1485 | value = int(leftover / length) |
|
1484 | 1486 | if value > 0: |
|
1485 | 1487 | leftover = leftover % length |
|
1486 | 1488 | time.append(u'%s%s' % (str(value), suffix)) |
|
1487 | 1489 | if leftover < 1: |
|
1488 | 1490 | break |
|
1489 | 1491 | return " ".join(time) |
|
1490 | 1492 | |
|
1491 | 1493 | |
|
1492 | 1494 | # Unfortunately the unicode 'micro' symbol can cause problems in |
|
1493 | 1495 | # certain terminals. |
|
1494 | 1496 | # See bug: https://bugs.launchpad.net/ipython/+bug/348466 |
|
1495 | 1497 | # Try to prevent crashes by being more secure than it needs to |
|
1496 | 1498 | # E.g. eclipse is able to print a µ, but has no sys.stdout.encoding set. |
|
1497 | 1499 | units = [u"s", u"ms",u'us',"ns"] # the save value |
|
1498 | 1500 | if hasattr(sys.stdout, 'encoding') and sys.stdout.encoding: |
|
1499 | 1501 | try: |
|
1500 | 1502 | u'\xb5'.encode(sys.stdout.encoding) |
|
1501 | 1503 | units = [u"s", u"ms",u'\xb5s',"ns"] |
|
1502 | 1504 | except: |
|
1503 | 1505 | pass |
|
1504 | 1506 | scaling = [1, 1e3, 1e6, 1e9] |
|
1505 | 1507 | |
|
1506 | 1508 | if timespan > 0.0: |
|
1507 | 1509 | order = min(-int(math.floor(math.log10(timespan)) // 3), 3) |
|
1508 | 1510 | else: |
|
1509 | 1511 | order = 3 |
|
1510 | 1512 | return u"%.*g %s" % (precision, timespan * scaling[order], units[order]) |
@@ -1,855 +1,855 b'' | |||
|
1 | 1 | """Implementation of magic functions for interaction with the OS. |
|
2 | 2 | |
|
3 | 3 | Note: this module is named 'osm' instead of 'os' to avoid a collision with the |
|
4 | 4 | builtin. |
|
5 | 5 | """ |
|
6 | 6 | # Copyright (c) IPython Development Team. |
|
7 | 7 | # Distributed under the terms of the Modified BSD License. |
|
8 | 8 | |
|
9 | 9 | import io |
|
10 | 10 | import os |
|
11 | 11 | import pathlib |
|
12 | 12 | import re |
|
13 | 13 | import sys |
|
14 | 14 | from pprint import pformat |
|
15 | 15 | |
|
16 | 16 | from IPython.core import magic_arguments |
|
17 | 17 | from IPython.core import oinspect |
|
18 | 18 | from IPython.core import page |
|
19 | 19 | from IPython.core.alias import AliasError, Alias |
|
20 | 20 | from IPython.core.error import UsageError |
|
21 | 21 | from IPython.core.magic import ( |
|
22 | 22 | Magics, compress_dhist, magics_class, line_magic, cell_magic, line_cell_magic |
|
23 | 23 | ) |
|
24 | 24 | from IPython.testing.skipdoctest import skip_doctest |
|
25 | 25 | from IPython.utils.openpy import source_to_unicode |
|
26 | 26 | from IPython.utils.process import abbrev_cwd |
|
27 | 27 | from IPython.utils.terminal import set_term_title |
|
28 | 28 | from traitlets import Bool |
|
29 | 29 | from warnings import warn |
|
30 | 30 | |
|
31 | 31 | |
|
32 | 32 | @magics_class |
|
33 | 33 | class OSMagics(Magics): |
|
34 | 34 | """Magics to interact with the underlying OS (shell-type functionality). |
|
35 | 35 | """ |
|
36 | 36 | |
|
37 | 37 | cd_force_quiet = Bool(False, |
|
38 | 38 | help="Force %cd magic to be quiet even if -q is not passed." |
|
39 | 39 | ).tag(config=True) |
|
40 | 40 | |
|
41 | 41 | def __init__(self, shell=None, **kwargs): |
|
42 | 42 | |
|
43 | 43 | # Now define isexec in a cross platform manner. |
|
44 | 44 | self.is_posix = False |
|
45 | 45 | self.execre = None |
|
46 | 46 | if os.name == 'posix': |
|
47 | 47 | self.is_posix = True |
|
48 | 48 | else: |
|
49 | 49 | try: |
|
50 | 50 | winext = os.environ['pathext'].replace(';','|').replace('.','') |
|
51 | 51 | except KeyError: |
|
52 | 52 | winext = 'exe|com|bat|py' |
|
53 | 53 | try: |
|
54 | 54 | self.execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE) |
|
55 | 55 | except re.error: |
|
56 | 56 | warn("Seems like your pathext environmental " |
|
57 | 57 | "variable is malformed. Please check it to " |
|
58 | 58 | "enable a proper handle of file extensions " |
|
59 | 59 | "managed for your system") |
|
60 | 60 | winext = 'exe|com|bat|py' |
|
61 | 61 | self.execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE) |
|
62 | 62 | |
|
63 | 63 | # call up the chain |
|
64 | 64 | super().__init__(shell=shell, **kwargs) |
|
65 | 65 | |
|
66 | 66 | |
|
67 | 67 | def _isexec_POSIX(self, file): |
|
68 | 68 | """ |
|
69 | 69 | Test for executable on a POSIX system |
|
70 | 70 | """ |
|
71 | 71 | if os.access(file.path, os.X_OK): |
|
72 | 72 | # will fail on maxOS if access is not X_OK |
|
73 | 73 | return file.is_file() |
|
74 | 74 | return False |
|
75 | 75 | |
|
76 | 76 | |
|
77 | 77 | |
|
78 | 78 | def _isexec_WIN(self, file): |
|
79 | 79 | """ |
|
80 | 80 | Test for executable file on non POSIX system |
|
81 | 81 | """ |
|
82 | 82 | return file.is_file() and self.execre.match(file.name) is not None |
|
83 | 83 | |
|
84 | 84 | def isexec(self, file): |
|
85 | 85 | """ |
|
86 | 86 | Test for executable file on non POSIX system |
|
87 | 87 | """ |
|
88 | 88 | if self.is_posix: |
|
89 | 89 | return self._isexec_POSIX(file) |
|
90 | 90 | else: |
|
91 | 91 | return self._isexec_WIN(file) |
|
92 | 92 | |
|
93 | 93 | |
|
94 | 94 | @skip_doctest |
|
95 | 95 | @line_magic |
|
96 | 96 | def alias(self, parameter_s=''): |
|
97 | 97 | """Define an alias for a system command. |
|
98 | 98 | |
|
99 | 99 | '%alias alias_name cmd' defines 'alias_name' as an alias for 'cmd' |
|
100 | 100 | |
|
101 | 101 | Then, typing 'alias_name params' will execute the system command 'cmd |
|
102 | 102 | params' (from your underlying operating system). |
|
103 | 103 | |
|
104 | 104 | Aliases have lower precedence than magic functions and Python normal |
|
105 | 105 | variables, so if 'foo' is both a Python variable and an alias, the |
|
106 | 106 | alias can not be executed until 'del foo' removes the Python variable. |
|
107 | 107 | |
|
108 | 108 | You can use the %l specifier in an alias definition to represent the |
|
109 | 109 | whole line when the alias is called. For example:: |
|
110 | 110 | |
|
111 | 111 | In [2]: alias bracket echo "Input in brackets: <%l>" |
|
112 | 112 | In [3]: bracket hello world |
|
113 | 113 | Input in brackets: <hello world> |
|
114 | 114 | |
|
115 | 115 | You can also define aliases with parameters using %s specifiers (one |
|
116 | 116 | per parameter):: |
|
117 | 117 | |
|
118 | 118 | In [1]: alias parts echo first %s second %s |
|
119 | 119 | In [2]: %parts A B |
|
120 | 120 | first A second B |
|
121 | 121 | In [3]: %parts A |
|
122 | 122 | Incorrect number of arguments: 2 expected. |
|
123 | 123 | parts is an alias to: 'echo first %s second %s' |
|
124 | 124 | |
|
125 | 125 | Note that %l and %s are mutually exclusive. You can only use one or |
|
126 | 126 | the other in your aliases. |
|
127 | 127 | |
|
128 | 128 | Aliases expand Python variables just like system calls using ! or !! |
|
129 | 129 | do: all expressions prefixed with '$' get expanded. For details of |
|
130 | 130 | the semantic rules, see PEP-215: |
|
131 | 131 | https://peps.python.org/pep-0215/. This is the library used by |
|
132 | 132 | IPython for variable expansion. If you want to access a true shell |
|
133 | 133 | variable, an extra $ is necessary to prevent its expansion by |
|
134 | 134 | IPython:: |
|
135 | 135 | |
|
136 | 136 | In [6]: alias show echo |
|
137 | 137 | In [7]: PATH='A Python string' |
|
138 | 138 | In [8]: show $PATH |
|
139 | 139 | A Python string |
|
140 | 140 | In [9]: show $$PATH |
|
141 | 141 | /usr/local/lf9560/bin:/usr/local/intel/compiler70/ia32/bin:... |
|
142 | 142 | |
|
143 | 143 | You can use the alias facility to access all of $PATH. See the %rehashx |
|
144 | 144 | function, which automatically creates aliases for the contents of your |
|
145 | 145 | $PATH. |
|
146 | 146 | |
|
147 | 147 | If called with no parameters, %alias prints the current alias table |
|
148 | 148 | for your system. For posix systems, the default aliases are 'cat', |
|
149 | 149 | 'cp', 'mv', 'rm', 'rmdir', and 'mkdir', and other platform-specific |
|
150 | 150 | aliases are added. For windows-based systems, the default aliases are |
|
151 | 151 | 'copy', 'ddir', 'echo', 'ls', 'ldir', 'mkdir', 'ren', and 'rmdir'. |
|
152 | 152 | |
|
153 | 153 | You can see the definition of alias by adding a question mark in the |
|
154 | 154 | end:: |
|
155 | 155 | |
|
156 | 156 | In [1]: cat? |
|
157 | 157 | Repr: <alias cat for 'cat'>""" |
|
158 | 158 | |
|
159 | 159 | par = parameter_s.strip() |
|
160 | 160 | if not par: |
|
161 | 161 | aliases = sorted(self.shell.alias_manager.aliases) |
|
162 | 162 | # stored = self.shell.db.get('stored_aliases', {} ) |
|
163 | 163 | # for k, v in stored: |
|
164 | 164 | # atab.append(k, v[0]) |
|
165 | 165 | |
|
166 | 166 | print("Total number of aliases:", len(aliases)) |
|
167 | 167 | sys.stdout.flush() |
|
168 | 168 | return aliases |
|
169 | 169 | |
|
170 | 170 | # Now try to define a new one |
|
171 | 171 | try: |
|
172 | 172 | alias,cmd = par.split(None, 1) |
|
173 | 173 | except TypeError: |
|
174 | 174 | print(oinspect.getdoc(self.alias)) |
|
175 | 175 | return |
|
176 | 176 | |
|
177 | 177 | try: |
|
178 | 178 | self.shell.alias_manager.define_alias(alias, cmd) |
|
179 | 179 | except AliasError as e: |
|
180 | 180 | print(e) |
|
181 | 181 | # end magic_alias |
|
182 | 182 | |
|
183 | 183 | @line_magic |
|
184 | 184 | def unalias(self, parameter_s=''): |
|
185 | 185 | """Remove an alias""" |
|
186 | 186 | |
|
187 | 187 | aname = parameter_s.strip() |
|
188 | 188 | try: |
|
189 | 189 | self.shell.alias_manager.undefine_alias(aname) |
|
190 | 190 | except ValueError as e: |
|
191 | 191 | print(e) |
|
192 | 192 | return |
|
193 | 193 | |
|
194 | 194 | stored = self.shell.db.get('stored_aliases', {} ) |
|
195 | 195 | if aname in stored: |
|
196 | 196 | print("Removing %stored alias",aname) |
|
197 | 197 | del stored[aname] |
|
198 | 198 | self.shell.db['stored_aliases'] = stored |
|
199 | 199 | |
|
200 | 200 | @line_magic |
|
201 | 201 | def rehashx(self, parameter_s=''): |
|
202 | 202 | """Update the alias table with all executable files in $PATH. |
|
203 | 203 | |
|
204 | 204 | rehashx explicitly checks that every entry in $PATH is a file |
|
205 | 205 | with execute access (os.X_OK). |
|
206 | 206 | |
|
207 | 207 | Under Windows, it checks executability as a match against a |
|
208 | 208 | '|'-separated string of extensions, stored in the IPython config |
|
209 | 209 | variable win_exec_ext. This defaults to 'exe|com|bat'. |
|
210 | 210 | |
|
211 | 211 | This function also resets the root module cache of module completer, |
|
212 | 212 | used on slow filesystems. |
|
213 | 213 | """ |
|
214 | 214 | from IPython.core.alias import InvalidAliasError |
|
215 | 215 | |
|
216 | 216 | # for the benefit of module completer in ipy_completers.py |
|
217 | 217 | del self.shell.db['rootmodules_cache'] |
|
218 | 218 | |
|
219 | 219 | path = [os.path.abspath(os.path.expanduser(p)) for p in |
|
220 | 220 | os.environ.get('PATH','').split(os.pathsep)] |
|
221 | 221 | |
|
222 | 222 | syscmdlist = [] |
|
223 | 223 | savedir = os.getcwd() |
|
224 | 224 | |
|
225 | 225 | # Now walk the paths looking for executables to alias. |
|
226 | 226 | try: |
|
227 | 227 | # write the whole loop for posix/Windows so we don't have an if in |
|
228 | 228 | # the innermost part |
|
229 | 229 | if self.is_posix: |
|
230 | 230 | for pdir in path: |
|
231 | 231 | try: |
|
232 | 232 | os.chdir(pdir) |
|
233 | 233 | except OSError: |
|
234 | 234 | continue |
|
235 | 235 | |
|
236 | 236 | # for python 3.6+ rewrite to: with os.scandir(pdir) as dirlist: |
|
237 | 237 | dirlist = os.scandir(path=pdir) |
|
238 | 238 | for ff in dirlist: |
|
239 | 239 | if self.isexec(ff): |
|
240 | 240 | fname = ff.name |
|
241 | 241 | try: |
|
242 | 242 | # Removes dots from the name since ipython |
|
243 | 243 | # will assume names with dots to be python. |
|
244 | 244 | if not self.shell.alias_manager.is_alias(fname): |
|
245 | 245 | self.shell.alias_manager.define_alias( |
|
246 | 246 | fname.replace('.',''), fname) |
|
247 | 247 | except InvalidAliasError: |
|
248 | 248 | pass |
|
249 | 249 | else: |
|
250 | 250 | syscmdlist.append(fname) |
|
251 | 251 | else: |
|
252 | 252 | no_alias = Alias.blacklist |
|
253 | 253 | for pdir in path: |
|
254 | 254 | try: |
|
255 | 255 | os.chdir(pdir) |
|
256 | 256 | except OSError: |
|
257 | 257 | continue |
|
258 | 258 | |
|
259 | 259 | # for python 3.6+ rewrite to: with os.scandir(pdir) as dirlist: |
|
260 | 260 | dirlist = os.scandir(pdir) |
|
261 | 261 | for ff in dirlist: |
|
262 | 262 | fname = ff.name |
|
263 | 263 | base, ext = os.path.splitext(fname) |
|
264 | 264 | if self.isexec(ff) and base.lower() not in no_alias: |
|
265 | 265 | if ext.lower() == '.exe': |
|
266 | 266 | fname = base |
|
267 | 267 | try: |
|
268 | 268 | # Removes dots from the name since ipython |
|
269 | 269 | # will assume names with dots to be python. |
|
270 | 270 | self.shell.alias_manager.define_alias( |
|
271 | 271 | base.lower().replace('.',''), fname) |
|
272 | 272 | except InvalidAliasError: |
|
273 | 273 | pass |
|
274 | 274 | syscmdlist.append(fname) |
|
275 | 275 | |
|
276 | 276 | self.shell.db['syscmdlist'] = syscmdlist |
|
277 | 277 | finally: |
|
278 | 278 | os.chdir(savedir) |
|
279 | 279 | |
|
280 | 280 | @skip_doctest |
|
281 | 281 | @line_magic |
|
282 | 282 | def pwd(self, parameter_s=''): |
|
283 | 283 | """Return the current working directory path. |
|
284 | 284 | |
|
285 | 285 | Examples |
|
286 | 286 | -------- |
|
287 | 287 | :: |
|
288 | 288 | |
|
289 | 289 | In [9]: pwd |
|
290 | 290 | Out[9]: '/home/tsuser/sprint/ipython' |
|
291 | 291 | """ |
|
292 | 292 | try: |
|
293 | 293 | return os.getcwd() |
|
294 | 294 | except FileNotFoundError as e: |
|
295 | 295 | raise UsageError("CWD no longer exists - please use %cd to change directory.") from e |
|
296 | 296 | |
|
297 | 297 | @skip_doctest |
|
298 | 298 | @line_magic |
|
299 | 299 | def cd(self, parameter_s=''): |
|
300 | 300 | """Change the current working directory. |
|
301 | 301 | |
|
302 | 302 | This command automatically maintains an internal list of directories |
|
303 | 303 | you visit during your IPython session, in the variable ``_dh``. The |
|
304 | 304 | command :magic:`%dhist` shows this history nicely formatted. You can |
|
305 | 305 | also do ``cd -<tab>`` to see directory history conveniently. |
|
306 | 306 | Usage: |
|
307 | 307 | |
|
308 | 308 | - ``cd 'dir'``: changes to directory 'dir'. |
|
309 | 309 | - ``cd -``: changes to the last visited directory. |
|
310 | 310 | - ``cd -<n>``: changes to the n-th directory in the directory history. |
|
311 | 311 | - ``cd --foo``: change to directory that matches 'foo' in history |
|
312 | 312 | - ``cd -b <bookmark_name>``: jump to a bookmark set by %bookmark |
|
313 | 313 | - Hitting a tab key after ``cd -b`` allows you to tab-complete |
|
314 | 314 | bookmark names. |
|
315 | 315 | |
|
316 | 316 | .. note:: |
|
317 | 317 | ``cd <bookmark_name>`` is enough if there is no directory |
|
318 | 318 | ``<bookmark_name>``, but a bookmark with the name exists. |
|
319 | 319 | |
|
320 | 320 | Options: |
|
321 | 321 | |
|
322 | 322 | -q Be quiet. Do not print the working directory after the |
|
323 | 323 | cd command is executed. By default IPython's cd |
|
324 | 324 | command does print this directory, since the default |
|
325 | 325 | prompts do not display path information. |
|
326 | 326 | |
|
327 | 327 | .. note:: |
|
328 | 328 | Note that ``!cd`` doesn't work for this purpose because the shell |
|
329 | 329 | where ``!command`` runs is immediately discarded after executing |
|
330 | 330 | 'command'. |
|
331 | 331 | |
|
332 | 332 | Examples |
|
333 | 333 | -------- |
|
334 | 334 | :: |
|
335 | 335 | |
|
336 | 336 | In [10]: cd parent/child |
|
337 | 337 | /home/tsuser/parent/child |
|
338 | 338 | """ |
|
339 | 339 | |
|
340 | 340 | try: |
|
341 | 341 | oldcwd = os.getcwd() |
|
342 | 342 | except FileNotFoundError: |
|
343 | 343 | # Happens if the CWD has been deleted. |
|
344 | 344 | oldcwd = None |
|
345 | 345 | |
|
346 | 346 | numcd = re.match(r'(-)(\d+)$',parameter_s) |
|
347 | 347 | # jump in directory history by number |
|
348 | 348 | if numcd: |
|
349 | 349 | nn = int(numcd.group(2)) |
|
350 | 350 | try: |
|
351 | 351 | ps = self.shell.user_ns['_dh'][nn] |
|
352 | 352 | except IndexError: |
|
353 | 353 | print('The requested directory does not exist in history.') |
|
354 | 354 | return |
|
355 | 355 | else: |
|
356 | 356 | opts = {} |
|
357 | 357 | elif parameter_s.startswith('--'): |
|
358 | 358 | ps = None |
|
359 | 359 | fallback = None |
|
360 | 360 | pat = parameter_s[2:] |
|
361 | 361 | dh = self.shell.user_ns['_dh'] |
|
362 | 362 | # first search only by basename (last component) |
|
363 | 363 | for ent in reversed(dh): |
|
364 | 364 | if pat in os.path.basename(ent) and os.path.isdir(ent): |
|
365 | 365 | ps = ent |
|
366 | 366 | break |
|
367 | 367 | |
|
368 | 368 | if fallback is None and pat in ent and os.path.isdir(ent): |
|
369 | 369 | fallback = ent |
|
370 | 370 | |
|
371 | 371 | # if we have no last part match, pick the first full path match |
|
372 | 372 | if ps is None: |
|
373 | 373 | ps = fallback |
|
374 | 374 | |
|
375 | 375 | if ps is None: |
|
376 | 376 | print("No matching entry in directory history") |
|
377 | 377 | return |
|
378 | 378 | else: |
|
379 | 379 | opts = {} |
|
380 | 380 | |
|
381 | 381 | |
|
382 | 382 | else: |
|
383 | 383 | opts, ps = self.parse_options(parameter_s, 'qb', mode='string') |
|
384 | 384 | # jump to previous |
|
385 | 385 | if ps == '-': |
|
386 | 386 | try: |
|
387 | 387 | ps = self.shell.user_ns['_dh'][-2] |
|
388 | 388 | except IndexError as e: |
|
389 | 389 | raise UsageError('%cd -: No previous directory to change to.') from e |
|
390 | 390 | # jump to bookmark if needed |
|
391 | 391 | else: |
|
392 | 392 | if not os.path.isdir(ps) or 'b' in opts: |
|
393 | 393 | bkms = self.shell.db.get('bookmarks', {}) |
|
394 | 394 | |
|
395 | 395 | if ps in bkms: |
|
396 | 396 | target = bkms[ps] |
|
397 | 397 | print('(bookmark:%s) -> %s' % (ps, target)) |
|
398 | 398 | ps = target |
|
399 | 399 | else: |
|
400 | 400 | if 'b' in opts: |
|
401 | 401 | raise UsageError("Bookmark '%s' not found. " |
|
402 | 402 | "Use '%%bookmark -l' to see your bookmarks." % ps) |
|
403 | 403 | |
|
404 | 404 | # at this point ps should point to the target dir |
|
405 | 405 | if ps: |
|
406 | 406 | try: |
|
407 | 407 | os.chdir(os.path.expanduser(ps)) |
|
408 | 408 | if hasattr(self.shell, 'term_title') and self.shell.term_title: |
|
409 | 409 | set_term_title(self.shell.term_title_format.format(cwd=abbrev_cwd())) |
|
410 | 410 | except OSError: |
|
411 | 411 | print(sys.exc_info()[1]) |
|
412 | 412 | else: |
|
413 | 413 | cwd = pathlib.Path.cwd() |
|
414 | 414 | dhist = self.shell.user_ns['_dh'] |
|
415 | 415 | if oldcwd != cwd: |
|
416 | 416 | dhist.append(cwd) |
|
417 | 417 | self.shell.db['dhist'] = compress_dhist(dhist)[-100:] |
|
418 | 418 | |
|
419 | 419 | else: |
|
420 | 420 | os.chdir(self.shell.home_dir) |
|
421 | 421 | if hasattr(self.shell, 'term_title') and self.shell.term_title: |
|
422 | 422 | set_term_title(self.shell.term_title_format.format(cwd="~")) |
|
423 | 423 | cwd = pathlib.Path.cwd() |
|
424 | 424 | dhist = self.shell.user_ns['_dh'] |
|
425 | 425 | |
|
426 | 426 | if oldcwd != cwd: |
|
427 | 427 | dhist.append(cwd) |
|
428 | 428 | self.shell.db['dhist'] = compress_dhist(dhist)[-100:] |
|
429 | 429 | if not 'q' in opts and not self.cd_force_quiet and self.shell.user_ns['_dh']: |
|
430 | 430 | print(self.shell.user_ns['_dh'][-1]) |
|
431 | 431 | |
|
432 | 432 | @line_magic |
|
433 | 433 | def env(self, parameter_s=''): |
|
434 | 434 | """Get, set, or list environment variables. |
|
435 | 435 | |
|
436 | 436 | Usage:\\ |
|
437 | 437 | |
|
438 | 438 | :``%env``: lists all environment variables/values |
|
439 | 439 | :``%env var``: get value for var |
|
440 | 440 | :``%env var val``: set value for var |
|
441 | 441 | :``%env var=val``: set value for var |
|
442 | 442 | :``%env var=$val``: set value for var, using python expansion if possible |
|
443 | 443 | """ |
|
444 | 444 | if parameter_s.strip(): |
|
445 | 445 | split = '=' if '=' in parameter_s else ' ' |
|
446 | 446 | bits = parameter_s.split(split) |
|
447 | 447 | if len(bits) == 1: |
|
448 | 448 | key = parameter_s.strip() |
|
449 | 449 | if key in os.environ: |
|
450 | 450 | return os.environ[key] |
|
451 | 451 | else: |
|
452 | 452 | err = "Environment does not have key: {0}".format(key) |
|
453 | 453 | raise UsageError(err) |
|
454 | 454 | if len(bits) > 1: |
|
455 | 455 | return self.set_env(parameter_s) |
|
456 | 456 | env = dict(os.environ) |
|
457 | 457 | # hide likely secrets when printing the whole environment |
|
458 | 458 | for key in list(env): |
|
459 | 459 | if any(s in key.lower() for s in ('key', 'token', 'secret')): |
|
460 | 460 | env[key] = '<hidden>' |
|
461 | 461 | |
|
462 | 462 | return env |
|
463 | 463 | |
|
464 | 464 | @line_magic |
|
465 | 465 | def set_env(self, parameter_s): |
|
466 | 466 | """Set environment variables. Assumptions are that either "val" is a |
|
467 | 467 | name in the user namespace, or val is something that evaluates to a |
|
468 | 468 | string. |
|
469 | 469 | |
|
470 | 470 | Usage:\\ |
|
471 | %set_env var val: set value for var | |
|
472 | %set_env var=val: set value for var | |
|
473 | %set_env var=$val: set value for var, using python expansion if possible | |
|
471 | :``%set_env var val``: set value for var | |
|
472 | :``%set_env var=val``: set value for var | |
|
473 | :``%set_env var=$val``: set value for var, using python expansion if possible | |
|
474 | 474 | """ |
|
475 | 475 | split = '=' if '=' in parameter_s else ' ' |
|
476 | 476 | bits = parameter_s.split(split, 1) |
|
477 | 477 | if not parameter_s.strip() or len(bits)<2: |
|
478 | 478 | raise UsageError("usage is 'set_env var=val'") |
|
479 | 479 | var = bits[0].strip() |
|
480 | 480 | val = bits[1].strip() |
|
481 | 481 | if re.match(r'.*\s.*', var): |
|
482 | 482 | # an environment variable with whitespace is almost certainly |
|
483 | 483 | # not what the user intended. what's more likely is the wrong |
|
484 | 484 | # split was chosen, ie for "set_env cmd_args A=B", we chose |
|
485 | 485 | # '=' for the split and should have chosen ' '. to get around |
|
486 | 486 | # this, users should just assign directly to os.environ or use |
|
487 | 487 | # standard magic {var} expansion. |
|
488 | 488 | err = "refusing to set env var with whitespace: '{0}'" |
|
489 | 489 | err = err.format(val) |
|
490 | 490 | raise UsageError(err) |
|
491 | 491 | os.environ[var] = val |
|
492 | 492 | print('env: {0}={1}'.format(var,val)) |
|
493 | 493 | |
|
494 | 494 | @line_magic |
|
495 | 495 | def pushd(self, parameter_s=''): |
|
496 | 496 | """Place the current dir on stack and change directory. |
|
497 | 497 | |
|
498 | 498 | Usage:\\ |
|
499 | 499 | %pushd ['dirname'] |
|
500 | 500 | """ |
|
501 | 501 | |
|
502 | 502 | dir_s = self.shell.dir_stack |
|
503 | 503 | tgt = os.path.expanduser(parameter_s) |
|
504 | 504 | cwd = os.getcwd().replace(self.shell.home_dir,'~') |
|
505 | 505 | if tgt: |
|
506 | 506 | self.cd(parameter_s) |
|
507 | 507 | dir_s.insert(0,cwd) |
|
508 | 508 | return self.shell.run_line_magic('dirs', '') |
|
509 | 509 | |
|
510 | 510 | @line_magic |
|
511 | 511 | def popd(self, parameter_s=''): |
|
512 | 512 | """Change to directory popped off the top of the stack. |
|
513 | 513 | """ |
|
514 | 514 | if not self.shell.dir_stack: |
|
515 | 515 | raise UsageError("%popd on empty stack") |
|
516 | 516 | top = self.shell.dir_stack.pop(0) |
|
517 | 517 | self.cd(top) |
|
518 | 518 | print("popd ->",top) |
|
519 | 519 | |
|
520 | 520 | @line_magic |
|
521 | 521 | def dirs(self, parameter_s=''): |
|
522 | 522 | """Return the current directory stack.""" |
|
523 | 523 | |
|
524 | 524 | return self.shell.dir_stack |
|
525 | 525 | |
|
526 | 526 | @line_magic |
|
527 | 527 | def dhist(self, parameter_s=''): |
|
528 | 528 | """Print your history of visited directories. |
|
529 | 529 | |
|
530 | 530 | %dhist -> print full history\\ |
|
531 | 531 | %dhist n -> print last n entries only\\ |
|
532 | 532 | %dhist n1 n2 -> print entries between n1 and n2 (n2 not included)\\ |
|
533 | 533 | |
|
534 | 534 | This history is automatically maintained by the %cd command, and |
|
535 | 535 | always available as the global list variable _dh. You can use %cd -<n> |
|
536 | 536 | to go to directory number <n>. |
|
537 | 537 | |
|
538 | 538 | Note that most of time, you should view directory history by entering |
|
539 | 539 | cd -<TAB>. |
|
540 | 540 | |
|
541 | 541 | """ |
|
542 | 542 | |
|
543 | 543 | dh = self.shell.user_ns['_dh'] |
|
544 | 544 | if parameter_s: |
|
545 | 545 | try: |
|
546 | 546 | args = map(int,parameter_s.split()) |
|
547 | 547 | except: |
|
548 | 548 | self.arg_err(self.dhist) |
|
549 | 549 | return |
|
550 | 550 | if len(args) == 1: |
|
551 | 551 | ini,fin = max(len(dh)-(args[0]),0),len(dh) |
|
552 | 552 | elif len(args) == 2: |
|
553 | 553 | ini,fin = args |
|
554 | 554 | fin = min(fin, len(dh)) |
|
555 | 555 | else: |
|
556 | 556 | self.arg_err(self.dhist) |
|
557 | 557 | return |
|
558 | 558 | else: |
|
559 | 559 | ini,fin = 0,len(dh) |
|
560 | 560 | print('Directory history (kept in _dh)') |
|
561 | 561 | for i in range(ini, fin): |
|
562 | 562 | print("%d: %s" % (i, dh[i])) |
|
563 | 563 | |
|
564 | 564 | @skip_doctest |
|
565 | 565 | @line_magic |
|
566 | 566 | def sc(self, parameter_s=''): |
|
567 | 567 | """Shell capture - run shell command and capture output (DEPRECATED use !). |
|
568 | 568 | |
|
569 | 569 | DEPRECATED. Suboptimal, retained for backwards compatibility. |
|
570 | 570 | |
|
571 | 571 | You should use the form 'var = !command' instead. Example: |
|
572 | 572 | |
|
573 | 573 | "%sc -l myfiles = ls ~" should now be written as |
|
574 | 574 | |
|
575 | 575 | "myfiles = !ls ~" |
|
576 | 576 | |
|
577 | 577 | myfiles.s, myfiles.l and myfiles.n still apply as documented |
|
578 | 578 | below. |
|
579 | 579 | |
|
580 | 580 | -- |
|
581 | 581 | %sc [options] varname=command |
|
582 | 582 | |
|
583 | 583 | IPython will run the given command using commands.getoutput(), and |
|
584 | 584 | will then update the user's interactive namespace with a variable |
|
585 | 585 | called varname, containing the value of the call. Your command can |
|
586 | 586 | contain shell wildcards, pipes, etc. |
|
587 | 587 | |
|
588 | 588 | The '=' sign in the syntax is mandatory, and the variable name you |
|
589 | 589 | supply must follow Python's standard conventions for valid names. |
|
590 | 590 | |
|
591 | 591 | (A special format without variable name exists for internal use) |
|
592 | 592 | |
|
593 | 593 | Options: |
|
594 | 594 | |
|
595 | 595 | -l: list output. Split the output on newlines into a list before |
|
596 | 596 | assigning it to the given variable. By default the output is stored |
|
597 | 597 | as a single string. |
|
598 | 598 | |
|
599 | 599 | -v: verbose. Print the contents of the variable. |
|
600 | 600 | |
|
601 | 601 | In most cases you should not need to split as a list, because the |
|
602 | 602 | returned value is a special type of string which can automatically |
|
603 | 603 | provide its contents either as a list (split on newlines) or as a |
|
604 | 604 | space-separated string. These are convenient, respectively, either |
|
605 | 605 | for sequential processing or to be passed to a shell command. |
|
606 | 606 | |
|
607 | 607 | For example:: |
|
608 | 608 | |
|
609 | 609 | # Capture into variable a |
|
610 | 610 | In [1]: sc a=ls *py |
|
611 | 611 | |
|
612 | 612 | # a is a string with embedded newlines |
|
613 | 613 | In [2]: a |
|
614 | 614 | Out[2]: 'setup.py\\nwin32_manual_post_install.py' |
|
615 | 615 | |
|
616 | 616 | # which can be seen as a list: |
|
617 | 617 | In [3]: a.l |
|
618 | 618 | Out[3]: ['setup.py', 'win32_manual_post_install.py'] |
|
619 | 619 | |
|
620 | 620 | # or as a whitespace-separated string: |
|
621 | 621 | In [4]: a.s |
|
622 | 622 | Out[4]: 'setup.py win32_manual_post_install.py' |
|
623 | 623 | |
|
624 | 624 | # a.s is useful to pass as a single command line: |
|
625 | 625 | In [5]: !wc -l $a.s |
|
626 | 626 | 146 setup.py |
|
627 | 627 | 130 win32_manual_post_install.py |
|
628 | 628 | 276 total |
|
629 | 629 | |
|
630 | 630 | # while the list form is useful to loop over: |
|
631 | 631 | In [6]: for f in a.l: |
|
632 | 632 | ...: !wc -l $f |
|
633 | 633 | ...: |
|
634 | 634 | 146 setup.py |
|
635 | 635 | 130 win32_manual_post_install.py |
|
636 | 636 | |
|
637 | 637 | Similarly, the lists returned by the -l option are also special, in |
|
638 | 638 | the sense that you can equally invoke the .s attribute on them to |
|
639 | 639 | automatically get a whitespace-separated string from their contents:: |
|
640 | 640 | |
|
641 | 641 | In [7]: sc -l b=ls *py |
|
642 | 642 | |
|
643 | 643 | In [8]: b |
|
644 | 644 | Out[8]: ['setup.py', 'win32_manual_post_install.py'] |
|
645 | 645 | |
|
646 | 646 | In [9]: b.s |
|
647 | 647 | Out[9]: 'setup.py win32_manual_post_install.py' |
|
648 | 648 | |
|
649 | 649 | In summary, both the lists and strings used for output capture have |
|
650 | 650 | the following special attributes:: |
|
651 | 651 | |
|
652 | 652 | .l (or .list) : value as list. |
|
653 | 653 | .n (or .nlstr): value as newline-separated string. |
|
654 | 654 | .s (or .spstr): value as space-separated string. |
|
655 | 655 | """ |
|
656 | 656 | |
|
657 | 657 | opts,args = self.parse_options(parameter_s, 'lv') |
|
658 | 658 | # Try to get a variable name and command to run |
|
659 | 659 | try: |
|
660 | 660 | # the variable name must be obtained from the parse_options |
|
661 | 661 | # output, which uses shlex.split to strip options out. |
|
662 | 662 | var,_ = args.split('=', 1) |
|
663 | 663 | var = var.strip() |
|
664 | 664 | # But the command has to be extracted from the original input |
|
665 | 665 | # parameter_s, not on what parse_options returns, to avoid the |
|
666 | 666 | # quote stripping which shlex.split performs on it. |
|
667 | 667 | _,cmd = parameter_s.split('=', 1) |
|
668 | 668 | except ValueError: |
|
669 | 669 | var,cmd = '','' |
|
670 | 670 | # If all looks ok, proceed |
|
671 | 671 | split = 'l' in opts |
|
672 | 672 | out = self.shell.getoutput(cmd, split=split) |
|
673 | 673 | if 'v' in opts: |
|
674 | 674 | print('%s ==\n%s' % (var, pformat(out))) |
|
675 | 675 | if var: |
|
676 | 676 | self.shell.user_ns.update({var:out}) |
|
677 | 677 | else: |
|
678 | 678 | return out |
|
679 | 679 | |
|
680 | 680 | @line_cell_magic |
|
681 | 681 | def sx(self, line='', cell=None): |
|
682 | 682 | """Shell execute - run shell command and capture output (!! is short-hand). |
|
683 | 683 | |
|
684 | 684 | %sx command |
|
685 | 685 | |
|
686 | 686 | IPython will run the given command using commands.getoutput(), and |
|
687 | 687 | return the result formatted as a list (split on '\\n'). Since the |
|
688 | 688 | output is _returned_, it will be stored in ipython's regular output |
|
689 | 689 | cache Out[N] and in the '_N' automatic variables. |
|
690 | 690 | |
|
691 | 691 | Notes: |
|
692 | 692 | |
|
693 | 693 | 1) If an input line begins with '!!', then %sx is automatically |
|
694 | 694 | invoked. That is, while:: |
|
695 | 695 | |
|
696 | 696 | !ls |
|
697 | 697 | |
|
698 | 698 | causes ipython to simply issue system('ls'), typing:: |
|
699 | 699 | |
|
700 | 700 | !!ls |
|
701 | 701 | |
|
702 | 702 | is a shorthand equivalent to:: |
|
703 | 703 | |
|
704 | 704 | %sx ls |
|
705 | 705 | |
|
706 | 706 | 2) %sx differs from %sc in that %sx automatically splits into a list, |
|
707 | 707 | like '%sc -l'. The reason for this is to make it as easy as possible |
|
708 | 708 | to process line-oriented shell output via further python commands. |
|
709 | 709 | %sc is meant to provide much finer control, but requires more |
|
710 | 710 | typing. |
|
711 | 711 | |
|
712 | 712 | 3) Just like %sc -l, this is a list with special attributes: |
|
713 | 713 | :: |
|
714 | 714 | |
|
715 | 715 | .l (or .list) : value as list. |
|
716 | 716 | .n (or .nlstr): value as newline-separated string. |
|
717 | 717 | .s (or .spstr): value as whitespace-separated string. |
|
718 | 718 | |
|
719 | 719 | This is very useful when trying to use such lists as arguments to |
|
720 | 720 | system commands.""" |
|
721 | 721 | |
|
722 | 722 | if cell is None: |
|
723 | 723 | # line magic |
|
724 | 724 | return self.shell.getoutput(line) |
|
725 | 725 | else: |
|
726 | 726 | opts,args = self.parse_options(line, '', 'out=') |
|
727 | 727 | output = self.shell.getoutput(cell) |
|
728 | 728 | out_name = opts.get('out', opts.get('o')) |
|
729 | 729 | if out_name: |
|
730 | 730 | self.shell.user_ns[out_name] = output |
|
731 | 731 | else: |
|
732 | 732 | return output |
|
733 | 733 | |
|
734 | 734 | system = line_cell_magic('system')(sx) |
|
735 | 735 | bang = cell_magic('!')(sx) |
|
736 | 736 | |
|
737 | 737 | @line_magic |
|
738 | 738 | def bookmark(self, parameter_s=''): |
|
739 | 739 | """Manage IPython's bookmark system. |
|
740 | 740 | |
|
741 | 741 | %bookmark <name> - set bookmark to current dir |
|
742 | 742 | %bookmark <name> <dir> - set bookmark to <dir> |
|
743 | 743 | %bookmark -l - list all bookmarks |
|
744 | 744 | %bookmark -d <name> - remove bookmark |
|
745 | 745 | %bookmark -r - remove all bookmarks |
|
746 | 746 | |
|
747 | 747 | You can later on access a bookmarked folder with:: |
|
748 | 748 | |
|
749 | 749 | %cd -b <name> |
|
750 | 750 | |
|
751 | 751 | or simply '%cd <name>' if there is no directory called <name> AND |
|
752 | 752 | there is such a bookmark defined. |
|
753 | 753 | |
|
754 | 754 | Your bookmarks persist through IPython sessions, but they are |
|
755 | 755 | associated with each profile.""" |
|
756 | 756 | |
|
757 | 757 | opts,args = self.parse_options(parameter_s,'drl',mode='list') |
|
758 | 758 | if len(args) > 2: |
|
759 | 759 | raise UsageError("%bookmark: too many arguments") |
|
760 | 760 | |
|
761 | 761 | bkms = self.shell.db.get('bookmarks',{}) |
|
762 | 762 | |
|
763 | 763 | if 'd' in opts: |
|
764 | 764 | try: |
|
765 | 765 | todel = args[0] |
|
766 | 766 | except IndexError as e: |
|
767 | 767 | raise UsageError( |
|
768 | 768 | "%bookmark -d: must provide a bookmark to delete") from e |
|
769 | 769 | else: |
|
770 | 770 | try: |
|
771 | 771 | del bkms[todel] |
|
772 | 772 | except KeyError as e: |
|
773 | 773 | raise UsageError( |
|
774 | 774 | "%%bookmark -d: Can't delete bookmark '%s'" % todel) from e |
|
775 | 775 | |
|
776 | 776 | elif 'r' in opts: |
|
777 | 777 | bkms = {} |
|
778 | 778 | elif 'l' in opts: |
|
779 | 779 | bks = sorted(bkms) |
|
780 | 780 | if bks: |
|
781 | 781 | size = max(map(len, bks)) |
|
782 | 782 | else: |
|
783 | 783 | size = 0 |
|
784 | 784 | fmt = '%-'+str(size)+'s -> %s' |
|
785 | 785 | print('Current bookmarks:') |
|
786 | 786 | for bk in bks: |
|
787 | 787 | print(fmt % (bk, bkms[bk])) |
|
788 | 788 | else: |
|
789 | 789 | if not args: |
|
790 | 790 | raise UsageError("%bookmark: You must specify the bookmark name") |
|
791 | 791 | elif len(args)==1: |
|
792 | 792 | bkms[args[0]] = os.getcwd() |
|
793 | 793 | elif len(args)==2: |
|
794 | 794 | bkms[args[0]] = args[1] |
|
795 | 795 | self.shell.db['bookmarks'] = bkms |
|
796 | 796 | |
|
797 | 797 | @line_magic |
|
798 | 798 | def pycat(self, parameter_s=''): |
|
799 | 799 | """Show a syntax-highlighted file through a pager. |
|
800 | 800 | |
|
801 | 801 | This magic is similar to the cat utility, but it will assume the file |
|
802 | 802 | to be Python source and will show it with syntax highlighting. |
|
803 | 803 | |
|
804 | 804 | This magic command can either take a local filename, an url, |
|
805 | 805 | an history range (see %history) or a macro as argument. |
|
806 | 806 | |
|
807 | 807 | If no parameter is given, prints out history of current session up to |
|
808 | 808 | this point. :: |
|
809 | 809 | |
|
810 | 810 | %pycat myscript.py |
|
811 | 811 | %pycat 7-27 |
|
812 | 812 | %pycat myMacro |
|
813 | 813 | %pycat http://www.example.com/myscript.py |
|
814 | 814 | """ |
|
815 | 815 | try: |
|
816 | 816 | cont = self.shell.find_user_code(parameter_s, skip_encoding_cookie=False) |
|
817 | 817 | except (ValueError, IOError): |
|
818 | 818 | print("Error: no such file, variable, URL, history range or macro") |
|
819 | 819 | return |
|
820 | 820 | |
|
821 | 821 | page.page(self.shell.pycolorize(source_to_unicode(cont))) |
|
822 | 822 | |
|
823 | 823 | @magic_arguments.magic_arguments() |
|
824 | 824 | @magic_arguments.argument( |
|
825 | 825 | '-a', '--append', action='store_true', default=False, |
|
826 | 826 | help='Append contents of the cell to an existing file. ' |
|
827 | 827 | 'The file will be created if it does not exist.' |
|
828 | 828 | ) |
|
829 | 829 | @magic_arguments.argument( |
|
830 | 830 | 'filename', type=str, |
|
831 | 831 | help='file to write' |
|
832 | 832 | ) |
|
833 | 833 | @cell_magic |
|
834 | 834 | def writefile(self, line, cell): |
|
835 | 835 | """Write the contents of the cell to a file. |
|
836 | 836 | |
|
837 | 837 | The file will be overwritten unless the -a (--append) flag is specified. |
|
838 | 838 | """ |
|
839 | 839 | args = magic_arguments.parse_argstring(self.writefile, line) |
|
840 | 840 | if re.match(r'^(\'.*\')|(".*")$', args.filename): |
|
841 | 841 | filename = os.path.expanduser(args.filename[1:-1]) |
|
842 | 842 | else: |
|
843 | 843 | filename = os.path.expanduser(args.filename) |
|
844 | 844 | |
|
845 | 845 | if os.path.exists(filename): |
|
846 | 846 | if args.append: |
|
847 | 847 | print("Appending to %s" % filename) |
|
848 | 848 | else: |
|
849 | 849 | print("Overwriting %s" % filename) |
|
850 | 850 | else: |
|
851 | 851 | print("Writing %s" % filename) |
|
852 | 852 | |
|
853 | 853 | mode = 'a' if args.append else 'w' |
|
854 | 854 | with io.open(filename, mode, encoding='utf-8') as f: |
|
855 | 855 | f.write(cell) |
@@ -1,169 +1,169 b'' | |||
|
1 | 1 | """Implementation of magic functions for matplotlib/pylab support. |
|
2 | 2 | """ |
|
3 | 3 | #----------------------------------------------------------------------------- |
|
4 | 4 | # Copyright (c) 2012 The IPython Development Team. |
|
5 | 5 | # |
|
6 | 6 | # Distributed under the terms of the Modified BSD License. |
|
7 | 7 | # |
|
8 | 8 | # The full license is in the file COPYING.txt, distributed with this software. |
|
9 | 9 | #----------------------------------------------------------------------------- |
|
10 | 10 | |
|
11 | 11 | #----------------------------------------------------------------------------- |
|
12 | 12 | # Imports |
|
13 | 13 | #----------------------------------------------------------------------------- |
|
14 | 14 | |
|
15 | 15 | # Our own packages |
|
16 | 16 | from traitlets.config.application import Application |
|
17 | 17 | from IPython.core import magic_arguments |
|
18 | 18 | from IPython.core.magic import Magics, magics_class, line_magic |
|
19 | 19 | from IPython.testing.skipdoctest import skip_doctest |
|
20 | 20 | from warnings import warn |
|
21 | 21 | from IPython.core.pylabtools import backends |
|
22 | 22 | |
|
23 | 23 | #----------------------------------------------------------------------------- |
|
24 | 24 | # Magic implementation classes |
|
25 | 25 | #----------------------------------------------------------------------------- |
|
26 | 26 | |
|
27 | 27 | magic_gui_arg = magic_arguments.argument( |
|
28 | 28 | 'gui', nargs='?', |
|
29 | 29 | help="""Name of the matplotlib backend to use %s. |
|
30 | 30 | If given, the corresponding matplotlib backend is used, |
|
31 | 31 | otherwise it will be matplotlib's default |
|
32 | 32 | (which you can set in your matplotlib config file). |
|
33 | 33 | """ % str(tuple(sorted(backends.keys()))) |
|
34 | 34 | ) |
|
35 | 35 | |
|
36 | 36 | |
|
37 | 37 | @magics_class |
|
38 | 38 | class PylabMagics(Magics): |
|
39 | 39 | """Magics related to matplotlib's pylab support""" |
|
40 | 40 | |
|
41 | 41 | @skip_doctest |
|
42 | 42 | @line_magic |
|
43 | 43 | @magic_arguments.magic_arguments() |
|
44 | 44 | @magic_arguments.argument('-l', '--list', action='store_true', |
|
45 | 45 | help='Show available matplotlib backends') |
|
46 | 46 | @magic_gui_arg |
|
47 | 47 | def matplotlib(self, line=''): |
|
48 | 48 | """Set up matplotlib to work interactively. |
|
49 | 49 | |
|
50 | 50 | This function lets you activate matplotlib interactive support |
|
51 | 51 | at any point during an IPython session. It does not import anything |
|
52 | 52 | into the interactive namespace. |
|
53 | 53 | |
|
54 | 54 | If you are using the inline matplotlib backend in the IPython Notebook |
|
55 | 55 | you can set which figure formats are enabled using the following:: |
|
56 | 56 | |
|
57 |
In [1]: from |
|
|
57 | In [1]: from matplotlib_inline.backend_inline import set_matplotlib_formats | |
|
58 | 58 | |
|
59 | 59 | In [2]: set_matplotlib_formats('pdf', 'svg') |
|
60 | 60 | |
|
61 | 61 | The default for inline figures sets `bbox_inches` to 'tight'. This can |
|
62 | 62 | cause discrepancies between the displayed image and the identical |
|
63 | 63 | image created using `savefig`. This behavior can be disabled using the |
|
64 | 64 | `%config` magic:: |
|
65 | 65 | |
|
66 | 66 | In [3]: %config InlineBackend.print_figure_kwargs = {'bbox_inches':None} |
|
67 | 67 | |
|
68 | In addition, see the docstring of | |
|
69 |
` |
|
|
70 |
` |
|
|
68 | In addition, see the docstrings of | |
|
69 | `matplotlib_inline.backend_inline.set_matplotlib_formats` and | |
|
70 | `matplotlib_inline.backend_inline.set_matplotlib_close` for more information on | |
|
71 | 71 | changing additional behaviors of the inline backend. |
|
72 | 72 | |
|
73 | 73 | Examples |
|
74 | 74 | -------- |
|
75 | 75 | To enable the inline backend for usage with the IPython Notebook:: |
|
76 | 76 | |
|
77 | 77 | In [1]: %matplotlib inline |
|
78 | 78 | |
|
79 | 79 | In this case, where the matplotlib default is TkAgg:: |
|
80 | 80 | |
|
81 | 81 | In [2]: %matplotlib |
|
82 | 82 | Using matplotlib backend: TkAgg |
|
83 | 83 | |
|
84 | 84 | But you can explicitly request a different GUI backend:: |
|
85 | 85 | |
|
86 | 86 | In [3]: %matplotlib qt |
|
87 | 87 | |
|
88 | 88 | You can list the available backends using the -l/--list option:: |
|
89 | 89 | |
|
90 | 90 | In [4]: %matplotlib --list |
|
91 | 91 | Available matplotlib backends: ['osx', 'qt4', 'qt5', 'gtk3', 'gtk4', 'notebook', 'wx', 'qt', 'nbagg', |
|
92 | 92 | 'gtk', 'tk', 'inline'] |
|
93 | 93 | """ |
|
94 | 94 | args = magic_arguments.parse_argstring(self.matplotlib, line) |
|
95 | 95 | if args.list: |
|
96 | 96 | backends_list = list(backends.keys()) |
|
97 | 97 | print("Available matplotlib backends: %s" % backends_list) |
|
98 | 98 | else: |
|
99 | 99 | gui, backend = self.shell.enable_matplotlib(args.gui.lower() if isinstance(args.gui, str) else args.gui) |
|
100 | 100 | self._show_matplotlib_backend(args.gui, backend) |
|
101 | 101 | |
|
102 | 102 | @skip_doctest |
|
103 | 103 | @line_magic |
|
104 | 104 | @magic_arguments.magic_arguments() |
|
105 | 105 | @magic_arguments.argument( |
|
106 | 106 | '--no-import-all', action='store_true', default=None, |
|
107 | 107 | help="""Prevent IPython from performing ``import *`` into the interactive namespace. |
|
108 | 108 | |
|
109 | 109 | You can govern the default behavior of this flag with the |
|
110 | 110 | InteractiveShellApp.pylab_import_all configurable. |
|
111 | 111 | """ |
|
112 | 112 | ) |
|
113 | 113 | @magic_gui_arg |
|
114 | 114 | def pylab(self, line=''): |
|
115 | 115 | """Load numpy and matplotlib to work interactively. |
|
116 | 116 | |
|
117 | 117 | This function lets you activate pylab (matplotlib, numpy and |
|
118 | 118 | interactive support) at any point during an IPython session. |
|
119 | 119 | |
|
120 | 120 | %pylab makes the following imports:: |
|
121 | 121 | |
|
122 | 122 | import numpy |
|
123 | 123 | import matplotlib |
|
124 | 124 | from matplotlib import pylab, mlab, pyplot |
|
125 | 125 | np = numpy |
|
126 | 126 | plt = pyplot |
|
127 | 127 | |
|
128 | 128 | from IPython.display import display |
|
129 | 129 | from IPython.core.pylabtools import figsize, getfigs |
|
130 | 130 | |
|
131 | 131 | from pylab import * |
|
132 | 132 | from numpy import * |
|
133 | 133 | |
|
134 | 134 | If you pass `--no-import-all`, the last two `*` imports will be excluded. |
|
135 | 135 | |
|
136 | 136 | See the %matplotlib magic for more details about activating matplotlib |
|
137 | 137 | without affecting the interactive namespace. |
|
138 | 138 | """ |
|
139 | 139 | args = magic_arguments.parse_argstring(self.pylab, line) |
|
140 | 140 | if args.no_import_all is None: |
|
141 | 141 | # get default from Application |
|
142 | 142 | if Application.initialized(): |
|
143 | 143 | app = Application.instance() |
|
144 | 144 | try: |
|
145 | 145 | import_all = app.pylab_import_all |
|
146 | 146 | except AttributeError: |
|
147 | 147 | import_all = True |
|
148 | 148 | else: |
|
149 | 149 | # nothing specified, no app - default True |
|
150 | 150 | import_all = True |
|
151 | 151 | else: |
|
152 | 152 | # invert no-import flag |
|
153 | 153 | import_all = not args.no_import_all |
|
154 | 154 | |
|
155 | 155 | gui, backend, clobbered = self.shell.enable_pylab(args.gui, import_all=import_all) |
|
156 | 156 | self._show_matplotlib_backend(args.gui, backend) |
|
157 | 157 | print( |
|
158 | 158 | "%pylab is deprecated, use %matplotlib inline and import the required libraries." |
|
159 | 159 | ) |
|
160 | 160 | print("Populating the interactive namespace from numpy and matplotlib") |
|
161 | 161 | if clobbered: |
|
162 | 162 | warn("pylab import has clobbered these variables: %s" % clobbered + |
|
163 | 163 | "\n`%matplotlib` prevents importing * from pylab and numpy" |
|
164 | 164 | ) |
|
165 | 165 | |
|
166 | 166 | def _show_matplotlib_backend(self, gui, backend): |
|
167 | 167 | """show matplotlib message backend message""" |
|
168 | 168 | if not gui or gui == 'auto': |
|
169 | 169 | print("Using matplotlib backend: %s" % backend) |
@@ -1,362 +1,362 b'' | |||
|
1 | 1 | """Magic functions for running cells in various scripts.""" |
|
2 | 2 | |
|
3 | 3 | # Copyright (c) IPython Development Team. |
|
4 | 4 | # Distributed under the terms of the Modified BSD License. |
|
5 | 5 | |
|
6 | 6 | import asyncio |
|
7 | 7 | import atexit |
|
8 | 8 | import errno |
|
9 | 9 | import os |
|
10 | 10 | import signal |
|
11 | 11 | import sys |
|
12 | 12 | import time |
|
13 | 13 | from subprocess import CalledProcessError |
|
14 | 14 | from threading import Thread |
|
15 | 15 | |
|
16 | 16 | from traitlets import Any, Dict, List, default |
|
17 | 17 | |
|
18 | 18 | from IPython.core import magic_arguments |
|
19 | 19 | from IPython.core.async_helpers import _AsyncIOProxy |
|
20 | 20 | from IPython.core.magic import Magics, cell_magic, line_magic, magics_class |
|
21 | 21 | from IPython.utils.process import arg_split |
|
22 | 22 | |
|
23 | 23 | #----------------------------------------------------------------------------- |
|
24 | 24 | # Magic implementation classes |
|
25 | 25 | #----------------------------------------------------------------------------- |
|
26 | 26 | |
|
27 | 27 | def script_args(f): |
|
28 | 28 | """single decorator for adding script args""" |
|
29 | 29 | args = [ |
|
30 | 30 | magic_arguments.argument( |
|
31 | 31 | '--out', type=str, |
|
32 | 32 | help="""The variable in which to store stdout from the script. |
|
33 | 33 | If the script is backgrounded, this will be the stdout *pipe*, |
|
34 | 34 | instead of the stderr text itself and will not be auto closed. |
|
35 | 35 | """ |
|
36 | 36 | ), |
|
37 | 37 | magic_arguments.argument( |
|
38 | 38 | '--err', type=str, |
|
39 | 39 | help="""The variable in which to store stderr from the script. |
|
40 | 40 | If the script is backgrounded, this will be the stderr *pipe*, |
|
41 | 41 | instead of the stderr text itself and will not be autoclosed. |
|
42 | 42 | """ |
|
43 | 43 | ), |
|
44 | 44 | magic_arguments.argument( |
|
45 | 45 | '--bg', action="store_true", |
|
46 | 46 | help="""Whether to run the script in the background. |
|
47 | 47 | If given, the only way to see the output of the command is |
|
48 | 48 | with --out/err. |
|
49 | 49 | """ |
|
50 | 50 | ), |
|
51 | 51 | magic_arguments.argument( |
|
52 | 52 | '--proc', type=str, |
|
53 | 53 | help="""The variable in which to store Popen instance. |
|
54 | 54 | This is used only when --bg option is given. |
|
55 | 55 | """ |
|
56 | 56 | ), |
|
57 | 57 | magic_arguments.argument( |
|
58 | 58 | '--no-raise-error', action="store_false", dest='raise_error', |
|
59 | 59 | help="""Whether you should raise an error message in addition to |
|
60 | 60 | a stream on stderr if you get a nonzero exit code. |
|
61 | 61 | """, |
|
62 | 62 | ), |
|
63 | 63 | ] |
|
64 | 64 | for arg in args: |
|
65 | 65 | f = arg(f) |
|
66 | 66 | return f |
|
67 | 67 | |
|
68 | 68 | |
|
69 | 69 | @magics_class |
|
70 | 70 | class ScriptMagics(Magics): |
|
71 | 71 | """Magics for talking to scripts |
|
72 | 72 | |
|
73 | 73 | This defines a base `%%script` cell magic for running a cell |
|
74 | 74 | with a program in a subprocess, and registers a few top-level |
|
75 | 75 | magics that call %%script with common interpreters. |
|
76 | 76 | """ |
|
77 | 77 | |
|
78 | 78 | event_loop = Any( |
|
79 | 79 | help=""" |
|
80 | 80 | The event loop on which to run subprocesses |
|
81 | 81 | |
|
82 | 82 | Not the main event loop, |
|
83 | 83 | because we want to be able to make blocking calls |
|
84 | 84 | and have certain requirements we don't want to impose on the main loop. |
|
85 | 85 | """ |
|
86 | 86 | ) |
|
87 | 87 | |
|
88 | 88 | script_magics = List( |
|
89 | 89 | help="""Extra script cell magics to define |
|
90 | 90 | |
|
91 | 91 | This generates simple wrappers of `%%script foo` as `%%foo`. |
|
92 | 92 | |
|
93 | 93 | If you want to add script magics that aren't on your path, |
|
94 | 94 | specify them in script_paths |
|
95 | 95 | """, |
|
96 | 96 | ).tag(config=True) |
|
97 | 97 | @default('script_magics') |
|
98 | 98 | def _script_magics_default(self): |
|
99 | 99 | """default to a common list of programs""" |
|
100 | 100 | |
|
101 | 101 | defaults = [ |
|
102 | 102 | 'sh', |
|
103 | 103 | 'bash', |
|
104 | 104 | 'perl', |
|
105 | 105 | 'ruby', |
|
106 | 106 | 'python', |
|
107 | 107 | 'python2', |
|
108 | 108 | 'python3', |
|
109 | 109 | 'pypy', |
|
110 | 110 | ] |
|
111 | 111 | if os.name == 'nt': |
|
112 | 112 | defaults.extend([ |
|
113 | 113 | 'cmd', |
|
114 | 114 | ]) |
|
115 | 115 | |
|
116 | 116 | return defaults |
|
117 | 117 | |
|
118 | 118 | script_paths = Dict( |
|
119 | 119 | help="""Dict mapping short 'ruby' names to full paths, such as '/opt/secret/bin/ruby' |
|
120 | 120 | |
|
121 | 121 | Only necessary for items in script_magics where the default path will not |
|
122 | 122 | find the right interpreter. |
|
123 | 123 | """ |
|
124 | 124 | ).tag(config=True) |
|
125 | 125 | |
|
126 | 126 | def __init__(self, shell=None): |
|
127 | 127 | super(ScriptMagics, self).__init__(shell=shell) |
|
128 | 128 | self._generate_script_magics() |
|
129 | 129 | self.bg_processes = [] |
|
130 | 130 | atexit.register(self.kill_bg_processes) |
|
131 | 131 | |
|
132 | 132 | def __del__(self): |
|
133 | 133 | self.kill_bg_processes() |
|
134 | 134 | |
|
135 | 135 | def _generate_script_magics(self): |
|
136 | 136 | cell_magics = self.magics['cell'] |
|
137 | 137 | for name in self.script_magics: |
|
138 | 138 | cell_magics[name] = self._make_script_magic(name) |
|
139 | 139 | |
|
140 | 140 | def _make_script_magic(self, name): |
|
141 | 141 | """make a named magic, that calls %%script with a particular program""" |
|
142 | 142 | # expand to explicit path if necessary: |
|
143 | 143 | script = self.script_paths.get(name, name) |
|
144 | 144 | |
|
145 | 145 | @magic_arguments.magic_arguments() |
|
146 | 146 | @script_args |
|
147 | 147 | def named_script_magic(line, cell): |
|
148 | 148 | # if line, add it as cl-flags |
|
149 | 149 | if line: |
|
150 | 150 | line = "%s %s" % (script, line) |
|
151 | 151 | else: |
|
152 | 152 | line = script |
|
153 | 153 | return self.shebang(line, cell) |
|
154 | 154 | |
|
155 | 155 | # write a basic docstring: |
|
156 | 156 | named_script_magic.__doc__ = \ |
|
157 | 157 | """%%{name} script magic |
|
158 | 158 | |
|
159 | 159 | Run cells with {script} in a subprocess. |
|
160 | 160 | |
|
161 | 161 | This is a shortcut for `%%script {script}` |
|
162 | 162 | """.format(**locals()) |
|
163 | 163 | |
|
164 | 164 | return named_script_magic |
|
165 | 165 | |
|
166 | 166 | @magic_arguments.magic_arguments() |
|
167 | 167 | @script_args |
|
168 | 168 | @cell_magic("script") |
|
169 | 169 | def shebang(self, line, cell): |
|
170 | 170 | """Run a cell via a shell command |
|
171 | 171 | |
|
172 | 172 | The `%%script` line is like the #! line of script, |
|
173 | 173 | specifying a program (bash, perl, ruby, etc.) with which to run. |
|
174 | 174 | |
|
175 | 175 | The rest of the cell is run by that program. |
|
176 | 176 | |
|
177 | 177 | Examples |
|
178 | 178 | -------- |
|
179 | 179 | :: |
|
180 | 180 | |
|
181 | 181 | In [1]: %%script bash |
|
182 | 182 | ...: for i in 1 2 3; do |
|
183 | 183 | ...: echo $i |
|
184 | 184 | ...: done |
|
185 | 185 | 1 |
|
186 | 186 | 2 |
|
187 | 187 | 3 |
|
188 | 188 | """ |
|
189 | 189 | |
|
190 | 190 | # Create the event loop in which to run script magics |
|
191 | 191 | # this operates on a background thread |
|
192 | 192 | if self.event_loop is None: |
|
193 | 193 | if sys.platform == "win32": |
|
194 | 194 | # don't override the current policy, |
|
195 | 195 | # just create an event loop |
|
196 | 196 | event_loop = asyncio.WindowsProactorEventLoopPolicy().new_event_loop() |
|
197 | 197 | else: |
|
198 | 198 | event_loop = asyncio.new_event_loop() |
|
199 | 199 | self.event_loop = event_loop |
|
200 | 200 | |
|
201 | 201 | # start the loop in a background thread |
|
202 | 202 | asyncio_thread = Thread(target=event_loop.run_forever, daemon=True) |
|
203 | 203 | asyncio_thread.start() |
|
204 | 204 | else: |
|
205 | 205 | event_loop = self.event_loop |
|
206 | 206 | |
|
207 | 207 | def in_thread(coro): |
|
208 | 208 | """Call a coroutine on the asyncio thread""" |
|
209 | 209 | return asyncio.run_coroutine_threadsafe(coro, event_loop).result() |
|
210 | 210 | |
|
211 | 211 | async def _handle_stream(stream, stream_arg, file_object): |
|
212 | 212 | while True: |
|
213 | line = (await stream.readline()).decode("utf8") | |
|
213 | line = (await stream.readline()).decode("utf8", errors="replace") | |
|
214 | 214 | if not line: |
|
215 | 215 | break |
|
216 | 216 | if stream_arg: |
|
217 | 217 | self.shell.user_ns[stream_arg] = line |
|
218 | 218 | else: |
|
219 | 219 | file_object.write(line) |
|
220 | 220 | file_object.flush() |
|
221 | 221 | |
|
222 | 222 | async def _stream_communicate(process, cell): |
|
223 | 223 | process.stdin.write(cell) |
|
224 | 224 | process.stdin.close() |
|
225 | 225 | stdout_task = asyncio.create_task( |
|
226 | 226 | _handle_stream(process.stdout, args.out, sys.stdout) |
|
227 | 227 | ) |
|
228 | 228 | stderr_task = asyncio.create_task( |
|
229 | 229 | _handle_stream(process.stderr, args.err, sys.stderr) |
|
230 | 230 | ) |
|
231 | 231 | await asyncio.wait([stdout_task, stderr_task]) |
|
232 | 232 | await process.wait() |
|
233 | 233 | |
|
234 | 234 | argv = arg_split(line, posix=not sys.platform.startswith("win")) |
|
235 | 235 | args, cmd = self.shebang.parser.parse_known_args(argv) |
|
236 | 236 | |
|
237 | 237 | try: |
|
238 | 238 | p = in_thread( |
|
239 | 239 | asyncio.create_subprocess_exec( |
|
240 | 240 | *cmd, |
|
241 | 241 | stdout=asyncio.subprocess.PIPE, |
|
242 | 242 | stderr=asyncio.subprocess.PIPE, |
|
243 | 243 | stdin=asyncio.subprocess.PIPE, |
|
244 | 244 | ) |
|
245 | 245 | ) |
|
246 | 246 | except OSError as e: |
|
247 | 247 | if e.errno == errno.ENOENT: |
|
248 | 248 | print("Couldn't find program: %r" % cmd[0]) |
|
249 | 249 | return |
|
250 | 250 | else: |
|
251 | 251 | raise |
|
252 | 252 | |
|
253 | 253 | if not cell.endswith('\n'): |
|
254 | 254 | cell += '\n' |
|
255 | 255 | cell = cell.encode('utf8', 'replace') |
|
256 | 256 | if args.bg: |
|
257 | 257 | self.bg_processes.append(p) |
|
258 | 258 | self._gc_bg_processes() |
|
259 | 259 | to_close = [] |
|
260 | 260 | if args.out: |
|
261 | 261 | self.shell.user_ns[args.out] = _AsyncIOProxy(p.stdout, event_loop) |
|
262 | 262 | else: |
|
263 | 263 | to_close.append(p.stdout) |
|
264 | 264 | if args.err: |
|
265 | 265 | self.shell.user_ns[args.err] = _AsyncIOProxy(p.stderr, event_loop) |
|
266 | 266 | else: |
|
267 | 267 | to_close.append(p.stderr) |
|
268 | 268 | event_loop.call_soon_threadsafe( |
|
269 | 269 | lambda: asyncio.Task(self._run_script(p, cell, to_close)) |
|
270 | 270 | ) |
|
271 | 271 | if args.proc: |
|
272 | 272 | proc_proxy = _AsyncIOProxy(p, event_loop) |
|
273 | 273 | proc_proxy.stdout = _AsyncIOProxy(p.stdout, event_loop) |
|
274 | 274 | proc_proxy.stderr = _AsyncIOProxy(p.stderr, event_loop) |
|
275 | 275 | self.shell.user_ns[args.proc] = proc_proxy |
|
276 | 276 | return |
|
277 | 277 | |
|
278 | 278 | try: |
|
279 | 279 | in_thread(_stream_communicate(p, cell)) |
|
280 | 280 | except KeyboardInterrupt: |
|
281 | 281 | try: |
|
282 | 282 | p.send_signal(signal.SIGINT) |
|
283 | 283 | in_thread(asyncio.wait_for(p.wait(), timeout=0.1)) |
|
284 | 284 | if p.returncode is not None: |
|
285 | 285 | print("Process is interrupted.") |
|
286 | 286 | return |
|
287 | 287 | p.terminate() |
|
288 | 288 | in_thread(asyncio.wait_for(p.wait(), timeout=0.1)) |
|
289 | 289 | if p.returncode is not None: |
|
290 | 290 | print("Process is terminated.") |
|
291 | 291 | return |
|
292 | 292 | p.kill() |
|
293 | 293 | print("Process is killed.") |
|
294 | 294 | except OSError: |
|
295 | 295 | pass |
|
296 | 296 | except Exception as e: |
|
297 | 297 | print("Error while terminating subprocess (pid=%i): %s" % (p.pid, e)) |
|
298 | 298 | return |
|
299 | 299 | |
|
300 | 300 | if args.raise_error and p.returncode != 0: |
|
301 | 301 | # If we get here and p.returncode is still None, we must have |
|
302 | 302 | # killed it but not yet seen its return code. We don't wait for it, |
|
303 | 303 | # in case it's stuck in uninterruptible sleep. -9 = SIGKILL |
|
304 | 304 | rc = p.returncode or -9 |
|
305 | 305 | raise CalledProcessError(rc, cell) |
|
306 | 306 | |
|
307 | 307 | shebang.__skip_doctest__ = os.name != "posix" |
|
308 | 308 | |
|
309 | 309 | async def _run_script(self, p, cell, to_close): |
|
310 | 310 | """callback for running the script in the background""" |
|
311 | 311 | |
|
312 | 312 | p.stdin.write(cell) |
|
313 | 313 | await p.stdin.drain() |
|
314 | 314 | p.stdin.close() |
|
315 | 315 | await p.stdin.wait_closed() |
|
316 | 316 | await p.wait() |
|
317 | 317 | # asyncio read pipes have no close |
|
318 | 318 | # but we should drain the data anyway |
|
319 | 319 | for s in to_close: |
|
320 | 320 | await s.read() |
|
321 | 321 | self._gc_bg_processes() |
|
322 | 322 | |
|
323 | 323 | @line_magic("killbgscripts") |
|
324 | 324 | def killbgscripts(self, _nouse_=''): |
|
325 | 325 | """Kill all BG processes started by %%script and its family.""" |
|
326 | 326 | self.kill_bg_processes() |
|
327 | 327 | print("All background processes were killed.") |
|
328 | 328 | |
|
329 | 329 | def kill_bg_processes(self): |
|
330 | 330 | """Kill all BG processes which are still running.""" |
|
331 | 331 | if not self.bg_processes: |
|
332 | 332 | return |
|
333 | 333 | for p in self.bg_processes: |
|
334 | 334 | if p.returncode is None: |
|
335 | 335 | try: |
|
336 | 336 | p.send_signal(signal.SIGINT) |
|
337 | 337 | except: |
|
338 | 338 | pass |
|
339 | 339 | time.sleep(0.1) |
|
340 | 340 | self._gc_bg_processes() |
|
341 | 341 | if not self.bg_processes: |
|
342 | 342 | return |
|
343 | 343 | for p in self.bg_processes: |
|
344 | 344 | if p.returncode is None: |
|
345 | 345 | try: |
|
346 | 346 | p.terminate() |
|
347 | 347 | except: |
|
348 | 348 | pass |
|
349 | 349 | time.sleep(0.1) |
|
350 | 350 | self._gc_bg_processes() |
|
351 | 351 | if not self.bg_processes: |
|
352 | 352 | return |
|
353 | 353 | for p in self.bg_processes: |
|
354 | 354 | if p.returncode is None: |
|
355 | 355 | try: |
|
356 | 356 | p.kill() |
|
357 | 357 | except: |
|
358 | 358 | pass |
|
359 | 359 | self._gc_bg_processes() |
|
360 | 360 | |
|
361 | 361 | def _gc_bg_processes(self): |
|
362 | 362 | self.bg_processes = [p for p in self.bg_processes if p.returncode is None] |
@@ -1,54 +1,54 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | """Release data for the IPython project.""" |
|
3 | 3 | |
|
4 | 4 | #----------------------------------------------------------------------------- |
|
5 | 5 | # Copyright (c) 2008, IPython Development Team. |
|
6 | 6 | # Copyright (c) 2001, Fernando Perez <fernando.perez@colorado.edu> |
|
7 | 7 | # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de> |
|
8 | 8 | # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu> |
|
9 | 9 | # |
|
10 | 10 | # Distributed under the terms of the Modified BSD License. |
|
11 | 11 | # |
|
12 | 12 | # The full license is in the file COPYING.txt, distributed with this software. |
|
13 | 13 | #----------------------------------------------------------------------------- |
|
14 | 14 | |
|
15 | 15 | # IPython version information. An empty _version_extra corresponds to a full |
|
16 | 16 | # release. 'dev' as a _version_extra string means this is a development |
|
17 | 17 | # version |
|
18 | 18 | _version_major = 8 |
|
19 |
_version_minor = |
|
|
19 | _version_minor = 10 | |
|
20 | 20 | _version_patch = 0 |
|
21 | 21 | _version_extra = ".dev" |
|
22 | 22 | # _version_extra = "rc1" |
|
23 | 23 | # _version_extra = "" # Uncomment this for full releases |
|
24 | 24 | |
|
25 | 25 | # Construct full version string from these. |
|
26 | 26 | _ver = [_version_major, _version_minor, _version_patch] |
|
27 | 27 | |
|
28 | 28 | __version__ = '.'.join(map(str, _ver)) |
|
29 | 29 | if _version_extra: |
|
30 | 30 | __version__ = __version__ + _version_extra |
|
31 | 31 | |
|
32 | 32 | version = __version__ # backwards compatibility name |
|
33 | 33 | version_info = (_version_major, _version_minor, _version_patch, _version_extra) |
|
34 | 34 | |
|
35 | 35 | # Change this when incrementing the kernel protocol version |
|
36 | 36 | kernel_protocol_version_info = (5, 0) |
|
37 | 37 | kernel_protocol_version = "%i.%i" % kernel_protocol_version_info |
|
38 | 38 | |
|
39 | 39 | license = "BSD-3-Clause" |
|
40 | 40 | |
|
41 | 41 | authors = {'Fernando' : ('Fernando Perez','fperez.net@gmail.com'), |
|
42 | 42 | 'Janko' : ('Janko Hauser','jhauser@zscout.de'), |
|
43 | 43 | 'Nathan' : ('Nathaniel Gray','n8gray@caltech.edu'), |
|
44 | 44 | 'Ville' : ('Ville Vainio','vivainio@gmail.com'), |
|
45 | 45 | 'Brian' : ('Brian E Granger', 'ellisonbg@gmail.com'), |
|
46 | 46 | 'Min' : ('Min Ragan-Kelley', 'benjaminrk@gmail.com'), |
|
47 | 47 | 'Thomas' : ('Thomas A. Kluyver', 'takowl@gmail.com'), |
|
48 | 48 | 'Jorgen' : ('Jorgen Stenarson', 'jorgen.stenarson@bostream.nu'), |
|
49 | 49 | 'Matthias' : ('Matthias Bussonnier', 'bussonniermatthias@gmail.com'), |
|
50 | 50 | } |
|
51 | 51 | |
|
52 | 52 | author = 'The IPython Development Team' |
|
53 | 53 | |
|
54 | 54 | author_email = 'ipython-dev@python.org' |
@@ -1,451 +1,451 b'' | |||
|
1 | 1 | # encoding: utf-8 |
|
2 | 2 | """ |
|
3 | 3 | A mixin for :class:`~IPython.core.application.Application` classes that |
|
4 | 4 | launch InteractiveShell instances, load extensions, etc. |
|
5 | 5 | """ |
|
6 | 6 | |
|
7 | 7 | # Copyright (c) IPython Development Team. |
|
8 | 8 | # Distributed under the terms of the Modified BSD License. |
|
9 | 9 | |
|
10 | 10 | import glob |
|
11 | 11 | from itertools import chain |
|
12 | 12 | import os |
|
13 | 13 | import sys |
|
14 | 14 | |
|
15 | 15 | from traitlets.config.application import boolean_flag |
|
16 | 16 | from traitlets.config.configurable import Configurable |
|
17 | 17 | from traitlets.config.loader import Config |
|
18 | 18 | from IPython.core.application import SYSTEM_CONFIG_DIRS, ENV_CONFIG_DIRS |
|
19 | 19 | from IPython.core import pylabtools |
|
20 | 20 | from IPython.utils.contexts import preserve_keys |
|
21 | 21 | from IPython.utils.path import filefind |
|
22 | 22 | from traitlets import ( |
|
23 | 23 | Unicode, Instance, List, Bool, CaselessStrEnum, observe, |
|
24 | 24 | DottedObjectName, |
|
25 | 25 | ) |
|
26 | 26 | from IPython.terminal import pt_inputhooks |
|
27 | 27 | |
|
28 | 28 | #----------------------------------------------------------------------------- |
|
29 | 29 | # Aliases and Flags |
|
30 | 30 | #----------------------------------------------------------------------------- |
|
31 | 31 | |
|
32 | 32 | gui_keys = tuple(sorted(pt_inputhooks.backends) + sorted(pt_inputhooks.aliases)) |
|
33 | 33 | |
|
34 | 34 | backend_keys = sorted(pylabtools.backends.keys()) |
|
35 | 35 | backend_keys.insert(0, 'auto') |
|
36 | 36 | |
|
37 | 37 | shell_flags = {} |
|
38 | 38 | |
|
39 | 39 | addflag = lambda *args: shell_flags.update(boolean_flag(*args)) |
|
40 | 40 | addflag('autoindent', 'InteractiveShell.autoindent', |
|
41 | 41 | 'Turn on autoindenting.', 'Turn off autoindenting.' |
|
42 | 42 | ) |
|
43 | 43 | addflag('automagic', 'InteractiveShell.automagic', |
|
44 | 44 | """Turn on the auto calling of magic commands. Type %%magic at the |
|
45 | 45 | IPython prompt for more information.""", |
|
46 | 46 | 'Turn off the auto calling of magic commands.' |
|
47 | 47 | ) |
|
48 | 48 | addflag('pdb', 'InteractiveShell.pdb', |
|
49 | 49 | "Enable auto calling the pdb debugger after every exception.", |
|
50 | 50 | "Disable auto calling the pdb debugger after every exception." |
|
51 | 51 | ) |
|
52 | 52 | addflag('pprint', 'PlainTextFormatter.pprint', |
|
53 | 53 | "Enable auto pretty printing of results.", |
|
54 | 54 | "Disable auto pretty printing of results." |
|
55 | 55 | ) |
|
56 | 56 | addflag('color-info', 'InteractiveShell.color_info', |
|
57 | 57 | """IPython can display information about objects via a set of functions, |
|
58 | 58 | and optionally can use colors for this, syntax highlighting |
|
59 | 59 | source code and various other elements. This is on by default, but can cause |
|
60 | 60 | problems with some pagers. If you see such problems, you can disable the |
|
61 | 61 | colours.""", |
|
62 | 62 | "Disable using colors for info related things." |
|
63 | 63 | ) |
|
64 | 64 | addflag('ignore-cwd', 'InteractiveShellApp.ignore_cwd', |
|
65 | 65 | "Exclude the current working directory from sys.path", |
|
66 | 66 | "Include the current working directory in sys.path", |
|
67 | 67 | ) |
|
68 | 68 | nosep_config = Config() |
|
69 | 69 | nosep_config.InteractiveShell.separate_in = '' |
|
70 | 70 | nosep_config.InteractiveShell.separate_out = '' |
|
71 | 71 | nosep_config.InteractiveShell.separate_out2 = '' |
|
72 | 72 | |
|
73 | 73 | shell_flags['nosep']=(nosep_config, "Eliminate all spacing between prompts.") |
|
74 | 74 | shell_flags['pylab'] = ( |
|
75 | 75 | {'InteractiveShellApp' : {'pylab' : 'auto'}}, |
|
76 | 76 | """Pre-load matplotlib and numpy for interactive use with |
|
77 | 77 | the default matplotlib backend.""" |
|
78 | 78 | ) |
|
79 | 79 | shell_flags['matplotlib'] = ( |
|
80 | 80 | {'InteractiveShellApp' : {'matplotlib' : 'auto'}}, |
|
81 | 81 | """Configure matplotlib for interactive use with |
|
82 | 82 | the default matplotlib backend.""" |
|
83 | 83 | ) |
|
84 | 84 | |
|
85 | 85 | # it's possible we don't want short aliases for *all* of these: |
|
86 | 86 | shell_aliases = dict( |
|
87 | 87 | autocall='InteractiveShell.autocall', |
|
88 | 88 | colors='InteractiveShell.colors', |
|
89 | 89 | logfile='InteractiveShell.logfile', |
|
90 | 90 | logappend='InteractiveShell.logappend', |
|
91 | 91 | c='InteractiveShellApp.code_to_run', |
|
92 | 92 | m='InteractiveShellApp.module_to_run', |
|
93 | 93 | ext="InteractiveShellApp.extra_extensions", |
|
94 | 94 | gui='InteractiveShellApp.gui', |
|
95 | 95 | pylab='InteractiveShellApp.pylab', |
|
96 | 96 | matplotlib='InteractiveShellApp.matplotlib', |
|
97 | 97 | ) |
|
98 | 98 | shell_aliases['cache-size'] = 'InteractiveShell.cache_size' |
|
99 | 99 | |
|
100 | 100 | #----------------------------------------------------------------------------- |
|
101 | 101 | # Main classes and functions |
|
102 | 102 | #----------------------------------------------------------------------------- |
|
103 | 103 | |
|
104 | 104 | class InteractiveShellApp(Configurable): |
|
105 | 105 | """A Mixin for applications that start InteractiveShell instances. |
|
106 | 106 | |
|
107 | 107 | Provides configurables for loading extensions and executing files |
|
108 | 108 | as part of configuring a Shell environment. |
|
109 | 109 | |
|
110 | 110 | The following methods should be called by the :meth:`initialize` method |
|
111 | 111 | of the subclass: |
|
112 | 112 | |
|
113 | 113 | - :meth:`init_path` |
|
114 | 114 | - :meth:`init_shell` (to be implemented by the subclass) |
|
115 | 115 | - :meth:`init_gui_pylab` |
|
116 | 116 | - :meth:`init_extensions` |
|
117 | 117 | - :meth:`init_code` |
|
118 | 118 | """ |
|
119 | 119 | extensions = List(Unicode(), |
|
120 | 120 | help="A list of dotted module names of IPython extensions to load." |
|
121 | 121 | ).tag(config=True) |
|
122 | 122 | |
|
123 | 123 | extra_extensions = List( |
|
124 | 124 | DottedObjectName(), |
|
125 | 125 | help=""" |
|
126 | 126 | Dotted module name(s) of one or more IPython extensions to load. |
|
127 | 127 | |
|
128 | 128 | For specifying extra extensions to load on the command-line. |
|
129 | 129 | |
|
130 | 130 | .. versionadded:: 7.10 |
|
131 | 131 | """, |
|
132 | 132 | ).tag(config=True) |
|
133 | 133 | |
|
134 | 134 | reraise_ipython_extension_failures = Bool(False, |
|
135 | 135 | help="Reraise exceptions encountered loading IPython extensions?", |
|
136 | 136 | ).tag(config=True) |
|
137 | 137 | |
|
138 | 138 | # Extensions that are always loaded (not configurable) |
|
139 | 139 | default_extensions = List(Unicode(), [u'storemagic']).tag(config=False) |
|
140 | 140 | |
|
141 | 141 | hide_initial_ns = Bool(True, |
|
142 | 142 | help="""Should variables loaded at startup (by startup files, exec_lines, etc.) |
|
143 | 143 | be hidden from tools like %who?""" |
|
144 | 144 | ).tag(config=True) |
|
145 | 145 | |
|
146 | 146 | exec_files = List(Unicode(), |
|
147 | 147 | help="""List of files to run at IPython startup.""" |
|
148 | 148 | ).tag(config=True) |
|
149 | 149 | exec_PYTHONSTARTUP = Bool(True, |
|
150 | 150 | help="""Run the file referenced by the PYTHONSTARTUP environment |
|
151 | 151 | variable at IPython startup.""" |
|
152 | 152 | ).tag(config=True) |
|
153 | 153 | file_to_run = Unicode('', |
|
154 | 154 | help="""A file to be run""").tag(config=True) |
|
155 | 155 | |
|
156 | 156 | exec_lines = List(Unicode(), |
|
157 | 157 | help="""lines of code to run at IPython startup.""" |
|
158 | 158 | ).tag(config=True) |
|
159 | 159 | code_to_run = Unicode('', |
|
160 | 160 | help="Execute the given command string." |
|
161 | 161 | ).tag(config=True) |
|
162 | 162 | module_to_run = Unicode('', |
|
163 | 163 | help="Run the module as a script." |
|
164 | 164 | ).tag(config=True) |
|
165 | 165 | gui = CaselessStrEnum(gui_keys, allow_none=True, |
|
166 | 166 | help="Enable GUI event loop integration with any of {0}.".format(gui_keys) |
|
167 | 167 | ).tag(config=True) |
|
168 | 168 | matplotlib = CaselessStrEnum(backend_keys, allow_none=True, |
|
169 | 169 | help="""Configure matplotlib for interactive use with |
|
170 | 170 | the default matplotlib backend.""" |
|
171 | 171 | ).tag(config=True) |
|
172 | 172 | pylab = CaselessStrEnum(backend_keys, allow_none=True, |
|
173 | 173 | help="""Pre-load matplotlib and numpy for interactive use, |
|
174 | 174 | selecting a particular matplotlib backend and loop integration. |
|
175 | 175 | """ |
|
176 | 176 | ).tag(config=True) |
|
177 | 177 | pylab_import_all = Bool(True, |
|
178 | 178 | help="""If true, IPython will populate the user namespace with numpy, pylab, etc. |
|
179 | 179 | and an ``import *`` is done from numpy and pylab, when using pylab mode. |
|
180 | 180 | |
|
181 | 181 | When False, pylab mode should not import any names into the user namespace. |
|
182 | 182 | """ |
|
183 | 183 | ).tag(config=True) |
|
184 | 184 | ignore_cwd = Bool( |
|
185 | 185 | False, |
|
186 | 186 | help="""If True, IPython will not add the current working directory to sys.path. |
|
187 | 187 | When False, the current working directory is added to sys.path, allowing imports |
|
188 | 188 | of modules defined in the current directory.""" |
|
189 | 189 | ).tag(config=True) |
|
190 | 190 | shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', |
|
191 | 191 | allow_none=True) |
|
192 | 192 | # whether interact-loop should start |
|
193 | 193 | interact = Bool(True) |
|
194 | 194 | |
|
195 | 195 | user_ns = Instance(dict, args=None, allow_none=True) |
|
196 | 196 | @observe('user_ns') |
|
197 | 197 | def _user_ns_changed(self, change): |
|
198 | 198 | if self.shell is not None: |
|
199 | 199 | self.shell.user_ns = change['new'] |
|
200 | 200 | self.shell.init_user_ns() |
|
201 | 201 | |
|
202 | 202 | def init_path(self): |
|
203 | 203 | """Add current working directory, '', to sys.path |
|
204 | 204 | |
|
205 | 205 | Unlike Python's default, we insert before the first `site-packages` |
|
206 | 206 | or `dist-packages` directory, |
|
207 | 207 | so that it is after the standard library. |
|
208 | 208 | |
|
209 | 209 | .. versionchanged:: 7.2 |
|
210 | 210 | Try to insert after the standard library, instead of first. |
|
211 | 211 | .. versionchanged:: 8.0 |
|
212 | 212 | Allow optionally not including the current directory in sys.path |
|
213 | 213 | """ |
|
214 | 214 | if '' in sys.path or self.ignore_cwd: |
|
215 | 215 | return |
|
216 | 216 | for idx, path in enumerate(sys.path): |
|
217 | 217 | parent, last_part = os.path.split(path) |
|
218 | 218 | if last_part in {'site-packages', 'dist-packages'}: |
|
219 | 219 | break |
|
220 | 220 | else: |
|
221 | 221 | # no site-packages or dist-packages found (?!) |
|
222 | 222 | # back to original behavior of inserting at the front |
|
223 | 223 | idx = 0 |
|
224 | 224 | sys.path.insert(idx, '') |
|
225 | 225 | |
|
226 | 226 | def init_shell(self): |
|
227 | 227 | raise NotImplementedError("Override in subclasses") |
|
228 | 228 | |
|
229 | 229 | def init_gui_pylab(self): |
|
230 | 230 | """Enable GUI event loop integration, taking pylab into account.""" |
|
231 | 231 | enable = False |
|
232 | 232 | shell = self.shell |
|
233 | 233 | if self.pylab: |
|
234 | 234 | enable = lambda key: shell.enable_pylab(key, import_all=self.pylab_import_all) |
|
235 | 235 | key = self.pylab |
|
236 | 236 | elif self.matplotlib: |
|
237 | 237 | enable = shell.enable_matplotlib |
|
238 | 238 | key = self.matplotlib |
|
239 | 239 | elif self.gui: |
|
240 | 240 | enable = shell.enable_gui |
|
241 | 241 | key = self.gui |
|
242 | 242 | |
|
243 | 243 | if not enable: |
|
244 | 244 | return |
|
245 | 245 | |
|
246 | 246 | try: |
|
247 | 247 | r = enable(key) |
|
248 | 248 | except ImportError: |
|
249 | 249 | self.log.warning("Eventloop or matplotlib integration failed. Is matplotlib installed?") |
|
250 | 250 | self.shell.showtraceback() |
|
251 | 251 | return |
|
252 | 252 | except Exception: |
|
253 | 253 | self.log.warning("GUI event loop or pylab initialization failed") |
|
254 | 254 | self.shell.showtraceback() |
|
255 | 255 | return |
|
256 | 256 | |
|
257 | 257 | if isinstance(r, tuple): |
|
258 | 258 | gui, backend = r[:2] |
|
259 | 259 | self.log.info("Enabling GUI event loop integration, " |
|
260 | 260 | "eventloop=%s, matplotlib=%s", gui, backend) |
|
261 | 261 | if key == "auto": |
|
262 | 262 | print("Using matplotlib backend: %s" % backend) |
|
263 | 263 | else: |
|
264 | 264 | gui = r |
|
265 | 265 | self.log.info("Enabling GUI event loop integration, " |
|
266 | 266 | "eventloop=%s", gui) |
|
267 | 267 | |
|
268 | 268 | def init_extensions(self): |
|
269 | 269 | """Load all IPython extensions in IPythonApp.extensions. |
|
270 | 270 | |
|
271 | 271 | This uses the :meth:`ExtensionManager.load_extensions` to load all |
|
272 | 272 | the extensions listed in ``self.extensions``. |
|
273 | 273 | """ |
|
274 | 274 | try: |
|
275 | 275 | self.log.debug("Loading IPython extensions...") |
|
276 | 276 | extensions = ( |
|
277 | 277 | self.default_extensions + self.extensions + self.extra_extensions |
|
278 | 278 | ) |
|
279 | 279 | for ext in extensions: |
|
280 | 280 | try: |
|
281 |
self.log.info("Loading IPython extension: %s" |
|
|
281 | self.log.info("Loading IPython extension: %s", ext) | |
|
282 | 282 | self.shell.extension_manager.load_extension(ext) |
|
283 | 283 | except: |
|
284 | 284 | if self.reraise_ipython_extension_failures: |
|
285 | 285 | raise |
|
286 | 286 | msg = ("Error in loading extension: {ext}\n" |
|
287 | 287 | "Check your config files in {location}".format( |
|
288 | 288 | ext=ext, |
|
289 | 289 | location=self.profile_dir.location |
|
290 | 290 | )) |
|
291 | 291 | self.log.warning(msg, exc_info=True) |
|
292 | 292 | except: |
|
293 | 293 | if self.reraise_ipython_extension_failures: |
|
294 | 294 | raise |
|
295 | 295 | self.log.warning("Unknown error in loading extensions:", exc_info=True) |
|
296 | 296 | |
|
297 | 297 | def init_code(self): |
|
298 | 298 | """run the pre-flight code, specified via exec_lines""" |
|
299 | 299 | self._run_startup_files() |
|
300 | 300 | self._run_exec_lines() |
|
301 | 301 | self._run_exec_files() |
|
302 | 302 | |
|
303 | 303 | # Hide variables defined here from %who etc. |
|
304 | 304 | if self.hide_initial_ns: |
|
305 | 305 | self.shell.user_ns_hidden.update(self.shell.user_ns) |
|
306 | 306 | |
|
307 | 307 | # command-line execution (ipython -i script.py, ipython -m module) |
|
308 | 308 | # should *not* be excluded from %whos |
|
309 | 309 | self._run_cmd_line_code() |
|
310 | 310 | self._run_module() |
|
311 | 311 | |
|
312 | 312 | # flush output, so itwon't be attached to the first cell |
|
313 | 313 | sys.stdout.flush() |
|
314 | 314 | sys.stderr.flush() |
|
315 | 315 | self.shell._sys_modules_keys = set(sys.modules.keys()) |
|
316 | 316 | |
|
317 | 317 | def _run_exec_lines(self): |
|
318 | 318 | """Run lines of code in IPythonApp.exec_lines in the user's namespace.""" |
|
319 | 319 | if not self.exec_lines: |
|
320 | 320 | return |
|
321 | 321 | try: |
|
322 | 322 | self.log.debug("Running code from IPythonApp.exec_lines...") |
|
323 | 323 | for line in self.exec_lines: |
|
324 | 324 | try: |
|
325 | 325 | self.log.info("Running code in user namespace: %s" % |
|
326 | 326 | line) |
|
327 | 327 | self.shell.run_cell(line, store_history=False) |
|
328 | 328 | except: |
|
329 | 329 | self.log.warning("Error in executing line in user " |
|
330 | 330 | "namespace: %s" % line) |
|
331 | 331 | self.shell.showtraceback() |
|
332 | 332 | except: |
|
333 | 333 | self.log.warning("Unknown error in handling IPythonApp.exec_lines:") |
|
334 | 334 | self.shell.showtraceback() |
|
335 | 335 | |
|
336 | 336 | def _exec_file(self, fname, shell_futures=False): |
|
337 | 337 | try: |
|
338 | 338 | full_filename = filefind(fname, [u'.', self.ipython_dir]) |
|
339 | 339 | except IOError: |
|
340 | 340 | self.log.warning("File not found: %r"%fname) |
|
341 | 341 | return |
|
342 | 342 | # Make sure that the running script gets a proper sys.argv as if it |
|
343 | 343 | # were run from a system shell. |
|
344 | 344 | save_argv = sys.argv |
|
345 | 345 | sys.argv = [full_filename] + self.extra_args[1:] |
|
346 | 346 | try: |
|
347 | 347 | if os.path.isfile(full_filename): |
|
348 | 348 | self.log.info("Running file in user namespace: %s" % |
|
349 | 349 | full_filename) |
|
350 | 350 | # Ensure that __file__ is always defined to match Python |
|
351 | 351 | # behavior. |
|
352 | 352 | with preserve_keys(self.shell.user_ns, '__file__'): |
|
353 | 353 | self.shell.user_ns['__file__'] = fname |
|
354 | 354 | if full_filename.endswith('.ipy') or full_filename.endswith('.ipynb'): |
|
355 | 355 | self.shell.safe_execfile_ipy(full_filename, |
|
356 | 356 | shell_futures=shell_futures) |
|
357 | 357 | else: |
|
358 | 358 | # default to python, even without extension |
|
359 | 359 | self.shell.safe_execfile(full_filename, |
|
360 | 360 | self.shell.user_ns, |
|
361 | 361 | shell_futures=shell_futures, |
|
362 | 362 | raise_exceptions=True) |
|
363 | 363 | finally: |
|
364 | 364 | sys.argv = save_argv |
|
365 | 365 | |
|
366 | 366 | def _run_startup_files(self): |
|
367 | 367 | """Run files from profile startup directory""" |
|
368 | 368 | startup_dirs = [self.profile_dir.startup_dir] + [ |
|
369 | 369 | os.path.join(p, 'startup') for p in chain(ENV_CONFIG_DIRS, SYSTEM_CONFIG_DIRS) |
|
370 | 370 | ] |
|
371 | 371 | startup_files = [] |
|
372 | 372 | |
|
373 | 373 | if self.exec_PYTHONSTARTUP and os.environ.get('PYTHONSTARTUP', False) and \ |
|
374 | 374 | not (self.file_to_run or self.code_to_run or self.module_to_run): |
|
375 | 375 | python_startup = os.environ['PYTHONSTARTUP'] |
|
376 | 376 | self.log.debug("Running PYTHONSTARTUP file %s...", python_startup) |
|
377 | 377 | try: |
|
378 | 378 | self._exec_file(python_startup) |
|
379 | 379 | except: |
|
380 | 380 | self.log.warning("Unknown error in handling PYTHONSTARTUP file %s:", python_startup) |
|
381 | 381 | self.shell.showtraceback() |
|
382 | 382 | for startup_dir in startup_dirs[::-1]: |
|
383 | 383 | startup_files += glob.glob(os.path.join(startup_dir, '*.py')) |
|
384 | 384 | startup_files += glob.glob(os.path.join(startup_dir, '*.ipy')) |
|
385 | 385 | if not startup_files: |
|
386 | 386 | return |
|
387 | 387 | |
|
388 | 388 | self.log.debug("Running startup files from %s...", startup_dir) |
|
389 | 389 | try: |
|
390 | 390 | for fname in sorted(startup_files): |
|
391 | 391 | self._exec_file(fname) |
|
392 | 392 | except: |
|
393 | 393 | self.log.warning("Unknown error in handling startup files:") |
|
394 | 394 | self.shell.showtraceback() |
|
395 | 395 | |
|
396 | 396 | def _run_exec_files(self): |
|
397 | 397 | """Run files from IPythonApp.exec_files""" |
|
398 | 398 | if not self.exec_files: |
|
399 | 399 | return |
|
400 | 400 | |
|
401 | 401 | self.log.debug("Running files in IPythonApp.exec_files...") |
|
402 | 402 | try: |
|
403 | 403 | for fname in self.exec_files: |
|
404 | 404 | self._exec_file(fname) |
|
405 | 405 | except: |
|
406 | 406 | self.log.warning("Unknown error in handling IPythonApp.exec_files:") |
|
407 | 407 | self.shell.showtraceback() |
|
408 | 408 | |
|
409 | 409 | def _run_cmd_line_code(self): |
|
410 | 410 | """Run code or file specified at the command-line""" |
|
411 | 411 | if self.code_to_run: |
|
412 | 412 | line = self.code_to_run |
|
413 | 413 | try: |
|
414 | 414 | self.log.info("Running code given at command line (c=): %s" % |
|
415 | 415 | line) |
|
416 | 416 | self.shell.run_cell(line, store_history=False) |
|
417 | 417 | except: |
|
418 | 418 | self.log.warning("Error in executing line in user namespace: %s" % |
|
419 | 419 | line) |
|
420 | 420 | self.shell.showtraceback() |
|
421 | 421 | if not self.interact: |
|
422 | 422 | self.exit(1) |
|
423 | 423 | |
|
424 | 424 | # Like Python itself, ignore the second if the first of these is present |
|
425 | 425 | elif self.file_to_run: |
|
426 | 426 | fname = self.file_to_run |
|
427 | 427 | if os.path.isdir(fname): |
|
428 | 428 | fname = os.path.join(fname, "__main__.py") |
|
429 | 429 | if not os.path.exists(fname): |
|
430 | 430 | self.log.warning("File '%s' doesn't exist", fname) |
|
431 | 431 | if not self.interact: |
|
432 | 432 | self.exit(2) |
|
433 | 433 | try: |
|
434 | 434 | self._exec_file(fname, shell_futures=True) |
|
435 | 435 | except: |
|
436 | 436 | self.shell.showtraceback(tb_offset=4) |
|
437 | 437 | if not self.interact: |
|
438 | 438 | self.exit(1) |
|
439 | 439 | |
|
440 | 440 | def _run_module(self): |
|
441 | 441 | """Run module specified at the command-line.""" |
|
442 | 442 | if self.module_to_run: |
|
443 | 443 | # Make sure that the module gets a proper sys.argv as if it were |
|
444 | 444 | # run using `python -m`. |
|
445 | 445 | save_argv = sys.argv |
|
446 | 446 | sys.argv = [sys.executable] + self.extra_args |
|
447 | 447 | try: |
|
448 | 448 | self.shell.safe_run_module(self.module_to_run, |
|
449 | 449 | self.shell.user_ns) |
|
450 | 450 | finally: |
|
451 | 451 | sys.argv = save_argv |
@@ -1,1454 +1,1513 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | """Tests for various magic functions.""" |
|
3 | 3 | |
|
4 | 4 | import gc |
|
5 | 5 | import io |
|
6 | 6 | import os |
|
7 | 7 | import re |
|
8 | 8 | import shlex |
|
9 | 9 | import sys |
|
10 | 10 | import warnings |
|
11 | 11 | from importlib import invalidate_caches |
|
12 | 12 | from io import StringIO |
|
13 | 13 | from pathlib import Path |
|
14 | 14 | from textwrap import dedent |
|
15 | 15 | from unittest import TestCase, mock |
|
16 | 16 | |
|
17 | 17 | import pytest |
|
18 | 18 | |
|
19 | 19 | from IPython import get_ipython |
|
20 | 20 | from IPython.core import magic |
|
21 | 21 | from IPython.core.error import UsageError |
|
22 | 22 | from IPython.core.magic import ( |
|
23 | 23 | Magics, |
|
24 | 24 | cell_magic, |
|
25 | 25 | line_magic, |
|
26 | 26 | magics_class, |
|
27 | 27 | register_cell_magic, |
|
28 | 28 | register_line_magic, |
|
29 | 29 | ) |
|
30 | 30 | from IPython.core.magics import code, execution, logging, osm, script |
|
31 | 31 | from IPython.testing import decorators as dec |
|
32 | 32 | from IPython.testing import tools as tt |
|
33 | 33 | from IPython.utils.io import capture_output |
|
34 | 34 | from IPython.utils.process import find_cmd |
|
35 | 35 | from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory |
|
36 | 36 | from IPython.utils.syspathcontext import prepended_to_syspath |
|
37 | 37 | |
|
38 | 38 | from .test_debugger import PdbTestInput |
|
39 | 39 | |
|
40 | 40 | from tempfile import NamedTemporaryFile |
|
41 | 41 | |
|
42 | 42 | @magic.magics_class |
|
43 | 43 | class DummyMagics(magic.Magics): pass |
|
44 | 44 | |
|
45 | 45 | def test_extract_code_ranges(): |
|
46 | 46 | instr = "1 3 5-6 7-9 10:15 17: :10 10- -13 :" |
|
47 | 47 | expected = [ |
|
48 | 48 | (0, 1), |
|
49 | 49 | (2, 3), |
|
50 | 50 | (4, 6), |
|
51 | 51 | (6, 9), |
|
52 | 52 | (9, 14), |
|
53 | 53 | (16, None), |
|
54 | 54 | (None, 9), |
|
55 | 55 | (9, None), |
|
56 | 56 | (None, 13), |
|
57 | 57 | (None, None), |
|
58 | 58 | ] |
|
59 | 59 | actual = list(code.extract_code_ranges(instr)) |
|
60 | 60 | assert actual == expected |
|
61 | 61 | |
|
62 | 62 | def test_extract_symbols(): |
|
63 | 63 | source = """import foo\na = 10\ndef b():\n return 42\n\n\nclass A: pass\n\n\n""" |
|
64 | 64 | symbols_args = ["a", "b", "A", "A,b", "A,a", "z"] |
|
65 | 65 | expected = [([], ['a']), |
|
66 | 66 | (["def b():\n return 42\n"], []), |
|
67 | 67 | (["class A: pass\n"], []), |
|
68 | 68 | (["class A: pass\n", "def b():\n return 42\n"], []), |
|
69 | 69 | (["class A: pass\n"], ['a']), |
|
70 | 70 | ([], ['z'])] |
|
71 | 71 | for symbols, exp in zip(symbols_args, expected): |
|
72 | 72 | assert code.extract_symbols(source, symbols) == exp |
|
73 | 73 | |
|
74 | 74 | |
|
75 | 75 | def test_extract_symbols_raises_exception_with_non_python_code(): |
|
76 | 76 | source = ("=begin A Ruby program :)=end\n" |
|
77 | 77 | "def hello\n" |
|
78 | 78 | "puts 'Hello world'\n" |
|
79 | 79 | "end") |
|
80 | 80 | with pytest.raises(SyntaxError): |
|
81 | 81 | code.extract_symbols(source, "hello") |
|
82 | 82 | |
|
83 | 83 | |
|
84 | 84 | def test_magic_not_found(): |
|
85 | 85 | # magic not found raises UsageError |
|
86 | 86 | with pytest.raises(UsageError): |
|
87 | 87 | _ip.run_line_magic("doesntexist", "") |
|
88 | 88 | |
|
89 | 89 | # ensure result isn't success when a magic isn't found |
|
90 | 90 | result = _ip.run_cell('%doesntexist') |
|
91 | 91 | assert isinstance(result.error_in_exec, UsageError) |
|
92 | 92 | |
|
93 | 93 | |
|
94 | 94 | def test_cell_magic_not_found(): |
|
95 | 95 | # magic not found raises UsageError |
|
96 | 96 | with pytest.raises(UsageError): |
|
97 | 97 | _ip.run_cell_magic('doesntexist', 'line', 'cell') |
|
98 | 98 | |
|
99 | 99 | # ensure result isn't success when a magic isn't found |
|
100 | 100 | result = _ip.run_cell('%%doesntexist') |
|
101 | 101 | assert isinstance(result.error_in_exec, UsageError) |
|
102 | 102 | |
|
103 | 103 | |
|
104 | 104 | def test_magic_error_status(): |
|
105 | 105 | def fail(shell): |
|
106 | 106 | 1/0 |
|
107 | 107 | _ip.register_magic_function(fail) |
|
108 | 108 | result = _ip.run_cell('%fail') |
|
109 | 109 | assert isinstance(result.error_in_exec, ZeroDivisionError) |
|
110 | 110 | |
|
111 | 111 | |
|
112 | 112 | def test_config(): |
|
113 | 113 | """ test that config magic does not raise |
|
114 | 114 | can happen if Configurable init is moved too early into |
|
115 | 115 | Magics.__init__ as then a Config object will be registered as a |
|
116 | 116 | magic. |
|
117 | 117 | """ |
|
118 | 118 | ## should not raise. |
|
119 | 119 | _ip.run_line_magic("config", "") |
|
120 | 120 | |
|
121 | 121 | |
|
122 | 122 | def test_config_available_configs(): |
|
123 | 123 | """ test that config magic prints available configs in unique and |
|
124 | 124 | sorted order. """ |
|
125 | 125 | with capture_output() as captured: |
|
126 | 126 | _ip.run_line_magic("config", "") |
|
127 | 127 | |
|
128 | 128 | stdout = captured.stdout |
|
129 | 129 | config_classes = stdout.strip().split('\n')[1:] |
|
130 | 130 | assert config_classes == sorted(set(config_classes)) |
|
131 | 131 | |
|
132 | 132 | def test_config_print_class(): |
|
133 | 133 | """ test that config with a classname prints the class's options. """ |
|
134 | 134 | with capture_output() as captured: |
|
135 | 135 | _ip.run_line_magic("config", "TerminalInteractiveShell") |
|
136 | 136 | |
|
137 | 137 | stdout = captured.stdout |
|
138 | 138 | assert re.match( |
|
139 | 139 | "TerminalInteractiveShell.* options", stdout.splitlines()[0] |
|
140 | 140 | ), f"{stdout}\n\n1st line of stdout not like 'TerminalInteractiveShell.* options'" |
|
141 | 141 | |
|
142 | 142 | |
|
143 | 143 | def test_rehashx(): |
|
144 | 144 | # clear up everything |
|
145 | 145 | _ip.alias_manager.clear_aliases() |
|
146 | 146 | del _ip.db['syscmdlist'] |
|
147 | 147 | |
|
148 | 148 | _ip.run_line_magic("rehashx", "") |
|
149 | 149 | # Practically ALL ipython development systems will have more than 10 aliases |
|
150 | 150 | |
|
151 | 151 | assert len(_ip.alias_manager.aliases) > 10 |
|
152 | 152 | for name, cmd in _ip.alias_manager.aliases: |
|
153 | 153 | # we must strip dots from alias names |
|
154 | 154 | assert "." not in name |
|
155 | 155 | |
|
156 | 156 | # rehashx must fill up syscmdlist |
|
157 | 157 | scoms = _ip.db['syscmdlist'] |
|
158 | 158 | assert len(scoms) > 10 |
|
159 | 159 | |
|
160 | 160 | |
|
161 | 161 | def test_magic_parse_options(): |
|
162 | 162 | """Test that we don't mangle paths when parsing magic options.""" |
|
163 | 163 | ip = get_ipython() |
|
164 | 164 | path = 'c:\\x' |
|
165 | 165 | m = DummyMagics(ip) |
|
166 | 166 | opts = m.parse_options('-f %s' % path,'f:')[0] |
|
167 | 167 | # argv splitting is os-dependent |
|
168 | 168 | if os.name == 'posix': |
|
169 | 169 | expected = 'c:x' |
|
170 | 170 | else: |
|
171 | 171 | expected = path |
|
172 | 172 | assert opts["f"] == expected |
|
173 | 173 | |
|
174 | 174 | |
|
175 | 175 | def test_magic_parse_long_options(): |
|
176 | 176 | """Magic.parse_options can handle --foo=bar long options""" |
|
177 | 177 | ip = get_ipython() |
|
178 | 178 | m = DummyMagics(ip) |
|
179 | 179 | opts, _ = m.parse_options("--foo --bar=bubble", "a", "foo", "bar=") |
|
180 | 180 | assert "foo" in opts |
|
181 | 181 | assert "bar" in opts |
|
182 | 182 | assert opts["bar"] == "bubble" |
|
183 | 183 | |
|
184 | 184 | |
|
185 | 185 | def doctest_hist_f(): |
|
186 | 186 | """Test %hist -f with temporary filename. |
|
187 | 187 | |
|
188 | 188 | In [9]: import tempfile |
|
189 | 189 | |
|
190 | 190 | In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-') |
|
191 | 191 | |
|
192 | 192 | In [11]: %hist -nl -f $tfile 3 |
|
193 | 193 | |
|
194 | 194 | In [13]: import os; os.unlink(tfile) |
|
195 | 195 | """ |
|
196 | 196 | |
|
197 | 197 | |
|
198 | 198 | def doctest_hist_op(): |
|
199 | 199 | """Test %hist -op |
|
200 | 200 | |
|
201 | 201 | In [1]: class b(float): |
|
202 | 202 | ...: pass |
|
203 | 203 | ...: |
|
204 | 204 | |
|
205 | 205 | In [2]: class s(object): |
|
206 | 206 | ...: def __str__(self): |
|
207 | 207 | ...: return 's' |
|
208 | 208 | ...: |
|
209 | 209 | |
|
210 | 210 | In [3]: |
|
211 | 211 | |
|
212 | 212 | In [4]: class r(b): |
|
213 | 213 | ...: def __repr__(self): |
|
214 | 214 | ...: return 'r' |
|
215 | 215 | ...: |
|
216 | 216 | |
|
217 | 217 | In [5]: class sr(s,r): pass |
|
218 | 218 | ...: |
|
219 | 219 | |
|
220 | 220 | In [6]: |
|
221 | 221 | |
|
222 | 222 | In [7]: bb=b() |
|
223 | 223 | |
|
224 | 224 | In [8]: ss=s() |
|
225 | 225 | |
|
226 | 226 | In [9]: rr=r() |
|
227 | 227 | |
|
228 | 228 | In [10]: ssrr=sr() |
|
229 | 229 | |
|
230 | 230 | In [11]: 4.5 |
|
231 | 231 | Out[11]: 4.5 |
|
232 | 232 | |
|
233 | 233 | In [12]: str(ss) |
|
234 | 234 | Out[12]: 's' |
|
235 | 235 | |
|
236 | 236 | In [13]: |
|
237 | 237 | |
|
238 | 238 | In [14]: %hist -op |
|
239 | 239 | >>> class b: |
|
240 | 240 | ... pass |
|
241 | 241 | ... |
|
242 | 242 | >>> class s(b): |
|
243 | 243 | ... def __str__(self): |
|
244 | 244 | ... return 's' |
|
245 | 245 | ... |
|
246 | 246 | >>> |
|
247 | 247 | >>> class r(b): |
|
248 | 248 | ... def __repr__(self): |
|
249 | 249 | ... return 'r' |
|
250 | 250 | ... |
|
251 | 251 | >>> class sr(s,r): pass |
|
252 | 252 | >>> |
|
253 | 253 | >>> bb=b() |
|
254 | 254 | >>> ss=s() |
|
255 | 255 | >>> rr=r() |
|
256 | 256 | >>> ssrr=sr() |
|
257 | 257 | >>> 4.5 |
|
258 | 258 | 4.5 |
|
259 | 259 | >>> str(ss) |
|
260 | 260 | 's' |
|
261 | 261 | >>> |
|
262 | 262 | """ |
|
263 | 263 | |
|
264 | 264 | def test_hist_pof(): |
|
265 | 265 | ip = get_ipython() |
|
266 | 266 | ip.run_cell("1+2", store_history=True) |
|
267 | 267 | #raise Exception(ip.history_manager.session_number) |
|
268 | 268 | #raise Exception(list(ip.history_manager._get_range_session())) |
|
269 | 269 | with TemporaryDirectory() as td: |
|
270 | 270 | tf = os.path.join(td, 'hist.py') |
|
271 | 271 | ip.run_line_magic('history', '-pof %s' % tf) |
|
272 | 272 | assert os.path.isfile(tf) |
|
273 | 273 | |
|
274 | 274 | |
|
275 | 275 | def test_macro(): |
|
276 | 276 | ip = get_ipython() |
|
277 | 277 | ip.history_manager.reset() # Clear any existing history. |
|
278 | 278 | cmds = ["a=1", "def b():\n return a**2", "print(a,b())"] |
|
279 | 279 | for i, cmd in enumerate(cmds, start=1): |
|
280 | 280 | ip.history_manager.store_inputs(i, cmd) |
|
281 | 281 | ip.run_line_magic("macro", "test 1-3") |
|
282 | 282 | assert ip.user_ns["test"].value == "\n".join(cmds) + "\n" |
|
283 | 283 | |
|
284 | 284 | # List macros |
|
285 | 285 | assert "test" in ip.run_line_magic("macro", "") |
|
286 | 286 | |
|
287 | 287 | |
|
288 | 288 | def test_macro_run(): |
|
289 | 289 | """Test that we can run a multi-line macro successfully.""" |
|
290 | 290 | ip = get_ipython() |
|
291 | 291 | ip.history_manager.reset() |
|
292 | 292 | cmds = ["a=10", "a+=1", "print(a)", "%macro test 2-3"] |
|
293 | 293 | for cmd in cmds: |
|
294 | 294 | ip.run_cell(cmd, store_history=True) |
|
295 | 295 | assert ip.user_ns["test"].value == "a+=1\nprint(a)\n" |
|
296 | 296 | with tt.AssertPrints("12"): |
|
297 | 297 | ip.run_cell("test") |
|
298 | 298 | with tt.AssertPrints("13"): |
|
299 | 299 | ip.run_cell("test") |
|
300 | 300 | |
|
301 | 301 | |
|
302 | 302 | def test_magic_magic(): |
|
303 | 303 | """Test %magic""" |
|
304 | 304 | ip = get_ipython() |
|
305 | 305 | with capture_output() as captured: |
|
306 | 306 | ip.run_line_magic("magic", "") |
|
307 | 307 | |
|
308 | 308 | stdout = captured.stdout |
|
309 | 309 | assert "%magic" in stdout |
|
310 | 310 | assert "IPython" in stdout |
|
311 | 311 | assert "Available" in stdout |
|
312 | 312 | |
|
313 | 313 | |
|
314 | 314 | @dec.skipif_not_numpy |
|
315 | 315 | def test_numpy_reset_array_undec(): |
|
316 | 316 | "Test '%reset array' functionality" |
|
317 | 317 | _ip.ex("import numpy as np") |
|
318 | 318 | _ip.ex("a = np.empty(2)") |
|
319 | 319 | assert "a" in _ip.user_ns |
|
320 | 320 | _ip.run_line_magic("reset", "-f array") |
|
321 | 321 | assert "a" not in _ip.user_ns |
|
322 | 322 | |
|
323 | 323 | |
|
324 | 324 | def test_reset_out(): |
|
325 | 325 | "Test '%reset out' magic" |
|
326 | 326 | _ip.run_cell("parrot = 'dead'", store_history=True) |
|
327 | 327 | # test '%reset -f out', make an Out prompt |
|
328 | 328 | _ip.run_cell("parrot", store_history=True) |
|
329 | 329 | assert "dead" in [_ip.user_ns[x] for x in ("_", "__", "___")] |
|
330 | 330 | _ip.run_line_magic("reset", "-f out") |
|
331 | 331 | assert "dead" not in [_ip.user_ns[x] for x in ("_", "__", "___")] |
|
332 | 332 | assert len(_ip.user_ns["Out"]) == 0 |
|
333 | 333 | |
|
334 | 334 | |
|
335 | 335 | def test_reset_in(): |
|
336 | 336 | "Test '%reset in' magic" |
|
337 | 337 | # test '%reset -f in' |
|
338 | 338 | _ip.run_cell("parrot", store_history=True) |
|
339 | 339 | assert "parrot" in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")] |
|
340 | 340 | _ip.run_line_magic("reset", "-f in") |
|
341 | 341 | assert "parrot" not in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")] |
|
342 | 342 | assert len(set(_ip.user_ns["In"])) == 1 |
|
343 | 343 | |
|
344 | 344 | |
|
345 | 345 | def test_reset_dhist(): |
|
346 | 346 | "Test '%reset dhist' magic" |
|
347 | 347 | _ip.run_cell("tmp = [d for d in _dh]") # copy before clearing |
|
348 | 348 | _ip.run_line_magic("cd", os.path.dirname(pytest.__file__)) |
|
349 | 349 | _ip.run_line_magic("cd", "-") |
|
350 | 350 | assert len(_ip.user_ns["_dh"]) > 0 |
|
351 | 351 | _ip.run_line_magic("reset", "-f dhist") |
|
352 | 352 | assert len(_ip.user_ns["_dh"]) == 0 |
|
353 | 353 | _ip.run_cell("_dh = [d for d in tmp]") # restore |
|
354 | 354 | |
|
355 | 355 | |
|
356 | 356 | def test_reset_in_length(): |
|
357 | 357 | "Test that '%reset in' preserves In[] length" |
|
358 | 358 | _ip.run_cell("print 'foo'") |
|
359 | 359 | _ip.run_cell("reset -f in") |
|
360 | 360 | assert len(_ip.user_ns["In"]) == _ip.displayhook.prompt_count + 1 |
|
361 | 361 | |
|
362 | 362 | |
|
363 | 363 | class TestResetErrors(TestCase): |
|
364 | 364 | |
|
365 | 365 | def test_reset_redefine(self): |
|
366 | 366 | |
|
367 | 367 | @magics_class |
|
368 | 368 | class KernelMagics(Magics): |
|
369 | 369 | @line_magic |
|
370 | 370 | def less(self, shell): pass |
|
371 | 371 | |
|
372 | 372 | _ip.register_magics(KernelMagics) |
|
373 | 373 | |
|
374 | 374 | with self.assertLogs() as cm: |
|
375 | 375 | # hack, we want to just capture logs, but assertLogs fails if not |
|
376 | 376 | # logs get produce. |
|
377 | 377 | # so log one things we ignore. |
|
378 | 378 | import logging as log_mod |
|
379 | 379 | log = log_mod.getLogger() |
|
380 | 380 | log.info('Nothing') |
|
381 | 381 | # end hack. |
|
382 | 382 | _ip.run_cell("reset -f") |
|
383 | 383 | |
|
384 | 384 | assert len(cm.output) == 1 |
|
385 | 385 | for out in cm.output: |
|
386 | 386 | assert "Invalid alias" not in out |
|
387 | 387 | |
|
388 | 388 | def test_tb_syntaxerror(): |
|
389 | 389 | """test %tb after a SyntaxError""" |
|
390 | 390 | ip = get_ipython() |
|
391 | 391 | ip.run_cell("for") |
|
392 | 392 | |
|
393 | 393 | # trap and validate stdout |
|
394 | 394 | save_stdout = sys.stdout |
|
395 | 395 | try: |
|
396 | 396 | sys.stdout = StringIO() |
|
397 | 397 | ip.run_cell("%tb") |
|
398 | 398 | out = sys.stdout.getvalue() |
|
399 | 399 | finally: |
|
400 | 400 | sys.stdout = save_stdout |
|
401 | 401 | # trim output, and only check the last line |
|
402 | 402 | last_line = out.rstrip().splitlines()[-1].strip() |
|
403 | 403 | assert last_line == "SyntaxError: invalid syntax" |
|
404 | 404 | |
|
405 | 405 | |
|
406 | 406 | def test_time(): |
|
407 | 407 | ip = get_ipython() |
|
408 | 408 | |
|
409 | 409 | with tt.AssertPrints("Wall time: "): |
|
410 | 410 | ip.run_cell("%time None") |
|
411 | 411 | |
|
412 | 412 | ip.run_cell("def f(kmjy):\n" |
|
413 | 413 | " %time print (2*kmjy)") |
|
414 | 414 | |
|
415 | 415 | with tt.AssertPrints("Wall time: "): |
|
416 | 416 | with tt.AssertPrints("hihi", suppress=False): |
|
417 | 417 | ip.run_cell("f('hi')") |
|
418 | 418 | |
|
419 | ||
|
420 | # ';' at the end of %time prevents instruction value to be printed. | |
|
421 | # This tests fix for #13837. | |
|
422 | def test_time_no_output_with_semicolon(): | |
|
423 | ip = get_ipython() | |
|
424 | ||
|
425 | # Test %time cases | |
|
426 | with tt.AssertPrints(" 123456"): | |
|
427 | with tt.AssertPrints("Wall time: ", suppress=False): | |
|
428 | with tt.AssertPrints("CPU times: ", suppress=False): | |
|
429 | ip.run_cell("%time 123000+456") | |
|
430 | ||
|
431 | with tt.AssertNotPrints(" 123456"): | |
|
432 | with tt.AssertPrints("Wall time: ", suppress=False): | |
|
433 | with tt.AssertPrints("CPU times: ", suppress=False): | |
|
434 | ip.run_cell("%time 123000+456;") | |
|
435 | ||
|
436 | with tt.AssertPrints(" 123456"): | |
|
437 | with tt.AssertPrints("Wall time: ", suppress=False): | |
|
438 | with tt.AssertPrints("CPU times: ", suppress=False): | |
|
439 | ip.run_cell("%time 123000+456 # Comment") | |
|
440 | ||
|
441 | with tt.AssertNotPrints(" 123456"): | |
|
442 | with tt.AssertPrints("Wall time: ", suppress=False): | |
|
443 | with tt.AssertPrints("CPU times: ", suppress=False): | |
|
444 | ip.run_cell("%time 123000+456; # Comment") | |
|
445 | ||
|
446 | with tt.AssertPrints(" 123456"): | |
|
447 | with tt.AssertPrints("Wall time: ", suppress=False): | |
|
448 | with tt.AssertPrints("CPU times: ", suppress=False): | |
|
449 | ip.run_cell("%time 123000+456 # ;Comment") | |
|
450 | ||
|
451 | # Test %%time cases | |
|
452 | with tt.AssertPrints("123456"): | |
|
453 | with tt.AssertPrints("Wall time: ", suppress=False): | |
|
454 | with tt.AssertPrints("CPU times: ", suppress=False): | |
|
455 | ip.run_cell("%%time\n123000+456\n\n\n") | |
|
456 | ||
|
457 | with tt.AssertNotPrints("123456"): | |
|
458 | with tt.AssertPrints("Wall time: ", suppress=False): | |
|
459 | with tt.AssertPrints("CPU times: ", suppress=False): | |
|
460 | ip.run_cell("%%time\n123000+456;\n\n\n") | |
|
461 | ||
|
462 | with tt.AssertPrints("123456"): | |
|
463 | with tt.AssertPrints("Wall time: ", suppress=False): | |
|
464 | with tt.AssertPrints("CPU times: ", suppress=False): | |
|
465 | ip.run_cell("%%time\n123000+456 # Comment\n\n\n") | |
|
466 | ||
|
467 | with tt.AssertNotPrints("123456"): | |
|
468 | with tt.AssertPrints("Wall time: ", suppress=False): | |
|
469 | with tt.AssertPrints("CPU times: ", suppress=False): | |
|
470 | ip.run_cell("%%time\n123000+456; # Comment\n\n\n") | |
|
471 | ||
|
472 | with tt.AssertPrints("123456"): | |
|
473 | with tt.AssertPrints("Wall time: ", suppress=False): | |
|
474 | with tt.AssertPrints("CPU times: ", suppress=False): | |
|
475 | ip.run_cell("%%time\n123000+456 # ;Comment\n\n\n") | |
|
476 | ||
|
477 | ||
|
419 | 478 | def test_time_last_not_expression(): |
|
420 | 479 | ip.run_cell("%%time\n" |
|
421 | 480 | "var_1 = 1\n" |
|
422 | 481 | "var_2 = 2\n") |
|
423 | 482 | assert ip.user_ns['var_1'] == 1 |
|
424 | 483 | del ip.user_ns['var_1'] |
|
425 | 484 | assert ip.user_ns['var_2'] == 2 |
|
426 | 485 | del ip.user_ns['var_2'] |
|
427 | 486 | |
|
428 | 487 | |
|
429 | 488 | @dec.skip_win32 |
|
430 | 489 | def test_time2(): |
|
431 | 490 | ip = get_ipython() |
|
432 | 491 | |
|
433 | 492 | with tt.AssertPrints("CPU times: user "): |
|
434 | 493 | ip.run_cell("%time None") |
|
435 | 494 | |
|
436 | 495 | def test_time3(): |
|
437 | 496 | """Erroneous magic function calls, issue gh-3334""" |
|
438 | 497 | ip = get_ipython() |
|
439 | 498 | ip.user_ns.pop('run', None) |
|
440 | 499 | |
|
441 | 500 | with tt.AssertNotPrints("not found", channel='stderr'): |
|
442 | 501 | ip.run_cell("%%time\n" |
|
443 | 502 | "run = 0\n" |
|
444 | 503 | "run += 1") |
|
445 | 504 | |
|
446 | 505 | def test_multiline_time(): |
|
447 | 506 | """Make sure last statement from time return a value.""" |
|
448 | 507 | ip = get_ipython() |
|
449 | 508 | ip.user_ns.pop('run', None) |
|
450 | 509 | |
|
451 | 510 | ip.run_cell( |
|
452 | 511 | dedent( |
|
453 | 512 | """\ |
|
454 | 513 | %%time |
|
455 | 514 | a = "ho" |
|
456 | 515 | b = "hey" |
|
457 | 516 | a+b |
|
458 | 517 | """ |
|
459 | 518 | ) |
|
460 | 519 | ) |
|
461 | 520 | assert ip.user_ns_hidden["_"] == "hohey" |
|
462 | 521 | |
|
463 | 522 | |
|
464 | 523 | def test_time_local_ns(): |
|
465 | 524 | """ |
|
466 | 525 | Test that local_ns is actually global_ns when running a cell magic |
|
467 | 526 | """ |
|
468 | 527 | ip = get_ipython() |
|
469 | 528 | ip.run_cell("%%time\n" "myvar = 1") |
|
470 | 529 | assert ip.user_ns["myvar"] == 1 |
|
471 | 530 | del ip.user_ns["myvar"] |
|
472 | 531 | |
|
473 | 532 | |
|
474 | 533 | def test_doctest_mode(): |
|
475 | 534 | "Toggle doctest_mode twice, it should be a no-op and run without error" |
|
476 | 535 | _ip.run_line_magic("doctest_mode", "") |
|
477 | 536 | _ip.run_line_magic("doctest_mode", "") |
|
478 | 537 | |
|
479 | 538 | |
|
480 | 539 | def test_parse_options(): |
|
481 | 540 | """Tests for basic options parsing in magics.""" |
|
482 | 541 | # These are only the most minimal of tests, more should be added later. At |
|
483 | 542 | # the very least we check that basic text/unicode calls work OK. |
|
484 | 543 | m = DummyMagics(_ip) |
|
485 | 544 | assert m.parse_options("foo", "")[1] == "foo" |
|
486 | 545 | assert m.parse_options("foo", "")[1] == "foo" |
|
487 | 546 | |
|
488 | 547 | |
|
489 | 548 | def test_parse_options_preserve_non_option_string(): |
|
490 | 549 | """Test to assert preservation of non-option part of magic-block, while parsing magic options.""" |
|
491 | 550 | m = DummyMagics(_ip) |
|
492 | 551 | opts, stmt = m.parse_options( |
|
493 | 552 | " -n1 -r 13 _ = 314 + foo", "n:r:", preserve_non_opts=True |
|
494 | 553 | ) |
|
495 | 554 | assert opts == {"n": "1", "r": "13"} |
|
496 | 555 | assert stmt == "_ = 314 + foo" |
|
497 | 556 | |
|
498 | 557 | |
|
499 | 558 | def test_run_magic_preserve_code_block(): |
|
500 | 559 | """Test to assert preservation of non-option part of magic-block, while running magic.""" |
|
501 | 560 | _ip.user_ns["spaces"] = [] |
|
502 | 561 | _ip.run_line_magic( |
|
503 | 562 | "timeit", "-n1 -r1 spaces.append([s.count(' ') for s in ['document']])" |
|
504 | 563 | ) |
|
505 | 564 | assert _ip.user_ns["spaces"] == [[0]] |
|
506 | 565 | |
|
507 | 566 | |
|
508 | 567 | def test_dirops(): |
|
509 | 568 | """Test various directory handling operations.""" |
|
510 | 569 | # curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/') |
|
511 | 570 | curpath = os.getcwd |
|
512 | 571 | startdir = os.getcwd() |
|
513 | 572 | ipdir = os.path.realpath(_ip.ipython_dir) |
|
514 | 573 | try: |
|
515 | 574 | _ip.run_line_magic("cd", '"%s"' % ipdir) |
|
516 | 575 | assert curpath() == ipdir |
|
517 | 576 | _ip.run_line_magic("cd", "-") |
|
518 | 577 | assert curpath() == startdir |
|
519 | 578 | _ip.run_line_magic("pushd", '"%s"' % ipdir) |
|
520 | 579 | assert curpath() == ipdir |
|
521 | 580 | _ip.run_line_magic("popd", "") |
|
522 | 581 | assert curpath() == startdir |
|
523 | 582 | finally: |
|
524 | 583 | os.chdir(startdir) |
|
525 | 584 | |
|
526 | 585 | |
|
527 | 586 | def test_cd_force_quiet(): |
|
528 | 587 | """Test OSMagics.cd_force_quiet option""" |
|
529 | 588 | _ip.config.OSMagics.cd_force_quiet = True |
|
530 | 589 | osmagics = osm.OSMagics(shell=_ip) |
|
531 | 590 | |
|
532 | 591 | startdir = os.getcwd() |
|
533 | 592 | ipdir = os.path.realpath(_ip.ipython_dir) |
|
534 | 593 | |
|
535 | 594 | try: |
|
536 | 595 | with tt.AssertNotPrints(ipdir): |
|
537 | 596 | osmagics.cd('"%s"' % ipdir) |
|
538 | 597 | with tt.AssertNotPrints(startdir): |
|
539 | 598 | osmagics.cd('-') |
|
540 | 599 | finally: |
|
541 | 600 | os.chdir(startdir) |
|
542 | 601 | |
|
543 | 602 | |
|
544 | 603 | def test_xmode(): |
|
545 | 604 | # Calling xmode three times should be a no-op |
|
546 | 605 | xmode = _ip.InteractiveTB.mode |
|
547 | 606 | for i in range(4): |
|
548 | 607 | _ip.run_line_magic("xmode", "") |
|
549 | 608 | assert _ip.InteractiveTB.mode == xmode |
|
550 | 609 | |
|
551 | 610 | def test_reset_hard(): |
|
552 | 611 | monitor = [] |
|
553 | 612 | class A(object): |
|
554 | 613 | def __del__(self): |
|
555 | 614 | monitor.append(1) |
|
556 | 615 | def __repr__(self): |
|
557 | 616 | return "<A instance>" |
|
558 | 617 | |
|
559 | 618 | _ip.user_ns["a"] = A() |
|
560 | 619 | _ip.run_cell("a") |
|
561 | 620 | |
|
562 | 621 | assert monitor == [] |
|
563 | 622 | _ip.run_line_magic("reset", "-f") |
|
564 | 623 | assert monitor == [1] |
|
565 | 624 | |
|
566 | 625 | class TestXdel(tt.TempFileMixin): |
|
567 | 626 | def test_xdel(self): |
|
568 | 627 | """Test that references from %run are cleared by xdel.""" |
|
569 | 628 | src = ("class A(object):\n" |
|
570 | 629 | " monitor = []\n" |
|
571 | 630 | " def __del__(self):\n" |
|
572 | 631 | " self.monitor.append(1)\n" |
|
573 | 632 | "a = A()\n") |
|
574 | 633 | self.mktmp(src) |
|
575 | 634 | # %run creates some hidden references... |
|
576 | 635 | _ip.run_line_magic("run", "%s" % self.fname) |
|
577 | 636 | # ... as does the displayhook. |
|
578 | 637 | _ip.run_cell("a") |
|
579 | 638 | |
|
580 | 639 | monitor = _ip.user_ns["A"].monitor |
|
581 | 640 | assert monitor == [] |
|
582 | 641 | |
|
583 | 642 | _ip.run_line_magic("xdel", "a") |
|
584 | 643 | |
|
585 | 644 | # Check that a's __del__ method has been called. |
|
586 | 645 | gc.collect(0) |
|
587 | 646 | assert monitor == [1] |
|
588 | 647 | |
|
589 | 648 | def doctest_who(): |
|
590 | 649 | """doctest for %who |
|
591 | 650 | |
|
592 | 651 | In [1]: %reset -sf |
|
593 | 652 | |
|
594 | 653 | In [2]: alpha = 123 |
|
595 | 654 | |
|
596 | 655 | In [3]: beta = 'beta' |
|
597 | 656 | |
|
598 | 657 | In [4]: %who int |
|
599 | 658 | alpha |
|
600 | 659 | |
|
601 | 660 | In [5]: %who str |
|
602 | 661 | beta |
|
603 | 662 | |
|
604 | 663 | In [6]: %whos |
|
605 | 664 | Variable Type Data/Info |
|
606 | 665 | ---------------------------- |
|
607 | 666 | alpha int 123 |
|
608 | 667 | beta str beta |
|
609 | 668 | |
|
610 | 669 | In [7]: %who_ls |
|
611 | 670 | Out[7]: ['alpha', 'beta'] |
|
612 | 671 | """ |
|
613 | 672 | |
|
614 | 673 | def test_whos(): |
|
615 | 674 | """Check that whos is protected against objects where repr() fails.""" |
|
616 | 675 | class A(object): |
|
617 | 676 | def __repr__(self): |
|
618 | 677 | raise Exception() |
|
619 | 678 | _ip.user_ns['a'] = A() |
|
620 | 679 | _ip.run_line_magic("whos", "") |
|
621 | 680 | |
|
622 | 681 | def doctest_precision(): |
|
623 | 682 | """doctest for %precision |
|
624 | 683 | |
|
625 | 684 | In [1]: f = get_ipython().display_formatter.formatters['text/plain'] |
|
626 | 685 | |
|
627 | 686 | In [2]: %precision 5 |
|
628 | 687 | Out[2]: '%.5f' |
|
629 | 688 | |
|
630 | 689 | In [3]: f.float_format |
|
631 | 690 | Out[3]: '%.5f' |
|
632 | 691 | |
|
633 | 692 | In [4]: %precision %e |
|
634 | 693 | Out[4]: '%e' |
|
635 | 694 | |
|
636 | 695 | In [5]: f(3.1415927) |
|
637 | 696 | Out[5]: '3.141593e+00' |
|
638 | 697 | """ |
|
639 | 698 | |
|
640 | 699 | def test_debug_magic(): |
|
641 | 700 | """Test debugging a small code with %debug |
|
642 | 701 | |
|
643 | 702 | In [1]: with PdbTestInput(['c']): |
|
644 | 703 | ...: %debug print("a b") #doctest: +ELLIPSIS |
|
645 | 704 | ...: |
|
646 | 705 | ... |
|
647 | 706 | ipdb> c |
|
648 | 707 | a b |
|
649 | 708 | In [2]: |
|
650 | 709 | """ |
|
651 | 710 | |
|
652 | 711 | def test_psearch(): |
|
653 | 712 | with tt.AssertPrints("dict.fromkeys"): |
|
654 | 713 | _ip.run_cell("dict.fr*?") |
|
655 | 714 | with tt.AssertPrints("π.is_integer"): |
|
656 | 715 | _ip.run_cell("π = 3.14;\nπ.is_integ*?") |
|
657 | 716 | |
|
658 | 717 | def test_timeit_shlex(): |
|
659 | 718 | """test shlex issues with timeit (#1109)""" |
|
660 | 719 | _ip.ex("def f(*a,**kw): pass") |
|
661 | 720 | _ip.run_line_magic("timeit", '-n1 "this is a bug".count(" ")') |
|
662 | 721 | _ip.run_line_magic("timeit", '-r1 -n1 f(" ", 1)') |
|
663 | 722 | _ip.run_line_magic("timeit", '-r1 -n1 f(" ", 1, " ", 2, " ")') |
|
664 | 723 | _ip.run_line_magic("timeit", '-r1 -n1 ("a " + "b")') |
|
665 | 724 | _ip.run_line_magic("timeit", '-r1 -n1 f("a " + "b")') |
|
666 | 725 | _ip.run_line_magic("timeit", '-r1 -n1 f("a " + "b ")') |
|
667 | 726 | |
|
668 | 727 | |
|
669 | 728 | def test_timeit_special_syntax(): |
|
670 | 729 | "Test %%timeit with IPython special syntax" |
|
671 | 730 | @register_line_magic |
|
672 | 731 | def lmagic(line): |
|
673 | 732 | ip = get_ipython() |
|
674 | 733 | ip.user_ns['lmagic_out'] = line |
|
675 | 734 | |
|
676 | 735 | # line mode test |
|
677 | 736 | _ip.run_line_magic("timeit", "-n1 -r1 %lmagic my line") |
|
678 | 737 | assert _ip.user_ns["lmagic_out"] == "my line" |
|
679 | 738 | # cell mode test |
|
680 | 739 | _ip.run_cell_magic("timeit", "-n1 -r1", "%lmagic my line2") |
|
681 | 740 | assert _ip.user_ns["lmagic_out"] == "my line2" |
|
682 | 741 | |
|
683 | 742 | |
|
684 | 743 | def test_timeit_return(): |
|
685 | 744 | """ |
|
686 | 745 | test whether timeit -o return object |
|
687 | 746 | """ |
|
688 | 747 | |
|
689 | 748 | res = _ip.run_line_magic('timeit','-n10 -r10 -o 1') |
|
690 | 749 | assert(res is not None) |
|
691 | 750 | |
|
692 | 751 | def test_timeit_quiet(): |
|
693 | 752 | """ |
|
694 | 753 | test quiet option of timeit magic |
|
695 | 754 | """ |
|
696 | 755 | with tt.AssertNotPrints("loops"): |
|
697 | 756 | _ip.run_cell("%timeit -n1 -r1 -q 1") |
|
698 | 757 | |
|
699 | 758 | def test_timeit_return_quiet(): |
|
700 | 759 | with tt.AssertNotPrints("loops"): |
|
701 | 760 | res = _ip.run_line_magic('timeit', '-n1 -r1 -q -o 1') |
|
702 | 761 | assert (res is not None) |
|
703 | 762 | |
|
704 | 763 | def test_timeit_invalid_return(): |
|
705 | 764 | with pytest.raises(SyntaxError): |
|
706 | 765 | _ip.run_line_magic('timeit', 'return') |
|
707 | 766 | |
|
708 | 767 | @dec.skipif(execution.profile is None) |
|
709 | 768 | def test_prun_special_syntax(): |
|
710 | 769 | "Test %%prun with IPython special syntax" |
|
711 | 770 | @register_line_magic |
|
712 | 771 | def lmagic(line): |
|
713 | 772 | ip = get_ipython() |
|
714 | 773 | ip.user_ns['lmagic_out'] = line |
|
715 | 774 | |
|
716 | 775 | # line mode test |
|
717 | 776 | _ip.run_line_magic("prun", "-q %lmagic my line") |
|
718 | 777 | assert _ip.user_ns["lmagic_out"] == "my line" |
|
719 | 778 | # cell mode test |
|
720 | 779 | _ip.run_cell_magic("prun", "-q", "%lmagic my line2") |
|
721 | 780 | assert _ip.user_ns["lmagic_out"] == "my line2" |
|
722 | 781 | |
|
723 | 782 | |
|
724 | 783 | @dec.skipif(execution.profile is None) |
|
725 | 784 | def test_prun_quotes(): |
|
726 | 785 | "Test that prun does not clobber string escapes (GH #1302)" |
|
727 | 786 | _ip.magic(r"prun -q x = '\t'") |
|
728 | 787 | assert _ip.user_ns["x"] == "\t" |
|
729 | 788 | |
|
730 | 789 | |
|
731 | 790 | def test_extension(): |
|
732 | 791 | # Debugging information for failures of this test |
|
733 | 792 | print('sys.path:') |
|
734 | 793 | for p in sys.path: |
|
735 | 794 | print(' ', p) |
|
736 | 795 | print('CWD', os.getcwd()) |
|
737 | 796 | |
|
738 | 797 | pytest.raises(ImportError, _ip.magic, "load_ext daft_extension") |
|
739 | 798 | daft_path = os.path.join(os.path.dirname(__file__), "daft_extension") |
|
740 | 799 | sys.path.insert(0, daft_path) |
|
741 | 800 | try: |
|
742 | 801 | _ip.user_ns.pop('arq', None) |
|
743 | 802 | invalidate_caches() # Clear import caches |
|
744 | 803 | _ip.run_line_magic("load_ext", "daft_extension") |
|
745 | 804 | assert _ip.user_ns["arq"] == 185 |
|
746 | 805 | _ip.run_line_magic("unload_ext", "daft_extension") |
|
747 | 806 | assert 'arq' not in _ip.user_ns |
|
748 | 807 | finally: |
|
749 | 808 | sys.path.remove(daft_path) |
|
750 | 809 | |
|
751 | 810 | |
|
752 | 811 | def test_notebook_export_json(): |
|
753 | 812 | pytest.importorskip("nbformat") |
|
754 | 813 | _ip = get_ipython() |
|
755 | 814 | _ip.history_manager.reset() # Clear any existing history. |
|
756 | 815 | cmds = ["a=1", "def b():\n return a**2", "print('noël, été', b())"] |
|
757 | 816 | for i, cmd in enumerate(cmds, start=1): |
|
758 | 817 | _ip.history_manager.store_inputs(i, cmd) |
|
759 | 818 | with TemporaryDirectory() as td: |
|
760 | 819 | outfile = os.path.join(td, "nb.ipynb") |
|
761 | 820 | _ip.run_line_magic("notebook", "%s" % outfile) |
|
762 | 821 | |
|
763 | 822 | |
|
764 | 823 | class TestEnv(TestCase): |
|
765 | 824 | |
|
766 | 825 | def test_env(self): |
|
767 | 826 | env = _ip.run_line_magic("env", "") |
|
768 | 827 | self.assertTrue(isinstance(env, dict)) |
|
769 | 828 | |
|
770 | 829 | def test_env_secret(self): |
|
771 | 830 | env = _ip.run_line_magic("env", "") |
|
772 | 831 | hidden = "<hidden>" |
|
773 | 832 | with mock.patch.dict( |
|
774 | 833 | os.environ, |
|
775 | 834 | { |
|
776 | 835 | "API_KEY": "abc123", |
|
777 | 836 | "SECRET_THING": "ssshhh", |
|
778 | 837 | "JUPYTER_TOKEN": "", |
|
779 | 838 | "VAR": "abc" |
|
780 | 839 | } |
|
781 | 840 | ): |
|
782 | 841 | env = _ip.run_line_magic("env", "") |
|
783 | 842 | assert env["API_KEY"] == hidden |
|
784 | 843 | assert env["SECRET_THING"] == hidden |
|
785 | 844 | assert env["JUPYTER_TOKEN"] == hidden |
|
786 | 845 | assert env["VAR"] == "abc" |
|
787 | 846 | |
|
788 | 847 | def test_env_get_set_simple(self): |
|
789 | 848 | env = _ip.run_line_magic("env", "var val1") |
|
790 | 849 | self.assertEqual(env, None) |
|
791 | 850 | self.assertEqual(os.environ["var"], "val1") |
|
792 | 851 | self.assertEqual(_ip.run_line_magic("env", "var"), "val1") |
|
793 | 852 | env = _ip.run_line_magic("env", "var=val2") |
|
794 | 853 | self.assertEqual(env, None) |
|
795 | 854 | self.assertEqual(os.environ['var'], 'val2') |
|
796 | 855 | |
|
797 | 856 | def test_env_get_set_complex(self): |
|
798 | 857 | env = _ip.run_line_magic("env", "var 'val1 '' 'val2") |
|
799 | 858 | self.assertEqual(env, None) |
|
800 | 859 | self.assertEqual(os.environ['var'], "'val1 '' 'val2") |
|
801 | 860 | self.assertEqual(_ip.run_line_magic("env", "var"), "'val1 '' 'val2") |
|
802 | 861 | env = _ip.run_line_magic("env", 'var=val2 val3="val4') |
|
803 | 862 | self.assertEqual(env, None) |
|
804 | 863 | self.assertEqual(os.environ['var'], 'val2 val3="val4') |
|
805 | 864 | |
|
806 | 865 | def test_env_set_bad_input(self): |
|
807 | 866 | self.assertRaises(UsageError, lambda: _ip.run_line_magic("set_env", "var")) |
|
808 | 867 | |
|
809 | 868 | def test_env_set_whitespace(self): |
|
810 | 869 | self.assertRaises(UsageError, lambda: _ip.run_line_magic("env", "var A=B")) |
|
811 | 870 | |
|
812 | 871 | |
|
813 | 872 | class CellMagicTestCase(TestCase): |
|
814 | 873 | |
|
815 | 874 | def check_ident(self, magic): |
|
816 | 875 | # Manually called, we get the result |
|
817 | 876 | out = _ip.run_cell_magic(magic, "a", "b") |
|
818 | 877 | assert out == ("a", "b") |
|
819 | 878 | # Via run_cell, it goes into the user's namespace via displayhook |
|
820 | 879 | _ip.run_cell("%%" + magic + " c\nd\n") |
|
821 | 880 | assert _ip.user_ns["_"] == ("c", "d\n") |
|
822 | 881 | |
|
823 | 882 | def test_cell_magic_func_deco(self): |
|
824 | 883 | "Cell magic using simple decorator" |
|
825 | 884 | @register_cell_magic |
|
826 | 885 | def cellm(line, cell): |
|
827 | 886 | return line, cell |
|
828 | 887 | |
|
829 | 888 | self.check_ident('cellm') |
|
830 | 889 | |
|
831 | 890 | def test_cell_magic_reg(self): |
|
832 | 891 | "Cell magic manually registered" |
|
833 | 892 | def cellm(line, cell): |
|
834 | 893 | return line, cell |
|
835 | 894 | |
|
836 | 895 | _ip.register_magic_function(cellm, 'cell', 'cellm2') |
|
837 | 896 | self.check_ident('cellm2') |
|
838 | 897 | |
|
839 | 898 | def test_cell_magic_class(self): |
|
840 | 899 | "Cell magics declared via a class" |
|
841 | 900 | @magics_class |
|
842 | 901 | class MyMagics(Magics): |
|
843 | 902 | |
|
844 | 903 | @cell_magic |
|
845 | 904 | def cellm3(self, line, cell): |
|
846 | 905 | return line, cell |
|
847 | 906 | |
|
848 | 907 | _ip.register_magics(MyMagics) |
|
849 | 908 | self.check_ident('cellm3') |
|
850 | 909 | |
|
851 | 910 | def test_cell_magic_class2(self): |
|
852 | 911 | "Cell magics declared via a class, #2" |
|
853 | 912 | @magics_class |
|
854 | 913 | class MyMagics2(Magics): |
|
855 | 914 | |
|
856 | 915 | @cell_magic('cellm4') |
|
857 | 916 | def cellm33(self, line, cell): |
|
858 | 917 | return line, cell |
|
859 | 918 | |
|
860 | 919 | _ip.register_magics(MyMagics2) |
|
861 | 920 | self.check_ident('cellm4') |
|
862 | 921 | # Check that nothing is registered as 'cellm33' |
|
863 | 922 | c33 = _ip.find_cell_magic('cellm33') |
|
864 | 923 | assert c33 == None |
|
865 | 924 | |
|
866 | 925 | def test_file(): |
|
867 | 926 | """Basic %%writefile""" |
|
868 | 927 | ip = get_ipython() |
|
869 | 928 | with TemporaryDirectory() as td: |
|
870 | 929 | fname = os.path.join(td, "file1") |
|
871 | 930 | ip.run_cell_magic( |
|
872 | 931 | "writefile", |
|
873 | 932 | fname, |
|
874 | 933 | "\n".join( |
|
875 | 934 | [ |
|
876 | 935 | "line1", |
|
877 | 936 | "line2", |
|
878 | 937 | ] |
|
879 | 938 | ), |
|
880 | 939 | ) |
|
881 | 940 | s = Path(fname).read_text(encoding="utf-8") |
|
882 | 941 | assert "line1\n" in s |
|
883 | 942 | assert "line2" in s |
|
884 | 943 | |
|
885 | 944 | |
|
886 | 945 | @dec.skip_win32 |
|
887 | 946 | def test_file_single_quote(): |
|
888 | 947 | """Basic %%writefile with embedded single quotes""" |
|
889 | 948 | ip = get_ipython() |
|
890 | 949 | with TemporaryDirectory() as td: |
|
891 | 950 | fname = os.path.join(td, "'file1'") |
|
892 | 951 | ip.run_cell_magic( |
|
893 | 952 | "writefile", |
|
894 | 953 | fname, |
|
895 | 954 | "\n".join( |
|
896 | 955 | [ |
|
897 | 956 | "line1", |
|
898 | 957 | "line2", |
|
899 | 958 | ] |
|
900 | 959 | ), |
|
901 | 960 | ) |
|
902 | 961 | s = Path(fname).read_text(encoding="utf-8") |
|
903 | 962 | assert "line1\n" in s |
|
904 | 963 | assert "line2" in s |
|
905 | 964 | |
|
906 | 965 | |
|
907 | 966 | @dec.skip_win32 |
|
908 | 967 | def test_file_double_quote(): |
|
909 | 968 | """Basic %%writefile with embedded double quotes""" |
|
910 | 969 | ip = get_ipython() |
|
911 | 970 | with TemporaryDirectory() as td: |
|
912 | 971 | fname = os.path.join(td, '"file1"') |
|
913 | 972 | ip.run_cell_magic( |
|
914 | 973 | "writefile", |
|
915 | 974 | fname, |
|
916 | 975 | "\n".join( |
|
917 | 976 | [ |
|
918 | 977 | "line1", |
|
919 | 978 | "line2", |
|
920 | 979 | ] |
|
921 | 980 | ), |
|
922 | 981 | ) |
|
923 | 982 | s = Path(fname).read_text(encoding="utf-8") |
|
924 | 983 | assert "line1\n" in s |
|
925 | 984 | assert "line2" in s |
|
926 | 985 | |
|
927 | 986 | |
|
928 | 987 | def test_file_var_expand(): |
|
929 | 988 | """%%writefile $filename""" |
|
930 | 989 | ip = get_ipython() |
|
931 | 990 | with TemporaryDirectory() as td: |
|
932 | 991 | fname = os.path.join(td, "file1") |
|
933 | 992 | ip.user_ns["filename"] = fname |
|
934 | 993 | ip.run_cell_magic( |
|
935 | 994 | "writefile", |
|
936 | 995 | "$filename", |
|
937 | 996 | "\n".join( |
|
938 | 997 | [ |
|
939 | 998 | "line1", |
|
940 | 999 | "line2", |
|
941 | 1000 | ] |
|
942 | 1001 | ), |
|
943 | 1002 | ) |
|
944 | 1003 | s = Path(fname).read_text(encoding="utf-8") |
|
945 | 1004 | assert "line1\n" in s |
|
946 | 1005 | assert "line2" in s |
|
947 | 1006 | |
|
948 | 1007 | |
|
949 | 1008 | def test_file_unicode(): |
|
950 | 1009 | """%%writefile with unicode cell""" |
|
951 | 1010 | ip = get_ipython() |
|
952 | 1011 | with TemporaryDirectory() as td: |
|
953 | 1012 | fname = os.path.join(td, 'file1') |
|
954 | 1013 | ip.run_cell_magic("writefile", fname, u'\n'.join([ |
|
955 | 1014 | u'liné1', |
|
956 | 1015 | u'liné2', |
|
957 | 1016 | ])) |
|
958 | 1017 | with io.open(fname, encoding='utf-8') as f: |
|
959 | 1018 | s = f.read() |
|
960 | 1019 | assert "liné1\n" in s |
|
961 | 1020 | assert "liné2" in s |
|
962 | 1021 | |
|
963 | 1022 | |
|
964 | 1023 | def test_file_amend(): |
|
965 | 1024 | """%%writefile -a amends files""" |
|
966 | 1025 | ip = get_ipython() |
|
967 | 1026 | with TemporaryDirectory() as td: |
|
968 | 1027 | fname = os.path.join(td, "file2") |
|
969 | 1028 | ip.run_cell_magic( |
|
970 | 1029 | "writefile", |
|
971 | 1030 | fname, |
|
972 | 1031 | "\n".join( |
|
973 | 1032 | [ |
|
974 | 1033 | "line1", |
|
975 | 1034 | "line2", |
|
976 | 1035 | ] |
|
977 | 1036 | ), |
|
978 | 1037 | ) |
|
979 | 1038 | ip.run_cell_magic( |
|
980 | 1039 | "writefile", |
|
981 | 1040 | "-a %s" % fname, |
|
982 | 1041 | "\n".join( |
|
983 | 1042 | [ |
|
984 | 1043 | "line3", |
|
985 | 1044 | "line4", |
|
986 | 1045 | ] |
|
987 | 1046 | ), |
|
988 | 1047 | ) |
|
989 | 1048 | s = Path(fname).read_text(encoding="utf-8") |
|
990 | 1049 | assert "line1\n" in s |
|
991 | 1050 | assert "line3\n" in s |
|
992 | 1051 | |
|
993 | 1052 | |
|
994 | 1053 | def test_file_spaces(): |
|
995 | 1054 | """%%file with spaces in filename""" |
|
996 | 1055 | ip = get_ipython() |
|
997 | 1056 | with TemporaryWorkingDirectory() as td: |
|
998 | 1057 | fname = "file name" |
|
999 | 1058 | ip.run_cell_magic( |
|
1000 | 1059 | "file", |
|
1001 | 1060 | '"%s"' % fname, |
|
1002 | 1061 | "\n".join( |
|
1003 | 1062 | [ |
|
1004 | 1063 | "line1", |
|
1005 | 1064 | "line2", |
|
1006 | 1065 | ] |
|
1007 | 1066 | ), |
|
1008 | 1067 | ) |
|
1009 | 1068 | s = Path(fname).read_text(encoding="utf-8") |
|
1010 | 1069 | assert "line1\n" in s |
|
1011 | 1070 | assert "line2" in s |
|
1012 | 1071 | |
|
1013 | 1072 | |
|
1014 | 1073 | def test_script_config(): |
|
1015 | 1074 | ip = get_ipython() |
|
1016 | 1075 | ip.config.ScriptMagics.script_magics = ['whoda'] |
|
1017 | 1076 | sm = script.ScriptMagics(shell=ip) |
|
1018 | 1077 | assert "whoda" in sm.magics["cell"] |
|
1019 | 1078 | |
|
1020 | 1079 | |
|
1021 | 1080 | def test_script_out(): |
|
1022 | 1081 | ip = get_ipython() |
|
1023 | 1082 | ip.run_cell_magic("script", f"--out output {sys.executable}", "print('hi')") |
|
1024 | 1083 | assert ip.user_ns["output"].strip() == "hi" |
|
1025 | 1084 | |
|
1026 | 1085 | |
|
1027 | 1086 | def test_script_err(): |
|
1028 | 1087 | ip = get_ipython() |
|
1029 | 1088 | ip.run_cell_magic( |
|
1030 | 1089 | "script", |
|
1031 | 1090 | f"--err error {sys.executable}", |
|
1032 | 1091 | "import sys; print('hello', file=sys.stderr)", |
|
1033 | 1092 | ) |
|
1034 | 1093 | assert ip.user_ns["error"].strip() == "hello" |
|
1035 | 1094 | |
|
1036 | 1095 | |
|
1037 | 1096 | def test_script_out_err(): |
|
1038 | 1097 | |
|
1039 | 1098 | ip = get_ipython() |
|
1040 | 1099 | ip.run_cell_magic( |
|
1041 | 1100 | "script", |
|
1042 | 1101 | f"--out output --err error {sys.executable}", |
|
1043 | 1102 | "\n".join( |
|
1044 | 1103 | [ |
|
1045 | 1104 | "import sys", |
|
1046 | 1105 | "print('hi')", |
|
1047 | 1106 | "print('hello', file=sys.stderr)", |
|
1048 | 1107 | ] |
|
1049 | 1108 | ), |
|
1050 | 1109 | ) |
|
1051 | 1110 | assert ip.user_ns["output"].strip() == "hi" |
|
1052 | 1111 | assert ip.user_ns["error"].strip() == "hello" |
|
1053 | 1112 | |
|
1054 | 1113 | |
|
1055 | 1114 | async def test_script_bg_out(): |
|
1056 | 1115 | ip = get_ipython() |
|
1057 | 1116 | ip.run_cell_magic("script", f"--bg --out output {sys.executable}", "print('hi')") |
|
1058 | 1117 | assert (await ip.user_ns["output"].read()).strip() == b"hi" |
|
1059 | 1118 | assert ip.user_ns["output"].at_eof() |
|
1060 | 1119 | |
|
1061 | 1120 | |
|
1062 | 1121 | async def test_script_bg_err(): |
|
1063 | 1122 | ip = get_ipython() |
|
1064 | 1123 | ip.run_cell_magic( |
|
1065 | 1124 | "script", |
|
1066 | 1125 | f"--bg --err error {sys.executable}", |
|
1067 | 1126 | "import sys; print('hello', file=sys.stderr)", |
|
1068 | 1127 | ) |
|
1069 | 1128 | assert (await ip.user_ns["error"].read()).strip() == b"hello" |
|
1070 | 1129 | assert ip.user_ns["error"].at_eof() |
|
1071 | 1130 | |
|
1072 | 1131 | |
|
1073 | 1132 | async def test_script_bg_out_err(): |
|
1074 | 1133 | ip = get_ipython() |
|
1075 | 1134 | ip.run_cell_magic( |
|
1076 | 1135 | "script", |
|
1077 | 1136 | f"--bg --out output --err error {sys.executable}", |
|
1078 | 1137 | "\n".join( |
|
1079 | 1138 | [ |
|
1080 | 1139 | "import sys", |
|
1081 | 1140 | "print('hi')", |
|
1082 | 1141 | "print('hello', file=sys.stderr)", |
|
1083 | 1142 | ] |
|
1084 | 1143 | ), |
|
1085 | 1144 | ) |
|
1086 | 1145 | assert (await ip.user_ns["output"].read()).strip() == b"hi" |
|
1087 | 1146 | assert (await ip.user_ns["error"].read()).strip() == b"hello" |
|
1088 | 1147 | assert ip.user_ns["output"].at_eof() |
|
1089 | 1148 | assert ip.user_ns["error"].at_eof() |
|
1090 | 1149 | |
|
1091 | 1150 | |
|
1092 | 1151 | async def test_script_bg_proc(): |
|
1093 | 1152 | ip = get_ipython() |
|
1094 | 1153 | ip.run_cell_magic( |
|
1095 | 1154 | "script", |
|
1096 | 1155 | f"--bg --out output --proc p {sys.executable}", |
|
1097 | 1156 | "\n".join( |
|
1098 | 1157 | [ |
|
1099 | 1158 | "import sys", |
|
1100 | 1159 | "print('hi')", |
|
1101 | 1160 | "print('hello', file=sys.stderr)", |
|
1102 | 1161 | ] |
|
1103 | 1162 | ), |
|
1104 | 1163 | ) |
|
1105 | 1164 | p = ip.user_ns["p"] |
|
1106 | 1165 | await p.wait() |
|
1107 | 1166 | assert p.returncode == 0 |
|
1108 | 1167 | assert (await p.stdout.read()).strip() == b"hi" |
|
1109 | 1168 | # not captured, so empty |
|
1110 | 1169 | assert (await p.stderr.read()) == b"" |
|
1111 | 1170 | assert p.stdout.at_eof() |
|
1112 | 1171 | assert p.stderr.at_eof() |
|
1113 | 1172 | |
|
1114 | 1173 | |
|
1115 | 1174 | def test_script_defaults(): |
|
1116 | 1175 | ip = get_ipython() |
|
1117 | 1176 | for cmd in ['sh', 'bash', 'perl', 'ruby']: |
|
1118 | 1177 | try: |
|
1119 | 1178 | find_cmd(cmd) |
|
1120 | 1179 | except Exception: |
|
1121 | 1180 | pass |
|
1122 | 1181 | else: |
|
1123 | 1182 | assert cmd in ip.magics_manager.magics["cell"] |
|
1124 | 1183 | |
|
1125 | 1184 | |
|
1126 | 1185 | @magics_class |
|
1127 | 1186 | class FooFoo(Magics): |
|
1128 | 1187 | """class with both %foo and %%foo magics""" |
|
1129 | 1188 | @line_magic('foo') |
|
1130 | 1189 | def line_foo(self, line): |
|
1131 | 1190 | "I am line foo" |
|
1132 | 1191 | pass |
|
1133 | 1192 | |
|
1134 | 1193 | @cell_magic("foo") |
|
1135 | 1194 | def cell_foo(self, line, cell): |
|
1136 | 1195 | "I am cell foo, not line foo" |
|
1137 | 1196 | pass |
|
1138 | 1197 | |
|
1139 | 1198 | def test_line_cell_info(): |
|
1140 | 1199 | """%%foo and %foo magics are distinguishable to inspect""" |
|
1141 | 1200 | ip = get_ipython() |
|
1142 | 1201 | ip.magics_manager.register(FooFoo) |
|
1143 | 1202 | oinfo = ip.object_inspect("foo") |
|
1144 | 1203 | assert oinfo["found"] is True |
|
1145 | 1204 | assert oinfo["ismagic"] is True |
|
1146 | 1205 | |
|
1147 | 1206 | oinfo = ip.object_inspect("%%foo") |
|
1148 | 1207 | assert oinfo["found"] is True |
|
1149 | 1208 | assert oinfo["ismagic"] is True |
|
1150 | 1209 | assert oinfo["docstring"] == FooFoo.cell_foo.__doc__ |
|
1151 | 1210 | |
|
1152 | 1211 | oinfo = ip.object_inspect("%foo") |
|
1153 | 1212 | assert oinfo["found"] is True |
|
1154 | 1213 | assert oinfo["ismagic"] is True |
|
1155 | 1214 | assert oinfo["docstring"] == FooFoo.line_foo.__doc__ |
|
1156 | 1215 | |
|
1157 | 1216 | |
|
1158 | 1217 | def test_multiple_magics(): |
|
1159 | 1218 | ip = get_ipython() |
|
1160 | 1219 | foo1 = FooFoo(ip) |
|
1161 | 1220 | foo2 = FooFoo(ip) |
|
1162 | 1221 | mm = ip.magics_manager |
|
1163 | 1222 | mm.register(foo1) |
|
1164 | 1223 | assert mm.magics["line"]["foo"].__self__ is foo1 |
|
1165 | 1224 | mm.register(foo2) |
|
1166 | 1225 | assert mm.magics["line"]["foo"].__self__ is foo2 |
|
1167 | 1226 | |
|
1168 | 1227 | |
|
1169 | 1228 | def test_alias_magic(): |
|
1170 | 1229 | """Test %alias_magic.""" |
|
1171 | 1230 | ip = get_ipython() |
|
1172 | 1231 | mm = ip.magics_manager |
|
1173 | 1232 | |
|
1174 | 1233 | # Basic operation: both cell and line magics are created, if possible. |
|
1175 | 1234 | ip.run_line_magic("alias_magic", "timeit_alias timeit") |
|
1176 | 1235 | assert "timeit_alias" in mm.magics["line"] |
|
1177 | 1236 | assert "timeit_alias" in mm.magics["cell"] |
|
1178 | 1237 | |
|
1179 | 1238 | # --cell is specified, line magic not created. |
|
1180 | 1239 | ip.run_line_magic("alias_magic", "--cell timeit_cell_alias timeit") |
|
1181 | 1240 | assert "timeit_cell_alias" not in mm.magics["line"] |
|
1182 | 1241 | assert "timeit_cell_alias" in mm.magics["cell"] |
|
1183 | 1242 | |
|
1184 | 1243 | # Test that line alias is created successfully. |
|
1185 | 1244 | ip.run_line_magic("alias_magic", "--line env_alias env") |
|
1186 | 1245 | assert ip.run_line_magic("env", "") == ip.run_line_magic("env_alias", "") |
|
1187 | 1246 | |
|
1188 | 1247 | # Test that line alias with parameters passed in is created successfully. |
|
1189 | 1248 | ip.run_line_magic( |
|
1190 | 1249 | "alias_magic", "--line history_alias history --params " + shlex.quote("3") |
|
1191 | 1250 | ) |
|
1192 | 1251 | assert "history_alias" in mm.magics["line"] |
|
1193 | 1252 | |
|
1194 | 1253 | |
|
1195 | 1254 | def test_save(): |
|
1196 | 1255 | """Test %save.""" |
|
1197 | 1256 | ip = get_ipython() |
|
1198 | 1257 | ip.history_manager.reset() # Clear any existing history. |
|
1199 | 1258 | cmds = ["a=1", "def b():\n return a**2", "print(a, b())"] |
|
1200 | 1259 | for i, cmd in enumerate(cmds, start=1): |
|
1201 | 1260 | ip.history_manager.store_inputs(i, cmd) |
|
1202 | 1261 | with TemporaryDirectory() as tmpdir: |
|
1203 | 1262 | file = os.path.join(tmpdir, "testsave.py") |
|
1204 | 1263 | ip.run_line_magic("save", "%s 1-10" % file) |
|
1205 | 1264 | content = Path(file).read_text(encoding="utf-8") |
|
1206 | 1265 | assert content.count(cmds[0]) == 1 |
|
1207 | 1266 | assert "coding: utf-8" in content |
|
1208 | 1267 | ip.run_line_magic("save", "-a %s 1-10" % file) |
|
1209 | 1268 | content = Path(file).read_text(encoding="utf-8") |
|
1210 | 1269 | assert content.count(cmds[0]) == 2 |
|
1211 | 1270 | assert "coding: utf-8" in content |
|
1212 | 1271 | |
|
1213 | 1272 | |
|
1214 | 1273 | def test_save_with_no_args(): |
|
1215 | 1274 | ip = get_ipython() |
|
1216 | 1275 | ip.history_manager.reset() # Clear any existing history. |
|
1217 | 1276 | cmds = ["a=1", "def b():\n return a**2", "print(a, b())", "%save"] |
|
1218 | 1277 | for i, cmd in enumerate(cmds, start=1): |
|
1219 | 1278 | ip.history_manager.store_inputs(i, cmd) |
|
1220 | 1279 | |
|
1221 | 1280 | with TemporaryDirectory() as tmpdir: |
|
1222 | 1281 | path = os.path.join(tmpdir, "testsave.py") |
|
1223 | 1282 | ip.run_line_magic("save", path) |
|
1224 | 1283 | content = Path(path).read_text(encoding="utf-8") |
|
1225 | 1284 | expected_content = dedent( |
|
1226 | 1285 | """\ |
|
1227 | 1286 | # coding: utf-8 |
|
1228 | 1287 | a=1 |
|
1229 | 1288 | def b(): |
|
1230 | 1289 | return a**2 |
|
1231 | 1290 | print(a, b()) |
|
1232 | 1291 | """ |
|
1233 | 1292 | ) |
|
1234 | 1293 | assert content == expected_content |
|
1235 | 1294 | |
|
1236 | 1295 | |
|
1237 | 1296 | def test_store(): |
|
1238 | 1297 | """Test %store.""" |
|
1239 | 1298 | ip = get_ipython() |
|
1240 | 1299 | ip.run_line_magic('load_ext', 'storemagic') |
|
1241 | 1300 | |
|
1242 | 1301 | # make sure the storage is empty |
|
1243 | 1302 | ip.run_line_magic("store", "-z") |
|
1244 | 1303 | ip.user_ns["var"] = 42 |
|
1245 | 1304 | ip.run_line_magic("store", "var") |
|
1246 | 1305 | ip.user_ns["var"] = 39 |
|
1247 | 1306 | ip.run_line_magic("store", "-r") |
|
1248 | 1307 | assert ip.user_ns["var"] == 42 |
|
1249 | 1308 | |
|
1250 | 1309 | ip.run_line_magic("store", "-d var") |
|
1251 | 1310 | ip.user_ns["var"] = 39 |
|
1252 | 1311 | ip.run_line_magic("store", "-r") |
|
1253 | 1312 | assert ip.user_ns["var"] == 39 |
|
1254 | 1313 | |
|
1255 | 1314 | |
|
1256 | 1315 | def _run_edit_test(arg_s, exp_filename=None, |
|
1257 | 1316 | exp_lineno=-1, |
|
1258 | 1317 | exp_contents=None, |
|
1259 | 1318 | exp_is_temp=None): |
|
1260 | 1319 | ip = get_ipython() |
|
1261 | 1320 | M = code.CodeMagics(ip) |
|
1262 | 1321 | last_call = ['',''] |
|
1263 | 1322 | opts,args = M.parse_options(arg_s,'prxn:') |
|
1264 | 1323 | filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call) |
|
1265 | 1324 | |
|
1266 | 1325 | if exp_filename is not None: |
|
1267 | 1326 | assert exp_filename == filename |
|
1268 | 1327 | if exp_contents is not None: |
|
1269 | 1328 | with io.open(filename, 'r', encoding='utf-8') as f: |
|
1270 | 1329 | contents = f.read() |
|
1271 | 1330 | assert exp_contents == contents |
|
1272 | 1331 | if exp_lineno != -1: |
|
1273 | 1332 | assert exp_lineno == lineno |
|
1274 | 1333 | if exp_is_temp is not None: |
|
1275 | 1334 | assert exp_is_temp == is_temp |
|
1276 | 1335 | |
|
1277 | 1336 | |
|
1278 | 1337 | def test_edit_interactive(): |
|
1279 | 1338 | """%edit on interactively defined objects""" |
|
1280 | 1339 | ip = get_ipython() |
|
1281 | 1340 | n = ip.execution_count |
|
1282 | 1341 | ip.run_cell("def foo(): return 1", store_history=True) |
|
1283 | 1342 | |
|
1284 | 1343 | with pytest.raises(code.InteractivelyDefined) as e: |
|
1285 | 1344 | _run_edit_test("foo") |
|
1286 | 1345 | assert e.value.index == n |
|
1287 | 1346 | |
|
1288 | 1347 | |
|
1289 | 1348 | def test_edit_cell(): |
|
1290 | 1349 | """%edit [cell id]""" |
|
1291 | 1350 | ip = get_ipython() |
|
1292 | 1351 | |
|
1293 | 1352 | ip.run_cell("def foo(): return 1", store_history=True) |
|
1294 | 1353 | |
|
1295 | 1354 | # test |
|
1296 | 1355 | _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True) |
|
1297 | 1356 | |
|
1298 | 1357 | def test_edit_fname(): |
|
1299 | 1358 | """%edit file""" |
|
1300 | 1359 | # test |
|
1301 | 1360 | _run_edit_test("test file.py", exp_filename="test file.py") |
|
1302 | 1361 | |
|
1303 | 1362 | def test_bookmark(): |
|
1304 | 1363 | ip = get_ipython() |
|
1305 | 1364 | ip.run_line_magic('bookmark', 'bmname') |
|
1306 | 1365 | with tt.AssertPrints('bmname'): |
|
1307 | 1366 | ip.run_line_magic('bookmark', '-l') |
|
1308 | 1367 | ip.run_line_magic('bookmark', '-d bmname') |
|
1309 | 1368 | |
|
1310 | 1369 | def test_ls_magic(): |
|
1311 | 1370 | ip = get_ipython() |
|
1312 | 1371 | json_formatter = ip.display_formatter.formatters['application/json'] |
|
1313 | 1372 | json_formatter.enabled = True |
|
1314 | 1373 | lsmagic = ip.run_line_magic("lsmagic", "") |
|
1315 | 1374 | with warnings.catch_warnings(record=True) as w: |
|
1316 | 1375 | j = json_formatter(lsmagic) |
|
1317 | 1376 | assert sorted(j) == ["cell", "line"] |
|
1318 | 1377 | assert w == [] # no warnings |
|
1319 | 1378 | |
|
1320 | 1379 | |
|
1321 | 1380 | def test_strip_initial_indent(): |
|
1322 | 1381 | def sii(s): |
|
1323 | 1382 | lines = s.splitlines() |
|
1324 | 1383 | return '\n'.join(code.strip_initial_indent(lines)) |
|
1325 | 1384 | |
|
1326 | 1385 | assert sii(" a = 1\nb = 2") == "a = 1\nb = 2" |
|
1327 | 1386 | assert sii(" a\n b\nc") == "a\n b\nc" |
|
1328 | 1387 | assert sii("a\n b") == "a\n b" |
|
1329 | 1388 | |
|
1330 | 1389 | def test_logging_magic_quiet_from_arg(): |
|
1331 | 1390 | _ip.config.LoggingMagics.quiet = False |
|
1332 | 1391 | lm = logging.LoggingMagics(shell=_ip) |
|
1333 | 1392 | with TemporaryDirectory() as td: |
|
1334 | 1393 | try: |
|
1335 | 1394 | with tt.AssertNotPrints(re.compile("Activating.*")): |
|
1336 | 1395 | lm.logstart('-q {}'.format( |
|
1337 | 1396 | os.path.join(td, "quiet_from_arg.log"))) |
|
1338 | 1397 | finally: |
|
1339 | 1398 | _ip.logger.logstop() |
|
1340 | 1399 | |
|
1341 | 1400 | def test_logging_magic_quiet_from_config(): |
|
1342 | 1401 | _ip.config.LoggingMagics.quiet = True |
|
1343 | 1402 | lm = logging.LoggingMagics(shell=_ip) |
|
1344 | 1403 | with TemporaryDirectory() as td: |
|
1345 | 1404 | try: |
|
1346 | 1405 | with tt.AssertNotPrints(re.compile("Activating.*")): |
|
1347 | 1406 | lm.logstart(os.path.join(td, "quiet_from_config.log")) |
|
1348 | 1407 | finally: |
|
1349 | 1408 | _ip.logger.logstop() |
|
1350 | 1409 | |
|
1351 | 1410 | |
|
1352 | 1411 | def test_logging_magic_not_quiet(): |
|
1353 | 1412 | _ip.config.LoggingMagics.quiet = False |
|
1354 | 1413 | lm = logging.LoggingMagics(shell=_ip) |
|
1355 | 1414 | with TemporaryDirectory() as td: |
|
1356 | 1415 | try: |
|
1357 | 1416 | with tt.AssertPrints(re.compile("Activating.*")): |
|
1358 | 1417 | lm.logstart(os.path.join(td, "not_quiet.log")) |
|
1359 | 1418 | finally: |
|
1360 | 1419 | _ip.logger.logstop() |
|
1361 | 1420 | |
|
1362 | 1421 | |
|
1363 | 1422 | def test_time_no_var_expand(): |
|
1364 | 1423 | _ip.user_ns["a"] = 5 |
|
1365 | 1424 | _ip.user_ns["b"] = [] |
|
1366 | 1425 | _ip.run_line_magic("time", 'b.append("{a}")') |
|
1367 | 1426 | assert _ip.user_ns["b"] == ["{a}"] |
|
1368 | 1427 | |
|
1369 | 1428 | |
|
1370 | 1429 | # this is slow, put at the end for local testing. |
|
1371 | 1430 | def test_timeit_arguments(): |
|
1372 | 1431 | "Test valid timeit arguments, should not cause SyntaxError (GH #1269)" |
|
1373 | 1432 | _ip.run_line_magic("timeit", "-n1 -r1 a=('#')") |
|
1374 | 1433 | |
|
1375 | 1434 | |
|
1376 | 1435 | MINIMAL_LAZY_MAGIC = """ |
|
1377 | 1436 | from IPython.core.magic import ( |
|
1378 | 1437 | Magics, |
|
1379 | 1438 | magics_class, |
|
1380 | 1439 | line_magic, |
|
1381 | 1440 | cell_magic, |
|
1382 | 1441 | ) |
|
1383 | 1442 | |
|
1384 | 1443 | |
|
1385 | 1444 | @magics_class |
|
1386 | 1445 | class LazyMagics(Magics): |
|
1387 | 1446 | @line_magic |
|
1388 | 1447 | def lazy_line(self, line): |
|
1389 | 1448 | print("Lazy Line") |
|
1390 | 1449 | |
|
1391 | 1450 | @cell_magic |
|
1392 | 1451 | def lazy_cell(self, line, cell): |
|
1393 | 1452 | print("Lazy Cell") |
|
1394 | 1453 | |
|
1395 | 1454 | |
|
1396 | 1455 | def load_ipython_extension(ipython): |
|
1397 | 1456 | ipython.register_magics(LazyMagics) |
|
1398 | 1457 | """ |
|
1399 | 1458 | |
|
1400 | 1459 | |
|
1401 | 1460 | def test_lazy_magics(): |
|
1402 | 1461 | with pytest.raises(UsageError): |
|
1403 | 1462 | ip.run_line_magic("lazy_line", "") |
|
1404 | 1463 | |
|
1405 | 1464 | startdir = os.getcwd() |
|
1406 | 1465 | |
|
1407 | 1466 | with TemporaryDirectory() as tmpdir: |
|
1408 | 1467 | with prepended_to_syspath(tmpdir): |
|
1409 | 1468 | ptempdir = Path(tmpdir) |
|
1410 | 1469 | tf = ptempdir / "lazy_magic_module.py" |
|
1411 | 1470 | tf.write_text(MINIMAL_LAZY_MAGIC) |
|
1412 | 1471 | ip.magics_manager.register_lazy("lazy_line", Path(tf.name).name[:-3]) |
|
1413 | 1472 | with tt.AssertPrints("Lazy Line"): |
|
1414 | 1473 | ip.run_line_magic("lazy_line", "") |
|
1415 | 1474 | |
|
1416 | 1475 | |
|
1417 | 1476 | TEST_MODULE = """ |
|
1418 | 1477 | print('Loaded my_tmp') |
|
1419 | 1478 | if __name__ == "__main__": |
|
1420 | 1479 | print('I just ran a script') |
|
1421 | 1480 | """ |
|
1422 | 1481 | |
|
1423 | 1482 | def test_run_module_from_import_hook(): |
|
1424 | 1483 | "Test that a module can be loaded via an import hook" |
|
1425 | 1484 | with TemporaryDirectory() as tmpdir: |
|
1426 | 1485 | fullpath = os.path.join(tmpdir, "my_tmp.py") |
|
1427 | 1486 | Path(fullpath).write_text(TEST_MODULE, encoding="utf-8") |
|
1428 | 1487 | |
|
1429 | 1488 | import importlib.abc |
|
1430 | 1489 | import importlib.util |
|
1431 | 1490 | |
|
1432 | 1491 | class MyTempImporter(importlib.abc.MetaPathFinder, importlib.abc.SourceLoader): |
|
1433 | 1492 | def find_spec(self, fullname, path, target=None): |
|
1434 | 1493 | if fullname == "my_tmp": |
|
1435 | 1494 | return importlib.util.spec_from_loader(fullname, self) |
|
1436 | 1495 | |
|
1437 | 1496 | def get_filename(self, fullname): |
|
1438 | 1497 | assert fullname == "my_tmp" |
|
1439 | 1498 | return fullpath |
|
1440 | 1499 | |
|
1441 | 1500 | def get_data(self, path): |
|
1442 | 1501 | assert Path(path).samefile(fullpath) |
|
1443 | 1502 | return Path(fullpath).read_text(encoding="utf-8") |
|
1444 | 1503 | |
|
1445 | 1504 | sys.meta_path.insert(0, MyTempImporter()) |
|
1446 | 1505 | |
|
1447 | 1506 | with capture_output() as captured: |
|
1448 | 1507 | _ip.run_line_magic("run", "-m my_tmp") |
|
1449 | 1508 | _ip.run_cell("import my_tmp") |
|
1450 | 1509 | |
|
1451 | 1510 | output = "Loaded my_tmp\nI just ran a script\nLoaded my_tmp\n" |
|
1452 | 1511 | assert output == captured.stdout |
|
1453 | 1512 | |
|
1454 | 1513 | sys.meta_path.pop(0) |
@@ -1,773 +1,808 b'' | |||
|
1 | 1 | """IPython terminal interface using prompt_toolkit""" |
|
2 | 2 | |
|
3 | 3 | import asyncio |
|
4 | 4 | import os |
|
5 | 5 | import sys |
|
6 | 6 | from warnings import warn |
|
7 | from typing import Union as UnionType | |
|
7 | 8 | |
|
8 | 9 | from IPython.core.async_helpers import get_asyncio_loop |
|
9 | 10 | from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC |
|
10 | 11 | from IPython.utils.py3compat import input |
|
11 | 12 | from IPython.utils.terminal import toggle_set_term_title, set_term_title, restore_term_title |
|
12 | 13 | from IPython.utils.process import abbrev_cwd |
|
13 | 14 | from traitlets import ( |
|
14 | 15 | Bool, |
|
15 | 16 | Unicode, |
|
16 | 17 | Dict, |
|
17 | 18 | Integer, |
|
18 | 19 | observe, |
|
19 | 20 | Instance, |
|
20 | 21 | Type, |
|
21 | 22 | default, |
|
22 | 23 | Enum, |
|
23 | 24 | Union, |
|
24 | 25 | Any, |
|
25 | 26 | validate, |
|
26 | 27 | Float, |
|
27 | 28 | ) |
|
28 | 29 | |
|
29 | 30 | from prompt_toolkit.auto_suggest import AutoSuggestFromHistory |
|
30 | 31 | from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode |
|
31 | 32 | from prompt_toolkit.filters import (HasFocus, Condition, IsDone) |
|
32 | 33 | from prompt_toolkit.formatted_text import PygmentsTokens |
|
33 | 34 | from prompt_toolkit.history import History |
|
34 | 35 | from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor |
|
35 | 36 | from prompt_toolkit.output import ColorDepth |
|
36 | 37 | from prompt_toolkit.patch_stdout import patch_stdout |
|
37 | 38 | from prompt_toolkit.shortcuts import PromptSession, CompleteStyle, print_formatted_text |
|
38 | 39 | from prompt_toolkit.styles import DynamicStyle, merge_styles |
|
39 | 40 | from prompt_toolkit.styles.pygments import style_from_pygments_cls, style_from_pygments_dict |
|
40 | 41 | from prompt_toolkit import __version__ as ptk_version |
|
41 | 42 | |
|
42 | 43 | from pygments.styles import get_style_by_name |
|
43 | 44 | from pygments.style import Style |
|
44 | 45 | from pygments.token import Token |
|
45 | 46 | |
|
46 | 47 | from .debugger import TerminalPdb, Pdb |
|
47 | 48 | from .magics import TerminalMagics |
|
48 | 49 | from .pt_inputhooks import get_inputhook_name_and_func |
|
49 | 50 | from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook |
|
50 | 51 | from .ptutils import IPythonPTCompleter, IPythonPTLexer |
|
51 | 52 | from .shortcuts import create_ipython_shortcuts |
|
53 | from .shortcuts.auto_suggest import ( | |
|
54 | NavigableAutoSuggestFromHistory, | |
|
55 | AppendAutoSuggestionInAnyLine, | |
|
56 | ) | |
|
52 | 57 | |
|
53 | 58 | PTK3 = ptk_version.startswith('3.') |
|
54 | 59 | |
|
55 | 60 | |
|
56 | 61 | class _NoStyle(Style): pass |
|
57 | 62 | |
|
58 | 63 | |
|
59 | 64 | |
|
60 | 65 | _style_overrides_light_bg = { |
|
61 | 66 | Token.Prompt: '#ansibrightblue', |
|
62 | 67 | Token.PromptNum: '#ansiblue bold', |
|
63 | 68 | Token.OutPrompt: '#ansibrightred', |
|
64 | 69 | Token.OutPromptNum: '#ansired bold', |
|
65 | 70 | } |
|
66 | 71 | |
|
67 | 72 | _style_overrides_linux = { |
|
68 | 73 | Token.Prompt: '#ansibrightgreen', |
|
69 | 74 | Token.PromptNum: '#ansigreen bold', |
|
70 | 75 | Token.OutPrompt: '#ansibrightred', |
|
71 | 76 | Token.OutPromptNum: '#ansired bold', |
|
72 | 77 | } |
|
73 | 78 | |
|
74 | 79 | def get_default_editor(): |
|
75 | 80 | try: |
|
76 | 81 | return os.environ['EDITOR'] |
|
77 | 82 | except KeyError: |
|
78 | 83 | pass |
|
79 | 84 | except UnicodeError: |
|
80 | 85 | warn("$EDITOR environment variable is not pure ASCII. Using platform " |
|
81 | 86 | "default editor.") |
|
82 | 87 | |
|
83 | 88 | if os.name == 'posix': |
|
84 | 89 | return 'vi' # the only one guaranteed to be there! |
|
85 | 90 | else: |
|
86 | 91 | return 'notepad' # same in Windows! |
|
87 | 92 | |
|
88 | 93 | # conservatively check for tty |
|
89 | 94 | # overridden streams can result in things like: |
|
90 | 95 | # - sys.stdin = None |
|
91 | 96 | # - no isatty method |
|
92 | 97 | for _name in ('stdin', 'stdout', 'stderr'): |
|
93 | 98 | _stream = getattr(sys, _name) |
|
94 | 99 | try: |
|
95 | 100 | if not _stream or not hasattr(_stream, "isatty") or not _stream.isatty(): |
|
96 | 101 | _is_tty = False |
|
97 | 102 | break |
|
98 | 103 | except ValueError: |
|
99 | 104 | # stream is closed |
|
100 | 105 | _is_tty = False |
|
101 | 106 | break |
|
102 | 107 | else: |
|
103 | 108 | _is_tty = True |
|
104 | 109 | |
|
105 | 110 | |
|
106 | 111 | _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty) |
|
107 | 112 | |
|
108 | 113 | def black_reformat_handler(text_before_cursor): |
|
109 | 114 | """ |
|
110 | 115 | We do not need to protect against error, |
|
111 | 116 | this is taken care at a higher level where any reformat error is ignored. |
|
112 | 117 | Indeed we may call reformatting on incomplete code. |
|
113 | 118 | """ |
|
114 | 119 | import black |
|
115 | 120 | |
|
116 | 121 | formatted_text = black.format_str(text_before_cursor, mode=black.FileMode()) |
|
117 | 122 | if not text_before_cursor.endswith("\n") and formatted_text.endswith("\n"): |
|
118 | 123 | formatted_text = formatted_text[:-1] |
|
119 | 124 | return formatted_text |
|
120 | 125 | |
|
121 | 126 | |
|
122 | 127 | def yapf_reformat_handler(text_before_cursor): |
|
123 | 128 | from yapf.yapflib import file_resources |
|
124 | 129 | from yapf.yapflib import yapf_api |
|
125 | 130 | |
|
126 | 131 | style_config = file_resources.GetDefaultStyleForDir(os.getcwd()) |
|
127 | 132 | formatted_text, was_formatted = yapf_api.FormatCode( |
|
128 | 133 | text_before_cursor, style_config=style_config |
|
129 | 134 | ) |
|
130 | 135 | if was_formatted: |
|
131 | 136 | if not text_before_cursor.endswith("\n") and formatted_text.endswith("\n"): |
|
132 | 137 | formatted_text = formatted_text[:-1] |
|
133 | 138 | return formatted_text |
|
134 | 139 | else: |
|
135 | 140 | return text_before_cursor |
|
136 | 141 | |
|
137 | 142 | |
|
138 | 143 | class PtkHistoryAdapter(History): |
|
139 | 144 | """ |
|
140 | 145 | Prompt toolkit has it's own way of handling history, Where it assumes it can |
|
141 | 146 | Push/pull from history. |
|
142 | 147 | |
|
143 | 148 | """ |
|
144 | 149 | |
|
145 | 150 | def __init__(self, shell): |
|
146 | 151 | super().__init__() |
|
147 | 152 | self.shell = shell |
|
148 | 153 | self._refresh() |
|
149 | 154 | |
|
150 | 155 | def append_string(self, string): |
|
151 | 156 | # we rely on sql for that. |
|
152 | 157 | self._loaded = False |
|
153 | 158 | self._refresh() |
|
154 | 159 | |
|
155 | 160 | def _refresh(self): |
|
156 | 161 | if not self._loaded: |
|
157 | 162 | self._loaded_strings = list(self.load_history_strings()) |
|
158 | 163 | |
|
159 | 164 | def load_history_strings(self): |
|
160 | 165 | last_cell = "" |
|
161 | 166 | res = [] |
|
162 | 167 | for __, ___, cell in self.shell.history_manager.get_tail( |
|
163 | 168 | self.shell.history_load_length, include_latest=True |
|
164 | 169 | ): |
|
165 | 170 | # Ignore blank lines and consecutive duplicates |
|
166 | 171 | cell = cell.rstrip() |
|
167 | 172 | if cell and (cell != last_cell): |
|
168 | 173 | res.append(cell) |
|
169 | 174 | last_cell = cell |
|
170 | 175 | yield from res[::-1] |
|
171 | 176 | |
|
172 | 177 | def store_string(self, string: str) -> None: |
|
173 | 178 | pass |
|
174 | 179 | |
|
175 | 180 | class TerminalInteractiveShell(InteractiveShell): |
|
176 | 181 | mime_renderers = Dict().tag(config=True) |
|
177 | 182 | |
|
178 | 183 | space_for_menu = Integer(6, help='Number of line at the bottom of the screen ' |
|
179 | 184 | 'to reserve for the tab completion menu, ' |
|
180 | 185 | 'search history, ...etc, the height of ' |
|
181 | 186 | 'these menus will at most this value. ' |
|
182 | 187 | 'Increase it is you prefer long and skinny ' |
|
183 | 188 | 'menus, decrease for short and wide.' |
|
184 | 189 | ).tag(config=True) |
|
185 | 190 | |
|
186 | pt_app = None | |
|
191 | pt_app: UnionType[PromptSession, None] = None | |
|
192 | auto_suggest: UnionType[ | |
|
193 | AutoSuggestFromHistory, NavigableAutoSuggestFromHistory, None | |
|
194 | ] = None | |
|
187 | 195 | debugger_history = None |
|
188 | 196 | |
|
189 | 197 | debugger_history_file = Unicode( |
|
190 | 198 | "~/.pdbhistory", help="File in which to store and read history" |
|
191 | 199 | ).tag(config=True) |
|
192 | 200 | |
|
193 | 201 | simple_prompt = Bool(_use_simple_prompt, |
|
194 | 202 | help="""Use `raw_input` for the REPL, without completion and prompt colors. |
|
195 | 203 | |
|
196 | 204 | Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are: |
|
197 | 205 | IPython own testing machinery, and emacs inferior-shell integration through elpy. |
|
198 | 206 | |
|
199 | 207 | This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT` |
|
200 | 208 | environment variable is set, or the current terminal is not a tty.""" |
|
201 | 209 | ).tag(config=True) |
|
202 | 210 | |
|
203 | 211 | @property |
|
204 | 212 | def debugger_cls(self): |
|
205 | 213 | return Pdb if self.simple_prompt else TerminalPdb |
|
206 | 214 | |
|
207 | 215 | confirm_exit = Bool(True, |
|
208 | 216 | help=""" |
|
209 | 217 | Set to confirm when you try to exit IPython with an EOF (Control-D |
|
210 | 218 | in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit', |
|
211 | 219 | you can force a direct exit without any confirmation.""", |
|
212 | 220 | ).tag(config=True) |
|
213 | 221 | |
|
214 | 222 | editing_mode = Unicode('emacs', |
|
215 | 223 | help="Shortcut style to use at the prompt. 'vi' or 'emacs'.", |
|
216 | 224 | ).tag(config=True) |
|
217 | 225 | |
|
218 | 226 | emacs_bindings_in_vi_insert_mode = Bool( |
|
219 | 227 | True, |
|
220 | 228 | help="Add shortcuts from 'emacs' insert mode to 'vi' insert mode.", |
|
221 | 229 | ).tag(config=True) |
|
222 | 230 | |
|
223 | 231 | modal_cursor = Bool( |
|
224 | 232 | True, |
|
225 | 233 | help=""" |
|
226 | 234 | Cursor shape changes depending on vi mode: beam in vi insert mode, |
|
227 | 235 | block in nav mode, underscore in replace mode.""", |
|
228 | 236 | ).tag(config=True) |
|
229 | 237 | |
|
230 | 238 | ttimeoutlen = Float( |
|
231 | 239 | 0.01, |
|
232 | 240 | help="""The time in milliseconds that is waited for a key code |
|
233 | 241 | to complete.""", |
|
234 | 242 | ).tag(config=True) |
|
235 | 243 | |
|
236 | 244 | timeoutlen = Float( |
|
237 | 245 | 0.5, |
|
238 | 246 | help="""The time in milliseconds that is waited for a mapped key |
|
239 | 247 | sequence to complete.""", |
|
240 | 248 | ).tag(config=True) |
|
241 | 249 | |
|
242 | 250 | autoformatter = Unicode( |
|
243 | 251 | None, |
|
244 | 252 | help="Autoformatter to reformat Terminal code. Can be `'black'`, `'yapf'` or `None`", |
|
245 | 253 | allow_none=True |
|
246 | 254 | ).tag(config=True) |
|
247 | 255 | |
|
248 | 256 | auto_match = Bool( |
|
249 | 257 | False, |
|
250 | 258 | help=""" |
|
251 | 259 | Automatically add/delete closing bracket or quote when opening bracket or quote is entered/deleted. |
|
252 | 260 | Brackets: (), [], {} |
|
253 | 261 | Quotes: '', \"\" |
|
254 | 262 | """, |
|
255 | 263 | ).tag(config=True) |
|
256 | 264 | |
|
257 | 265 | mouse_support = Bool(False, |
|
258 | 266 | help="Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)" |
|
259 | 267 | ).tag(config=True) |
|
260 | 268 | |
|
261 | 269 | # We don't load the list of styles for the help string, because loading |
|
262 | 270 | # Pygments plugins takes time and can cause unexpected errors. |
|
263 | 271 | highlighting_style = Union([Unicode('legacy'), Type(klass=Style)], |
|
264 | 272 | help="""The name or class of a Pygments style to use for syntax |
|
265 | 273 | highlighting. To see available styles, run `pygmentize -L styles`.""" |
|
266 | 274 | ).tag(config=True) |
|
267 | 275 | |
|
268 | 276 | @validate('editing_mode') |
|
269 | 277 | def _validate_editing_mode(self, proposal): |
|
270 | 278 | if proposal['value'].lower() == 'vim': |
|
271 | 279 | proposal['value']= 'vi' |
|
272 | 280 | elif proposal['value'].lower() == 'default': |
|
273 | 281 | proposal['value']= 'emacs' |
|
274 | 282 | |
|
275 | 283 | if hasattr(EditingMode, proposal['value'].upper()): |
|
276 | 284 | return proposal['value'].lower() |
|
277 | 285 | |
|
278 | 286 | return self.editing_mode |
|
279 | 287 | |
|
280 | 288 | |
|
281 | 289 | @observe('editing_mode') |
|
282 | 290 | def _editing_mode(self, change): |
|
283 | 291 | if self.pt_app: |
|
284 | 292 | self.pt_app.editing_mode = getattr(EditingMode, change.new.upper()) |
|
285 | 293 | |
|
286 | 294 | def _set_formatter(self, formatter): |
|
287 | 295 | if formatter is None: |
|
288 | 296 | self.reformat_handler = lambda x:x |
|
289 | 297 | elif formatter == 'black': |
|
290 | 298 | self.reformat_handler = black_reformat_handler |
|
291 | 299 | elif formatter == "yapf": |
|
292 | 300 | self.reformat_handler = yapf_reformat_handler |
|
293 | 301 | else: |
|
294 | 302 | raise ValueError |
|
295 | 303 | |
|
296 | 304 | @observe("autoformatter") |
|
297 | 305 | def _autoformatter_changed(self, change): |
|
298 | 306 | formatter = change.new |
|
299 | 307 | self._set_formatter(formatter) |
|
300 | 308 | |
|
301 | 309 | @observe('highlighting_style') |
|
302 | 310 | @observe('colors') |
|
303 | 311 | def _highlighting_style_changed(self, change): |
|
304 | 312 | self.refresh_style() |
|
305 | 313 | |
|
306 | 314 | def refresh_style(self): |
|
307 | 315 | self._style = self._make_style_from_name_or_cls(self.highlighting_style) |
|
308 | 316 | |
|
309 | 317 | |
|
310 | 318 | highlighting_style_overrides = Dict( |
|
311 | 319 | help="Override highlighting format for specific tokens" |
|
312 | 320 | ).tag(config=True) |
|
313 | 321 | |
|
314 | 322 | true_color = Bool(False, |
|
315 | 323 | help="""Use 24bit colors instead of 256 colors in prompt highlighting. |
|
316 | 324 | If your terminal supports true color, the following command should |
|
317 | 325 | print ``TRUECOLOR`` in orange:: |
|
318 | 326 | |
|
319 | 327 | printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\" |
|
320 | 328 | """, |
|
321 | 329 | ).tag(config=True) |
|
322 | 330 | |
|
323 | 331 | editor = Unicode(get_default_editor(), |
|
324 | 332 | help="Set the editor used by IPython (default to $EDITOR/vi/notepad)." |
|
325 | 333 | ).tag(config=True) |
|
326 | 334 | |
|
327 | 335 | prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True) |
|
328 | 336 | |
|
329 | 337 | prompts = Instance(Prompts) |
|
330 | 338 | |
|
331 | 339 | @default('prompts') |
|
332 | 340 | def _prompts_default(self): |
|
333 | 341 | return self.prompts_class(self) |
|
334 | 342 | |
|
335 | 343 | # @observe('prompts') |
|
336 | 344 | # def _(self, change): |
|
337 | 345 | # self._update_layout() |
|
338 | 346 | |
|
339 | 347 | @default('displayhook_class') |
|
340 | 348 | def _displayhook_class_default(self): |
|
341 | 349 | return RichPromptDisplayHook |
|
342 | 350 | |
|
343 | 351 | term_title = Bool(True, |
|
344 | 352 | help="Automatically set the terminal title" |
|
345 | 353 | ).tag(config=True) |
|
346 | 354 | |
|
347 | 355 | term_title_format = Unicode("IPython: {cwd}", |
|
348 | 356 | help="Customize the terminal title format. This is a python format string. " + |
|
349 | 357 | "Available substitutions are: {cwd}." |
|
350 | 358 | ).tag(config=True) |
|
351 | 359 | |
|
352 | 360 | display_completions = Enum(('column', 'multicolumn','readlinelike'), |
|
353 | 361 | help= ( "Options for displaying tab completions, 'column', 'multicolumn', and " |
|
354 | 362 | "'readlinelike'. These options are for `prompt_toolkit`, see " |
|
355 | 363 | "`prompt_toolkit` documentation for more information." |
|
356 | 364 | ), |
|
357 | 365 | default_value='multicolumn').tag(config=True) |
|
358 | 366 | |
|
359 | 367 | highlight_matching_brackets = Bool(True, |
|
360 | 368 | help="Highlight matching brackets.", |
|
361 | 369 | ).tag(config=True) |
|
362 | 370 | |
|
363 | 371 | extra_open_editor_shortcuts = Bool(False, |
|
364 | 372 | help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. " |
|
365 | 373 | "This is in addition to the F2 binding, which is always enabled." |
|
366 | 374 | ).tag(config=True) |
|
367 | 375 | |
|
368 | 376 | handle_return = Any(None, |
|
369 | 377 | help="Provide an alternative handler to be called when the user presses " |
|
370 | 378 | "Return. This is an advanced option intended for debugging, which " |
|
371 | 379 | "may be changed or removed in later releases." |
|
372 | 380 | ).tag(config=True) |
|
373 | 381 | |
|
374 | 382 | enable_history_search = Bool(True, |
|
375 | 383 | help="Allows to enable/disable the prompt toolkit history search" |
|
376 | 384 | ).tag(config=True) |
|
377 | 385 | |
|
378 | 386 | autosuggestions_provider = Unicode( |
|
379 | "AutoSuggestFromHistory", | |
|
387 | "NavigableAutoSuggestFromHistory", | |
|
380 | 388 | help="Specifies from which source automatic suggestions are provided. " |
|
381 |
"Can be set to `'AutoSuggestFromHistory |
|
|
382 |
" |
|
|
389 | "Can be set to ``'NavigableAutoSuggestFromHistory'`` (:kbd:`up` and " | |
|
390 | ":kbd:`down` swap suggestions), ``'AutoSuggestFromHistory'``, " | |
|
391 | " or ``None`` to disable automatic suggestions. " | |
|
392 | "Default is `'NavigableAutoSuggestFromHistory`'.", | |
|
383 | 393 | allow_none=True, |
|
384 | 394 | ).tag(config=True) |
|
385 | 395 | |
|
386 | 396 | def _set_autosuggestions(self, provider): |
|
397 | # disconnect old handler | |
|
398 | if self.auto_suggest and isinstance( | |
|
399 | self.auto_suggest, NavigableAutoSuggestFromHistory | |
|
400 | ): | |
|
401 | self.auto_suggest.disconnect() | |
|
387 | 402 | if provider is None: |
|
388 | 403 | self.auto_suggest = None |
|
389 | 404 | elif provider == "AutoSuggestFromHistory": |
|
390 | 405 | self.auto_suggest = AutoSuggestFromHistory() |
|
406 | elif provider == "NavigableAutoSuggestFromHistory": | |
|
407 | self.auto_suggest = NavigableAutoSuggestFromHistory() | |
|
391 | 408 | else: |
|
392 | 409 | raise ValueError("No valid provider.") |
|
393 | 410 | if self.pt_app: |
|
394 | 411 | self.pt_app.auto_suggest = self.auto_suggest |
|
395 | 412 | |
|
396 | 413 | @observe("autosuggestions_provider") |
|
397 | 414 | def _autosuggestions_provider_changed(self, change): |
|
398 | 415 | provider = change.new |
|
399 | 416 | self._set_autosuggestions(provider) |
|
400 | 417 | |
|
401 | 418 | prompt_includes_vi_mode = Bool(True, |
|
402 | 419 | help="Display the current vi mode (when using vi editing mode)." |
|
403 | 420 | ).tag(config=True) |
|
404 | 421 | |
|
405 | 422 | @observe('term_title') |
|
406 | 423 | def init_term_title(self, change=None): |
|
407 | 424 | # Enable or disable the terminal title. |
|
408 | 425 | if self.term_title and _is_tty: |
|
409 | 426 | toggle_set_term_title(True) |
|
410 | 427 | set_term_title(self.term_title_format.format(cwd=abbrev_cwd())) |
|
411 | 428 | else: |
|
412 | 429 | toggle_set_term_title(False) |
|
413 | 430 | |
|
414 | 431 | def restore_term_title(self): |
|
415 | 432 | if self.term_title and _is_tty: |
|
416 | 433 | restore_term_title() |
|
417 | 434 | |
|
418 | 435 | def init_display_formatter(self): |
|
419 | 436 | super(TerminalInteractiveShell, self).init_display_formatter() |
|
420 | 437 | # terminal only supports plain text |
|
421 | 438 | self.display_formatter.active_types = ["text/plain"] |
|
422 | 439 | |
|
423 | 440 | def init_prompt_toolkit_cli(self): |
|
424 | 441 | if self.simple_prompt: |
|
425 | 442 | # Fall back to plain non-interactive output for tests. |
|
426 | 443 | # This is very limited. |
|
427 | 444 | def prompt(): |
|
428 | 445 | prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens()) |
|
429 | 446 | lines = [input(prompt_text)] |
|
430 | 447 | prompt_continuation = "".join(x[1] for x in self.prompts.continuation_prompt_tokens()) |
|
431 | 448 | while self.check_complete('\n'.join(lines))[0] == 'incomplete': |
|
432 | 449 | lines.append( input(prompt_continuation) ) |
|
433 | 450 | return '\n'.join(lines) |
|
434 | 451 | self.prompt_for_code = prompt |
|
435 | 452 | return |
|
436 | 453 | |
|
437 | 454 | # Set up keyboard shortcuts |
|
438 | 455 | key_bindings = create_ipython_shortcuts(self) |
|
439 | 456 | |
|
440 | 457 | |
|
441 | 458 | # Pre-populate history from IPython's history database |
|
442 | 459 | history = PtkHistoryAdapter(self) |
|
443 | 460 | |
|
444 | 461 | self._style = self._make_style_from_name_or_cls(self.highlighting_style) |
|
445 | 462 | self.style = DynamicStyle(lambda: self._style) |
|
446 | 463 | |
|
447 | 464 | editing_mode = getattr(EditingMode, self.editing_mode.upper()) |
|
448 | 465 | |
|
449 | 466 | self.pt_loop = asyncio.new_event_loop() |
|
450 | 467 | self.pt_app = PromptSession( |
|
451 | 468 | auto_suggest=self.auto_suggest, |
|
452 | 469 | editing_mode=editing_mode, |
|
453 | 470 | key_bindings=key_bindings, |
|
454 | 471 | history=history, |
|
455 | 472 | completer=IPythonPTCompleter(shell=self), |
|
456 | 473 | enable_history_search=self.enable_history_search, |
|
457 | 474 | style=self.style, |
|
458 | 475 | include_default_pygments_style=False, |
|
459 | 476 | mouse_support=self.mouse_support, |
|
460 | 477 | enable_open_in_editor=self.extra_open_editor_shortcuts, |
|
461 | 478 | color_depth=self.color_depth, |
|
462 | 479 | tempfile_suffix=".py", |
|
463 | 480 | **self._extra_prompt_options() |
|
464 | 481 | ) |
|
482 | if isinstance(self.auto_suggest, NavigableAutoSuggestFromHistory): | |
|
483 | self.auto_suggest.connect(self.pt_app) | |
|
465 | 484 | |
|
466 | 485 | def _make_style_from_name_or_cls(self, name_or_cls): |
|
467 | 486 | """ |
|
468 | 487 | Small wrapper that make an IPython compatible style from a style name |
|
469 | 488 | |
|
470 | 489 | We need that to add style for prompt ... etc. |
|
471 | 490 | """ |
|
472 | 491 | style_overrides = {} |
|
473 | 492 | if name_or_cls == 'legacy': |
|
474 | 493 | legacy = self.colors.lower() |
|
475 | 494 | if legacy == 'linux': |
|
476 | 495 | style_cls = get_style_by_name('monokai') |
|
477 | 496 | style_overrides = _style_overrides_linux |
|
478 | 497 | elif legacy == 'lightbg': |
|
479 | 498 | style_overrides = _style_overrides_light_bg |
|
480 | 499 | style_cls = get_style_by_name('pastie') |
|
481 | 500 | elif legacy == 'neutral': |
|
482 | 501 | # The default theme needs to be visible on both a dark background |
|
483 | 502 | # and a light background, because we can't tell what the terminal |
|
484 | 503 | # looks like. These tweaks to the default theme help with that. |
|
485 | 504 | style_cls = get_style_by_name('default') |
|
486 | 505 | style_overrides.update({ |
|
487 | 506 | Token.Number: '#ansigreen', |
|
488 | 507 | Token.Operator: 'noinherit', |
|
489 | 508 | Token.String: '#ansiyellow', |
|
490 | 509 | Token.Name.Function: '#ansiblue', |
|
491 | 510 | Token.Name.Class: 'bold #ansiblue', |
|
492 | 511 | Token.Name.Namespace: 'bold #ansiblue', |
|
493 | 512 | Token.Name.Variable.Magic: '#ansiblue', |
|
494 | 513 | Token.Prompt: '#ansigreen', |
|
495 | 514 | Token.PromptNum: '#ansibrightgreen bold', |
|
496 | 515 | Token.OutPrompt: '#ansired', |
|
497 | 516 | Token.OutPromptNum: '#ansibrightred bold', |
|
498 | 517 | }) |
|
499 | 518 | |
|
500 | 519 | # Hack: Due to limited color support on the Windows console |
|
501 | 520 | # the prompt colors will be wrong without this |
|
502 | 521 | if os.name == 'nt': |
|
503 | 522 | style_overrides.update({ |
|
504 | 523 | Token.Prompt: '#ansidarkgreen', |
|
505 | 524 | Token.PromptNum: '#ansigreen bold', |
|
506 | 525 | Token.OutPrompt: '#ansidarkred', |
|
507 | 526 | Token.OutPromptNum: '#ansired bold', |
|
508 | 527 | }) |
|
509 | 528 | elif legacy =='nocolor': |
|
510 | 529 | style_cls=_NoStyle |
|
511 | 530 | style_overrides = {} |
|
512 | 531 | else : |
|
513 | 532 | raise ValueError('Got unknown colors: ', legacy) |
|
514 | 533 | else : |
|
515 | 534 | if isinstance(name_or_cls, str): |
|
516 | 535 | style_cls = get_style_by_name(name_or_cls) |
|
517 | 536 | else: |
|
518 | 537 | style_cls = name_or_cls |
|
519 | 538 | style_overrides = { |
|
520 | 539 | Token.Prompt: '#ansigreen', |
|
521 | 540 | Token.PromptNum: '#ansibrightgreen bold', |
|
522 | 541 | Token.OutPrompt: '#ansired', |
|
523 | 542 | Token.OutPromptNum: '#ansibrightred bold', |
|
524 | 543 | } |
|
525 | 544 | style_overrides.update(self.highlighting_style_overrides) |
|
526 | 545 | style = merge_styles([ |
|
527 | 546 | style_from_pygments_cls(style_cls), |
|
528 | 547 | style_from_pygments_dict(style_overrides), |
|
529 | 548 | ]) |
|
530 | 549 | |
|
531 | 550 | return style |
|
532 | 551 | |
|
533 | 552 | @property |
|
534 | 553 | def pt_complete_style(self): |
|
535 | 554 | return { |
|
536 | 555 | 'multicolumn': CompleteStyle.MULTI_COLUMN, |
|
537 | 556 | 'column': CompleteStyle.COLUMN, |
|
538 | 557 | 'readlinelike': CompleteStyle.READLINE_LIKE, |
|
539 | 558 | }[self.display_completions] |
|
540 | 559 | |
|
541 | 560 | @property |
|
542 | 561 | def color_depth(self): |
|
543 | 562 | return (ColorDepth.TRUE_COLOR if self.true_color else None) |
|
544 | 563 | |
|
545 | 564 | def _extra_prompt_options(self): |
|
546 | 565 | """ |
|
547 | 566 | Return the current layout option for the current Terminal InteractiveShell |
|
548 | 567 | """ |
|
549 | 568 | def get_message(): |
|
550 | 569 | return PygmentsTokens(self.prompts.in_prompt_tokens()) |
|
551 | 570 | |
|
552 | 571 | if self.editing_mode == 'emacs': |
|
553 | 572 | # with emacs mode the prompt is (usually) static, so we call only |
|
554 | 573 | # the function once. With VI mode it can toggle between [ins] and |
|
555 | 574 | # [nor] so we can't precompute. |
|
556 | 575 | # here I'm going to favor the default keybinding which almost |
|
557 | 576 | # everybody uses to decrease CPU usage. |
|
558 | 577 | # if we have issues with users with custom Prompts we can see how to |
|
559 | 578 | # work around this. |
|
560 | 579 | get_message = get_message() |
|
561 | 580 | |
|
562 | 581 | options = { |
|
563 |
|
|
|
564 |
|
|
|
565 |
|
|
|
566 |
|
|
|
567 |
|
|
|
568 |
|
|
|
569 |
|
|
|
570 |
|
|
|
571 | 'complete_style': self.pt_complete_style, | |
|
572 | ||
|
582 | "complete_in_thread": False, | |
|
583 | "lexer": IPythonPTLexer(), | |
|
584 | "reserve_space_for_menu": self.space_for_menu, | |
|
585 | "message": get_message, | |
|
586 | "prompt_continuation": ( | |
|
587 | lambda width, lineno, is_soft_wrap: PygmentsTokens( | |
|
588 | self.prompts.continuation_prompt_tokens(width) | |
|
589 | ) | |
|
590 | ), | |
|
591 | "multiline": True, | |
|
592 | "complete_style": self.pt_complete_style, | |
|
593 | "input_processors": [ | |
|
573 | 594 | # Highlight matching brackets, but only when this setting is |
|
574 | 595 | # enabled, and only when the DEFAULT_BUFFER has the focus. |
|
575 |
|
|
|
576 |
|
|
|
577 |
|
|
|
578 | Condition(lambda: self.highlight_matching_brackets))], | |
|
596 | ConditionalProcessor( | |
|
597 | processor=HighlightMatchingBracketProcessor(chars="[](){}"), | |
|
598 | filter=HasFocus(DEFAULT_BUFFER) | |
|
599 | & ~IsDone() | |
|
600 | & Condition(lambda: self.highlight_matching_brackets), | |
|
601 | ), | |
|
602 | # Show auto-suggestion in lines other than the last line. | |
|
603 | ConditionalProcessor( | |
|
604 | processor=AppendAutoSuggestionInAnyLine(), | |
|
605 | filter=HasFocus(DEFAULT_BUFFER) | |
|
606 | & ~IsDone() | |
|
607 | & Condition( | |
|
608 | lambda: isinstance( | |
|
609 | self.auto_suggest, NavigableAutoSuggestFromHistory | |
|
610 | ) | |
|
611 | ), | |
|
612 | ), | |
|
613 | ], | |
|
579 | 614 |
|
|
580 | 615 | if not PTK3: |
|
581 | 616 | options['inputhook'] = self.inputhook |
|
582 | 617 | |
|
583 | 618 | return options |
|
584 | 619 | |
|
585 | 620 | def prompt_for_code(self): |
|
586 | 621 | if self.rl_next_input: |
|
587 | 622 | default = self.rl_next_input |
|
588 | 623 | self.rl_next_input = None |
|
589 | 624 | else: |
|
590 | 625 | default = '' |
|
591 | 626 | |
|
592 | 627 | # In order to make sure that asyncio code written in the |
|
593 | 628 | # interactive shell doesn't interfere with the prompt, we run the |
|
594 | 629 | # prompt in a different event loop. |
|
595 | 630 | # If we don't do this, people could spawn coroutine with a |
|
596 | 631 | # while/true inside which will freeze the prompt. |
|
597 | 632 | |
|
598 | 633 | policy = asyncio.get_event_loop_policy() |
|
599 | 634 | old_loop = get_asyncio_loop() |
|
600 | 635 | |
|
601 | 636 | # FIXME: prompt_toolkit is using the deprecated `asyncio.get_event_loop` |
|
602 | 637 | # to get the current event loop. |
|
603 | 638 | # This will probably be replaced by an attribute or input argument, |
|
604 | 639 | # at which point we can stop calling the soon-to-be-deprecated `set_event_loop` here. |
|
605 | 640 | if old_loop is not self.pt_loop: |
|
606 | 641 | policy.set_event_loop(self.pt_loop) |
|
607 | 642 | try: |
|
608 | 643 | with patch_stdout(raw=True): |
|
609 | 644 | text = self.pt_app.prompt( |
|
610 | 645 | default=default, |
|
611 | 646 | **self._extra_prompt_options()) |
|
612 | 647 | finally: |
|
613 | 648 | # Restore the original event loop. |
|
614 | 649 | if old_loop is not None and old_loop is not self.pt_loop: |
|
615 | 650 | policy.set_event_loop(old_loop) |
|
616 | 651 | |
|
617 | 652 | return text |
|
618 | 653 | |
|
619 | 654 | def enable_win_unicode_console(self): |
|
620 | 655 | # Since IPython 7.10 doesn't support python < 3.6 and PEP 528, Python uses the unicode APIs for the Windows |
|
621 | 656 | # console by default, so WUC shouldn't be needed. |
|
622 | 657 | warn("`enable_win_unicode_console` is deprecated since IPython 7.10, does not do anything and will be removed in the future", |
|
623 | 658 | DeprecationWarning, |
|
624 | 659 | stacklevel=2) |
|
625 | 660 | |
|
626 | 661 | def init_io(self): |
|
627 | 662 | if sys.platform not in {'win32', 'cli'}: |
|
628 | 663 | return |
|
629 | 664 | |
|
630 | 665 | import colorama |
|
631 | 666 | colorama.init() |
|
632 | 667 | |
|
633 | 668 | def init_magics(self): |
|
634 | 669 | super(TerminalInteractiveShell, self).init_magics() |
|
635 | 670 | self.register_magics(TerminalMagics) |
|
636 | 671 | |
|
637 | 672 | def init_alias(self): |
|
638 | 673 | # The parent class defines aliases that can be safely used with any |
|
639 | 674 | # frontend. |
|
640 | 675 | super(TerminalInteractiveShell, self).init_alias() |
|
641 | 676 | |
|
642 | 677 | # Now define aliases that only make sense on the terminal, because they |
|
643 | 678 | # need direct access to the console in a way that we can't emulate in |
|
644 | 679 | # GUI or web frontend |
|
645 | 680 | if os.name == 'posix': |
|
646 | 681 | for cmd in ('clear', 'more', 'less', 'man'): |
|
647 | 682 | self.alias_manager.soft_define_alias(cmd, cmd) |
|
648 | 683 | |
|
649 | 684 | |
|
650 | def __init__(self, *args, **kwargs): | |
|
685 | def __init__(self, *args, **kwargs) -> None: | |
|
651 | 686 | super(TerminalInteractiveShell, self).__init__(*args, **kwargs) |
|
652 | 687 | self._set_autosuggestions(self.autosuggestions_provider) |
|
653 | 688 | self.init_prompt_toolkit_cli() |
|
654 | 689 | self.init_term_title() |
|
655 | 690 | self.keep_running = True |
|
656 | 691 | self._set_formatter(self.autoformatter) |
|
657 | 692 | |
|
658 | 693 | |
|
659 | 694 | def ask_exit(self): |
|
660 | 695 | self.keep_running = False |
|
661 | 696 | |
|
662 | 697 | rl_next_input = None |
|
663 | 698 | |
|
664 | 699 | def interact(self): |
|
665 | 700 | self.keep_running = True |
|
666 | 701 | while self.keep_running: |
|
667 | 702 | print(self.separate_in, end='') |
|
668 | 703 | |
|
669 | 704 | try: |
|
670 | 705 | code = self.prompt_for_code() |
|
671 | 706 | except EOFError: |
|
672 | 707 | if (not self.confirm_exit) \ |
|
673 | 708 | or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'): |
|
674 | 709 | self.ask_exit() |
|
675 | 710 | |
|
676 | 711 | else: |
|
677 | 712 | if code: |
|
678 | 713 | self.run_cell(code, store_history=True) |
|
679 | 714 | |
|
680 | 715 | def mainloop(self): |
|
681 | 716 | # An extra layer of protection in case someone mashing Ctrl-C breaks |
|
682 | 717 | # out of our internal code. |
|
683 | 718 | while True: |
|
684 | 719 | try: |
|
685 | 720 | self.interact() |
|
686 | 721 | break |
|
687 | 722 | except KeyboardInterrupt as e: |
|
688 | 723 | print("\n%s escaped interact()\n" % type(e).__name__) |
|
689 | 724 | finally: |
|
690 | 725 | # An interrupt during the eventloop will mess up the |
|
691 | 726 | # internal state of the prompt_toolkit library. |
|
692 | 727 | # Stopping the eventloop fixes this, see |
|
693 | 728 | # https://github.com/ipython/ipython/pull/9867 |
|
694 | 729 | if hasattr(self, '_eventloop'): |
|
695 | 730 | self._eventloop.stop() |
|
696 | 731 | |
|
697 | 732 | self.restore_term_title() |
|
698 | 733 | |
|
699 | 734 | # try to call some at-exit operation optimistically as some things can't |
|
700 | 735 | # be done during interpreter shutdown. this is technically inaccurate as |
|
701 | 736 | # this make mainlool not re-callable, but that should be a rare if not |
|
702 | 737 | # in existent use case. |
|
703 | 738 | |
|
704 | 739 | self._atexit_once() |
|
705 | 740 | |
|
706 | 741 | |
|
707 | 742 | _inputhook = None |
|
708 | 743 | def inputhook(self, context): |
|
709 | 744 | if self._inputhook is not None: |
|
710 | 745 | self._inputhook(context) |
|
711 | 746 | |
|
712 | 747 | active_eventloop = None |
|
713 | 748 | def enable_gui(self, gui=None): |
|
714 | 749 | if gui and (gui not in {"inline", "webagg"}): |
|
715 | 750 | self.active_eventloop, self._inputhook = get_inputhook_name_and_func(gui) |
|
716 | 751 | else: |
|
717 | 752 | self.active_eventloop = self._inputhook = None |
|
718 | 753 | |
|
719 | 754 | # For prompt_toolkit 3.0. We have to create an asyncio event loop with |
|
720 | 755 | # this inputhook. |
|
721 | 756 | if PTK3: |
|
722 | 757 | import asyncio |
|
723 | 758 | from prompt_toolkit.eventloop import new_eventloop_with_inputhook |
|
724 | 759 | |
|
725 | 760 | if gui == 'asyncio': |
|
726 | 761 | # When we integrate the asyncio event loop, run the UI in the |
|
727 | 762 | # same event loop as the rest of the code. don't use an actual |
|
728 | 763 | # input hook. (Asyncio is not made for nesting event loops.) |
|
729 | 764 | self.pt_loop = get_asyncio_loop() |
|
730 | 765 | |
|
731 | 766 | elif self._inputhook: |
|
732 | 767 | # If an inputhook was set, create a new asyncio event loop with |
|
733 | 768 | # this inputhook for the prompt. |
|
734 | 769 | self.pt_loop = new_eventloop_with_inputhook(self._inputhook) |
|
735 | 770 | else: |
|
736 | 771 | # When there's no inputhook, run the prompt in a separate |
|
737 | 772 | # asyncio event loop. |
|
738 | 773 | self.pt_loop = asyncio.new_event_loop() |
|
739 | 774 | |
|
740 | 775 | # Run !system commands directly, not through pipes, so terminal programs |
|
741 | 776 | # work correctly. |
|
742 | 777 | system = InteractiveShell.system_raw |
|
743 | 778 | |
|
744 | 779 | def auto_rewrite_input(self, cmd): |
|
745 | 780 | """Overridden from the parent class to use fancy rewriting prompt""" |
|
746 | 781 | if not self.show_rewritten_input: |
|
747 | 782 | return |
|
748 | 783 | |
|
749 | 784 | tokens = self.prompts.rewrite_prompt_tokens() |
|
750 | 785 | if self.pt_app: |
|
751 | 786 | print_formatted_text(PygmentsTokens(tokens), end='', |
|
752 | 787 | style=self.pt_app.app.style) |
|
753 | 788 | print(cmd) |
|
754 | 789 | else: |
|
755 | 790 | prompt = ''.join(s for t, s in tokens) |
|
756 | 791 | print(prompt, cmd, sep='') |
|
757 | 792 | |
|
758 | 793 | _prompts_before = None |
|
759 | 794 | def switch_doctest_mode(self, mode): |
|
760 | 795 | """Switch prompts to classic for %doctest_mode""" |
|
761 | 796 | if mode: |
|
762 | 797 | self._prompts_before = self.prompts |
|
763 | 798 | self.prompts = ClassicPrompts(self) |
|
764 | 799 | elif self._prompts_before: |
|
765 | 800 | self.prompts = self._prompts_before |
|
766 | 801 | self._prompts_before = None |
|
767 | 802 | # self._update_layout() |
|
768 | 803 | |
|
769 | 804 | |
|
770 | 805 | InteractiveShellABC.register(TerminalInteractiveShell) |
|
771 | 806 | |
|
772 | 807 | if __name__ == '__main__': |
|
773 | 808 | TerminalInteractiveShell.instance().interact() |
@@ -1,343 +1,343 b'' | |||
|
1 | 1 | #!/usr/bin/env python |
|
2 | 2 | # encoding: utf-8 |
|
3 | 3 | """ |
|
4 | 4 | The :class:`~traitlets.config.application.Application` object for the command |
|
5 | 5 | line :command:`ipython` program. |
|
6 | 6 | """ |
|
7 | 7 | |
|
8 | 8 | # Copyright (c) IPython Development Team. |
|
9 | 9 | # Distributed under the terms of the Modified BSD License. |
|
10 | 10 | |
|
11 | 11 | |
|
12 | 12 | import logging |
|
13 | 13 | import os |
|
14 | 14 | import sys |
|
15 | 15 | import warnings |
|
16 | 16 | |
|
17 | 17 | from traitlets.config.loader import Config |
|
18 | 18 | from traitlets.config.application import boolean_flag, catch_config_error |
|
19 | 19 | from IPython.core import release |
|
20 | 20 | from IPython.core import usage |
|
21 | 21 | from IPython.core.completer import IPCompleter |
|
22 | 22 | from IPython.core.crashhandler import CrashHandler |
|
23 | 23 | from IPython.core.formatters import PlainTextFormatter |
|
24 | 24 | from IPython.core.history import HistoryManager |
|
25 | 25 | from IPython.core.application import ( |
|
26 | 26 | ProfileDir, BaseIPythonApplication, base_flags, base_aliases |
|
27 | 27 | ) |
|
28 | 28 | from IPython.core.magic import MagicsManager |
|
29 | 29 | from IPython.core.magics import ( |
|
30 | 30 | ScriptMagics, LoggingMagics |
|
31 | 31 | ) |
|
32 | 32 | from IPython.core.shellapp import ( |
|
33 | 33 | InteractiveShellApp, shell_flags, shell_aliases |
|
34 | 34 | ) |
|
35 | 35 | from IPython.extensions.storemagic import StoreMagics |
|
36 | 36 | from .interactiveshell import TerminalInteractiveShell |
|
37 | 37 | from IPython.paths import get_ipython_dir |
|
38 | 38 | from traitlets import ( |
|
39 | 39 | Bool, List, default, observe, Type |
|
40 | 40 | ) |
|
41 | 41 | |
|
42 | 42 | #----------------------------------------------------------------------------- |
|
43 | 43 | # Globals, utilities and helpers |
|
44 | 44 | #----------------------------------------------------------------------------- |
|
45 | 45 | |
|
46 | 46 | _examples = """ |
|
47 | 47 | ipython --matplotlib # enable matplotlib integration |
|
48 | 48 | ipython --matplotlib=qt # enable matplotlib integration with qt4 backend |
|
49 | 49 | |
|
50 | 50 | ipython --log-level=DEBUG # set logging to DEBUG |
|
51 | 51 | ipython --profile=foo # start with profile foo |
|
52 | 52 | |
|
53 | 53 | ipython profile create foo # create profile foo w/ default config files |
|
54 | 54 | ipython help profile # show the help for the profile subcmd |
|
55 | 55 | |
|
56 | 56 | ipython locate # print the path to the IPython directory |
|
57 | 57 | ipython locate profile foo # print the path to the directory for profile `foo` |
|
58 | 58 | """ |
|
59 | 59 | |
|
60 | 60 | #----------------------------------------------------------------------------- |
|
61 | 61 | # Crash handler for this application |
|
62 | 62 | #----------------------------------------------------------------------------- |
|
63 | 63 | |
|
64 | 64 | class IPAppCrashHandler(CrashHandler): |
|
65 | 65 | """sys.excepthook for IPython itself, leaves a detailed report on disk.""" |
|
66 | 66 | |
|
67 | 67 | def __init__(self, app): |
|
68 | 68 | contact_name = release.author |
|
69 | 69 | contact_email = release.author_email |
|
70 | 70 | bug_tracker = 'https://github.com/ipython/ipython/issues' |
|
71 | 71 | super(IPAppCrashHandler,self).__init__( |
|
72 | 72 | app, contact_name, contact_email, bug_tracker |
|
73 | 73 | ) |
|
74 | 74 | |
|
75 | 75 | def make_report(self,traceback): |
|
76 | 76 | """Return a string containing a crash report.""" |
|
77 | 77 | |
|
78 | 78 | sec_sep = self.section_sep |
|
79 | 79 | # Start with parent report |
|
80 | 80 | report = [super(IPAppCrashHandler, self).make_report(traceback)] |
|
81 | 81 | # Add interactive-specific info we may have |
|
82 | 82 | rpt_add = report.append |
|
83 | 83 | try: |
|
84 | 84 | rpt_add(sec_sep+"History of session input:") |
|
85 | 85 | for line in self.app.shell.user_ns['_ih']: |
|
86 | 86 | rpt_add(line) |
|
87 | 87 | rpt_add('\n*** Last line of input (may not be in above history):\n') |
|
88 | 88 | rpt_add(self.app.shell._last_input_line+'\n') |
|
89 | 89 | except: |
|
90 | 90 | pass |
|
91 | 91 | |
|
92 | 92 | return ''.join(report) |
|
93 | 93 | |
|
94 | 94 | #----------------------------------------------------------------------------- |
|
95 | 95 | # Aliases and Flags |
|
96 | 96 | #----------------------------------------------------------------------------- |
|
97 | 97 | flags = dict(base_flags) |
|
98 | 98 | flags.update(shell_flags) |
|
99 | 99 | frontend_flags = {} |
|
100 | 100 | addflag = lambda *args: frontend_flags.update(boolean_flag(*args)) |
|
101 | 101 | addflag('autoedit-syntax', 'TerminalInteractiveShell.autoedit_syntax', |
|
102 | 102 | 'Turn on auto editing of files with syntax errors.', |
|
103 | 103 | 'Turn off auto editing of files with syntax errors.' |
|
104 | 104 | ) |
|
105 | 105 | addflag('simple-prompt', 'TerminalInteractiveShell.simple_prompt', |
|
106 | 106 | "Force simple minimal prompt using `raw_input`", |
|
107 | 107 | "Use a rich interactive prompt with prompt_toolkit", |
|
108 | 108 | ) |
|
109 | 109 | |
|
110 | 110 | addflag('banner', 'TerminalIPythonApp.display_banner', |
|
111 | 111 | "Display a banner upon starting IPython.", |
|
112 | 112 | "Don't display a banner upon starting IPython." |
|
113 | 113 | ) |
|
114 | 114 | addflag('confirm-exit', 'TerminalInteractiveShell.confirm_exit', |
|
115 | 115 | """Set to confirm when you try to exit IPython with an EOF (Control-D |
|
116 | 116 | in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit', |
|
117 | 117 | you can force a direct exit without any confirmation.""", |
|
118 | 118 | "Don't prompt the user when exiting." |
|
119 | 119 | ) |
|
120 | 120 | addflag('term-title', 'TerminalInteractiveShell.term_title', |
|
121 | 121 | "Enable auto setting the terminal title.", |
|
122 | 122 | "Disable auto setting the terminal title." |
|
123 | 123 | ) |
|
124 | 124 | classic_config = Config() |
|
125 | 125 | classic_config.InteractiveShell.cache_size = 0 |
|
126 | 126 | classic_config.PlainTextFormatter.pprint = False |
|
127 | 127 | classic_config.TerminalInteractiveShell.prompts_class='IPython.terminal.prompts.ClassicPrompts' |
|
128 | 128 | classic_config.InteractiveShell.separate_in = '' |
|
129 | 129 | classic_config.InteractiveShell.separate_out = '' |
|
130 | 130 | classic_config.InteractiveShell.separate_out2 = '' |
|
131 | 131 | classic_config.InteractiveShell.colors = 'NoColor' |
|
132 | 132 | classic_config.InteractiveShell.xmode = 'Plain' |
|
133 | 133 | |
|
134 | 134 | frontend_flags['classic']=( |
|
135 | 135 | classic_config, |
|
136 | 136 | "Gives IPython a similar feel to the classic Python prompt." |
|
137 | 137 | ) |
|
138 | 138 | # # log doesn't make so much sense this way anymore |
|
139 | 139 | # paa('--log','-l', |
|
140 | 140 | # action='store_true', dest='InteractiveShell.logstart', |
|
141 | 141 | # help="Start logging to the default log file (./ipython_log.py).") |
|
142 | 142 | # |
|
143 | 143 | # # quick is harder to implement |
|
144 | 144 | frontend_flags['quick']=( |
|
145 | 145 | {'TerminalIPythonApp' : {'quick' : True}}, |
|
146 | 146 | "Enable quick startup with no config files." |
|
147 | 147 | ) |
|
148 | 148 | |
|
149 | 149 | frontend_flags['i'] = ( |
|
150 | 150 | {'TerminalIPythonApp' : {'force_interact' : True}}, |
|
151 | 151 | """If running code from the command line, become interactive afterwards. |
|
152 | 152 | It is often useful to follow this with `--` to treat remaining flags as |
|
153 | 153 | script arguments. |
|
154 | 154 | """ |
|
155 | 155 | ) |
|
156 | 156 | flags.update(frontend_flags) |
|
157 | 157 | |
|
158 | 158 | aliases = dict(base_aliases) |
|
159 | aliases.update(shell_aliases) | |
|
159 | aliases.update(shell_aliases) # type: ignore[arg-type] | |
|
160 | 160 | |
|
161 | 161 | #----------------------------------------------------------------------------- |
|
162 | 162 | # Main classes and functions |
|
163 | 163 | #----------------------------------------------------------------------------- |
|
164 | 164 | |
|
165 | 165 | |
|
166 | 166 | class LocateIPythonApp(BaseIPythonApplication): |
|
167 | 167 | description = """print the path to the IPython dir""" |
|
168 | 168 | subcommands = dict( |
|
169 | 169 | profile=('IPython.core.profileapp.ProfileLocate', |
|
170 | 170 | "print the path to an IPython profile directory", |
|
171 | 171 | ), |
|
172 | 172 | ) |
|
173 | 173 | def start(self): |
|
174 | 174 | if self.subapp is not None: |
|
175 | 175 | return self.subapp.start() |
|
176 | 176 | else: |
|
177 | 177 | print(self.ipython_dir) |
|
178 | 178 | |
|
179 | 179 | |
|
180 | 180 | class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp): |
|
181 | 181 | name = u'ipython' |
|
182 | 182 | description = usage.cl_usage |
|
183 | crash_handler_class = IPAppCrashHandler | |
|
183 | crash_handler_class = IPAppCrashHandler # typing: ignore[assignment] | |
|
184 | 184 | examples = _examples |
|
185 | 185 | |
|
186 | 186 | flags = flags |
|
187 | 187 | aliases = aliases |
|
188 | 188 | classes = List() |
|
189 | 189 | |
|
190 | 190 | interactive_shell_class = Type( |
|
191 | 191 | klass=object, # use default_value otherwise which only allow subclasses. |
|
192 | 192 | default_value=TerminalInteractiveShell, |
|
193 | 193 | help="Class to use to instantiate the TerminalInteractiveShell object. Useful for custom Frontends" |
|
194 | 194 | ).tag(config=True) |
|
195 | 195 | |
|
196 | 196 | @default('classes') |
|
197 | 197 | def _classes_default(self): |
|
198 | 198 | """This has to be in a method, for TerminalIPythonApp to be available.""" |
|
199 | 199 | return [ |
|
200 | 200 | InteractiveShellApp, # ShellApp comes before TerminalApp, because |
|
201 | 201 | self.__class__, # it will also affect subclasses (e.g. QtConsole) |
|
202 | 202 | TerminalInteractiveShell, |
|
203 | 203 | HistoryManager, |
|
204 | 204 | MagicsManager, |
|
205 | 205 | ProfileDir, |
|
206 | 206 | PlainTextFormatter, |
|
207 | 207 | IPCompleter, |
|
208 | 208 | ScriptMagics, |
|
209 | 209 | LoggingMagics, |
|
210 | 210 | StoreMagics, |
|
211 | 211 | ] |
|
212 | 212 | |
|
213 | 213 | subcommands = dict( |
|
214 | 214 | profile = ("IPython.core.profileapp.ProfileApp", |
|
215 | 215 | "Create and manage IPython profiles." |
|
216 | 216 | ), |
|
217 | 217 | kernel = ("ipykernel.kernelapp.IPKernelApp", |
|
218 | 218 | "Start a kernel without an attached frontend." |
|
219 | 219 | ), |
|
220 | 220 | locate=('IPython.terminal.ipapp.LocateIPythonApp', |
|
221 | 221 | LocateIPythonApp.description |
|
222 | 222 | ), |
|
223 | 223 | history=('IPython.core.historyapp.HistoryApp', |
|
224 | 224 | "Manage the IPython history database." |
|
225 | 225 | ), |
|
226 | 226 | ) |
|
227 | 227 | |
|
228 | 228 | |
|
229 | 229 | # *do* autocreate requested profile, but don't create the config file. |
|
230 | 230 | auto_create=Bool(True) |
|
231 | 231 | # configurables |
|
232 | 232 | quick = Bool(False, |
|
233 | 233 | help="""Start IPython quickly by skipping the loading of config files.""" |
|
234 | 234 | ).tag(config=True) |
|
235 | 235 | @observe('quick') |
|
236 | 236 | def _quick_changed(self, change): |
|
237 | 237 | if change['new']: |
|
238 | 238 | self.load_config_file = lambda *a, **kw: None |
|
239 | 239 | |
|
240 | 240 | display_banner = Bool(True, |
|
241 | 241 | help="Whether to display a banner upon starting IPython." |
|
242 | 242 | ).tag(config=True) |
|
243 | 243 | |
|
244 | 244 | # if there is code of files to run from the cmd line, don't interact |
|
245 | 245 | # unless the --i flag (App.force_interact) is true. |
|
246 | 246 | force_interact = Bool(False, |
|
247 | 247 | help="""If a command or file is given via the command-line, |
|
248 | 248 | e.g. 'ipython foo.py', start an interactive shell after executing the |
|
249 | 249 | file or command.""" |
|
250 | 250 | ).tag(config=True) |
|
251 | 251 | @observe('force_interact') |
|
252 | 252 | def _force_interact_changed(self, change): |
|
253 | 253 | if change['new']: |
|
254 | 254 | self.interact = True |
|
255 | 255 | |
|
256 | 256 | @observe('file_to_run', 'code_to_run', 'module_to_run') |
|
257 | 257 | def _file_to_run_changed(self, change): |
|
258 | 258 | new = change['new'] |
|
259 | 259 | if new: |
|
260 | 260 | self.something_to_run = True |
|
261 | 261 | if new and not self.force_interact: |
|
262 | 262 | self.interact = False |
|
263 | 263 | |
|
264 | 264 | # internal, not-configurable |
|
265 | 265 | something_to_run=Bool(False) |
|
266 | 266 | |
|
267 | 267 | @catch_config_error |
|
268 | 268 | def initialize(self, argv=None): |
|
269 | 269 | """Do actions after construct, but before starting the app.""" |
|
270 | 270 | super(TerminalIPythonApp, self).initialize(argv) |
|
271 | 271 | if self.subapp is not None: |
|
272 | 272 | # don't bother initializing further, starting subapp |
|
273 | 273 | return |
|
274 | 274 | # print self.extra_args |
|
275 | 275 | if self.extra_args and not self.something_to_run: |
|
276 | 276 | self.file_to_run = self.extra_args[0] |
|
277 | 277 | self.init_path() |
|
278 | 278 | # create the shell |
|
279 | 279 | self.init_shell() |
|
280 | 280 | # and draw the banner |
|
281 | 281 | self.init_banner() |
|
282 | 282 | # Now a variety of things that happen after the banner is printed. |
|
283 | 283 | self.init_gui_pylab() |
|
284 | 284 | self.init_extensions() |
|
285 | 285 | self.init_code() |
|
286 | 286 | |
|
287 | 287 | def init_shell(self): |
|
288 | 288 | """initialize the InteractiveShell instance""" |
|
289 | 289 | # Create an InteractiveShell instance. |
|
290 | 290 | # shell.display_banner should always be False for the terminal |
|
291 | 291 | # based app, because we call shell.show_banner() by hand below |
|
292 | 292 | # so the banner shows *before* all extension loading stuff. |
|
293 | 293 | self.shell = self.interactive_shell_class.instance(parent=self, |
|
294 | 294 | profile_dir=self.profile_dir, |
|
295 | 295 | ipython_dir=self.ipython_dir, user_ns=self.user_ns) |
|
296 | 296 | self.shell.configurables.append(self) |
|
297 | 297 | |
|
298 | 298 | def init_banner(self): |
|
299 | 299 | """optionally display the banner""" |
|
300 | 300 | if self.display_banner and self.interact: |
|
301 | 301 | self.shell.show_banner() |
|
302 | 302 | # Make sure there is a space below the banner. |
|
303 | 303 | if self.log_level <= logging.INFO: print() |
|
304 | 304 | |
|
305 | 305 | def _pylab_changed(self, name, old, new): |
|
306 | 306 | """Replace --pylab='inline' with --pylab='auto'""" |
|
307 | 307 | if new == 'inline': |
|
308 | 308 | warnings.warn("'inline' not available as pylab backend, " |
|
309 | 309 | "using 'auto' instead.") |
|
310 | 310 | self.pylab = 'auto' |
|
311 | 311 | |
|
312 | 312 | def start(self): |
|
313 | 313 | if self.subapp is not None: |
|
314 | 314 | return self.subapp.start() |
|
315 | 315 | # perform any prexec steps: |
|
316 | 316 | if self.interact: |
|
317 | 317 | self.log.debug("Starting IPython's mainloop...") |
|
318 | 318 | self.shell.mainloop() |
|
319 | 319 | else: |
|
320 | 320 | self.log.debug("IPython not interactive...") |
|
321 | 321 | self.shell.restore_term_title() |
|
322 | 322 | if not self.shell.last_execution_succeeded: |
|
323 | 323 | sys.exit(1) |
|
324 | 324 | |
|
325 | 325 | def load_default_config(ipython_dir=None): |
|
326 | 326 | """Load the default config file from the default ipython_dir. |
|
327 | 327 | |
|
328 | 328 | This is useful for embedded shells. |
|
329 | 329 | """ |
|
330 | 330 | if ipython_dir is None: |
|
331 | 331 | ipython_dir = get_ipython_dir() |
|
332 | 332 | |
|
333 | 333 | profile_dir = os.path.join(ipython_dir, 'profile_default') |
|
334 | 334 | app = TerminalIPythonApp() |
|
335 | 335 | app.config_file_paths.append(profile_dir) |
|
336 | 336 | app.load_config_file() |
|
337 | 337 | return app.config |
|
338 | 338 | |
|
339 | 339 | launch_new_instance = TerminalIPythonApp.launch_instance |
|
340 | 340 | |
|
341 | 341 | |
|
342 | 342 | if __name__ == '__main__': |
|
343 | 343 | launch_new_instance() |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed | |
This diff has been collapsed as it changes many lines, (608 lines changed) Show them Hide them |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
General Comments 0
You need to be logged in to leave comments.
Login now