##// END OF EJS Templates
Merge pull request #13388 from Carreau/reformat-docstring...
Matthias Bussonnier -
r27298:9065df74 merge
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,2253 +1,2261 b''
1 """Completion for IPython.
1 """Completion for IPython.
2
2
3 This module started as fork of the rlcompleter module in the Python standard
3 This module started as fork of the rlcompleter module in the Python standard
4 library. The original enhancements made to rlcompleter have been sent
4 library. The original enhancements made to rlcompleter have been sent
5 upstream and were accepted as of Python 2.3,
5 upstream and were accepted as of Python 2.3,
6
6
7 This module now support a wide variety of completion mechanism both available
7 This module now support a wide variety of completion mechanism both available
8 for normal classic Python code, as well as completer for IPython specific
8 for normal classic Python code, as well as completer for IPython specific
9 Syntax like magics.
9 Syntax like magics.
10
10
11 Latex and Unicode completion
11 Latex and Unicode completion
12 ============================
12 ============================
13
13
14 IPython and compatible frontends not only can complete your code, but can help
14 IPython and compatible frontends not only can complete your code, but can help
15 you to input a wide range of characters. In particular we allow you to insert
15 you to input a wide range of characters. In particular we allow you to insert
16 a unicode character using the tab completion mechanism.
16 a unicode character using the tab completion mechanism.
17
17
18 Forward latex/unicode completion
18 Forward latex/unicode completion
19 --------------------------------
19 --------------------------------
20
20
21 Forward completion allows you to easily type a unicode character using its latex
21 Forward completion allows you to easily type a unicode character using its latex
22 name, or unicode long description. To do so type a backslash follow by the
22 name, or unicode long description. To do so type a backslash follow by the
23 relevant name and press tab:
23 relevant name and press tab:
24
24
25
25
26 Using latex completion:
26 Using latex completion:
27
27
28 .. code::
28 .. code::
29
29
30 \\alpha<tab>
30 \\alpha<tab>
31 α
31 α
32
32
33 or using unicode completion:
33 or using unicode completion:
34
34
35
35
36 .. code::
36 .. code::
37
37
38 \\GREEK SMALL LETTER ALPHA<tab>
38 \\GREEK SMALL LETTER ALPHA<tab>
39 α
39 α
40
40
41
41
42 Only valid Python identifiers will complete. Combining characters (like arrow or
42 Only valid Python identifiers will complete. Combining characters (like arrow or
43 dots) are also available, unlike latex they need to be put after the their
43 dots) are also available, unlike latex they need to be put after the their
44 counterpart that is to say, `F\\\\vec<tab>` is correct, not `\\\\vec<tab>F`.
44 counterpart that is to say, `F\\\\vec<tab>` is correct, not `\\\\vec<tab>F`.
45
45
46 Some browsers are known to display combining characters incorrectly.
46 Some browsers are known to display combining characters incorrectly.
47
47
48 Backward latex completion
48 Backward latex completion
49 -------------------------
49 -------------------------
50
50
51 It is sometime challenging to know how to type a character, if you are using
51 It is sometime challenging to know how to type a character, if you are using
52 IPython, or any compatible frontend you can prepend backslash to the character
52 IPython, or any compatible frontend you can prepend backslash to the character
53 and press `<tab>` to expand it to its latex form.
53 and press `<tab>` to expand it to its latex form.
54
54
55 .. code::
55 .. code::
56
56
57 \\α<tab>
57 \\α<tab>
58 \\alpha
58 \\alpha
59
59
60
60
61 Both forward and backward completions can be deactivated by setting the
61 Both forward and backward completions can be deactivated by setting the
62 ``Completer.backslash_combining_completions`` option to ``False``.
62 ``Completer.backslash_combining_completions`` option to ``False``.
63
63
64
64
65 Experimental
65 Experimental
66 ============
66 ============
67
67
68 Starting with IPython 6.0, this module can make use of the Jedi library to
68 Starting with IPython 6.0, this module can make use of the Jedi library to
69 generate completions both using static analysis of the code, and dynamically
69 generate completions both using static analysis of the code, and dynamically
70 inspecting multiple namespaces. Jedi is an autocompletion and static analysis
70 inspecting multiple namespaces. Jedi is an autocompletion and static analysis
71 for Python. The APIs attached to this new mechanism is unstable and will
71 for Python. The APIs attached to this new mechanism is unstable and will
72 raise unless use in an :any:`provisionalcompleter` context manager.
72 raise unless use in an :any:`provisionalcompleter` context manager.
73
73
74 You will find that the following are experimental:
74 You will find that the following are experimental:
75
75
76 - :any:`provisionalcompleter`
76 - :any:`provisionalcompleter`
77 - :any:`IPCompleter.completions`
77 - :any:`IPCompleter.completions`
78 - :any:`Completion`
78 - :any:`Completion`
79 - :any:`rectify_completions`
79 - :any:`rectify_completions`
80
80
81 .. note::
81 .. note::
82
82
83 better name for :any:`rectify_completions` ?
83 better name for :any:`rectify_completions` ?
84
84
85 We welcome any feedback on these new API, and we also encourage you to try this
85 We welcome any feedback on these new API, and we also encourage you to try this
86 module in debug mode (start IPython with ``--Completer.debug=True``) in order
86 module in debug mode (start IPython with ``--Completer.debug=True``) in order
87 to have extra logging information if :any:`jedi` is crashing, or if current
87 to have extra logging information if :any:`jedi` is crashing, or if current
88 IPython completer pending deprecations are returning results not yet handled
88 IPython completer pending deprecations are returning results not yet handled
89 by :any:`jedi`
89 by :any:`jedi`
90
90
91 Using Jedi for tab completion allow snippets like the following to work without
91 Using Jedi for tab completion allow snippets like the following to work without
92 having to execute any code:
92 having to execute any code:
93
93
94 >>> myvar = ['hello', 42]
94 >>> myvar = ['hello', 42]
95 ... myvar[1].bi<tab>
95 ... myvar[1].bi<tab>
96
96
97 Tab completion will be able to infer that ``myvar[1]`` is a real number without
97 Tab completion will be able to infer that ``myvar[1]`` is a real number without
98 executing any code unlike the previously available ``IPCompleter.greedy``
98 executing any code unlike the previously available ``IPCompleter.greedy``
99 option.
99 option.
100
100
101 Be sure to update :any:`jedi` to the latest stable version or to try the
101 Be sure to update :any:`jedi` to the latest stable version or to try the
102 current development version to get better completions.
102 current development version to get better completions.
103 """
103 """
104
104
105
105
106 # Copyright (c) IPython Development Team.
106 # Copyright (c) IPython Development Team.
107 # Distributed under the terms of the Modified BSD License.
107 # Distributed under the terms of the Modified BSD License.
108 #
108 #
109 # Some of this code originated from rlcompleter in the Python standard library
109 # Some of this code originated from rlcompleter in the Python standard library
110 # Copyright (C) 2001 Python Software Foundation, www.python.org
110 # Copyright (C) 2001 Python Software Foundation, www.python.org
111
111
112
112
113 import builtins as builtin_mod
113 import builtins as builtin_mod
114 import glob
114 import glob
115 import inspect
115 import inspect
116 import itertools
116 import itertools
117 import keyword
117 import keyword
118 import os
118 import os
119 import re
119 import re
120 import string
120 import string
121 import sys
121 import sys
122 import time
122 import time
123 import unicodedata
123 import unicodedata
124 import uuid
124 import uuid
125 import warnings
125 import warnings
126 from contextlib import contextmanager
126 from contextlib import contextmanager
127 from importlib import import_module
127 from importlib import import_module
128 from types import SimpleNamespace
128 from types import SimpleNamespace
129 from typing import Iterable, Iterator, List, Tuple, Union, Any, Sequence, Dict, NamedTuple, Pattern, Optional
129 from typing import Iterable, Iterator, List, Tuple, Union, Any, Sequence, Dict, NamedTuple, Pattern, Optional
130
130
131 from IPython.core.error import TryNext
131 from IPython.core.error import TryNext
132 from IPython.core.inputtransformer2 import ESC_MAGIC
132 from IPython.core.inputtransformer2 import ESC_MAGIC
133 from IPython.core.latex_symbols import latex_symbols, reverse_latex_symbol
133 from IPython.core.latex_symbols import latex_symbols, reverse_latex_symbol
134 from IPython.core.oinspect import InspectColors
134 from IPython.core.oinspect import InspectColors
135 from IPython.testing.skipdoctest import skip_doctest
135 from IPython.testing.skipdoctest import skip_doctest
136 from IPython.utils import generics
136 from IPython.utils import generics
137 from IPython.utils.dir2 import dir2, get_real_method
137 from IPython.utils.dir2 import dir2, get_real_method
138 from IPython.utils.path import ensure_dir_exists
138 from IPython.utils.path import ensure_dir_exists
139 from IPython.utils.process import arg_split
139 from IPython.utils.process import arg_split
140 from traitlets import Bool, Enum, Int, List as ListTrait, Unicode, default, observe
140 from traitlets import Bool, Enum, Int, List as ListTrait, Unicode, default, observe
141 from traitlets.config.configurable import Configurable
141 from traitlets.config.configurable import Configurable
142
142
143 import __main__
143 import __main__
144
144
145 # skip module docstests
145 # skip module docstests
146 __skip_doctest__ = True
146 __skip_doctest__ = True
147
147
148 try:
148 try:
149 import jedi
149 import jedi
150 jedi.settings.case_insensitive_completion = False
150 jedi.settings.case_insensitive_completion = False
151 import jedi.api.helpers
151 import jedi.api.helpers
152 import jedi.api.classes
152 import jedi.api.classes
153 JEDI_INSTALLED = True
153 JEDI_INSTALLED = True
154 except ImportError:
154 except ImportError:
155 JEDI_INSTALLED = False
155 JEDI_INSTALLED = False
156 #-----------------------------------------------------------------------------
156 #-----------------------------------------------------------------------------
157 # Globals
157 # Globals
158 #-----------------------------------------------------------------------------
158 #-----------------------------------------------------------------------------
159
159
160 # ranges where we have most of the valid unicode names. We could be more finer
160 # ranges where we have most of the valid unicode names. We could be more finer
161 # grained but is it worth it for performance While unicode have character in the
161 # grained but is it worth it for performance While unicode have character in the
162 # range 0, 0x110000, we seem to have name for about 10% of those. (131808 as I
162 # range 0, 0x110000, we seem to have name for about 10% of those. (131808 as I
163 # write this). With below range we cover them all, with a density of ~67%
163 # write this). With below range we cover them all, with a density of ~67%
164 # biggest next gap we consider only adds up about 1% density and there are 600
164 # biggest next gap we consider only adds up about 1% density and there are 600
165 # gaps that would need hard coding.
165 # gaps that would need hard coding.
166 _UNICODE_RANGES = [(32, 0x3134b), (0xe0001, 0xe01f0)]
166 _UNICODE_RANGES = [(32, 0x3134b), (0xe0001, 0xe01f0)]
167
167
168 # Public API
168 # Public API
169 __all__ = ['Completer','IPCompleter']
169 __all__ = ['Completer','IPCompleter']
170
170
171 if sys.platform == 'win32':
171 if sys.platform == 'win32':
172 PROTECTABLES = ' '
172 PROTECTABLES = ' '
173 else:
173 else:
174 PROTECTABLES = ' ()[]{}?=\\|;:\'#*"^&'
174 PROTECTABLES = ' ()[]{}?=\\|;:\'#*"^&'
175
175
176 # Protect against returning an enormous number of completions which the frontend
176 # Protect against returning an enormous number of completions which the frontend
177 # may have trouble processing.
177 # may have trouble processing.
178 MATCHES_LIMIT = 500
178 MATCHES_LIMIT = 500
179
179
180
180
181 class ProvisionalCompleterWarning(FutureWarning):
181 class ProvisionalCompleterWarning(FutureWarning):
182 """
182 """
183 Exception raise by an experimental feature in this module.
183 Exception raise by an experimental feature in this module.
184
184
185 Wrap code in :any:`provisionalcompleter` context manager if you
185 Wrap code in :any:`provisionalcompleter` context manager if you
186 are certain you want to use an unstable feature.
186 are certain you want to use an unstable feature.
187 """
187 """
188 pass
188 pass
189
189
190 warnings.filterwarnings('error', category=ProvisionalCompleterWarning)
190 warnings.filterwarnings('error', category=ProvisionalCompleterWarning)
191
191
192
192
193 @skip_doctest
193 @skip_doctest
194 @contextmanager
194 @contextmanager
195 def provisionalcompleter(action='ignore'):
195 def provisionalcompleter(action='ignore'):
196 """
196 """
197 This context manager has to be used in any place where unstable completer
197 This context manager has to be used in any place where unstable completer
198 behavior and API may be called.
198 behavior and API may be called.
199
199
200 >>> with provisionalcompleter():
200 >>> with provisionalcompleter():
201 ... completer.do_experimental_things() # works
201 ... completer.do_experimental_things() # works
202
202
203 >>> completer.do_experimental_things() # raises.
203 >>> completer.do_experimental_things() # raises.
204
204
205 .. note::
205 .. note::
206
206
207 Unstable
207 Unstable
208
208
209 By using this context manager you agree that the API in use may change
209 By using this context manager you agree that the API in use may change
210 without warning, and that you won't complain if they do so.
210 without warning, and that you won't complain if they do so.
211
211
212 You also understand that, if the API is not to your liking, you should report
212 You also understand that, if the API is not to your liking, you should report
213 a bug to explain your use case upstream.
213 a bug to explain your use case upstream.
214
214
215 We'll be happy to get your feedback, feature requests, and improvements on
215 We'll be happy to get your feedback, feature requests, and improvements on
216 any of the unstable APIs!
216 any of the unstable APIs!
217 """
217 """
218 with warnings.catch_warnings():
218 with warnings.catch_warnings():
219 warnings.filterwarnings(action, category=ProvisionalCompleterWarning)
219 warnings.filterwarnings(action, category=ProvisionalCompleterWarning)
220 yield
220 yield
221
221
222
222
223 def has_open_quotes(s):
223 def has_open_quotes(s):
224 """Return whether a string has open quotes.
224 """Return whether a string has open quotes.
225
225
226 This simply counts whether the number of quote characters of either type in
226 This simply counts whether the number of quote characters of either type in
227 the string is odd.
227 the string is odd.
228
228
229 Returns
229 Returns
230 -------
230 -------
231 If there is an open quote, the quote character is returned. Else, return
231 If there is an open quote, the quote character is returned. Else, return
232 False.
232 False.
233 """
233 """
234 # We check " first, then ', so complex cases with nested quotes will get
234 # We check " first, then ', so complex cases with nested quotes will get
235 # the " to take precedence.
235 # the " to take precedence.
236 if s.count('"') % 2:
236 if s.count('"') % 2:
237 return '"'
237 return '"'
238 elif s.count("'") % 2:
238 elif s.count("'") % 2:
239 return "'"
239 return "'"
240 else:
240 else:
241 return False
241 return False
242
242
243
243
244 def protect_filename(s, protectables=PROTECTABLES):
244 def protect_filename(s, protectables=PROTECTABLES):
245 """Escape a string to protect certain characters."""
245 """Escape a string to protect certain characters."""
246 if set(s) & set(protectables):
246 if set(s) & set(protectables):
247 if sys.platform == "win32":
247 if sys.platform == "win32":
248 return '"' + s + '"'
248 return '"' + s + '"'
249 else:
249 else:
250 return "".join(("\\" + c if c in protectables else c) for c in s)
250 return "".join(("\\" + c if c in protectables else c) for c in s)
251 else:
251 else:
252 return s
252 return s
253
253
254
254
255 def expand_user(path:str) -> Tuple[str, bool, str]:
255 def expand_user(path:str) -> Tuple[str, bool, str]:
256 """Expand ``~``-style usernames in strings.
256 """Expand ``~``-style usernames in strings.
257
257
258 This is similar to :func:`os.path.expanduser`, but it computes and returns
258 This is similar to :func:`os.path.expanduser`, but it computes and returns
259 extra information that will be useful if the input was being used in
259 extra information that will be useful if the input was being used in
260 computing completions, and you wish to return the completions with the
260 computing completions, and you wish to return the completions with the
261 original '~' instead of its expanded value.
261 original '~' instead of its expanded value.
262
262
263 Parameters
263 Parameters
264 ----------
264 ----------
265 path : str
265 path : str
266 String to be expanded. If no ~ is present, the output is the same as the
266 String to be expanded. If no ~ is present, the output is the same as the
267 input.
267 input.
268
268
269 Returns
269 Returns
270 -------
270 -------
271 newpath : str
271 newpath : str
272 Result of ~ expansion in the input path.
272 Result of ~ expansion in the input path.
273 tilde_expand : bool
273 tilde_expand : bool
274 Whether any expansion was performed or not.
274 Whether any expansion was performed or not.
275 tilde_val : str
275 tilde_val : str
276 The value that ~ was replaced with.
276 The value that ~ was replaced with.
277 """
277 """
278 # Default values
278 # Default values
279 tilde_expand = False
279 tilde_expand = False
280 tilde_val = ''
280 tilde_val = ''
281 newpath = path
281 newpath = path
282
282
283 if path.startswith('~'):
283 if path.startswith('~'):
284 tilde_expand = True
284 tilde_expand = True
285 rest = len(path)-1
285 rest = len(path)-1
286 newpath = os.path.expanduser(path)
286 newpath = os.path.expanduser(path)
287 if rest:
287 if rest:
288 tilde_val = newpath[:-rest]
288 tilde_val = newpath[:-rest]
289 else:
289 else:
290 tilde_val = newpath
290 tilde_val = newpath
291
291
292 return newpath, tilde_expand, tilde_val
292 return newpath, tilde_expand, tilde_val
293
293
294
294
295 def compress_user(path:str, tilde_expand:bool, tilde_val:str) -> str:
295 def compress_user(path:str, tilde_expand:bool, tilde_val:str) -> str:
296 """Does the opposite of expand_user, with its outputs.
296 """Does the opposite of expand_user, with its outputs.
297 """
297 """
298 if tilde_expand:
298 if tilde_expand:
299 return path.replace(tilde_val, '~')
299 return path.replace(tilde_val, '~')
300 else:
300 else:
301 return path
301 return path
302
302
303
303
304 def completions_sorting_key(word):
304 def completions_sorting_key(word):
305 """key for sorting completions
305 """key for sorting completions
306
306
307 This does several things:
307 This does several things:
308
308
309 - Demote any completions starting with underscores to the end
309 - Demote any completions starting with underscores to the end
310 - Insert any %magic and %%cellmagic completions in the alphabetical order
310 - Insert any %magic and %%cellmagic completions in the alphabetical order
311 by their name
311 by their name
312 """
312 """
313 prio1, prio2 = 0, 0
313 prio1, prio2 = 0, 0
314
314
315 if word.startswith('__'):
315 if word.startswith('__'):
316 prio1 = 2
316 prio1 = 2
317 elif word.startswith('_'):
317 elif word.startswith('_'):
318 prio1 = 1
318 prio1 = 1
319
319
320 if word.endswith('='):
320 if word.endswith('='):
321 prio1 = -1
321 prio1 = -1
322
322
323 if word.startswith('%%'):
323 if word.startswith('%%'):
324 # If there's another % in there, this is something else, so leave it alone
324 # If there's another % in there, this is something else, so leave it alone
325 if not "%" in word[2:]:
325 if not "%" in word[2:]:
326 word = word[2:]
326 word = word[2:]
327 prio2 = 2
327 prio2 = 2
328 elif word.startswith('%'):
328 elif word.startswith('%'):
329 if not "%" in word[1:]:
329 if not "%" in word[1:]:
330 word = word[1:]
330 word = word[1:]
331 prio2 = 1
331 prio2 = 1
332
332
333 return prio1, word, prio2
333 return prio1, word, prio2
334
334
335
335
336 class _FakeJediCompletion:
336 class _FakeJediCompletion:
337 """
337 """
338 This is a workaround to communicate to the UI that Jedi has crashed and to
338 This is a workaround to communicate to the UI that Jedi has crashed and to
339 report a bug. Will be used only id :any:`IPCompleter.debug` is set to true.
339 report a bug. Will be used only id :any:`IPCompleter.debug` is set to true.
340
340
341 Added in IPython 6.0 so should likely be removed for 7.0
341 Added in IPython 6.0 so should likely be removed for 7.0
342
342
343 """
343 """
344
344
345 def __init__(self, name):
345 def __init__(self, name):
346
346
347 self.name = name
347 self.name = name
348 self.complete = name
348 self.complete = name
349 self.type = 'crashed'
349 self.type = 'crashed'
350 self.name_with_symbols = name
350 self.name_with_symbols = name
351 self.signature = ''
351 self.signature = ''
352 self._origin = 'fake'
352 self._origin = 'fake'
353
353
354 def __repr__(self):
354 def __repr__(self):
355 return '<Fake completion object jedi has crashed>'
355 return '<Fake completion object jedi has crashed>'
356
356
357
357
358 class Completion:
358 class Completion:
359 """
359 """
360 Completion object used and return by IPython completers.
360 Completion object used and return by IPython completers.
361
361
362 .. warning::
362 .. warning::
363
363
364 Unstable
364 Unstable
365
365
366 This function is unstable, API may change without warning.
366 This function is unstable, API may change without warning.
367 It will also raise unless use in proper context manager.
367 It will also raise unless use in proper context manager.
368
368
369 This act as a middle ground :any:`Completion` object between the
369 This act as a middle ground :any:`Completion` object between the
370 :any:`jedi.api.classes.Completion` object and the Prompt Toolkit completion
370 :any:`jedi.api.classes.Completion` object and the Prompt Toolkit completion
371 object. While Jedi need a lot of information about evaluator and how the
371 object. While Jedi need a lot of information about evaluator and how the
372 code should be ran/inspected, PromptToolkit (and other frontend) mostly
372 code should be ran/inspected, PromptToolkit (and other frontend) mostly
373 need user facing information.
373 need user facing information.
374
374
375 - Which range should be replaced replaced by what.
375 - Which range should be replaced replaced by what.
376 - Some metadata (like completion type), or meta information to displayed to
376 - Some metadata (like completion type), or meta information to displayed to
377 the use user.
377 the use user.
378
378
379 For debugging purpose we can also store the origin of the completion (``jedi``,
379 For debugging purpose we can also store the origin of the completion (``jedi``,
380 ``IPython.python_matches``, ``IPython.magics_matches``...).
380 ``IPython.python_matches``, ``IPython.magics_matches``...).
381 """
381 """
382
382
383 __slots__ = ['start', 'end', 'text', 'type', 'signature', '_origin']
383 __slots__ = ['start', 'end', 'text', 'type', 'signature', '_origin']
384
384
385 def __init__(self, start: int, end: int, text: str, *, type: str=None, _origin='', signature='') -> None:
385 def __init__(self, start: int, end: int, text: str, *, type: str=None, _origin='', signature='') -> None:
386 warnings.warn("``Completion`` is a provisional API (as of IPython 6.0). "
386 warnings.warn("``Completion`` is a provisional API (as of IPython 6.0). "
387 "It may change without warnings. "
387 "It may change without warnings. "
388 "Use in corresponding context manager.",
388 "Use in corresponding context manager.",
389 category=ProvisionalCompleterWarning, stacklevel=2)
389 category=ProvisionalCompleterWarning, stacklevel=2)
390
390
391 self.start = start
391 self.start = start
392 self.end = end
392 self.end = end
393 self.text = text
393 self.text = text
394 self.type = type
394 self.type = type
395 self.signature = signature
395 self.signature = signature
396 self._origin = _origin
396 self._origin = _origin
397
397
398 def __repr__(self):
398 def __repr__(self):
399 return '<Completion start=%s end=%s text=%r type=%r, signature=%r,>' % \
399 return '<Completion start=%s end=%s text=%r type=%r, signature=%r,>' % \
400 (self.start, self.end, self.text, self.type or '?', self.signature or '?')
400 (self.start, self.end, self.text, self.type or '?', self.signature or '?')
401
401
402 def __eq__(self, other)->Bool:
402 def __eq__(self, other)->Bool:
403 """
403 """
404 Equality and hash do not hash the type (as some completer may not be
404 Equality and hash do not hash the type (as some completer may not be
405 able to infer the type), but are use to (partially) de-duplicate
405 able to infer the type), but are use to (partially) de-duplicate
406 completion.
406 completion.
407
407
408 Completely de-duplicating completion is a bit tricker that just
408 Completely de-duplicating completion is a bit tricker that just
409 comparing as it depends on surrounding text, which Completions are not
409 comparing as it depends on surrounding text, which Completions are not
410 aware of.
410 aware of.
411 """
411 """
412 return self.start == other.start and \
412 return self.start == other.start and \
413 self.end == other.end and \
413 self.end == other.end and \
414 self.text == other.text
414 self.text == other.text
415
415
416 def __hash__(self):
416 def __hash__(self):
417 return hash((self.start, self.end, self.text))
417 return hash((self.start, self.end, self.text))
418
418
419
419
420 _IC = Iterable[Completion]
420 _IC = Iterable[Completion]
421
421
422
422
423 def _deduplicate_completions(text: str, completions: _IC)-> _IC:
423 def _deduplicate_completions(text: str, completions: _IC)-> _IC:
424 """
424 """
425 Deduplicate a set of completions.
425 Deduplicate a set of completions.
426
426
427 .. warning::
427 .. warning::
428
428
429 Unstable
429 Unstable
430
430
431 This function is unstable, API may change without warning.
431 This function is unstable, API may change without warning.
432
432
433 Parameters
433 Parameters
434 ----------
434 ----------
435 text : str
435 text : str
436 text that should be completed.
436 text that should be completed.
437 completions : Iterator[Completion]
437 completions : Iterator[Completion]
438 iterator over the completions to deduplicate
438 iterator over the completions to deduplicate
439
439
440 Yields
440 Yields
441 ------
441 ------
442 `Completions` objects
442 `Completions` objects
443 Completions coming from multiple sources, may be different but end up having
443 Completions coming from multiple sources, may be different but end up having
444 the same effect when applied to ``text``. If this is the case, this will
444 the same effect when applied to ``text``. If this is the case, this will
445 consider completions as equal and only emit the first encountered.
445 consider completions as equal and only emit the first encountered.
446 Not folded in `completions()` yet for debugging purpose, and to detect when
446 Not folded in `completions()` yet for debugging purpose, and to detect when
447 the IPython completer does return things that Jedi does not, but should be
447 the IPython completer does return things that Jedi does not, but should be
448 at some point.
448 at some point.
449 """
449 """
450 completions = list(completions)
450 completions = list(completions)
451 if not completions:
451 if not completions:
452 return
452 return
453
453
454 new_start = min(c.start for c in completions)
454 new_start = min(c.start for c in completions)
455 new_end = max(c.end for c in completions)
455 new_end = max(c.end for c in completions)
456
456
457 seen = set()
457 seen = set()
458 for c in completions:
458 for c in completions:
459 new_text = text[new_start:c.start] + c.text + text[c.end:new_end]
459 new_text = text[new_start:c.start] + c.text + text[c.end:new_end]
460 if new_text not in seen:
460 if new_text not in seen:
461 yield c
461 yield c
462 seen.add(new_text)
462 seen.add(new_text)
463
463
464
464
465 def rectify_completions(text: str, completions: _IC, *, _debug=False)->_IC:
465 def rectify_completions(text: str, completions: _IC, *, _debug: bool = False) -> _IC:
466 """
466 """
467 Rectify a set of completions to all have the same ``start`` and ``end``
467 Rectify a set of completions to all have the same ``start`` and ``end``
468
468
469 .. warning::
469 .. warning::
470
470
471 Unstable
471 Unstable
472
472
473 This function is unstable, API may change without warning.
473 This function is unstable, API may change without warning.
474 It will also raise unless use in proper context manager.
474 It will also raise unless use in proper context manager.
475
475
476 Parameters
476 Parameters
477 ----------
477 ----------
478 text : str
478 text : str
479 text that should be completed.
479 text that should be completed.
480 completions : Iterator[Completion]
480 completions : Iterator[Completion]
481 iterator over the completions to rectify
481 iterator over the completions to rectify
482 _debug : bool
483 Log failed completion
482
484
483 Notes
485 Notes
484 -----
486 -----
485 :any:`jedi.api.classes.Completion` s returned by Jedi may not have the same start and end, though
487 :any:`jedi.api.classes.Completion` s returned by Jedi may not have the same start and end, though
486 the Jupyter Protocol requires them to behave like so. This will readjust
488 the Jupyter Protocol requires them to behave like so. This will readjust
487 the completion to have the same ``start`` and ``end`` by padding both
489 the completion to have the same ``start`` and ``end`` by padding both
488 extremities with surrounding text.
490 extremities with surrounding text.
489
491
490 During stabilisation should support a ``_debug`` option to log which
492 During stabilisation should support a ``_debug`` option to log which
491 completion are return by the IPython completer and not found in Jedi in
493 completion are return by the IPython completer and not found in Jedi in
492 order to make upstream bug report.
494 order to make upstream bug report.
493 """
495 """
494 warnings.warn("`rectify_completions` is a provisional API (as of IPython 6.0). "
496 warnings.warn("`rectify_completions` is a provisional API (as of IPython 6.0). "
495 "It may change without warnings. "
497 "It may change without warnings. "
496 "Use in corresponding context manager.",
498 "Use in corresponding context manager.",
497 category=ProvisionalCompleterWarning, stacklevel=2)
499 category=ProvisionalCompleterWarning, stacklevel=2)
498
500
499 completions = list(completions)
501 completions = list(completions)
500 if not completions:
502 if not completions:
501 return
503 return
502 starts = (c.start for c in completions)
504 starts = (c.start for c in completions)
503 ends = (c.end for c in completions)
505 ends = (c.end for c in completions)
504
506
505 new_start = min(starts)
507 new_start = min(starts)
506 new_end = max(ends)
508 new_end = max(ends)
507
509
508 seen_jedi = set()
510 seen_jedi = set()
509 seen_python_matches = set()
511 seen_python_matches = set()
510 for c in completions:
512 for c in completions:
511 new_text = text[new_start:c.start] + c.text + text[c.end:new_end]
513 new_text = text[new_start:c.start] + c.text + text[c.end:new_end]
512 if c._origin == 'jedi':
514 if c._origin == 'jedi':
513 seen_jedi.add(new_text)
515 seen_jedi.add(new_text)
514 elif c._origin == 'IPCompleter.python_matches':
516 elif c._origin == 'IPCompleter.python_matches':
515 seen_python_matches.add(new_text)
517 seen_python_matches.add(new_text)
516 yield Completion(new_start, new_end, new_text, type=c.type, _origin=c._origin, signature=c.signature)
518 yield Completion(new_start, new_end, new_text, type=c.type, _origin=c._origin, signature=c.signature)
517 diff = seen_python_matches.difference(seen_jedi)
519 diff = seen_python_matches.difference(seen_jedi)
518 if diff and _debug:
520 if diff and _debug:
519 print('IPython.python matches have extras:', diff)
521 print('IPython.python matches have extras:', diff)
520
522
521
523
522 if sys.platform == 'win32':
524 if sys.platform == 'win32':
523 DELIMS = ' \t\n`!@#$^&*()=+[{]}|;\'",<>?'
525 DELIMS = ' \t\n`!@#$^&*()=+[{]}|;\'",<>?'
524 else:
526 else:
525 DELIMS = ' \t\n`!@#$^&*()=+[{]}\\|;:\'",<>?'
527 DELIMS = ' \t\n`!@#$^&*()=+[{]}\\|;:\'",<>?'
526
528
527 GREEDY_DELIMS = ' =\r\n'
529 GREEDY_DELIMS = ' =\r\n'
528
530
529
531
530 class CompletionSplitter(object):
532 class CompletionSplitter(object):
531 """An object to split an input line in a manner similar to readline.
533 """An object to split an input line in a manner similar to readline.
532
534
533 By having our own implementation, we can expose readline-like completion in
535 By having our own implementation, we can expose readline-like completion in
534 a uniform manner to all frontends. This object only needs to be given the
536 a uniform manner to all frontends. This object only needs to be given the
535 line of text to be split and the cursor position on said line, and it
537 line of text to be split and the cursor position on said line, and it
536 returns the 'word' to be completed on at the cursor after splitting the
538 returns the 'word' to be completed on at the cursor after splitting the
537 entire line.
539 entire line.
538
540
539 What characters are used as splitting delimiters can be controlled by
541 What characters are used as splitting delimiters can be controlled by
540 setting the ``delims`` attribute (this is a property that internally
542 setting the ``delims`` attribute (this is a property that internally
541 automatically builds the necessary regular expression)"""
543 automatically builds the necessary regular expression)"""
542
544
543 # Private interface
545 # Private interface
544
546
545 # A string of delimiter characters. The default value makes sense for
547 # A string of delimiter characters. The default value makes sense for
546 # IPython's most typical usage patterns.
548 # IPython's most typical usage patterns.
547 _delims = DELIMS
549 _delims = DELIMS
548
550
549 # The expression (a normal string) to be compiled into a regular expression
551 # The expression (a normal string) to be compiled into a regular expression
550 # for actual splitting. We store it as an attribute mostly for ease of
552 # for actual splitting. We store it as an attribute mostly for ease of
551 # debugging, since this type of code can be so tricky to debug.
553 # debugging, since this type of code can be so tricky to debug.
552 _delim_expr = None
554 _delim_expr = None
553
555
554 # The regular expression that does the actual splitting
556 # The regular expression that does the actual splitting
555 _delim_re = None
557 _delim_re = None
556
558
557 def __init__(self, delims=None):
559 def __init__(self, delims=None):
558 delims = CompletionSplitter._delims if delims is None else delims
560 delims = CompletionSplitter._delims if delims is None else delims
559 self.delims = delims
561 self.delims = delims
560
562
561 @property
563 @property
562 def delims(self):
564 def delims(self):
563 """Return the string of delimiter characters."""
565 """Return the string of delimiter characters."""
564 return self._delims
566 return self._delims
565
567
566 @delims.setter
568 @delims.setter
567 def delims(self, delims):
569 def delims(self, delims):
568 """Set the delimiters for line splitting."""
570 """Set the delimiters for line splitting."""
569 expr = '[' + ''.join('\\'+ c for c in delims) + ']'
571 expr = '[' + ''.join('\\'+ c for c in delims) + ']'
570 self._delim_re = re.compile(expr)
572 self._delim_re = re.compile(expr)
571 self._delims = delims
573 self._delims = delims
572 self._delim_expr = expr
574 self._delim_expr = expr
573
575
574 def split_line(self, line, cursor_pos=None):
576 def split_line(self, line, cursor_pos=None):
575 """Split a line of text with a cursor at the given position.
577 """Split a line of text with a cursor at the given position.
576 """
578 """
577 l = line if cursor_pos is None else line[:cursor_pos]
579 l = line if cursor_pos is None else line[:cursor_pos]
578 return self._delim_re.split(l)[-1]
580 return self._delim_re.split(l)[-1]
579
581
580
582
581
583
582 class Completer(Configurable):
584 class Completer(Configurable):
583
585
584 greedy = Bool(False,
586 greedy = Bool(False,
585 help="""Activate greedy completion
587 help="""Activate greedy completion
586 PENDING DEPRECATION. this is now mostly taken care of with Jedi.
588 PENDING DEPRECATION. this is now mostly taken care of with Jedi.
587
589
588 This will enable completion on elements of lists, results of function calls, etc.,
590 This will enable completion on elements of lists, results of function calls, etc.,
589 but can be unsafe because the code is actually evaluated on TAB.
591 but can be unsafe because the code is actually evaluated on TAB.
590 """
592 """
591 ).tag(config=True)
593 ).tag(config=True)
592
594
593 use_jedi = Bool(default_value=JEDI_INSTALLED,
595 use_jedi = Bool(default_value=JEDI_INSTALLED,
594 help="Experimental: Use Jedi to generate autocompletions. "
596 help="Experimental: Use Jedi to generate autocompletions. "
595 "Default to True if jedi is installed.").tag(config=True)
597 "Default to True if jedi is installed.").tag(config=True)
596
598
597 jedi_compute_type_timeout = Int(default_value=400,
599 jedi_compute_type_timeout = Int(default_value=400,
598 help="""Experimental: restrict time (in milliseconds) during which Jedi can compute types.
600 help="""Experimental: restrict time (in milliseconds) during which Jedi can compute types.
599 Set to 0 to stop computing types. Non-zero value lower than 100ms may hurt
601 Set to 0 to stop computing types. Non-zero value lower than 100ms may hurt
600 performance by preventing jedi to build its cache.
602 performance by preventing jedi to build its cache.
601 """).tag(config=True)
603 """).tag(config=True)
602
604
603 debug = Bool(default_value=False,
605 debug = Bool(default_value=False,
604 help='Enable debug for the Completer. Mostly print extra '
606 help='Enable debug for the Completer. Mostly print extra '
605 'information for experimental jedi integration.')\
607 'information for experimental jedi integration.')\
606 .tag(config=True)
608 .tag(config=True)
607
609
608 backslash_combining_completions = Bool(True,
610 backslash_combining_completions = Bool(True,
609 help="Enable unicode completions, e.g. \\alpha<tab> . "
611 help="Enable unicode completions, e.g. \\alpha<tab> . "
610 "Includes completion of latex commands, unicode names, and expanding "
612 "Includes completion of latex commands, unicode names, and expanding "
611 "unicode characters back to latex commands.").tag(config=True)
613 "unicode characters back to latex commands.").tag(config=True)
612
614
613 def __init__(self, namespace=None, global_namespace=None, **kwargs):
615 def __init__(self, namespace=None, global_namespace=None, **kwargs):
614 """Create a new completer for the command line.
616 """Create a new completer for the command line.
615
617
616 Completer(namespace=ns, global_namespace=ns2) -> completer instance.
618 Completer(namespace=ns, global_namespace=ns2) -> completer instance.
617
619
618 If unspecified, the default namespace where completions are performed
620 If unspecified, the default namespace where completions are performed
619 is __main__ (technically, __main__.__dict__). Namespaces should be
621 is __main__ (technically, __main__.__dict__). Namespaces should be
620 given as dictionaries.
622 given as dictionaries.
621
623
622 An optional second namespace can be given. This allows the completer
624 An optional second namespace can be given. This allows the completer
623 to handle cases where both the local and global scopes need to be
625 to handle cases where both the local and global scopes need to be
624 distinguished.
626 distinguished.
625 """
627 """
626
628
627 # Don't bind to namespace quite yet, but flag whether the user wants a
629 # Don't bind to namespace quite yet, but flag whether the user wants a
628 # specific namespace or to use __main__.__dict__. This will allow us
630 # specific namespace or to use __main__.__dict__. This will allow us
629 # to bind to __main__.__dict__ at completion time, not now.
631 # to bind to __main__.__dict__ at completion time, not now.
630 if namespace is None:
632 if namespace is None:
631 self.use_main_ns = True
633 self.use_main_ns = True
632 else:
634 else:
633 self.use_main_ns = False
635 self.use_main_ns = False
634 self.namespace = namespace
636 self.namespace = namespace
635
637
636 # The global namespace, if given, can be bound directly
638 # The global namespace, if given, can be bound directly
637 if global_namespace is None:
639 if global_namespace is None:
638 self.global_namespace = {}
640 self.global_namespace = {}
639 else:
641 else:
640 self.global_namespace = global_namespace
642 self.global_namespace = global_namespace
641
643
642 self.custom_matchers = []
644 self.custom_matchers = []
643
645
644 super(Completer, self).__init__(**kwargs)
646 super(Completer, self).__init__(**kwargs)
645
647
646 def complete(self, text, state):
648 def complete(self, text, state):
647 """Return the next possible completion for 'text'.
649 """Return the next possible completion for 'text'.
648
650
649 This is called successively with state == 0, 1, 2, ... until it
651 This is called successively with state == 0, 1, 2, ... until it
650 returns None. The completion should begin with 'text'.
652 returns None. The completion should begin with 'text'.
651
653
652 """
654 """
653 if self.use_main_ns:
655 if self.use_main_ns:
654 self.namespace = __main__.__dict__
656 self.namespace = __main__.__dict__
655
657
656 if state == 0:
658 if state == 0:
657 if "." in text:
659 if "." in text:
658 self.matches = self.attr_matches(text)
660 self.matches = self.attr_matches(text)
659 else:
661 else:
660 self.matches = self.global_matches(text)
662 self.matches = self.global_matches(text)
661 try:
663 try:
662 return self.matches[state]
664 return self.matches[state]
663 except IndexError:
665 except IndexError:
664 return None
666 return None
665
667
666 def global_matches(self, text):
668 def global_matches(self, text):
667 """Compute matches when text is a simple name.
669 """Compute matches when text is a simple name.
668
670
669 Return a list of all keywords, built-in functions and names currently
671 Return a list of all keywords, built-in functions and names currently
670 defined in self.namespace or self.global_namespace that match.
672 defined in self.namespace or self.global_namespace that match.
671
673
672 """
674 """
673 matches = []
675 matches = []
674 match_append = matches.append
676 match_append = matches.append
675 n = len(text)
677 n = len(text)
676 for lst in [keyword.kwlist,
678 for lst in [keyword.kwlist,
677 builtin_mod.__dict__.keys(),
679 builtin_mod.__dict__.keys(),
678 self.namespace.keys(),
680 self.namespace.keys(),
679 self.global_namespace.keys()]:
681 self.global_namespace.keys()]:
680 for word in lst:
682 for word in lst:
681 if word[:n] == text and word != "__builtins__":
683 if word[:n] == text and word != "__builtins__":
682 match_append(word)
684 match_append(word)
683
685
684 snake_case_re = re.compile(r"[^_]+(_[^_]+)+?\Z")
686 snake_case_re = re.compile(r"[^_]+(_[^_]+)+?\Z")
685 for lst in [self.namespace.keys(),
687 for lst in [self.namespace.keys(),
686 self.global_namespace.keys()]:
688 self.global_namespace.keys()]:
687 shortened = {"_".join([sub[0] for sub in word.split('_')]) : word
689 shortened = {"_".join([sub[0] for sub in word.split('_')]) : word
688 for word in lst if snake_case_re.match(word)}
690 for word in lst if snake_case_re.match(word)}
689 for word in shortened.keys():
691 for word in shortened.keys():
690 if word[:n] == text and word != "__builtins__":
692 if word[:n] == text and word != "__builtins__":
691 match_append(shortened[word])
693 match_append(shortened[word])
692 return matches
694 return matches
693
695
694 def attr_matches(self, text):
696 def attr_matches(self, text):
695 """Compute matches when text contains a dot.
697 """Compute matches when text contains a dot.
696
698
697 Assuming the text is of the form NAME.NAME....[NAME], and is
699 Assuming the text is of the form NAME.NAME....[NAME], and is
698 evaluatable in self.namespace or self.global_namespace, it will be
700 evaluatable in self.namespace or self.global_namespace, it will be
699 evaluated and its attributes (as revealed by dir()) are used as
701 evaluated and its attributes (as revealed by dir()) are used as
700 possible completions. (For class instances, class members are
702 possible completions. (For class instances, class members are
701 also considered.)
703 also considered.)
702
704
703 WARNING: this can still invoke arbitrary C code, if an object
705 WARNING: this can still invoke arbitrary C code, if an object
704 with a __getattr__ hook is evaluated.
706 with a __getattr__ hook is evaluated.
705
707
706 """
708 """
707
709
708 # Another option, seems to work great. Catches things like ''.<tab>
710 # Another option, seems to work great. Catches things like ''.<tab>
709 m = re.match(r"(\S+(\.\w+)*)\.(\w*)$", text)
711 m = re.match(r"(\S+(\.\w+)*)\.(\w*)$", text)
710
712
711 if m:
713 if m:
712 expr, attr = m.group(1, 3)
714 expr, attr = m.group(1, 3)
713 elif self.greedy:
715 elif self.greedy:
714 m2 = re.match(r"(.+)\.(\w*)$", self.line_buffer)
716 m2 = re.match(r"(.+)\.(\w*)$", self.line_buffer)
715 if not m2:
717 if not m2:
716 return []
718 return []
717 expr, attr = m2.group(1,2)
719 expr, attr = m2.group(1,2)
718 else:
720 else:
719 return []
721 return []
720
722
721 try:
723 try:
722 obj = eval(expr, self.namespace)
724 obj = eval(expr, self.namespace)
723 except:
725 except:
724 try:
726 try:
725 obj = eval(expr, self.global_namespace)
727 obj = eval(expr, self.global_namespace)
726 except:
728 except:
727 return []
729 return []
728
730
729 if self.limit_to__all__ and hasattr(obj, '__all__'):
731 if self.limit_to__all__ and hasattr(obj, '__all__'):
730 words = get__all__entries(obj)
732 words = get__all__entries(obj)
731 else:
733 else:
732 words = dir2(obj)
734 words = dir2(obj)
733
735
734 try:
736 try:
735 words = generics.complete_object(obj, words)
737 words = generics.complete_object(obj, words)
736 except TryNext:
738 except TryNext:
737 pass
739 pass
738 except AssertionError:
740 except AssertionError:
739 raise
741 raise
740 except Exception:
742 except Exception:
741 # Silence errors from completion function
743 # Silence errors from completion function
742 #raise # dbg
744 #raise # dbg
743 pass
745 pass
744 # Build match list to return
746 # Build match list to return
745 n = len(attr)
747 n = len(attr)
746 return [u"%s.%s" % (expr, w) for w in words if w[:n] == attr ]
748 return [u"%s.%s" % (expr, w) for w in words if w[:n] == attr ]
747
749
748
750
749 def get__all__entries(obj):
751 def get__all__entries(obj):
750 """returns the strings in the __all__ attribute"""
752 """returns the strings in the __all__ attribute"""
751 try:
753 try:
752 words = getattr(obj, '__all__')
754 words = getattr(obj, '__all__')
753 except:
755 except:
754 return []
756 return []
755
757
756 return [w for w in words if isinstance(w, str)]
758 return [w for w in words if isinstance(w, str)]
757
759
758
760
759 def match_dict_keys(keys: List[Union[str, bytes, Tuple[Union[str, bytes]]]], prefix: str, delims: str,
761 def match_dict_keys(keys: List[Union[str, bytes, Tuple[Union[str, bytes]]]], prefix: str, delims: str,
760 extra_prefix: Optional[Tuple[str, bytes]]=None) -> Tuple[str, int, List[str]]:
762 extra_prefix: Optional[Tuple[str, bytes]]=None) -> Tuple[str, int, List[str]]:
761 """Used by dict_key_matches, matching the prefix to a list of keys
763 """Used by dict_key_matches, matching the prefix to a list of keys
762
764
763 Parameters
765 Parameters
764 ----------
766 ----------
765 keys
767 keys
766 list of keys in dictionary currently being completed.
768 list of keys in dictionary currently being completed.
767 prefix
769 prefix
768 Part of the text already typed by the user. E.g. `mydict[b'fo`
770 Part of the text already typed by the user. E.g. `mydict[b'fo`
769 delims
771 delims
770 String of delimiters to consider when finding the current key.
772 String of delimiters to consider when finding the current key.
771 extra_prefix : optional
773 extra_prefix : optional
772 Part of the text already typed in multi-key index cases. E.g. for
774 Part of the text already typed in multi-key index cases. E.g. for
773 `mydict['foo', "bar", 'b`, this would be `('foo', 'bar')`.
775 `mydict['foo', "bar", 'b`, this would be `('foo', 'bar')`.
774
776
775 Returns
777 Returns
776 -------
778 -------
777 A tuple of three elements: ``quote``, ``token_start``, ``matched``, with
779 A tuple of three elements: ``quote``, ``token_start``, ``matched``, with
778 ``quote`` being the quote that need to be used to close current string.
780 ``quote`` being the quote that need to be used to close current string.
779 ``token_start`` the position where the replacement should start occurring,
781 ``token_start`` the position where the replacement should start occurring,
780 ``matches`` a list of replacement/completion
782 ``matches`` a list of replacement/completion
781
783
782 """
784 """
783 prefix_tuple = extra_prefix if extra_prefix else ()
785 prefix_tuple = extra_prefix if extra_prefix else ()
784 Nprefix = len(prefix_tuple)
786 Nprefix = len(prefix_tuple)
785 def filter_prefix_tuple(key):
787 def filter_prefix_tuple(key):
786 # Reject too short keys
788 # Reject too short keys
787 if len(key) <= Nprefix:
789 if len(key) <= Nprefix:
788 return False
790 return False
789 # Reject keys with non str/bytes in it
791 # Reject keys with non str/bytes in it
790 for k in key:
792 for k in key:
791 if not isinstance(k, (str, bytes)):
793 if not isinstance(k, (str, bytes)):
792 return False
794 return False
793 # Reject keys that do not match the prefix
795 # Reject keys that do not match the prefix
794 for k, pt in zip(key, prefix_tuple):
796 for k, pt in zip(key, prefix_tuple):
795 if k != pt:
797 if k != pt:
796 return False
798 return False
797 # All checks passed!
799 # All checks passed!
798 return True
800 return True
799
801
800 filtered_keys:List[Union[str,bytes]] = []
802 filtered_keys:List[Union[str,bytes]] = []
801 def _add_to_filtered_keys(key):
803 def _add_to_filtered_keys(key):
802 if isinstance(key, (str, bytes)):
804 if isinstance(key, (str, bytes)):
803 filtered_keys.append(key)
805 filtered_keys.append(key)
804
806
805 for k in keys:
807 for k in keys:
806 if isinstance(k, tuple):
808 if isinstance(k, tuple):
807 if filter_prefix_tuple(k):
809 if filter_prefix_tuple(k):
808 _add_to_filtered_keys(k[Nprefix])
810 _add_to_filtered_keys(k[Nprefix])
809 else:
811 else:
810 _add_to_filtered_keys(k)
812 _add_to_filtered_keys(k)
811
813
812 if not prefix:
814 if not prefix:
813 return '', 0, [repr(k) for k in filtered_keys]
815 return '', 0, [repr(k) for k in filtered_keys]
814 quote_match = re.search('["\']', prefix)
816 quote_match = re.search('["\']', prefix)
815 assert quote_match is not None # silence mypy
817 assert quote_match is not None # silence mypy
816 quote = quote_match.group()
818 quote = quote_match.group()
817 try:
819 try:
818 prefix_str = eval(prefix + quote, {})
820 prefix_str = eval(prefix + quote, {})
819 except Exception:
821 except Exception:
820 return '', 0, []
822 return '', 0, []
821
823
822 pattern = '[^' + ''.join('\\' + c for c in delims) + ']*$'
824 pattern = '[^' + ''.join('\\' + c for c in delims) + ']*$'
823 token_match = re.search(pattern, prefix, re.UNICODE)
825 token_match = re.search(pattern, prefix, re.UNICODE)
824 assert token_match is not None # silence mypy
826 assert token_match is not None # silence mypy
825 token_start = token_match.start()
827 token_start = token_match.start()
826 token_prefix = token_match.group()
828 token_prefix = token_match.group()
827
829
828 matched:List[str] = []
830 matched:List[str] = []
829 for key in filtered_keys:
831 for key in filtered_keys:
830 try:
832 try:
831 if not key.startswith(prefix_str):
833 if not key.startswith(prefix_str):
832 continue
834 continue
833 except (AttributeError, TypeError, UnicodeError):
835 except (AttributeError, TypeError, UnicodeError):
834 # Python 3+ TypeError on b'a'.startswith('a') or vice-versa
836 # Python 3+ TypeError on b'a'.startswith('a') or vice-versa
835 continue
837 continue
836
838
837 # reformat remainder of key to begin with prefix
839 # reformat remainder of key to begin with prefix
838 rem = key[len(prefix_str):]
840 rem = key[len(prefix_str):]
839 # force repr wrapped in '
841 # force repr wrapped in '
840 rem_repr = repr(rem + '"') if isinstance(rem, str) else repr(rem + b'"')
842 rem_repr = repr(rem + '"') if isinstance(rem, str) else repr(rem + b'"')
841 rem_repr = rem_repr[1 + rem_repr.index("'"):-2]
843 rem_repr = rem_repr[1 + rem_repr.index("'"):-2]
842 if quote == '"':
844 if quote == '"':
843 # The entered prefix is quoted with ",
845 # The entered prefix is quoted with ",
844 # but the match is quoted with '.
846 # but the match is quoted with '.
845 # A contained " hence needs escaping for comparison:
847 # A contained " hence needs escaping for comparison:
846 rem_repr = rem_repr.replace('"', '\\"')
848 rem_repr = rem_repr.replace('"', '\\"')
847
849
848 # then reinsert prefix from start of token
850 # then reinsert prefix from start of token
849 matched.append('%s%s' % (token_prefix, rem_repr))
851 matched.append('%s%s' % (token_prefix, rem_repr))
850 return quote, token_start, matched
852 return quote, token_start, matched
851
853
852
854
853 def cursor_to_position(text:str, line:int, column:int)->int:
855 def cursor_to_position(text:str, line:int, column:int)->int:
854 """
856 """
855 Convert the (line,column) position of the cursor in text to an offset in a
857 Convert the (line,column) position of the cursor in text to an offset in a
856 string.
858 string.
857
859
858 Parameters
860 Parameters
859 ----------
861 ----------
860 text : str
862 text : str
861 The text in which to calculate the cursor offset
863 The text in which to calculate the cursor offset
862 line : int
864 line : int
863 Line of the cursor; 0-indexed
865 Line of the cursor; 0-indexed
864 column : int
866 column : int
865 Column of the cursor 0-indexed
867 Column of the cursor 0-indexed
866
868
867 Returns
869 Returns
868 -------
870 -------
869 Position of the cursor in ``text``, 0-indexed.
871 Position of the cursor in ``text``, 0-indexed.
870
872
871 See Also
873 See Also
872 --------
874 --------
873 position_to_cursor : reciprocal of this function
875 position_to_cursor : reciprocal of this function
874
876
875 """
877 """
876 lines = text.split('\n')
878 lines = text.split('\n')
877 assert line <= len(lines), '{} <= {}'.format(str(line), str(len(lines)))
879 assert line <= len(lines), '{} <= {}'.format(str(line), str(len(lines)))
878
880
879 return sum(len(l) + 1 for l in lines[:line]) + column
881 return sum(len(l) + 1 for l in lines[:line]) + column
880
882
881 def position_to_cursor(text:str, offset:int)->Tuple[int, int]:
883 def position_to_cursor(text:str, offset:int)->Tuple[int, int]:
882 """
884 """
883 Convert the position of the cursor in text (0 indexed) to a line
885 Convert the position of the cursor in text (0 indexed) to a line
884 number(0-indexed) and a column number (0-indexed) pair
886 number(0-indexed) and a column number (0-indexed) pair
885
887
886 Position should be a valid position in ``text``.
888 Position should be a valid position in ``text``.
887
889
888 Parameters
890 Parameters
889 ----------
891 ----------
890 text : str
892 text : str
891 The text in which to calculate the cursor offset
893 The text in which to calculate the cursor offset
892 offset : int
894 offset : int
893 Position of the cursor in ``text``, 0-indexed.
895 Position of the cursor in ``text``, 0-indexed.
894
896
895 Returns
897 Returns
896 -------
898 -------
897 (line, column) : (int, int)
899 (line, column) : (int, int)
898 Line of the cursor; 0-indexed, column of the cursor 0-indexed
900 Line of the cursor; 0-indexed, column of the cursor 0-indexed
899
901
900 See Also
902 See Also
901 --------
903 --------
902 cursor_to_position : reciprocal of this function
904 cursor_to_position : reciprocal of this function
903
905
904 """
906 """
905
907
906 assert 0 <= offset <= len(text) , "0 <= %s <= %s" % (offset , len(text))
908 assert 0 <= offset <= len(text) , "0 <= %s <= %s" % (offset , len(text))
907
909
908 before = text[:offset]
910 before = text[:offset]
909 blines = before.split('\n') # ! splitnes trim trailing \n
911 blines = before.split('\n') # ! splitnes trim trailing \n
910 line = before.count('\n')
912 line = before.count('\n')
911 col = len(blines[-1])
913 col = len(blines[-1])
912 return line, col
914 return line, col
913
915
914
916
915 def _safe_isinstance(obj, module, class_name):
917 def _safe_isinstance(obj, module, class_name):
916 """Checks if obj is an instance of module.class_name if loaded
918 """Checks if obj is an instance of module.class_name if loaded
917 """
919 """
918 return (module in sys.modules and
920 return (module in sys.modules and
919 isinstance(obj, getattr(import_module(module), class_name)))
921 isinstance(obj, getattr(import_module(module), class_name)))
920
922
921 def back_unicode_name_matches(text:str) -> Tuple[str, Sequence[str]]:
923 def back_unicode_name_matches(text:str) -> Tuple[str, Sequence[str]]:
922 """Match Unicode characters back to Unicode name
924 """Match Unicode characters back to Unicode name
923
925
924 This does ``☃`` -> ``\\snowman``
926 This does ``☃`` -> ``\\snowman``
925
927
926 Note that snowman is not a valid python3 combining character but will be expanded.
928 Note that snowman is not a valid python3 combining character but will be expanded.
927 Though it will not recombine back to the snowman character by the completion machinery.
929 Though it will not recombine back to the snowman character by the completion machinery.
928
930
929 This will not either back-complete standard sequences like \\n, \\b ...
931 This will not either back-complete standard sequences like \\n, \\b ...
930
932
931 Returns
933 Returns
932 =======
934 =======
933
935
934 Return a tuple with two elements:
936 Return a tuple with two elements:
935
937
936 - The Unicode character that was matched (preceded with a backslash), or
938 - The Unicode character that was matched (preceded with a backslash), or
937 empty string,
939 empty string,
938 - a sequence (of 1), name for the match Unicode character, preceded by
940 - a sequence (of 1), name for the match Unicode character, preceded by
939 backslash, or empty if no match.
941 backslash, or empty if no match.
940
942
941 """
943 """
942 if len(text)<2:
944 if len(text)<2:
943 return '', ()
945 return '', ()
944 maybe_slash = text[-2]
946 maybe_slash = text[-2]
945 if maybe_slash != '\\':
947 if maybe_slash != '\\':
946 return '', ()
948 return '', ()
947
949
948 char = text[-1]
950 char = text[-1]
949 # no expand on quote for completion in strings.
951 # no expand on quote for completion in strings.
950 # nor backcomplete standard ascii keys
952 # nor backcomplete standard ascii keys
951 if char in string.ascii_letters or char in ('"',"'"):
953 if char in string.ascii_letters or char in ('"',"'"):
952 return '', ()
954 return '', ()
953 try :
955 try :
954 unic = unicodedata.name(char)
956 unic = unicodedata.name(char)
955 return '\\'+char,('\\'+unic,)
957 return '\\'+char,('\\'+unic,)
956 except KeyError:
958 except KeyError:
957 pass
959 pass
958 return '', ()
960 return '', ()
959
961
960 def back_latex_name_matches(text:str) -> Tuple[str, Sequence[str]] :
962 def back_latex_name_matches(text:str) -> Tuple[str, Sequence[str]] :
961 """Match latex characters back to unicode name
963 """Match latex characters back to unicode name
962
964
963 This does ``\\ℵ`` -> ``\\aleph``
965 This does ``\\ℵ`` -> ``\\aleph``
964
966
965 """
967 """
966 if len(text)<2:
968 if len(text)<2:
967 return '', ()
969 return '', ()
968 maybe_slash = text[-2]
970 maybe_slash = text[-2]
969 if maybe_slash != '\\':
971 if maybe_slash != '\\':
970 return '', ()
972 return '', ()
971
973
972
974
973 char = text[-1]
975 char = text[-1]
974 # no expand on quote for completion in strings.
976 # no expand on quote for completion in strings.
975 # nor backcomplete standard ascii keys
977 # nor backcomplete standard ascii keys
976 if char in string.ascii_letters or char in ('"',"'"):
978 if char in string.ascii_letters or char in ('"',"'"):
977 return '', ()
979 return '', ()
978 try :
980 try :
979 latex = reverse_latex_symbol[char]
981 latex = reverse_latex_symbol[char]
980 # '\\' replace the \ as well
982 # '\\' replace the \ as well
981 return '\\'+char,[latex]
983 return '\\'+char,[latex]
982 except KeyError:
984 except KeyError:
983 pass
985 pass
984 return '', ()
986 return '', ()
985
987
986
988
987 def _formatparamchildren(parameter) -> str:
989 def _formatparamchildren(parameter) -> str:
988 """
990 """
989 Get parameter name and value from Jedi Private API
991 Get parameter name and value from Jedi Private API
990
992
991 Jedi does not expose a simple way to get `param=value` from its API.
993 Jedi does not expose a simple way to get `param=value` from its API.
992
994
993 Parameters
995 Parameters
994 ----------
996 ----------
995 parameter
997 parameter
996 Jedi's function `Param`
998 Jedi's function `Param`
997
999
998 Returns
1000 Returns
999 -------
1001 -------
1000 A string like 'a', 'b=1', '*args', '**kwargs'
1002 A string like 'a', 'b=1', '*args', '**kwargs'
1001
1003
1002 """
1004 """
1003 description = parameter.description
1005 description = parameter.description
1004 if not description.startswith('param '):
1006 if not description.startswith('param '):
1005 raise ValueError('Jedi function parameter description have change format.'
1007 raise ValueError('Jedi function parameter description have change format.'
1006 'Expected "param ...", found %r".' % description)
1008 'Expected "param ...", found %r".' % description)
1007 return description[6:]
1009 return description[6:]
1008
1010
1009 def _make_signature(completion)-> str:
1011 def _make_signature(completion)-> str:
1010 """
1012 """
1011 Make the signature from a jedi completion
1013 Make the signature from a jedi completion
1012
1014
1013 Parameters
1015 Parameters
1014 ----------
1016 ----------
1015 completion : jedi.Completion
1017 completion : jedi.Completion
1016 object does not complete a function type
1018 object does not complete a function type
1017
1019
1018 Returns
1020 Returns
1019 -------
1021 -------
1020 a string consisting of the function signature, with the parenthesis but
1022 a string consisting of the function signature, with the parenthesis but
1021 without the function name. example:
1023 without the function name. example:
1022 `(a, *args, b=1, **kwargs)`
1024 `(a, *args, b=1, **kwargs)`
1023
1025
1024 """
1026 """
1025
1027
1026 # it looks like this might work on jedi 0.17
1028 # it looks like this might work on jedi 0.17
1027 if hasattr(completion, 'get_signatures'):
1029 if hasattr(completion, 'get_signatures'):
1028 signatures = completion.get_signatures()
1030 signatures = completion.get_signatures()
1029 if not signatures:
1031 if not signatures:
1030 return '(?)'
1032 return '(?)'
1031
1033
1032 c0 = completion.get_signatures()[0]
1034 c0 = completion.get_signatures()[0]
1033 return '('+c0.to_string().split('(', maxsplit=1)[1]
1035 return '('+c0.to_string().split('(', maxsplit=1)[1]
1034
1036
1035 return '(%s)'% ', '.join([f for f in (_formatparamchildren(p) for signature in completion.get_signatures()
1037 return '(%s)'% ', '.join([f for f in (_formatparamchildren(p) for signature in completion.get_signatures()
1036 for p in signature.defined_names()) if f])
1038 for p in signature.defined_names()) if f])
1037
1039
1038
1040
1039 class _CompleteResult(NamedTuple):
1041 class _CompleteResult(NamedTuple):
1040 matched_text : str
1042 matched_text : str
1041 matches: Sequence[str]
1043 matches: Sequence[str]
1042 matches_origin: Sequence[str]
1044 matches_origin: Sequence[str]
1043 jedi_matches: Any
1045 jedi_matches: Any
1044
1046
1045
1047
1046 class IPCompleter(Completer):
1048 class IPCompleter(Completer):
1047 """Extension of the completer class with IPython-specific features"""
1049 """Extension of the completer class with IPython-specific features"""
1048
1050
1049 __dict_key_regexps: Optional[Dict[bool,Pattern]] = None
1051 __dict_key_regexps: Optional[Dict[bool,Pattern]] = None
1050
1052
1051 @observe('greedy')
1053 @observe('greedy')
1052 def _greedy_changed(self, change):
1054 def _greedy_changed(self, change):
1053 """update the splitter and readline delims when greedy is changed"""
1055 """update the splitter and readline delims when greedy is changed"""
1054 if change['new']:
1056 if change['new']:
1055 self.splitter.delims = GREEDY_DELIMS
1057 self.splitter.delims = GREEDY_DELIMS
1056 else:
1058 else:
1057 self.splitter.delims = DELIMS
1059 self.splitter.delims = DELIMS
1058
1060
1059 dict_keys_only = Bool(False,
1061 dict_keys_only = Bool(False,
1060 help="""Whether to show dict key matches only""")
1062 help="""Whether to show dict key matches only""")
1061
1063
1062 merge_completions = Bool(True,
1064 merge_completions = Bool(True,
1063 help="""Whether to merge completion results into a single list
1065 help="""Whether to merge completion results into a single list
1064
1066
1065 If False, only the completion results from the first non-empty
1067 If False, only the completion results from the first non-empty
1066 completer will be returned.
1068 completer will be returned.
1067 """
1069 """
1068 ).tag(config=True)
1070 ).tag(config=True)
1069 omit__names = Enum((0,1,2), default_value=2,
1071 omit__names = Enum((0,1,2), default_value=2,
1070 help="""Instruct the completer to omit private method names
1072 help="""Instruct the completer to omit private method names
1071
1073
1072 Specifically, when completing on ``object.<tab>``.
1074 Specifically, when completing on ``object.<tab>``.
1073
1075
1074 When 2 [default]: all names that start with '_' will be excluded.
1076 When 2 [default]: all names that start with '_' will be excluded.
1075
1077
1076 When 1: all 'magic' names (``__foo__``) will be excluded.
1078 When 1: all 'magic' names (``__foo__``) will be excluded.
1077
1079
1078 When 0: nothing will be excluded.
1080 When 0: nothing will be excluded.
1079 """
1081 """
1080 ).tag(config=True)
1082 ).tag(config=True)
1081 limit_to__all__ = Bool(False,
1083 limit_to__all__ = Bool(False,
1082 help="""
1084 help="""
1083 DEPRECATED as of version 5.0.
1085 DEPRECATED as of version 5.0.
1084
1086
1085 Instruct the completer to use __all__ for the completion
1087 Instruct the completer to use __all__ for the completion
1086
1088
1087 Specifically, when completing on ``object.<tab>``.
1089 Specifically, when completing on ``object.<tab>``.
1088
1090
1089 When True: only those names in obj.__all__ will be included.
1091 When True: only those names in obj.__all__ will be included.
1090
1092
1091 When False [default]: the __all__ attribute is ignored
1093 When False [default]: the __all__ attribute is ignored
1092 """,
1094 """,
1093 ).tag(config=True)
1095 ).tag(config=True)
1094
1096
1095 profile_completions = Bool(
1097 profile_completions = Bool(
1096 default_value=False,
1098 default_value=False,
1097 help="If True, emit profiling data for completion subsystem using cProfile."
1099 help="If True, emit profiling data for completion subsystem using cProfile."
1098 ).tag(config=True)
1100 ).tag(config=True)
1099
1101
1100 profiler_output_dir = Unicode(
1102 profiler_output_dir = Unicode(
1101 default_value=".completion_profiles",
1103 default_value=".completion_profiles",
1102 help="Template for path at which to output profile data for completions."
1104 help="Template for path at which to output profile data for completions."
1103 ).tag(config=True)
1105 ).tag(config=True)
1104
1106
1105 @observe('limit_to__all__')
1107 @observe('limit_to__all__')
1106 def _limit_to_all_changed(self, change):
1108 def _limit_to_all_changed(self, change):
1107 warnings.warn('`IPython.core.IPCompleter.limit_to__all__` configuration '
1109 warnings.warn('`IPython.core.IPCompleter.limit_to__all__` configuration '
1108 'value has been deprecated since IPython 5.0, will be made to have '
1110 'value has been deprecated since IPython 5.0, will be made to have '
1109 'no effects and then removed in future version of IPython.',
1111 'no effects and then removed in future version of IPython.',
1110 UserWarning)
1112 UserWarning)
1111
1113
1112 def __init__(
1114 def __init__(
1113 self, shell=None, namespace=None, global_namespace=None, config=None, **kwargs
1115 self, shell=None, namespace=None, global_namespace=None, config=None, **kwargs
1114 ):
1116 ):
1115 """IPCompleter() -> completer
1117 """IPCompleter() -> completer
1116
1118
1117 Return a completer object.
1119 Return a completer object.
1118
1120
1119 Parameters
1121 Parameters
1120 ----------
1122 ----------
1121 shell
1123 shell
1122 a pointer to the ipython shell itself. This is needed
1124 a pointer to the ipython shell itself. This is needed
1123 because this completer knows about magic functions, and those can
1125 because this completer knows about magic functions, and those can
1124 only be accessed via the ipython instance.
1126 only be accessed via the ipython instance.
1125 namespace : dict, optional
1127 namespace : dict, optional
1126 an optional dict where completions are performed.
1128 an optional dict where completions are performed.
1127 global_namespace : dict, optional
1129 global_namespace : dict, optional
1128 secondary optional dict for completions, to
1130 secondary optional dict for completions, to
1129 handle cases (such as IPython embedded inside functions) where
1131 handle cases (such as IPython embedded inside functions) where
1130 both Python scopes are visible.
1132 both Python scopes are visible.
1131 use_readline : bool, optional
1133 config : Config
1132 DEPRECATED, ignored since IPython 6.0, will have no effects
1134 traitlet's config object
1135 **kwargs
1136 passed to super class unmodified.
1133 """
1137 """
1134
1138
1135 self.magic_escape = ESC_MAGIC
1139 self.magic_escape = ESC_MAGIC
1136 self.splitter = CompletionSplitter()
1140 self.splitter = CompletionSplitter()
1137
1141
1138 # _greedy_changed() depends on splitter and readline being defined:
1142 # _greedy_changed() depends on splitter and readline being defined:
1139 Completer.__init__(self, namespace=namespace, global_namespace=global_namespace,
1143 super().__init__(
1140 config=config, **kwargs)
1144 namespace=namespace,
1145 global_namespace=global_namespace,
1146 config=config,
1147 **kwargs
1148 )
1141
1149
1142 # List where completion matches will be stored
1150 # List where completion matches will be stored
1143 self.matches = []
1151 self.matches = []
1144 self.shell = shell
1152 self.shell = shell
1145 # Regexp to split filenames with spaces in them
1153 # Regexp to split filenames with spaces in them
1146 self.space_name_re = re.compile(r'([^\\] )')
1154 self.space_name_re = re.compile(r'([^\\] )')
1147 # Hold a local ref. to glob.glob for speed
1155 # Hold a local ref. to glob.glob for speed
1148 self.glob = glob.glob
1156 self.glob = glob.glob
1149
1157
1150 # Determine if we are running on 'dumb' terminals, like (X)Emacs
1158 # Determine if we are running on 'dumb' terminals, like (X)Emacs
1151 # buffers, to avoid completion problems.
1159 # buffers, to avoid completion problems.
1152 term = os.environ.get('TERM','xterm')
1160 term = os.environ.get('TERM','xterm')
1153 self.dumb_terminal = term in ['dumb','emacs']
1161 self.dumb_terminal = term in ['dumb','emacs']
1154
1162
1155 # Special handling of backslashes needed in win32 platforms
1163 # Special handling of backslashes needed in win32 platforms
1156 if sys.platform == "win32":
1164 if sys.platform == "win32":
1157 self.clean_glob = self._clean_glob_win32
1165 self.clean_glob = self._clean_glob_win32
1158 else:
1166 else:
1159 self.clean_glob = self._clean_glob
1167 self.clean_glob = self._clean_glob
1160
1168
1161 #regexp to parse docstring for function signature
1169 #regexp to parse docstring for function signature
1162 self.docstring_sig_re = re.compile(r'^[\w|\s.]+\(([^)]*)\).*')
1170 self.docstring_sig_re = re.compile(r'^[\w|\s.]+\(([^)]*)\).*')
1163 self.docstring_kwd_re = re.compile(r'[\s|\[]*(\w+)(?:\s*=\s*.*)')
1171 self.docstring_kwd_re = re.compile(r'[\s|\[]*(\w+)(?:\s*=\s*.*)')
1164 #use this if positional argument name is also needed
1172 #use this if positional argument name is also needed
1165 #= re.compile(r'[\s|\[]*(\w+)(?:\s*=?\s*.*)')
1173 #= re.compile(r'[\s|\[]*(\w+)(?:\s*=?\s*.*)')
1166
1174
1167 self.magic_arg_matchers = [
1175 self.magic_arg_matchers = [
1168 self.magic_config_matches,
1176 self.magic_config_matches,
1169 self.magic_color_matches,
1177 self.magic_color_matches,
1170 ]
1178 ]
1171
1179
1172 # This is set externally by InteractiveShell
1180 # This is set externally by InteractiveShell
1173 self.custom_completers = None
1181 self.custom_completers = None
1174
1182
1175 # This is a list of names of unicode characters that can be completed
1183 # This is a list of names of unicode characters that can be completed
1176 # into their corresponding unicode value. The list is large, so we
1184 # into their corresponding unicode value. The list is large, so we
1177 # laziliy initialize it on first use. Consuming code should access this
1185 # laziliy initialize it on first use. Consuming code should access this
1178 # attribute through the `@unicode_names` property.
1186 # attribute through the `@unicode_names` property.
1179 self._unicode_names = None
1187 self._unicode_names = None
1180
1188
1181 @property
1189 @property
1182 def matchers(self) -> List[Any]:
1190 def matchers(self) -> List[Any]:
1183 """All active matcher routines for completion"""
1191 """All active matcher routines for completion"""
1184 if self.dict_keys_only:
1192 if self.dict_keys_only:
1185 return [self.dict_key_matches]
1193 return [self.dict_key_matches]
1186
1194
1187 if self.use_jedi:
1195 if self.use_jedi:
1188 return [
1196 return [
1189 *self.custom_matchers,
1197 *self.custom_matchers,
1190 self.dict_key_matches,
1198 self.dict_key_matches,
1191 self.file_matches,
1199 self.file_matches,
1192 self.magic_matches,
1200 self.magic_matches,
1193 ]
1201 ]
1194 else:
1202 else:
1195 return [
1203 return [
1196 *self.custom_matchers,
1204 *self.custom_matchers,
1197 self.dict_key_matches,
1205 self.dict_key_matches,
1198 self.python_matches,
1206 self.python_matches,
1199 self.file_matches,
1207 self.file_matches,
1200 self.magic_matches,
1208 self.magic_matches,
1201 self.python_func_kw_matches,
1209 self.python_func_kw_matches,
1202 ]
1210 ]
1203
1211
1204 def all_completions(self, text:str) -> List[str]:
1212 def all_completions(self, text:str) -> List[str]:
1205 """
1213 """
1206 Wrapper around the completion methods for the benefit of emacs.
1214 Wrapper around the completion methods for the benefit of emacs.
1207 """
1215 """
1208 prefix = text.rpartition('.')[0]
1216 prefix = text.rpartition('.')[0]
1209 with provisionalcompleter():
1217 with provisionalcompleter():
1210 return ['.'.join([prefix, c.text]) if prefix and self.use_jedi else c.text
1218 return ['.'.join([prefix, c.text]) if prefix and self.use_jedi else c.text
1211 for c in self.completions(text, len(text))]
1219 for c in self.completions(text, len(text))]
1212
1220
1213 return self.complete(text)[1]
1221 return self.complete(text)[1]
1214
1222
1215 def _clean_glob(self, text:str):
1223 def _clean_glob(self, text:str):
1216 return self.glob("%s*" % text)
1224 return self.glob("%s*" % text)
1217
1225
1218 def _clean_glob_win32(self, text:str):
1226 def _clean_glob_win32(self, text:str):
1219 return [f.replace("\\","/")
1227 return [f.replace("\\","/")
1220 for f in self.glob("%s*" % text)]
1228 for f in self.glob("%s*" % text)]
1221
1229
1222 def file_matches(self, text:str)->List[str]:
1230 def file_matches(self, text:str)->List[str]:
1223 """Match filenames, expanding ~USER type strings.
1231 """Match filenames, expanding ~USER type strings.
1224
1232
1225 Most of the seemingly convoluted logic in this completer is an
1233 Most of the seemingly convoluted logic in this completer is an
1226 attempt to handle filenames with spaces in them. And yet it's not
1234 attempt to handle filenames with spaces in them. And yet it's not
1227 quite perfect, because Python's readline doesn't expose all of the
1235 quite perfect, because Python's readline doesn't expose all of the
1228 GNU readline details needed for this to be done correctly.
1236 GNU readline details needed for this to be done correctly.
1229
1237
1230 For a filename with a space in it, the printed completions will be
1238 For a filename with a space in it, the printed completions will be
1231 only the parts after what's already been typed (instead of the
1239 only the parts after what's already been typed (instead of the
1232 full completions, as is normally done). I don't think with the
1240 full completions, as is normally done). I don't think with the
1233 current (as of Python 2.3) Python readline it's possible to do
1241 current (as of Python 2.3) Python readline it's possible to do
1234 better."""
1242 better."""
1235
1243
1236 # chars that require escaping with backslash - i.e. chars
1244 # chars that require escaping with backslash - i.e. chars
1237 # that readline treats incorrectly as delimiters, but we
1245 # that readline treats incorrectly as delimiters, but we
1238 # don't want to treat as delimiters in filename matching
1246 # don't want to treat as delimiters in filename matching
1239 # when escaped with backslash
1247 # when escaped with backslash
1240 if text.startswith('!'):
1248 if text.startswith('!'):
1241 text = text[1:]
1249 text = text[1:]
1242 text_prefix = u'!'
1250 text_prefix = u'!'
1243 else:
1251 else:
1244 text_prefix = u''
1252 text_prefix = u''
1245
1253
1246 text_until_cursor = self.text_until_cursor
1254 text_until_cursor = self.text_until_cursor
1247 # track strings with open quotes
1255 # track strings with open quotes
1248 open_quotes = has_open_quotes(text_until_cursor)
1256 open_quotes = has_open_quotes(text_until_cursor)
1249
1257
1250 if '(' in text_until_cursor or '[' in text_until_cursor:
1258 if '(' in text_until_cursor or '[' in text_until_cursor:
1251 lsplit = text
1259 lsplit = text
1252 else:
1260 else:
1253 try:
1261 try:
1254 # arg_split ~ shlex.split, but with unicode bugs fixed by us
1262 # arg_split ~ shlex.split, but with unicode bugs fixed by us
1255 lsplit = arg_split(text_until_cursor)[-1]
1263 lsplit = arg_split(text_until_cursor)[-1]
1256 except ValueError:
1264 except ValueError:
1257 # typically an unmatched ", or backslash without escaped char.
1265 # typically an unmatched ", or backslash without escaped char.
1258 if open_quotes:
1266 if open_quotes:
1259 lsplit = text_until_cursor.split(open_quotes)[-1]
1267 lsplit = text_until_cursor.split(open_quotes)[-1]
1260 else:
1268 else:
1261 return []
1269 return []
1262 except IndexError:
1270 except IndexError:
1263 # tab pressed on empty line
1271 # tab pressed on empty line
1264 lsplit = ""
1272 lsplit = ""
1265
1273
1266 if not open_quotes and lsplit != protect_filename(lsplit):
1274 if not open_quotes and lsplit != protect_filename(lsplit):
1267 # if protectables are found, do matching on the whole escaped name
1275 # if protectables are found, do matching on the whole escaped name
1268 has_protectables = True
1276 has_protectables = True
1269 text0,text = text,lsplit
1277 text0,text = text,lsplit
1270 else:
1278 else:
1271 has_protectables = False
1279 has_protectables = False
1272 text = os.path.expanduser(text)
1280 text = os.path.expanduser(text)
1273
1281
1274 if text == "":
1282 if text == "":
1275 return [text_prefix + protect_filename(f) for f in self.glob("*")]
1283 return [text_prefix + protect_filename(f) for f in self.glob("*")]
1276
1284
1277 # Compute the matches from the filesystem
1285 # Compute the matches from the filesystem
1278 if sys.platform == 'win32':
1286 if sys.platform == 'win32':
1279 m0 = self.clean_glob(text)
1287 m0 = self.clean_glob(text)
1280 else:
1288 else:
1281 m0 = self.clean_glob(text.replace('\\', ''))
1289 m0 = self.clean_glob(text.replace('\\', ''))
1282
1290
1283 if has_protectables:
1291 if has_protectables:
1284 # If we had protectables, we need to revert our changes to the
1292 # If we had protectables, we need to revert our changes to the
1285 # beginning of filename so that we don't double-write the part
1293 # beginning of filename so that we don't double-write the part
1286 # of the filename we have so far
1294 # of the filename we have so far
1287 len_lsplit = len(lsplit)
1295 len_lsplit = len(lsplit)
1288 matches = [text_prefix + text0 +
1296 matches = [text_prefix + text0 +
1289 protect_filename(f[len_lsplit:]) for f in m0]
1297 protect_filename(f[len_lsplit:]) for f in m0]
1290 else:
1298 else:
1291 if open_quotes:
1299 if open_quotes:
1292 # if we have a string with an open quote, we don't need to
1300 # if we have a string with an open quote, we don't need to
1293 # protect the names beyond the quote (and we _shouldn't_, as
1301 # protect the names beyond the quote (and we _shouldn't_, as
1294 # it would cause bugs when the filesystem call is made).
1302 # it would cause bugs when the filesystem call is made).
1295 matches = m0 if sys.platform == "win32" else\
1303 matches = m0 if sys.platform == "win32" else\
1296 [protect_filename(f, open_quotes) for f in m0]
1304 [protect_filename(f, open_quotes) for f in m0]
1297 else:
1305 else:
1298 matches = [text_prefix +
1306 matches = [text_prefix +
1299 protect_filename(f) for f in m0]
1307 protect_filename(f) for f in m0]
1300
1308
1301 # Mark directories in input list by appending '/' to their names.
1309 # Mark directories in input list by appending '/' to their names.
1302 return [x+'/' if os.path.isdir(x) else x for x in matches]
1310 return [x+'/' if os.path.isdir(x) else x for x in matches]
1303
1311
1304 def magic_matches(self, text:str):
1312 def magic_matches(self, text:str):
1305 """Match magics"""
1313 """Match magics"""
1306 # Get all shell magics now rather than statically, so magics loaded at
1314 # Get all shell magics now rather than statically, so magics loaded at
1307 # runtime show up too.
1315 # runtime show up too.
1308 lsm = self.shell.magics_manager.lsmagic()
1316 lsm = self.shell.magics_manager.lsmagic()
1309 line_magics = lsm['line']
1317 line_magics = lsm['line']
1310 cell_magics = lsm['cell']
1318 cell_magics = lsm['cell']
1311 pre = self.magic_escape
1319 pre = self.magic_escape
1312 pre2 = pre+pre
1320 pre2 = pre+pre
1313
1321
1314 explicit_magic = text.startswith(pre)
1322 explicit_magic = text.startswith(pre)
1315
1323
1316 # Completion logic:
1324 # Completion logic:
1317 # - user gives %%: only do cell magics
1325 # - user gives %%: only do cell magics
1318 # - user gives %: do both line and cell magics
1326 # - user gives %: do both line and cell magics
1319 # - no prefix: do both
1327 # - no prefix: do both
1320 # In other words, line magics are skipped if the user gives %% explicitly
1328 # In other words, line magics are skipped if the user gives %% explicitly
1321 #
1329 #
1322 # We also exclude magics that match any currently visible names:
1330 # We also exclude magics that match any currently visible names:
1323 # https://github.com/ipython/ipython/issues/4877, unless the user has
1331 # https://github.com/ipython/ipython/issues/4877, unless the user has
1324 # typed a %:
1332 # typed a %:
1325 # https://github.com/ipython/ipython/issues/10754
1333 # https://github.com/ipython/ipython/issues/10754
1326 bare_text = text.lstrip(pre)
1334 bare_text = text.lstrip(pre)
1327 global_matches = self.global_matches(bare_text)
1335 global_matches = self.global_matches(bare_text)
1328 if not explicit_magic:
1336 if not explicit_magic:
1329 def matches(magic):
1337 def matches(magic):
1330 """
1338 """
1331 Filter magics, in particular remove magics that match
1339 Filter magics, in particular remove magics that match
1332 a name present in global namespace.
1340 a name present in global namespace.
1333 """
1341 """
1334 return ( magic.startswith(bare_text) and
1342 return ( magic.startswith(bare_text) and
1335 magic not in global_matches )
1343 magic not in global_matches )
1336 else:
1344 else:
1337 def matches(magic):
1345 def matches(magic):
1338 return magic.startswith(bare_text)
1346 return magic.startswith(bare_text)
1339
1347
1340 comp = [ pre2+m for m in cell_magics if matches(m)]
1348 comp = [ pre2+m for m in cell_magics if matches(m)]
1341 if not text.startswith(pre2):
1349 if not text.startswith(pre2):
1342 comp += [ pre+m for m in line_magics if matches(m)]
1350 comp += [ pre+m for m in line_magics if matches(m)]
1343
1351
1344 return comp
1352 return comp
1345
1353
1346 def magic_config_matches(self, text:str) -> List[str]:
1354 def magic_config_matches(self, text:str) -> List[str]:
1347 """ Match class names and attributes for %config magic """
1355 """ Match class names and attributes for %config magic """
1348 texts = text.strip().split()
1356 texts = text.strip().split()
1349
1357
1350 if len(texts) > 0 and (texts[0] == 'config' or texts[0] == '%config'):
1358 if len(texts) > 0 and (texts[0] == 'config' or texts[0] == '%config'):
1351 # get all configuration classes
1359 # get all configuration classes
1352 classes = sorted(set([ c for c in self.shell.configurables
1360 classes = sorted(set([ c for c in self.shell.configurables
1353 if c.__class__.class_traits(config=True)
1361 if c.__class__.class_traits(config=True)
1354 ]), key=lambda x: x.__class__.__name__)
1362 ]), key=lambda x: x.__class__.__name__)
1355 classnames = [ c.__class__.__name__ for c in classes ]
1363 classnames = [ c.__class__.__name__ for c in classes ]
1356
1364
1357 # return all classnames if config or %config is given
1365 # return all classnames if config or %config is given
1358 if len(texts) == 1:
1366 if len(texts) == 1:
1359 return classnames
1367 return classnames
1360
1368
1361 # match classname
1369 # match classname
1362 classname_texts = texts[1].split('.')
1370 classname_texts = texts[1].split('.')
1363 classname = classname_texts[0]
1371 classname = classname_texts[0]
1364 classname_matches = [ c for c in classnames
1372 classname_matches = [ c for c in classnames
1365 if c.startswith(classname) ]
1373 if c.startswith(classname) ]
1366
1374
1367 # return matched classes or the matched class with attributes
1375 # return matched classes or the matched class with attributes
1368 if texts[1].find('.') < 0:
1376 if texts[1].find('.') < 0:
1369 return classname_matches
1377 return classname_matches
1370 elif len(classname_matches) == 1 and \
1378 elif len(classname_matches) == 1 and \
1371 classname_matches[0] == classname:
1379 classname_matches[0] == classname:
1372 cls = classes[classnames.index(classname)].__class__
1380 cls = classes[classnames.index(classname)].__class__
1373 help = cls.class_get_help()
1381 help = cls.class_get_help()
1374 # strip leading '--' from cl-args:
1382 # strip leading '--' from cl-args:
1375 help = re.sub(re.compile(r'^--', re.MULTILINE), '', help)
1383 help = re.sub(re.compile(r'^--', re.MULTILINE), '', help)
1376 return [ attr.split('=')[0]
1384 return [ attr.split('=')[0]
1377 for attr in help.strip().splitlines()
1385 for attr in help.strip().splitlines()
1378 if attr.startswith(texts[1]) ]
1386 if attr.startswith(texts[1]) ]
1379 return []
1387 return []
1380
1388
1381 def magic_color_matches(self, text:str) -> List[str] :
1389 def magic_color_matches(self, text:str) -> List[str] :
1382 """ Match color schemes for %colors magic"""
1390 """ Match color schemes for %colors magic"""
1383 texts = text.split()
1391 texts = text.split()
1384 if text.endswith(' '):
1392 if text.endswith(' '):
1385 # .split() strips off the trailing whitespace. Add '' back
1393 # .split() strips off the trailing whitespace. Add '' back
1386 # so that: '%colors ' -> ['%colors', '']
1394 # so that: '%colors ' -> ['%colors', '']
1387 texts.append('')
1395 texts.append('')
1388
1396
1389 if len(texts) == 2 and (texts[0] == 'colors' or texts[0] == '%colors'):
1397 if len(texts) == 2 and (texts[0] == 'colors' or texts[0] == '%colors'):
1390 prefix = texts[1]
1398 prefix = texts[1]
1391 return [ color for color in InspectColors.keys()
1399 return [ color for color in InspectColors.keys()
1392 if color.startswith(prefix) ]
1400 if color.startswith(prefix) ]
1393 return []
1401 return []
1394
1402
1395 def _jedi_matches(self, cursor_column:int, cursor_line:int, text:str) -> Iterable[Any]:
1403 def _jedi_matches(self, cursor_column:int, cursor_line:int, text:str) -> Iterable[Any]:
1396 """
1404 """
1397 Return a list of :any:`jedi.api.Completions` object from a ``text`` and
1405 Return a list of :any:`jedi.api.Completions` object from a ``text`` and
1398 cursor position.
1406 cursor position.
1399
1407
1400 Parameters
1408 Parameters
1401 ----------
1409 ----------
1402 cursor_column : int
1410 cursor_column : int
1403 column position of the cursor in ``text``, 0-indexed.
1411 column position of the cursor in ``text``, 0-indexed.
1404 cursor_line : int
1412 cursor_line : int
1405 line position of the cursor in ``text``, 0-indexed
1413 line position of the cursor in ``text``, 0-indexed
1406 text : str
1414 text : str
1407 text to complete
1415 text to complete
1408
1416
1409 Notes
1417 Notes
1410 -----
1418 -----
1411 If ``IPCompleter.debug`` is ``True`` may return a :any:`_FakeJediCompletion`
1419 If ``IPCompleter.debug`` is ``True`` may return a :any:`_FakeJediCompletion`
1412 object containing a string with the Jedi debug information attached.
1420 object containing a string with the Jedi debug information attached.
1413 """
1421 """
1414 namespaces = [self.namespace]
1422 namespaces = [self.namespace]
1415 if self.global_namespace is not None:
1423 if self.global_namespace is not None:
1416 namespaces.append(self.global_namespace)
1424 namespaces.append(self.global_namespace)
1417
1425
1418 completion_filter = lambda x:x
1426 completion_filter = lambda x:x
1419 offset = cursor_to_position(text, cursor_line, cursor_column)
1427 offset = cursor_to_position(text, cursor_line, cursor_column)
1420 # filter output if we are completing for object members
1428 # filter output if we are completing for object members
1421 if offset:
1429 if offset:
1422 pre = text[offset-1]
1430 pre = text[offset-1]
1423 if pre == '.':
1431 if pre == '.':
1424 if self.omit__names == 2:
1432 if self.omit__names == 2:
1425 completion_filter = lambda c:not c.name.startswith('_')
1433 completion_filter = lambda c:not c.name.startswith('_')
1426 elif self.omit__names == 1:
1434 elif self.omit__names == 1:
1427 completion_filter = lambda c:not (c.name.startswith('__') and c.name.endswith('__'))
1435 completion_filter = lambda c:not (c.name.startswith('__') and c.name.endswith('__'))
1428 elif self.omit__names == 0:
1436 elif self.omit__names == 0:
1429 completion_filter = lambda x:x
1437 completion_filter = lambda x:x
1430 else:
1438 else:
1431 raise ValueError("Don't understand self.omit__names == {}".format(self.omit__names))
1439 raise ValueError("Don't understand self.omit__names == {}".format(self.omit__names))
1432
1440
1433 interpreter = jedi.Interpreter(text[:offset], namespaces)
1441 interpreter = jedi.Interpreter(text[:offset], namespaces)
1434 try_jedi = True
1442 try_jedi = True
1435
1443
1436 try:
1444 try:
1437 # find the first token in the current tree -- if it is a ' or " then we are in a string
1445 # find the first token in the current tree -- if it is a ' or " then we are in a string
1438 completing_string = False
1446 completing_string = False
1439 try:
1447 try:
1440 first_child = next(c for c in interpreter._get_module().tree_node.children if hasattr(c, 'value'))
1448 first_child = next(c for c in interpreter._get_module().tree_node.children if hasattr(c, 'value'))
1441 except StopIteration:
1449 except StopIteration:
1442 pass
1450 pass
1443 else:
1451 else:
1444 # note the value may be ', ", or it may also be ''' or """, or
1452 # note the value may be ', ", or it may also be ''' or """, or
1445 # in some cases, """what/you/typed..., but all of these are
1453 # in some cases, """what/you/typed..., but all of these are
1446 # strings.
1454 # strings.
1447 completing_string = len(first_child.value) > 0 and first_child.value[0] in {"'", '"'}
1455 completing_string = len(first_child.value) > 0 and first_child.value[0] in {"'", '"'}
1448
1456
1449 # if we are in a string jedi is likely not the right candidate for
1457 # if we are in a string jedi is likely not the right candidate for
1450 # now. Skip it.
1458 # now. Skip it.
1451 try_jedi = not completing_string
1459 try_jedi = not completing_string
1452 except Exception as e:
1460 except Exception as e:
1453 # many of things can go wrong, we are using private API just don't crash.
1461 # many of things can go wrong, we are using private API just don't crash.
1454 if self.debug:
1462 if self.debug:
1455 print("Error detecting if completing a non-finished string :", e, '|')
1463 print("Error detecting if completing a non-finished string :", e, '|')
1456
1464
1457 if not try_jedi:
1465 if not try_jedi:
1458 return []
1466 return []
1459 try:
1467 try:
1460 return filter(completion_filter, interpreter.complete(column=cursor_column, line=cursor_line + 1))
1468 return filter(completion_filter, interpreter.complete(column=cursor_column, line=cursor_line + 1))
1461 except Exception as e:
1469 except Exception as e:
1462 if self.debug:
1470 if self.debug:
1463 return [_FakeJediCompletion('Oops Jedi has crashed, please report a bug with the following:\n"""\n%s\ns"""' % (e))]
1471 return [_FakeJediCompletion('Oops Jedi has crashed, please report a bug with the following:\n"""\n%s\ns"""' % (e))]
1464 else:
1472 else:
1465 return []
1473 return []
1466
1474
1467 def python_matches(self, text:str)->List[str]:
1475 def python_matches(self, text:str)->List[str]:
1468 """Match attributes or global python names"""
1476 """Match attributes or global python names"""
1469 if "." in text:
1477 if "." in text:
1470 try:
1478 try:
1471 matches = self.attr_matches(text)
1479 matches = self.attr_matches(text)
1472 if text.endswith('.') and self.omit__names:
1480 if text.endswith('.') and self.omit__names:
1473 if self.omit__names == 1:
1481 if self.omit__names == 1:
1474 # true if txt is _not_ a __ name, false otherwise:
1482 # true if txt is _not_ a __ name, false otherwise:
1475 no__name = (lambda txt:
1483 no__name = (lambda txt:
1476 re.match(r'.*\.__.*?__',txt) is None)
1484 re.match(r'.*\.__.*?__',txt) is None)
1477 else:
1485 else:
1478 # true if txt is _not_ a _ name, false otherwise:
1486 # true if txt is _not_ a _ name, false otherwise:
1479 no__name = (lambda txt:
1487 no__name = (lambda txt:
1480 re.match(r'\._.*?',txt[txt.rindex('.'):]) is None)
1488 re.match(r'\._.*?',txt[txt.rindex('.'):]) is None)
1481 matches = filter(no__name, matches)
1489 matches = filter(no__name, matches)
1482 except NameError:
1490 except NameError:
1483 # catches <undefined attributes>.<tab>
1491 # catches <undefined attributes>.<tab>
1484 matches = []
1492 matches = []
1485 else:
1493 else:
1486 matches = self.global_matches(text)
1494 matches = self.global_matches(text)
1487 return matches
1495 return matches
1488
1496
1489 def _default_arguments_from_docstring(self, doc):
1497 def _default_arguments_from_docstring(self, doc):
1490 """Parse the first line of docstring for call signature.
1498 """Parse the first line of docstring for call signature.
1491
1499
1492 Docstring should be of the form 'min(iterable[, key=func])\n'.
1500 Docstring should be of the form 'min(iterable[, key=func])\n'.
1493 It can also parse cython docstring of the form
1501 It can also parse cython docstring of the form
1494 'Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)'.
1502 'Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)'.
1495 """
1503 """
1496 if doc is None:
1504 if doc is None:
1497 return []
1505 return []
1498
1506
1499 #care only the firstline
1507 #care only the firstline
1500 line = doc.lstrip().splitlines()[0]
1508 line = doc.lstrip().splitlines()[0]
1501
1509
1502 #p = re.compile(r'^[\w|\s.]+\(([^)]*)\).*')
1510 #p = re.compile(r'^[\w|\s.]+\(([^)]*)\).*')
1503 #'min(iterable[, key=func])\n' -> 'iterable[, key=func]'
1511 #'min(iterable[, key=func])\n' -> 'iterable[, key=func]'
1504 sig = self.docstring_sig_re.search(line)
1512 sig = self.docstring_sig_re.search(line)
1505 if sig is None:
1513 if sig is None:
1506 return []
1514 return []
1507 # iterable[, key=func]' -> ['iterable[' ,' key=func]']
1515 # iterable[, key=func]' -> ['iterable[' ,' key=func]']
1508 sig = sig.groups()[0].split(',')
1516 sig = sig.groups()[0].split(',')
1509 ret = []
1517 ret = []
1510 for s in sig:
1518 for s in sig:
1511 #re.compile(r'[\s|\[]*(\w+)(?:\s*=\s*.*)')
1519 #re.compile(r'[\s|\[]*(\w+)(?:\s*=\s*.*)')
1512 ret += self.docstring_kwd_re.findall(s)
1520 ret += self.docstring_kwd_re.findall(s)
1513 return ret
1521 return ret
1514
1522
1515 def _default_arguments(self, obj):
1523 def _default_arguments(self, obj):
1516 """Return the list of default arguments of obj if it is callable,
1524 """Return the list of default arguments of obj if it is callable,
1517 or empty list otherwise."""
1525 or empty list otherwise."""
1518 call_obj = obj
1526 call_obj = obj
1519 ret = []
1527 ret = []
1520 if inspect.isbuiltin(obj):
1528 if inspect.isbuiltin(obj):
1521 pass
1529 pass
1522 elif not (inspect.isfunction(obj) or inspect.ismethod(obj)):
1530 elif not (inspect.isfunction(obj) or inspect.ismethod(obj)):
1523 if inspect.isclass(obj):
1531 if inspect.isclass(obj):
1524 #for cython embedsignature=True the constructor docstring
1532 #for cython embedsignature=True the constructor docstring
1525 #belongs to the object itself not __init__
1533 #belongs to the object itself not __init__
1526 ret += self._default_arguments_from_docstring(
1534 ret += self._default_arguments_from_docstring(
1527 getattr(obj, '__doc__', ''))
1535 getattr(obj, '__doc__', ''))
1528 # for classes, check for __init__,__new__
1536 # for classes, check for __init__,__new__
1529 call_obj = (getattr(obj, '__init__', None) or
1537 call_obj = (getattr(obj, '__init__', None) or
1530 getattr(obj, '__new__', None))
1538 getattr(obj, '__new__', None))
1531 # for all others, check if they are __call__able
1539 # for all others, check if they are __call__able
1532 elif hasattr(obj, '__call__'):
1540 elif hasattr(obj, '__call__'):
1533 call_obj = obj.__call__
1541 call_obj = obj.__call__
1534 ret += self._default_arguments_from_docstring(
1542 ret += self._default_arguments_from_docstring(
1535 getattr(call_obj, '__doc__', ''))
1543 getattr(call_obj, '__doc__', ''))
1536
1544
1537 _keeps = (inspect.Parameter.KEYWORD_ONLY,
1545 _keeps = (inspect.Parameter.KEYWORD_ONLY,
1538 inspect.Parameter.POSITIONAL_OR_KEYWORD)
1546 inspect.Parameter.POSITIONAL_OR_KEYWORD)
1539
1547
1540 try:
1548 try:
1541 sig = inspect.signature(obj)
1549 sig = inspect.signature(obj)
1542 ret.extend(k for k, v in sig.parameters.items() if
1550 ret.extend(k for k, v in sig.parameters.items() if
1543 v.kind in _keeps)
1551 v.kind in _keeps)
1544 except ValueError:
1552 except ValueError:
1545 pass
1553 pass
1546
1554
1547 return list(set(ret))
1555 return list(set(ret))
1548
1556
1549 def python_func_kw_matches(self, text):
1557 def python_func_kw_matches(self, text):
1550 """Match named parameters (kwargs) of the last open function"""
1558 """Match named parameters (kwargs) of the last open function"""
1551
1559
1552 if "." in text: # a parameter cannot be dotted
1560 if "." in text: # a parameter cannot be dotted
1553 return []
1561 return []
1554 try: regexp = self.__funcParamsRegex
1562 try: regexp = self.__funcParamsRegex
1555 except AttributeError:
1563 except AttributeError:
1556 regexp = self.__funcParamsRegex = re.compile(r'''
1564 regexp = self.__funcParamsRegex = re.compile(r'''
1557 '.*?(?<!\\)' | # single quoted strings or
1565 '.*?(?<!\\)' | # single quoted strings or
1558 ".*?(?<!\\)" | # double quoted strings or
1566 ".*?(?<!\\)" | # double quoted strings or
1559 \w+ | # identifier
1567 \w+ | # identifier
1560 \S # other characters
1568 \S # other characters
1561 ''', re.VERBOSE | re.DOTALL)
1569 ''', re.VERBOSE | re.DOTALL)
1562 # 1. find the nearest identifier that comes before an unclosed
1570 # 1. find the nearest identifier that comes before an unclosed
1563 # parenthesis before the cursor
1571 # parenthesis before the cursor
1564 # e.g. for "foo (1+bar(x), pa<cursor>,a=1)", the candidate is "foo"
1572 # e.g. for "foo (1+bar(x), pa<cursor>,a=1)", the candidate is "foo"
1565 tokens = regexp.findall(self.text_until_cursor)
1573 tokens = regexp.findall(self.text_until_cursor)
1566 iterTokens = reversed(tokens); openPar = 0
1574 iterTokens = reversed(tokens); openPar = 0
1567
1575
1568 for token in iterTokens:
1576 for token in iterTokens:
1569 if token == ')':
1577 if token == ')':
1570 openPar -= 1
1578 openPar -= 1
1571 elif token == '(':
1579 elif token == '(':
1572 openPar += 1
1580 openPar += 1
1573 if openPar > 0:
1581 if openPar > 0:
1574 # found the last unclosed parenthesis
1582 # found the last unclosed parenthesis
1575 break
1583 break
1576 else:
1584 else:
1577 return []
1585 return []
1578 # 2. Concatenate dotted names ("foo.bar" for "foo.bar(x, pa" )
1586 # 2. Concatenate dotted names ("foo.bar" for "foo.bar(x, pa" )
1579 ids = []
1587 ids = []
1580 isId = re.compile(r'\w+$').match
1588 isId = re.compile(r'\w+$').match
1581
1589
1582 while True:
1590 while True:
1583 try:
1591 try:
1584 ids.append(next(iterTokens))
1592 ids.append(next(iterTokens))
1585 if not isId(ids[-1]):
1593 if not isId(ids[-1]):
1586 ids.pop(); break
1594 ids.pop(); break
1587 if not next(iterTokens) == '.':
1595 if not next(iterTokens) == '.':
1588 break
1596 break
1589 except StopIteration:
1597 except StopIteration:
1590 break
1598 break
1591
1599
1592 # Find all named arguments already assigned to, as to avoid suggesting
1600 # Find all named arguments already assigned to, as to avoid suggesting
1593 # them again
1601 # them again
1594 usedNamedArgs = set()
1602 usedNamedArgs = set()
1595 par_level = -1
1603 par_level = -1
1596 for token, next_token in zip(tokens, tokens[1:]):
1604 for token, next_token in zip(tokens, tokens[1:]):
1597 if token == '(':
1605 if token == '(':
1598 par_level += 1
1606 par_level += 1
1599 elif token == ')':
1607 elif token == ')':
1600 par_level -= 1
1608 par_level -= 1
1601
1609
1602 if par_level != 0:
1610 if par_level != 0:
1603 continue
1611 continue
1604
1612
1605 if next_token != '=':
1613 if next_token != '=':
1606 continue
1614 continue
1607
1615
1608 usedNamedArgs.add(token)
1616 usedNamedArgs.add(token)
1609
1617
1610 argMatches = []
1618 argMatches = []
1611 try:
1619 try:
1612 callableObj = '.'.join(ids[::-1])
1620 callableObj = '.'.join(ids[::-1])
1613 namedArgs = self._default_arguments(eval(callableObj,
1621 namedArgs = self._default_arguments(eval(callableObj,
1614 self.namespace))
1622 self.namespace))
1615
1623
1616 # Remove used named arguments from the list, no need to show twice
1624 # Remove used named arguments from the list, no need to show twice
1617 for namedArg in set(namedArgs) - usedNamedArgs:
1625 for namedArg in set(namedArgs) - usedNamedArgs:
1618 if namedArg.startswith(text):
1626 if namedArg.startswith(text):
1619 argMatches.append("%s=" %namedArg)
1627 argMatches.append("%s=" %namedArg)
1620 except:
1628 except:
1621 pass
1629 pass
1622
1630
1623 return argMatches
1631 return argMatches
1624
1632
1625 @staticmethod
1633 @staticmethod
1626 def _get_keys(obj: Any) -> List[Any]:
1634 def _get_keys(obj: Any) -> List[Any]:
1627 # Objects can define their own completions by defining an
1635 # Objects can define their own completions by defining an
1628 # _ipy_key_completions_() method.
1636 # _ipy_key_completions_() method.
1629 method = get_real_method(obj, '_ipython_key_completions_')
1637 method = get_real_method(obj, '_ipython_key_completions_')
1630 if method is not None:
1638 if method is not None:
1631 return method()
1639 return method()
1632
1640
1633 # Special case some common in-memory dict-like types
1641 # Special case some common in-memory dict-like types
1634 if isinstance(obj, dict) or\
1642 if isinstance(obj, dict) or\
1635 _safe_isinstance(obj, 'pandas', 'DataFrame'):
1643 _safe_isinstance(obj, 'pandas', 'DataFrame'):
1636 try:
1644 try:
1637 return list(obj.keys())
1645 return list(obj.keys())
1638 except Exception:
1646 except Exception:
1639 return []
1647 return []
1640 elif _safe_isinstance(obj, 'numpy', 'ndarray') or\
1648 elif _safe_isinstance(obj, 'numpy', 'ndarray') or\
1641 _safe_isinstance(obj, 'numpy', 'void'):
1649 _safe_isinstance(obj, 'numpy', 'void'):
1642 return obj.dtype.names or []
1650 return obj.dtype.names or []
1643 return []
1651 return []
1644
1652
1645 def dict_key_matches(self, text:str) -> List[str]:
1653 def dict_key_matches(self, text:str) -> List[str]:
1646 "Match string keys in a dictionary, after e.g. 'foo[' "
1654 "Match string keys in a dictionary, after e.g. 'foo[' "
1647
1655
1648
1656
1649 if self.__dict_key_regexps is not None:
1657 if self.__dict_key_regexps is not None:
1650 regexps = self.__dict_key_regexps
1658 regexps = self.__dict_key_regexps
1651 else:
1659 else:
1652 dict_key_re_fmt = r'''(?x)
1660 dict_key_re_fmt = r'''(?x)
1653 ( # match dict-referring expression wrt greedy setting
1661 ( # match dict-referring expression wrt greedy setting
1654 %s
1662 %s
1655 )
1663 )
1656 \[ # open bracket
1664 \[ # open bracket
1657 \s* # and optional whitespace
1665 \s* # and optional whitespace
1658 # Capture any number of str-like objects (e.g. "a", "b", 'c')
1666 # Capture any number of str-like objects (e.g. "a", "b", 'c')
1659 ((?:[uUbB]? # string prefix (r not handled)
1667 ((?:[uUbB]? # string prefix (r not handled)
1660 (?:
1668 (?:
1661 '(?:[^']|(?<!\\)\\')*'
1669 '(?:[^']|(?<!\\)\\')*'
1662 |
1670 |
1663 "(?:[^"]|(?<!\\)\\")*"
1671 "(?:[^"]|(?<!\\)\\")*"
1664 )
1672 )
1665 \s*,\s*
1673 \s*,\s*
1666 )*)
1674 )*)
1667 ([uUbB]? # string prefix (r not handled)
1675 ([uUbB]? # string prefix (r not handled)
1668 (?: # unclosed string
1676 (?: # unclosed string
1669 '(?:[^']|(?<!\\)\\')*
1677 '(?:[^']|(?<!\\)\\')*
1670 |
1678 |
1671 "(?:[^"]|(?<!\\)\\")*
1679 "(?:[^"]|(?<!\\)\\")*
1672 )
1680 )
1673 )?
1681 )?
1674 $
1682 $
1675 '''
1683 '''
1676 regexps = self.__dict_key_regexps = {
1684 regexps = self.__dict_key_regexps = {
1677 False: re.compile(dict_key_re_fmt % r'''
1685 False: re.compile(dict_key_re_fmt % r'''
1678 # identifiers separated by .
1686 # identifiers separated by .
1679 (?!\d)\w+
1687 (?!\d)\w+
1680 (?:\.(?!\d)\w+)*
1688 (?:\.(?!\d)\w+)*
1681 '''),
1689 '''),
1682 True: re.compile(dict_key_re_fmt % '''
1690 True: re.compile(dict_key_re_fmt % '''
1683 .+
1691 .+
1684 ''')
1692 ''')
1685 }
1693 }
1686
1694
1687 match = regexps[self.greedy].search(self.text_until_cursor)
1695 match = regexps[self.greedy].search(self.text_until_cursor)
1688
1696
1689 if match is None:
1697 if match is None:
1690 return []
1698 return []
1691
1699
1692 expr, prefix0, prefix = match.groups()
1700 expr, prefix0, prefix = match.groups()
1693 try:
1701 try:
1694 obj = eval(expr, self.namespace)
1702 obj = eval(expr, self.namespace)
1695 except Exception:
1703 except Exception:
1696 try:
1704 try:
1697 obj = eval(expr, self.global_namespace)
1705 obj = eval(expr, self.global_namespace)
1698 except Exception:
1706 except Exception:
1699 return []
1707 return []
1700
1708
1701 keys = self._get_keys(obj)
1709 keys = self._get_keys(obj)
1702 if not keys:
1710 if not keys:
1703 return keys
1711 return keys
1704
1712
1705 extra_prefix = eval(prefix0) if prefix0 != '' else None
1713 extra_prefix = eval(prefix0) if prefix0 != '' else None
1706
1714
1707 closing_quote, token_offset, matches = match_dict_keys(keys, prefix, self.splitter.delims, extra_prefix=extra_prefix)
1715 closing_quote, token_offset, matches = match_dict_keys(keys, prefix, self.splitter.delims, extra_prefix=extra_prefix)
1708 if not matches:
1716 if not matches:
1709 return matches
1717 return matches
1710
1718
1711 # get the cursor position of
1719 # get the cursor position of
1712 # - the text being completed
1720 # - the text being completed
1713 # - the start of the key text
1721 # - the start of the key text
1714 # - the start of the completion
1722 # - the start of the completion
1715 text_start = len(self.text_until_cursor) - len(text)
1723 text_start = len(self.text_until_cursor) - len(text)
1716 if prefix:
1724 if prefix:
1717 key_start = match.start(3)
1725 key_start = match.start(3)
1718 completion_start = key_start + token_offset
1726 completion_start = key_start + token_offset
1719 else:
1727 else:
1720 key_start = completion_start = match.end()
1728 key_start = completion_start = match.end()
1721
1729
1722 # grab the leading prefix, to make sure all completions start with `text`
1730 # grab the leading prefix, to make sure all completions start with `text`
1723 if text_start > key_start:
1731 if text_start > key_start:
1724 leading = ''
1732 leading = ''
1725 else:
1733 else:
1726 leading = text[text_start:completion_start]
1734 leading = text[text_start:completion_start]
1727
1735
1728 # the index of the `[` character
1736 # the index of the `[` character
1729 bracket_idx = match.end(1)
1737 bracket_idx = match.end(1)
1730
1738
1731 # append closing quote and bracket as appropriate
1739 # append closing quote and bracket as appropriate
1732 # this is *not* appropriate if the opening quote or bracket is outside
1740 # this is *not* appropriate if the opening quote or bracket is outside
1733 # the text given to this method
1741 # the text given to this method
1734 suf = ''
1742 suf = ''
1735 continuation = self.line_buffer[len(self.text_until_cursor):]
1743 continuation = self.line_buffer[len(self.text_until_cursor):]
1736 if key_start > text_start and closing_quote:
1744 if key_start > text_start and closing_quote:
1737 # quotes were opened inside text, maybe close them
1745 # quotes were opened inside text, maybe close them
1738 if continuation.startswith(closing_quote):
1746 if continuation.startswith(closing_quote):
1739 continuation = continuation[len(closing_quote):]
1747 continuation = continuation[len(closing_quote):]
1740 else:
1748 else:
1741 suf += closing_quote
1749 suf += closing_quote
1742 if bracket_idx > text_start:
1750 if bracket_idx > text_start:
1743 # brackets were opened inside text, maybe close them
1751 # brackets were opened inside text, maybe close them
1744 if not continuation.startswith(']'):
1752 if not continuation.startswith(']'):
1745 suf += ']'
1753 suf += ']'
1746
1754
1747 return [leading + k + suf for k in matches]
1755 return [leading + k + suf for k in matches]
1748
1756
1749 @staticmethod
1757 @staticmethod
1750 def unicode_name_matches(text:str) -> Tuple[str, List[str]] :
1758 def unicode_name_matches(text:str) -> Tuple[str, List[str]] :
1751 """Match Latex-like syntax for unicode characters base
1759 """Match Latex-like syntax for unicode characters base
1752 on the name of the character.
1760 on the name of the character.
1753
1761
1754 This does ``\\GREEK SMALL LETTER ETA`` -> ``η``
1762 This does ``\\GREEK SMALL LETTER ETA`` -> ``η``
1755
1763
1756 Works only on valid python 3 identifier, or on combining characters that
1764 Works only on valid python 3 identifier, or on combining characters that
1757 will combine to form a valid identifier.
1765 will combine to form a valid identifier.
1758 """
1766 """
1759 slashpos = text.rfind('\\')
1767 slashpos = text.rfind('\\')
1760 if slashpos > -1:
1768 if slashpos > -1:
1761 s = text[slashpos+1:]
1769 s = text[slashpos+1:]
1762 try :
1770 try :
1763 unic = unicodedata.lookup(s)
1771 unic = unicodedata.lookup(s)
1764 # allow combining chars
1772 # allow combining chars
1765 if ('a'+unic).isidentifier():
1773 if ('a'+unic).isidentifier():
1766 return '\\'+s,[unic]
1774 return '\\'+s,[unic]
1767 except KeyError:
1775 except KeyError:
1768 pass
1776 pass
1769 return '', []
1777 return '', []
1770
1778
1771
1779
1772 def latex_matches(self, text:str) -> Tuple[str, Sequence[str]]:
1780 def latex_matches(self, text:str) -> Tuple[str, Sequence[str]]:
1773 """Match Latex syntax for unicode characters.
1781 """Match Latex syntax for unicode characters.
1774
1782
1775 This does both ``\\alp`` -> ``\\alpha`` and ``\\alpha`` -> ``α``
1783 This does both ``\\alp`` -> ``\\alpha`` and ``\\alpha`` -> ``α``
1776 """
1784 """
1777 slashpos = text.rfind('\\')
1785 slashpos = text.rfind('\\')
1778 if slashpos > -1:
1786 if slashpos > -1:
1779 s = text[slashpos:]
1787 s = text[slashpos:]
1780 if s in latex_symbols:
1788 if s in latex_symbols:
1781 # Try to complete a full latex symbol to unicode
1789 # Try to complete a full latex symbol to unicode
1782 # \\alpha -> α
1790 # \\alpha -> α
1783 return s, [latex_symbols[s]]
1791 return s, [latex_symbols[s]]
1784 else:
1792 else:
1785 # If a user has partially typed a latex symbol, give them
1793 # If a user has partially typed a latex symbol, give them
1786 # a full list of options \al -> [\aleph, \alpha]
1794 # a full list of options \al -> [\aleph, \alpha]
1787 matches = [k for k in latex_symbols if k.startswith(s)]
1795 matches = [k for k in latex_symbols if k.startswith(s)]
1788 if matches:
1796 if matches:
1789 return s, matches
1797 return s, matches
1790 return '', ()
1798 return '', ()
1791
1799
1792 def dispatch_custom_completer(self, text):
1800 def dispatch_custom_completer(self, text):
1793 if not self.custom_completers:
1801 if not self.custom_completers:
1794 return
1802 return
1795
1803
1796 line = self.line_buffer
1804 line = self.line_buffer
1797 if not line.strip():
1805 if not line.strip():
1798 return None
1806 return None
1799
1807
1800 # Create a little structure to pass all the relevant information about
1808 # Create a little structure to pass all the relevant information about
1801 # the current completion to any custom completer.
1809 # the current completion to any custom completer.
1802 event = SimpleNamespace()
1810 event = SimpleNamespace()
1803 event.line = line
1811 event.line = line
1804 event.symbol = text
1812 event.symbol = text
1805 cmd = line.split(None,1)[0]
1813 cmd = line.split(None,1)[0]
1806 event.command = cmd
1814 event.command = cmd
1807 event.text_until_cursor = self.text_until_cursor
1815 event.text_until_cursor = self.text_until_cursor
1808
1816
1809 # for foo etc, try also to find completer for %foo
1817 # for foo etc, try also to find completer for %foo
1810 if not cmd.startswith(self.magic_escape):
1818 if not cmd.startswith(self.magic_escape):
1811 try_magic = self.custom_completers.s_matches(
1819 try_magic = self.custom_completers.s_matches(
1812 self.magic_escape + cmd)
1820 self.magic_escape + cmd)
1813 else:
1821 else:
1814 try_magic = []
1822 try_magic = []
1815
1823
1816 for c in itertools.chain(self.custom_completers.s_matches(cmd),
1824 for c in itertools.chain(self.custom_completers.s_matches(cmd),
1817 try_magic,
1825 try_magic,
1818 self.custom_completers.flat_matches(self.text_until_cursor)):
1826 self.custom_completers.flat_matches(self.text_until_cursor)):
1819 try:
1827 try:
1820 res = c(event)
1828 res = c(event)
1821 if res:
1829 if res:
1822 # first, try case sensitive match
1830 # first, try case sensitive match
1823 withcase = [r for r in res if r.startswith(text)]
1831 withcase = [r for r in res if r.startswith(text)]
1824 if withcase:
1832 if withcase:
1825 return withcase
1833 return withcase
1826 # if none, then case insensitive ones are ok too
1834 # if none, then case insensitive ones are ok too
1827 text_low = text.lower()
1835 text_low = text.lower()
1828 return [r for r in res if r.lower().startswith(text_low)]
1836 return [r for r in res if r.lower().startswith(text_low)]
1829 except TryNext:
1837 except TryNext:
1830 pass
1838 pass
1831 except KeyboardInterrupt:
1839 except KeyboardInterrupt:
1832 """
1840 """
1833 If custom completer take too long,
1841 If custom completer take too long,
1834 let keyboard interrupt abort and return nothing.
1842 let keyboard interrupt abort and return nothing.
1835 """
1843 """
1836 break
1844 break
1837
1845
1838 return None
1846 return None
1839
1847
1840 def completions(self, text: str, offset: int)->Iterator[Completion]:
1848 def completions(self, text: str, offset: int)->Iterator[Completion]:
1841 """
1849 """
1842 Returns an iterator over the possible completions
1850 Returns an iterator over the possible completions
1843
1851
1844 .. warning::
1852 .. warning::
1845
1853
1846 Unstable
1854 Unstable
1847
1855
1848 This function is unstable, API may change without warning.
1856 This function is unstable, API may change without warning.
1849 It will also raise unless use in proper context manager.
1857 It will also raise unless use in proper context manager.
1850
1858
1851 Parameters
1859 Parameters
1852 ----------
1860 ----------
1853 text : str
1861 text : str
1854 Full text of the current input, multi line string.
1862 Full text of the current input, multi line string.
1855 offset : int
1863 offset : int
1856 Integer representing the position of the cursor in ``text``. Offset
1864 Integer representing the position of the cursor in ``text``. Offset
1857 is 0-based indexed.
1865 is 0-based indexed.
1858
1866
1859 Yields
1867 Yields
1860 ------
1868 ------
1861 Completion
1869 Completion
1862
1870
1863 Notes
1871 Notes
1864 -----
1872 -----
1865 The cursor on a text can either be seen as being "in between"
1873 The cursor on a text can either be seen as being "in between"
1866 characters or "On" a character depending on the interface visible to
1874 characters or "On" a character depending on the interface visible to
1867 the user. For consistency the cursor being on "in between" characters X
1875 the user. For consistency the cursor being on "in between" characters X
1868 and Y is equivalent to the cursor being "on" character Y, that is to say
1876 and Y is equivalent to the cursor being "on" character Y, that is to say
1869 the character the cursor is on is considered as being after the cursor.
1877 the character the cursor is on is considered as being after the cursor.
1870
1878
1871 Combining characters may span more that one position in the
1879 Combining characters may span more that one position in the
1872 text.
1880 text.
1873
1881
1874 .. note::
1882 .. note::
1875
1883
1876 If ``IPCompleter.debug`` is :any:`True` will yield a ``--jedi/ipython--``
1884 If ``IPCompleter.debug`` is :any:`True` will yield a ``--jedi/ipython--``
1877 fake Completion token to distinguish completion returned by Jedi
1885 fake Completion token to distinguish completion returned by Jedi
1878 and usual IPython completion.
1886 and usual IPython completion.
1879
1887
1880 .. note::
1888 .. note::
1881
1889
1882 Completions are not completely deduplicated yet. If identical
1890 Completions are not completely deduplicated yet. If identical
1883 completions are coming from different sources this function does not
1891 completions are coming from different sources this function does not
1884 ensure that each completion object will only be present once.
1892 ensure that each completion object will only be present once.
1885 """
1893 """
1886 warnings.warn("_complete is a provisional API (as of IPython 6.0). "
1894 warnings.warn("_complete is a provisional API (as of IPython 6.0). "
1887 "It may change without warnings. "
1895 "It may change without warnings. "
1888 "Use in corresponding context manager.",
1896 "Use in corresponding context manager.",
1889 category=ProvisionalCompleterWarning, stacklevel=2)
1897 category=ProvisionalCompleterWarning, stacklevel=2)
1890
1898
1891 seen = set()
1899 seen = set()
1892 profiler:Optional[cProfile.Profile]
1900 profiler:Optional[cProfile.Profile]
1893 try:
1901 try:
1894 if self.profile_completions:
1902 if self.profile_completions:
1895 import cProfile
1903 import cProfile
1896 profiler = cProfile.Profile()
1904 profiler = cProfile.Profile()
1897 profiler.enable()
1905 profiler.enable()
1898 else:
1906 else:
1899 profiler = None
1907 profiler = None
1900
1908
1901 for c in self._completions(text, offset, _timeout=self.jedi_compute_type_timeout/1000):
1909 for c in self._completions(text, offset, _timeout=self.jedi_compute_type_timeout/1000):
1902 if c and (c in seen):
1910 if c and (c in seen):
1903 continue
1911 continue
1904 yield c
1912 yield c
1905 seen.add(c)
1913 seen.add(c)
1906 except KeyboardInterrupt:
1914 except KeyboardInterrupt:
1907 """if completions take too long and users send keyboard interrupt,
1915 """if completions take too long and users send keyboard interrupt,
1908 do not crash and return ASAP. """
1916 do not crash and return ASAP. """
1909 pass
1917 pass
1910 finally:
1918 finally:
1911 if profiler is not None:
1919 if profiler is not None:
1912 profiler.disable()
1920 profiler.disable()
1913 ensure_dir_exists(self.profiler_output_dir)
1921 ensure_dir_exists(self.profiler_output_dir)
1914 output_path = os.path.join(self.profiler_output_dir, str(uuid.uuid4()))
1922 output_path = os.path.join(self.profiler_output_dir, str(uuid.uuid4()))
1915 print("Writing profiler output to", output_path)
1923 print("Writing profiler output to", output_path)
1916 profiler.dump_stats(output_path)
1924 profiler.dump_stats(output_path)
1917
1925
1918 def _completions(self, full_text: str, offset: int, *, _timeout) -> Iterator[Completion]:
1926 def _completions(self, full_text: str, offset: int, *, _timeout) -> Iterator[Completion]:
1919 """
1927 """
1920 Core completion module.Same signature as :any:`completions`, with the
1928 Core completion module.Same signature as :any:`completions`, with the
1921 extra `timeout` parameter (in seconds).
1929 extra `timeout` parameter (in seconds).
1922
1930
1923 Computing jedi's completion ``.type`` can be quite expensive (it is a
1931 Computing jedi's completion ``.type`` can be quite expensive (it is a
1924 lazy property) and can require some warm-up, more warm up than just
1932 lazy property) and can require some warm-up, more warm up than just
1925 computing the ``name`` of a completion. The warm-up can be :
1933 computing the ``name`` of a completion. The warm-up can be :
1926
1934
1927 - Long warm-up the first time a module is encountered after
1935 - Long warm-up the first time a module is encountered after
1928 install/update: actually build parse/inference tree.
1936 install/update: actually build parse/inference tree.
1929
1937
1930 - first time the module is encountered in a session: load tree from
1938 - first time the module is encountered in a session: load tree from
1931 disk.
1939 disk.
1932
1940
1933 We don't want to block completions for tens of seconds so we give the
1941 We don't want to block completions for tens of seconds so we give the
1934 completer a "budget" of ``_timeout`` seconds per invocation to compute
1942 completer a "budget" of ``_timeout`` seconds per invocation to compute
1935 completions types, the completions that have not yet been computed will
1943 completions types, the completions that have not yet been computed will
1936 be marked as "unknown" an will have a chance to be computed next round
1944 be marked as "unknown" an will have a chance to be computed next round
1937 are things get cached.
1945 are things get cached.
1938
1946
1939 Keep in mind that Jedi is not the only thing treating the completion so
1947 Keep in mind that Jedi is not the only thing treating the completion so
1940 keep the timeout short-ish as if we take more than 0.3 second we still
1948 keep the timeout short-ish as if we take more than 0.3 second we still
1941 have lots of processing to do.
1949 have lots of processing to do.
1942
1950
1943 """
1951 """
1944 deadline = time.monotonic() + _timeout
1952 deadline = time.monotonic() + _timeout
1945
1953
1946
1954
1947 before = full_text[:offset]
1955 before = full_text[:offset]
1948 cursor_line, cursor_column = position_to_cursor(full_text, offset)
1956 cursor_line, cursor_column = position_to_cursor(full_text, offset)
1949
1957
1950 matched_text, matches, matches_origin, jedi_matches = self._complete(
1958 matched_text, matches, matches_origin, jedi_matches = self._complete(
1951 full_text=full_text, cursor_line=cursor_line, cursor_pos=cursor_column)
1959 full_text=full_text, cursor_line=cursor_line, cursor_pos=cursor_column)
1952
1960
1953 iter_jm = iter(jedi_matches)
1961 iter_jm = iter(jedi_matches)
1954 if _timeout:
1962 if _timeout:
1955 for jm in iter_jm:
1963 for jm in iter_jm:
1956 try:
1964 try:
1957 type_ = jm.type
1965 type_ = jm.type
1958 except Exception:
1966 except Exception:
1959 if self.debug:
1967 if self.debug:
1960 print("Error in Jedi getting type of ", jm)
1968 print("Error in Jedi getting type of ", jm)
1961 type_ = None
1969 type_ = None
1962 delta = len(jm.name_with_symbols) - len(jm.complete)
1970 delta = len(jm.name_with_symbols) - len(jm.complete)
1963 if type_ == 'function':
1971 if type_ == 'function':
1964 signature = _make_signature(jm)
1972 signature = _make_signature(jm)
1965 else:
1973 else:
1966 signature = ''
1974 signature = ''
1967 yield Completion(start=offset - delta,
1975 yield Completion(start=offset - delta,
1968 end=offset,
1976 end=offset,
1969 text=jm.name_with_symbols,
1977 text=jm.name_with_symbols,
1970 type=type_,
1978 type=type_,
1971 signature=signature,
1979 signature=signature,
1972 _origin='jedi')
1980 _origin='jedi')
1973
1981
1974 if time.monotonic() > deadline:
1982 if time.monotonic() > deadline:
1975 break
1983 break
1976
1984
1977 for jm in iter_jm:
1985 for jm in iter_jm:
1978 delta = len(jm.name_with_symbols) - len(jm.complete)
1986 delta = len(jm.name_with_symbols) - len(jm.complete)
1979 yield Completion(start=offset - delta,
1987 yield Completion(start=offset - delta,
1980 end=offset,
1988 end=offset,
1981 text=jm.name_with_symbols,
1989 text=jm.name_with_symbols,
1982 type='<unknown>', # don't compute type for speed
1990 type='<unknown>', # don't compute type for speed
1983 _origin='jedi',
1991 _origin='jedi',
1984 signature='')
1992 signature='')
1985
1993
1986
1994
1987 start_offset = before.rfind(matched_text)
1995 start_offset = before.rfind(matched_text)
1988
1996
1989 # TODO:
1997 # TODO:
1990 # Suppress this, right now just for debug.
1998 # Suppress this, right now just for debug.
1991 if jedi_matches and matches and self.debug:
1999 if jedi_matches and matches and self.debug:
1992 yield Completion(start=start_offset, end=offset, text='--jedi/ipython--',
2000 yield Completion(start=start_offset, end=offset, text='--jedi/ipython--',
1993 _origin='debug', type='none', signature='')
2001 _origin='debug', type='none', signature='')
1994
2002
1995 # I'm unsure if this is always true, so let's assert and see if it
2003 # I'm unsure if this is always true, so let's assert and see if it
1996 # crash
2004 # crash
1997 assert before.endswith(matched_text)
2005 assert before.endswith(matched_text)
1998 for m, t in zip(matches, matches_origin):
2006 for m, t in zip(matches, matches_origin):
1999 yield Completion(start=start_offset, end=offset, text=m, _origin=t, signature='', type='<unknown>')
2007 yield Completion(start=start_offset, end=offset, text=m, _origin=t, signature='', type='<unknown>')
2000
2008
2001
2009
2002 def complete(self, text=None, line_buffer=None, cursor_pos=None) -> Tuple[str, Sequence[str]]:
2010 def complete(self, text=None, line_buffer=None, cursor_pos=None) -> Tuple[str, Sequence[str]]:
2003 """Find completions for the given text and line context.
2011 """Find completions for the given text and line context.
2004
2012
2005 Note that both the text and the line_buffer are optional, but at least
2013 Note that both the text and the line_buffer are optional, but at least
2006 one of them must be given.
2014 one of them must be given.
2007
2015
2008 Parameters
2016 Parameters
2009 ----------
2017 ----------
2010 text : string, optional
2018 text : string, optional
2011 Text to perform the completion on. If not given, the line buffer
2019 Text to perform the completion on. If not given, the line buffer
2012 is split using the instance's CompletionSplitter object.
2020 is split using the instance's CompletionSplitter object.
2013 line_buffer : string, optional
2021 line_buffer : string, optional
2014 If not given, the completer attempts to obtain the current line
2022 If not given, the completer attempts to obtain the current line
2015 buffer via readline. This keyword allows clients which are
2023 buffer via readline. This keyword allows clients which are
2016 requesting for text completions in non-readline contexts to inform
2024 requesting for text completions in non-readline contexts to inform
2017 the completer of the entire text.
2025 the completer of the entire text.
2018 cursor_pos : int, optional
2026 cursor_pos : int, optional
2019 Index of the cursor in the full line buffer. Should be provided by
2027 Index of the cursor in the full line buffer. Should be provided by
2020 remote frontends where kernel has no access to frontend state.
2028 remote frontends where kernel has no access to frontend state.
2021
2029
2022 Returns
2030 Returns
2023 -------
2031 -------
2024 Tuple of two items:
2032 Tuple of two items:
2025 text : str
2033 text : str
2026 Text that was actually used in the completion.
2034 Text that was actually used in the completion.
2027 matches : list
2035 matches : list
2028 A list of completion matches.
2036 A list of completion matches.
2029
2037
2030 Notes
2038 Notes
2031 -----
2039 -----
2032 This API is likely to be deprecated and replaced by
2040 This API is likely to be deprecated and replaced by
2033 :any:`IPCompleter.completions` in the future.
2041 :any:`IPCompleter.completions` in the future.
2034
2042
2035 """
2043 """
2036 warnings.warn('`Completer.complete` is pending deprecation since '
2044 warnings.warn('`Completer.complete` is pending deprecation since '
2037 'IPython 6.0 and will be replaced by `Completer.completions`.',
2045 'IPython 6.0 and will be replaced by `Completer.completions`.',
2038 PendingDeprecationWarning)
2046 PendingDeprecationWarning)
2039 # potential todo, FOLD the 3rd throw away argument of _complete
2047 # potential todo, FOLD the 3rd throw away argument of _complete
2040 # into the first 2 one.
2048 # into the first 2 one.
2041 return self._complete(line_buffer=line_buffer, cursor_pos=cursor_pos, text=text, cursor_line=0)[:2]
2049 return self._complete(line_buffer=line_buffer, cursor_pos=cursor_pos, text=text, cursor_line=0)[:2]
2042
2050
2043 def _complete(self, *, cursor_line, cursor_pos, line_buffer=None, text=None,
2051 def _complete(self, *, cursor_line, cursor_pos, line_buffer=None, text=None,
2044 full_text=None) -> _CompleteResult:
2052 full_text=None) -> _CompleteResult:
2045 """
2053 """
2046 Like complete but can also returns raw jedi completions as well as the
2054 Like complete but can also returns raw jedi completions as well as the
2047 origin of the completion text. This could (and should) be made much
2055 origin of the completion text. This could (and should) be made much
2048 cleaner but that will be simpler once we drop the old (and stateful)
2056 cleaner but that will be simpler once we drop the old (and stateful)
2049 :any:`complete` API.
2057 :any:`complete` API.
2050
2058
2051 With current provisional API, cursor_pos act both (depending on the
2059 With current provisional API, cursor_pos act both (depending on the
2052 caller) as the offset in the ``text`` or ``line_buffer``, or as the
2060 caller) as the offset in the ``text`` or ``line_buffer``, or as the
2053 ``column`` when passing multiline strings this could/should be renamed
2061 ``column`` when passing multiline strings this could/should be renamed
2054 but would add extra noise.
2062 but would add extra noise.
2055
2063
2056 Parameters
2064 Parameters
2057 ----------
2065 ----------
2058 cursor_line :
2066 cursor_line
2059 Index of the line the cursor is on. 0 indexed.
2067 Index of the line the cursor is on. 0 indexed.
2060 cursor_pos :
2068 cursor_pos
2061 Position of the cursor in the current line/line_buffer/text. 0
2069 Position of the cursor in the current line/line_buffer/text. 0
2062 indexed.
2070 indexed.
2063 line_buffer : optional, str
2071 line_buffer : optional, str
2064 The current line the cursor is in, this is mostly due to legacy
2072 The current line the cursor is in, this is mostly due to legacy
2065 reason that readline coudl only give a us the single current line.
2073 reason that readline coudl only give a us the single current line.
2066 Prefer `full_text`.
2074 Prefer `full_text`.
2067 text : str
2075 text : str
2068 The current "token" the cursor is in, mostly also for historical
2076 The current "token" the cursor is in, mostly also for historical
2069 reasons. as the completer would trigger only after the current line
2077 reasons. as the completer would trigger only after the current line
2070 was parsed.
2078 was parsed.
2071 full_text : str
2079 full_text : str
2072 Full text of the current cell.
2080 Full text of the current cell.
2073
2081
2074 Returns
2082 Returns
2075 -------
2083 -------
2076 A tuple of N elements which are (likely):
2084 A tuple of N elements which are (likely):
2077 matched_text: ? the text that the complete matched
2085 matched_text: ? the text that the complete matched
2078 matches: list of completions ?
2086 matches: list of completions ?
2079 matches_origin: ? list same length as matches, and where each completion came from
2087 matches_origin: ? list same length as matches, and where each completion came from
2080 jedi_matches: list of Jedi matches, have it's own structure.
2088 jedi_matches: list of Jedi matches, have it's own structure.
2081 """
2089 """
2082
2090
2083
2091
2084 # if the cursor position isn't given, the only sane assumption we can
2092 # if the cursor position isn't given, the only sane assumption we can
2085 # make is that it's at the end of the line (the common case)
2093 # make is that it's at the end of the line (the common case)
2086 if cursor_pos is None:
2094 if cursor_pos is None:
2087 cursor_pos = len(line_buffer) if text is None else len(text)
2095 cursor_pos = len(line_buffer) if text is None else len(text)
2088
2096
2089 if self.use_main_ns:
2097 if self.use_main_ns:
2090 self.namespace = __main__.__dict__
2098 self.namespace = __main__.__dict__
2091
2099
2092 # if text is either None or an empty string, rely on the line buffer
2100 # if text is either None or an empty string, rely on the line buffer
2093 if (not line_buffer) and full_text:
2101 if (not line_buffer) and full_text:
2094 line_buffer = full_text.split('\n')[cursor_line]
2102 line_buffer = full_text.split('\n')[cursor_line]
2095 if not text: # issue #11508: check line_buffer before calling split_line
2103 if not text: # issue #11508: check line_buffer before calling split_line
2096 text = self.splitter.split_line(line_buffer, cursor_pos) if line_buffer else ''
2104 text = self.splitter.split_line(line_buffer, cursor_pos) if line_buffer else ''
2097
2105
2098 if self.backslash_combining_completions:
2106 if self.backslash_combining_completions:
2099 # allow deactivation of these on windows.
2107 # allow deactivation of these on windows.
2100 base_text = text if not line_buffer else line_buffer[:cursor_pos]
2108 base_text = text if not line_buffer else line_buffer[:cursor_pos]
2101
2109
2102 for meth in (self.latex_matches,
2110 for meth in (self.latex_matches,
2103 self.unicode_name_matches,
2111 self.unicode_name_matches,
2104 back_latex_name_matches,
2112 back_latex_name_matches,
2105 back_unicode_name_matches,
2113 back_unicode_name_matches,
2106 self.fwd_unicode_match):
2114 self.fwd_unicode_match):
2107 name_text, name_matches = meth(base_text)
2115 name_text, name_matches = meth(base_text)
2108 if name_text:
2116 if name_text:
2109 return _CompleteResult(name_text, name_matches[:MATCHES_LIMIT], \
2117 return _CompleteResult(name_text, name_matches[:MATCHES_LIMIT], \
2110 [meth.__qualname__]*min(len(name_matches), MATCHES_LIMIT), ())
2118 [meth.__qualname__]*min(len(name_matches), MATCHES_LIMIT), ())
2111
2119
2112
2120
2113 # If no line buffer is given, assume the input text is all there was
2121 # If no line buffer is given, assume the input text is all there was
2114 if line_buffer is None:
2122 if line_buffer is None:
2115 line_buffer = text
2123 line_buffer = text
2116
2124
2117 self.line_buffer = line_buffer
2125 self.line_buffer = line_buffer
2118 self.text_until_cursor = self.line_buffer[:cursor_pos]
2126 self.text_until_cursor = self.line_buffer[:cursor_pos]
2119
2127
2120 # Do magic arg matches
2128 # Do magic arg matches
2121 for matcher in self.magic_arg_matchers:
2129 for matcher in self.magic_arg_matchers:
2122 matches = list(matcher(line_buffer))[:MATCHES_LIMIT]
2130 matches = list(matcher(line_buffer))[:MATCHES_LIMIT]
2123 if matches:
2131 if matches:
2124 origins = [matcher.__qualname__] * len(matches)
2132 origins = [matcher.__qualname__] * len(matches)
2125 return _CompleteResult(text, matches, origins, ())
2133 return _CompleteResult(text, matches, origins, ())
2126
2134
2127 # Start with a clean slate of completions
2135 # Start with a clean slate of completions
2128 matches = []
2136 matches = []
2129
2137
2130 # FIXME: we should extend our api to return a dict with completions for
2138 # FIXME: we should extend our api to return a dict with completions for
2131 # different types of objects. The rlcomplete() method could then
2139 # different types of objects. The rlcomplete() method could then
2132 # simply collapse the dict into a list for readline, but we'd have
2140 # simply collapse the dict into a list for readline, but we'd have
2133 # richer completion semantics in other environments.
2141 # richer completion semantics in other environments.
2134 completions:Iterable[Any] = []
2142 completions:Iterable[Any] = []
2135 if self.use_jedi:
2143 if self.use_jedi:
2136 if not full_text:
2144 if not full_text:
2137 full_text = line_buffer
2145 full_text = line_buffer
2138 completions = self._jedi_matches(
2146 completions = self._jedi_matches(
2139 cursor_pos, cursor_line, full_text)
2147 cursor_pos, cursor_line, full_text)
2140
2148
2141 if self.merge_completions:
2149 if self.merge_completions:
2142 matches = []
2150 matches = []
2143 for matcher in self.matchers:
2151 for matcher in self.matchers:
2144 try:
2152 try:
2145 matches.extend([(m, matcher.__qualname__)
2153 matches.extend([(m, matcher.__qualname__)
2146 for m in matcher(text)])
2154 for m in matcher(text)])
2147 except:
2155 except:
2148 # Show the ugly traceback if the matcher causes an
2156 # Show the ugly traceback if the matcher causes an
2149 # exception, but do NOT crash the kernel!
2157 # exception, but do NOT crash the kernel!
2150 sys.excepthook(*sys.exc_info())
2158 sys.excepthook(*sys.exc_info())
2151 else:
2159 else:
2152 for matcher in self.matchers:
2160 for matcher in self.matchers:
2153 matches = [(m, matcher.__qualname__)
2161 matches = [(m, matcher.__qualname__)
2154 for m in matcher(text)]
2162 for m in matcher(text)]
2155 if matches:
2163 if matches:
2156 break
2164 break
2157
2165
2158 seen = set()
2166 seen = set()
2159 filtered_matches = set()
2167 filtered_matches = set()
2160 for m in matches:
2168 for m in matches:
2161 t, c = m
2169 t, c = m
2162 if t not in seen:
2170 if t not in seen:
2163 filtered_matches.add(m)
2171 filtered_matches.add(m)
2164 seen.add(t)
2172 seen.add(t)
2165
2173
2166 _filtered_matches = sorted(filtered_matches, key=lambda x: completions_sorting_key(x[0]))
2174 _filtered_matches = sorted(filtered_matches, key=lambda x: completions_sorting_key(x[0]))
2167
2175
2168 custom_res = [(m, 'custom') for m in self.dispatch_custom_completer(text) or []]
2176 custom_res = [(m, 'custom') for m in self.dispatch_custom_completer(text) or []]
2169
2177
2170 _filtered_matches = custom_res or _filtered_matches
2178 _filtered_matches = custom_res or _filtered_matches
2171
2179
2172 _filtered_matches = _filtered_matches[:MATCHES_LIMIT]
2180 _filtered_matches = _filtered_matches[:MATCHES_LIMIT]
2173 _matches = [m[0] for m in _filtered_matches]
2181 _matches = [m[0] for m in _filtered_matches]
2174 origins = [m[1] for m in _filtered_matches]
2182 origins = [m[1] for m in _filtered_matches]
2175
2183
2176 self.matches = _matches
2184 self.matches = _matches
2177
2185
2178 return _CompleteResult(text, _matches, origins, completions)
2186 return _CompleteResult(text, _matches, origins, completions)
2179
2187
2180 def fwd_unicode_match(self, text:str) -> Tuple[str, Sequence[str]]:
2188 def fwd_unicode_match(self, text:str) -> Tuple[str, Sequence[str]]:
2181 """
2189 """
2182 Forward match a string starting with a backslash with a list of
2190 Forward match a string starting with a backslash with a list of
2183 potential Unicode completions.
2191 potential Unicode completions.
2184
2192
2185 Will compute list list of Unicode character names on first call and cache it.
2193 Will compute list list of Unicode character names on first call and cache it.
2186
2194
2187 Returns
2195 Returns
2188 -------
2196 -------
2189 At tuple with:
2197 At tuple with:
2190 - matched text (empty if no matches)
2198 - matched text (empty if no matches)
2191 - list of potential completions, empty tuple otherwise)
2199 - list of potential completions, empty tuple otherwise)
2192 """
2200 """
2193 # TODO: self.unicode_names is here a list we traverse each time with ~100k elements.
2201 # TODO: self.unicode_names is here a list we traverse each time with ~100k elements.
2194 # We could do a faster match using a Trie.
2202 # We could do a faster match using a Trie.
2195
2203
2196 # Using pygtrie the following seem to work:
2204 # Using pygtrie the following seem to work:
2197
2205
2198 # s = PrefixSet()
2206 # s = PrefixSet()
2199
2207
2200 # for c in range(0,0x10FFFF + 1):
2208 # for c in range(0,0x10FFFF + 1):
2201 # try:
2209 # try:
2202 # s.add(unicodedata.name(chr(c)))
2210 # s.add(unicodedata.name(chr(c)))
2203 # except ValueError:
2211 # except ValueError:
2204 # pass
2212 # pass
2205 # [''.join(k) for k in s.iter(prefix)]
2213 # [''.join(k) for k in s.iter(prefix)]
2206
2214
2207 # But need to be timed and adds an extra dependency.
2215 # But need to be timed and adds an extra dependency.
2208
2216
2209 slashpos = text.rfind('\\')
2217 slashpos = text.rfind('\\')
2210 # if text starts with slash
2218 # if text starts with slash
2211 if slashpos > -1:
2219 if slashpos > -1:
2212 # PERF: It's important that we don't access self._unicode_names
2220 # PERF: It's important that we don't access self._unicode_names
2213 # until we're inside this if-block. _unicode_names is lazily
2221 # until we're inside this if-block. _unicode_names is lazily
2214 # initialized, and it takes a user-noticeable amount of time to
2222 # initialized, and it takes a user-noticeable amount of time to
2215 # initialize it, so we don't want to initialize it unless we're
2223 # initialize it, so we don't want to initialize it unless we're
2216 # actually going to use it.
2224 # actually going to use it.
2217 s = text[slashpos+1:]
2225 s = text[slashpos+1:]
2218 candidates = [x for x in self.unicode_names if x.startswith(s)]
2226 candidates = [x for x in self.unicode_names if x.startswith(s)]
2219 if candidates:
2227 if candidates:
2220 return s, candidates
2228 return s, candidates
2221 else:
2229 else:
2222 return '', ()
2230 return '', ()
2223
2231
2224 # if text does not start with slash
2232 # if text does not start with slash
2225 else:
2233 else:
2226 return '', ()
2234 return '', ()
2227
2235
2228 @property
2236 @property
2229 def unicode_names(self) -> List[str]:
2237 def unicode_names(self) -> List[str]:
2230 """List of names of unicode code points that can be completed.
2238 """List of names of unicode code points that can be completed.
2231
2239
2232 The list is lazily initialized on first access.
2240 The list is lazily initialized on first access.
2233 """
2241 """
2234 if self._unicode_names is None:
2242 if self._unicode_names is None:
2235 names = []
2243 names = []
2236 for c in range(0,0x10FFFF + 1):
2244 for c in range(0,0x10FFFF + 1):
2237 try:
2245 try:
2238 names.append(unicodedata.name(chr(c)))
2246 names.append(unicodedata.name(chr(c)))
2239 except ValueError:
2247 except ValueError:
2240 pass
2248 pass
2241 self._unicode_names = _unicode_name_compute(_UNICODE_RANGES)
2249 self._unicode_names = _unicode_name_compute(_UNICODE_RANGES)
2242
2250
2243 return self._unicode_names
2251 return self._unicode_names
2244
2252
2245 def _unicode_name_compute(ranges:List[Tuple[int,int]]) -> List[str]:
2253 def _unicode_name_compute(ranges:List[Tuple[int,int]]) -> List[str]:
2246 names = []
2254 names = []
2247 for start,stop in ranges:
2255 for start,stop in ranges:
2248 for c in range(start, stop) :
2256 for c in range(start, stop) :
2249 try:
2257 try:
2250 names.append(unicodedata.name(chr(c)))
2258 names.append(unicodedata.name(chr(c)))
2251 except ValueError:
2259 except ValueError:
2252 pass
2260 pass
2253 return names
2261 return names
@@ -1,223 +1,237 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """sys.excepthook for IPython itself, leaves a detailed report on disk.
2 """sys.excepthook for IPython itself, leaves a detailed report on disk.
3
3
4 Authors:
4 Authors:
5
5
6 * Fernando Perez
6 * Fernando Perez
7 * Brian E. Granger
7 * Brian E. Granger
8 """
8 """
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
11 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
12 # Copyright (C) 2008-2011 The IPython Development Team
12 # Copyright (C) 2008-2011 The IPython Development Team
13 #
13 #
14 # Distributed under the terms of the BSD License. The full license is in
14 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
15 # the file COPYING, distributed as part of this software.
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 import os
22 import os
23 import sys
23 import sys
24 import traceback
24 import traceback
25 from pprint import pformat
25 from pprint import pformat
26 from pathlib import Path
26 from pathlib import Path
27
27
28 from IPython.core import ultratb
28 from IPython.core import ultratb
29 from IPython.core.release import author_email
29 from IPython.core.release import author_email
30 from IPython.utils.sysinfo import sys_info
30 from IPython.utils.sysinfo import sys_info
31 from IPython.utils.py3compat import input
31 from IPython.utils.py3compat import input
32
32
33 from IPython.core.release import __version__ as version
33 from IPython.core.release import __version__ as version
34
34
35 from typing import Optional
36
35 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
36 # Code
38 # Code
37 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
38
40
39 # Template for the user message.
41 # Template for the user message.
40 _default_message_template = """\
42 _default_message_template = """\
41 Oops, {app_name} crashed. We do our best to make it stable, but...
43 Oops, {app_name} crashed. We do our best to make it stable, but...
42
44
43 A crash report was automatically generated with the following information:
45 A crash report was automatically generated with the following information:
44 - A verbatim copy of the crash traceback.
46 - A verbatim copy of the crash traceback.
45 - A copy of your input history during this session.
47 - A copy of your input history during this session.
46 - Data on your current {app_name} configuration.
48 - Data on your current {app_name} configuration.
47
49
48 It was left in the file named:
50 It was left in the file named:
49 \t'{crash_report_fname}'
51 \t'{crash_report_fname}'
50 If you can email this file to the developers, the information in it will help
52 If you can email this file to the developers, the information in it will help
51 them in understanding and correcting the problem.
53 them in understanding and correcting the problem.
52
54
53 You can mail it to: {contact_name} at {contact_email}
55 You can mail it to: {contact_name} at {contact_email}
54 with the subject '{app_name} Crash Report'.
56 with the subject '{app_name} Crash Report'.
55
57
56 If you want to do it now, the following command will work (under Unix):
58 If you want to do it now, the following command will work (under Unix):
57 mail -s '{app_name} Crash Report' {contact_email} < {crash_report_fname}
59 mail -s '{app_name} Crash Report' {contact_email} < {crash_report_fname}
58
60
59 In your email, please also include information about:
61 In your email, please also include information about:
60 - The operating system under which the crash happened: Linux, macOS, Windows,
62 - The operating system under which the crash happened: Linux, macOS, Windows,
61 other, and which exact version (for example: Ubuntu 16.04.3, macOS 10.13.2,
63 other, and which exact version (for example: Ubuntu 16.04.3, macOS 10.13.2,
62 Windows 10 Pro), and whether it is 32-bit or 64-bit;
64 Windows 10 Pro), and whether it is 32-bit or 64-bit;
63 - How {app_name} was installed: using pip or conda, from GitHub, as part of
65 - How {app_name} was installed: using pip or conda, from GitHub, as part of
64 a Docker container, or other, providing more detail if possible;
66 a Docker container, or other, providing more detail if possible;
65 - How to reproduce the crash: what exact sequence of instructions can one
67 - How to reproduce the crash: what exact sequence of instructions can one
66 input to get the same crash? Ideally, find a minimal yet complete sequence
68 input to get the same crash? Ideally, find a minimal yet complete sequence
67 of instructions that yields the crash.
69 of instructions that yields the crash.
68
70
69 To ensure accurate tracking of this issue, please file a report about it at:
71 To ensure accurate tracking of this issue, please file a report about it at:
70 {bug_tracker}
72 {bug_tracker}
71 """
73 """
72
74
73 _lite_message_template = """
75 _lite_message_template = """
74 If you suspect this is an IPython {version} bug, please report it at:
76 If you suspect this is an IPython {version} bug, please report it at:
75 https://github.com/ipython/ipython/issues
77 https://github.com/ipython/ipython/issues
76 or send an email to the mailing list at {email}
78 or send an email to the mailing list at {email}
77
79
78 You can print a more detailed traceback right now with "%tb", or use "%debug"
80 You can print a more detailed traceback right now with "%tb", or use "%debug"
79 to interactively debug it.
81 to interactively debug it.
80
82
81 Extra-detailed tracebacks for bug-reporting purposes can be enabled via:
83 Extra-detailed tracebacks for bug-reporting purposes can be enabled via:
82 {config}Application.verbose_crash=True
84 {config}Application.verbose_crash=True
83 """
85 """
84
86
85
87
86 class CrashHandler(object):
88 class CrashHandler(object):
87 """Customizable crash handlers for IPython applications.
89 """Customizable crash handlers for IPython applications.
88
90
89 Instances of this class provide a :meth:`__call__` method which can be
91 Instances of this class provide a :meth:`__call__` method which can be
90 used as a ``sys.excepthook``. The :meth:`__call__` signature is::
92 used as a ``sys.excepthook``. The :meth:`__call__` signature is::
91
93
92 def __call__(self, etype, evalue, etb)
94 def __call__(self, etype, evalue, etb)
93 """
95 """
94
96
95 message_template = _default_message_template
97 message_template = _default_message_template
96 section_sep = '\n\n'+'*'*75+'\n\n'
98 section_sep = '\n\n'+'*'*75+'\n\n'
97
99
98 def __init__(self, app, contact_name=None, contact_email=None,
100 def __init__(
99 bug_tracker=None, show_crash_traceback=True, call_pdb=False):
101 self,
102 app,
103 contact_name: Optional[str] = None,
104 contact_email: Optional[str] = None,
105 bug_tracker: Optional[str] = None,
106 show_crash_traceback: bool = True,
107 call_pdb: bool = False,
108 ):
100 """Create a new crash handler
109 """Create a new crash handler
101
110
102 Parameters
111 Parameters
103 ----------
112 ----------
104 app : Application
113 app : Application
105 A running :class:`Application` instance, which will be queried at
114 A running :class:`Application` instance, which will be queried at
106 crash time for internal information.
115 crash time for internal information.
107 contact_name : str
116 contact_name : str
108 A string with the name of the person to contact.
117 A string with the name of the person to contact.
109 contact_email : str
118 contact_email : str
110 A string with the email address of the contact.
119 A string with the email address of the contact.
111 bug_tracker : str
120 bug_tracker : str
112 A string with the URL for your project's bug tracker.
121 A string with the URL for your project's bug tracker.
113 show_crash_traceback : bool
122 show_crash_traceback : bool
114 If false, don't print the crash traceback on stderr, only generate
123 If false, don't print the crash traceback on stderr, only generate
115 the on-disk report
124 the on-disk report
116 Non-argument instance attributes
125 call_pdb
126 Whether to call pdb on crash
127
128 Attributes
129 ----------
117 These instances contain some non-argument attributes which allow for
130 These instances contain some non-argument attributes which allow for
118 further customization of the crash handler's behavior. Please see the
131 further customization of the crash handler's behavior. Please see the
119 source for further details.
132 source for further details.
133
120 """
134 """
121 self.crash_report_fname = "Crash_report_%s.txt" % app.name
135 self.crash_report_fname = "Crash_report_%s.txt" % app.name
122 self.app = app
136 self.app = app
123 self.call_pdb = call_pdb
137 self.call_pdb = call_pdb
124 #self.call_pdb = True # dbg
138 #self.call_pdb = True # dbg
125 self.show_crash_traceback = show_crash_traceback
139 self.show_crash_traceback = show_crash_traceback
126 self.info = dict(app_name = app.name,
140 self.info = dict(app_name = app.name,
127 contact_name = contact_name,
141 contact_name = contact_name,
128 contact_email = contact_email,
142 contact_email = contact_email,
129 bug_tracker = bug_tracker,
143 bug_tracker = bug_tracker,
130 crash_report_fname = self.crash_report_fname)
144 crash_report_fname = self.crash_report_fname)
131
145
132
146
133 def __call__(self, etype, evalue, etb):
147 def __call__(self, etype, evalue, etb):
134 """Handle an exception, call for compatible with sys.excepthook"""
148 """Handle an exception, call for compatible with sys.excepthook"""
135
149
136 # do not allow the crash handler to be called twice without reinstalling it
150 # do not allow the crash handler to be called twice without reinstalling it
137 # this prevents unlikely errors in the crash handling from entering an
151 # this prevents unlikely errors in the crash handling from entering an
138 # infinite loop.
152 # infinite loop.
139 sys.excepthook = sys.__excepthook__
153 sys.excepthook = sys.__excepthook__
140
154
141 # Report tracebacks shouldn't use color in general (safer for users)
155 # Report tracebacks shouldn't use color in general (safer for users)
142 color_scheme = 'NoColor'
156 color_scheme = 'NoColor'
143
157
144 # Use this ONLY for developer debugging (keep commented out for release)
158 # Use this ONLY for developer debugging (keep commented out for release)
145 #color_scheme = 'Linux' # dbg
159 #color_scheme = 'Linux' # dbg
146 try:
160 try:
147 rptdir = self.app.ipython_dir
161 rptdir = self.app.ipython_dir
148 except:
162 except:
149 rptdir = Path.cwd()
163 rptdir = Path.cwd()
150 if rptdir is None or not Path.is_dir(rptdir):
164 if rptdir is None or not Path.is_dir(rptdir):
151 rptdir = Path.cwd()
165 rptdir = Path.cwd()
152 report_name = rptdir / self.crash_report_fname
166 report_name = rptdir / self.crash_report_fname
153 # write the report filename into the instance dict so it can get
167 # write the report filename into the instance dict so it can get
154 # properly expanded out in the user message template
168 # properly expanded out in the user message template
155 self.crash_report_fname = report_name
169 self.crash_report_fname = report_name
156 self.info['crash_report_fname'] = report_name
170 self.info['crash_report_fname'] = report_name
157 TBhandler = ultratb.VerboseTB(
171 TBhandler = ultratb.VerboseTB(
158 color_scheme=color_scheme,
172 color_scheme=color_scheme,
159 long_header=1,
173 long_header=1,
160 call_pdb=self.call_pdb,
174 call_pdb=self.call_pdb,
161 )
175 )
162 if self.call_pdb:
176 if self.call_pdb:
163 TBhandler(etype,evalue,etb)
177 TBhandler(etype,evalue,etb)
164 return
178 return
165 else:
179 else:
166 traceback = TBhandler.text(etype,evalue,etb,context=31)
180 traceback = TBhandler.text(etype,evalue,etb,context=31)
167
181
168 # print traceback to screen
182 # print traceback to screen
169 if self.show_crash_traceback:
183 if self.show_crash_traceback:
170 print(traceback, file=sys.stderr)
184 print(traceback, file=sys.stderr)
171
185
172 # and generate a complete report on disk
186 # and generate a complete report on disk
173 try:
187 try:
174 report = open(report_name,'w')
188 report = open(report_name,'w')
175 except:
189 except:
176 print('Could not create crash report on disk.', file=sys.stderr)
190 print('Could not create crash report on disk.', file=sys.stderr)
177 return
191 return
178
192
179 with report:
193 with report:
180 # Inform user on stderr of what happened
194 # Inform user on stderr of what happened
181 print('\n'+'*'*70+'\n', file=sys.stderr)
195 print('\n'+'*'*70+'\n', file=sys.stderr)
182 print(self.message_template.format(**self.info), file=sys.stderr)
196 print(self.message_template.format(**self.info), file=sys.stderr)
183
197
184 # Construct report on disk
198 # Construct report on disk
185 report.write(self.make_report(traceback))
199 report.write(self.make_report(traceback))
186
200
187 input("Hit <Enter> to quit (your terminal may close):")
201 input("Hit <Enter> to quit (your terminal may close):")
188
202
189 def make_report(self,traceback):
203 def make_report(self,traceback):
190 """Return a string containing a crash report."""
204 """Return a string containing a crash report."""
191
205
192 sec_sep = self.section_sep
206 sec_sep = self.section_sep
193
207
194 report = ['*'*75+'\n\n'+'IPython post-mortem report\n\n']
208 report = ['*'*75+'\n\n'+'IPython post-mortem report\n\n']
195 rpt_add = report.append
209 rpt_add = report.append
196 rpt_add(sys_info())
210 rpt_add(sys_info())
197
211
198 try:
212 try:
199 config = pformat(self.app.config)
213 config = pformat(self.app.config)
200 rpt_add(sec_sep)
214 rpt_add(sec_sep)
201 rpt_add('Application name: %s\n\n' % self.app_name)
215 rpt_add('Application name: %s\n\n' % self.app_name)
202 rpt_add('Current user configuration structure:\n\n')
216 rpt_add('Current user configuration structure:\n\n')
203 rpt_add(config)
217 rpt_add(config)
204 except:
218 except:
205 pass
219 pass
206 rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
220 rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
207
221
208 return ''.join(report)
222 return ''.join(report)
209
223
210
224
211 def crash_handler_lite(etype, evalue, tb):
225 def crash_handler_lite(etype, evalue, tb):
212 """a light excepthook, adding a small message to the usual traceback"""
226 """a light excepthook, adding a small message to the usual traceback"""
213 traceback.print_exception(etype, evalue, tb)
227 traceback.print_exception(etype, evalue, tb)
214
228
215 from IPython.core.interactiveshell import InteractiveShell
229 from IPython.core.interactiveshell import InteractiveShell
216 if InteractiveShell.initialized():
230 if InteractiveShell.initialized():
217 # we are in a Shell environment, give %magic example
231 # we are in a Shell environment, give %magic example
218 config = "%config "
232 config = "%config "
219 else:
233 else:
220 # we are not in a shell, show generic config
234 # we are not in a shell, show generic config
221 config = "c."
235 config = "c."
222 print(_lite_message_template.format(email=author_email, config=config, version=version), file=sys.stderr)
236 print(_lite_message_template.format(email=author_email, config=config, version=version), file=sys.stderr)
223
237
@@ -1,1003 +1,1000 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Pdb debugger class.
3 Pdb debugger class.
4
4
5
5
6 This is an extension to PDB which adds a number of new features.
6 This is an extension to PDB which adds a number of new features.
7 Note that there is also the `IPython.terminal.debugger` class which provides UI
7 Note that there is also the `IPython.terminal.debugger` class which provides UI
8 improvements.
8 improvements.
9
9
10 We also strongly recommend to use this via the `ipdb` package, which provides
10 We also strongly recommend to use this via the `ipdb` package, which provides
11 extra configuration options.
11 extra configuration options.
12
12
13 Among other things, this subclass of PDB:
13 Among other things, this subclass of PDB:
14 - supports many IPython magics like pdef/psource
14 - supports many IPython magics like pdef/psource
15 - hide frames in tracebacks based on `__tracebackhide__`
15 - hide frames in tracebacks based on `__tracebackhide__`
16 - allows to skip frames based on `__debuggerskip__`
16 - allows to skip frames based on `__debuggerskip__`
17
17
18 The skipping and hiding frames are configurable via the `skip_predicates`
18 The skipping and hiding frames are configurable via the `skip_predicates`
19 command.
19 command.
20
20
21 By default, frames from readonly files will be hidden, frames containing
21 By default, frames from readonly files will be hidden, frames containing
22 ``__tracebackhide__=True`` will be hidden.
22 ``__tracebackhide__=True`` will be hidden.
23
23
24 Frames containing ``__debuggerskip__`` will be stepped over, frames who's parent
24 Frames containing ``__debuggerskip__`` will be stepped over, frames who's parent
25 frames value of ``__debuggerskip__`` is ``True`` will be skipped.
25 frames value of ``__debuggerskip__`` is ``True`` will be skipped.
26
26
27 >>> def helpers_helper():
27 >>> def helpers_helper():
28 ... pass
28 ... pass
29 ...
29 ...
30 ... def helper_1():
30 ... def helper_1():
31 ... print("don't step in me")
31 ... print("don't step in me")
32 ... helpers_helpers() # will be stepped over unless breakpoint set.
32 ... helpers_helpers() # will be stepped over unless breakpoint set.
33 ...
33 ...
34 ...
34 ...
35 ... def helper_2():
35 ... def helper_2():
36 ... print("in me neither")
36 ... print("in me neither")
37 ...
37 ...
38
38
39 One can define a decorator that wraps a function between the two helpers:
39 One can define a decorator that wraps a function between the two helpers:
40
40
41 >>> def pdb_skipped_decorator(function):
41 >>> def pdb_skipped_decorator(function):
42 ...
42 ...
43 ...
43 ...
44 ... def wrapped_fn(*args, **kwargs):
44 ... def wrapped_fn(*args, **kwargs):
45 ... __debuggerskip__ = True
45 ... __debuggerskip__ = True
46 ... helper_1()
46 ... helper_1()
47 ... __debuggerskip__ = False
47 ... __debuggerskip__ = False
48 ... result = function(*args, **kwargs)
48 ... result = function(*args, **kwargs)
49 ... __debuggerskip__ = True
49 ... __debuggerskip__ = True
50 ... helper_2()
50 ... helper_2()
51 ... # setting __debuggerskip__ to False again is not necessary
51 ... # setting __debuggerskip__ to False again is not necessary
52 ... return result
52 ... return result
53 ...
53 ...
54 ... return wrapped_fn
54 ... return wrapped_fn
55
55
56 When decorating a function, ipdb will directly step into ``bar()`` by
56 When decorating a function, ipdb will directly step into ``bar()`` by
57 default:
57 default:
58
58
59 >>> @foo_decorator
59 >>> @foo_decorator
60 ... def bar(x, y):
60 ... def bar(x, y):
61 ... return x * y
61 ... return x * y
62
62
63
63
64 You can toggle the behavior with
64 You can toggle the behavior with
65
65
66 ipdb> skip_predicates debuggerskip false
66 ipdb> skip_predicates debuggerskip false
67
67
68 or configure it in your ``.pdbrc``
68 or configure it in your ``.pdbrc``
69
69
70
70
71
71
72 License
72 License
73 -------
73 -------
74
74
75 Modified from the standard pdb.Pdb class to avoid including readline, so that
75 Modified from the standard pdb.Pdb class to avoid including readline, so that
76 the command line completion of other programs which include this isn't
76 the command line completion of other programs which include this isn't
77 damaged.
77 damaged.
78
78
79 In the future, this class will be expanded with improvements over the standard
79 In the future, this class will be expanded with improvements over the standard
80 pdb.
80 pdb.
81
81
82 The original code in this file is mainly lifted out of cmd.py in Python 2.2,
82 The original code in this file is mainly lifted out of cmd.py in Python 2.2,
83 with minor changes. Licensing should therefore be under the standard Python
83 with minor changes. Licensing should therefore be under the standard Python
84 terms. For details on the PSF (Python Software Foundation) standard license,
84 terms. For details on the PSF (Python Software Foundation) standard license,
85 see:
85 see:
86
86
87 https://docs.python.org/2/license.html
87 https://docs.python.org/2/license.html
88
88
89
89
90 All the changes since then are under the same license as IPython.
90 All the changes since then are under the same license as IPython.
91
91
92 """
92 """
93
93
94 #*****************************************************************************
94 #*****************************************************************************
95 #
95 #
96 # This file is licensed under the PSF license.
96 # This file is licensed under the PSF license.
97 #
97 #
98 # Copyright (C) 2001 Python Software Foundation, www.python.org
98 # Copyright (C) 2001 Python Software Foundation, www.python.org
99 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
99 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
100 #
100 #
101 #
101 #
102 #*****************************************************************************
102 #*****************************************************************************
103
103
104 import bdb
104 import bdb
105 import inspect
105 import inspect
106 import linecache
106 import linecache
107 import sys
107 import sys
108 import warnings
108 import warnings
109 import re
109 import re
110 import os
110 import os
111
111
112 from IPython import get_ipython
112 from IPython import get_ipython
113 from IPython.utils import PyColorize
113 from IPython.utils import PyColorize
114 from IPython.utils import coloransi, py3compat
114 from IPython.utils import coloransi, py3compat
115 from IPython.core.excolors import exception_colors
115 from IPython.core.excolors import exception_colors
116
116
117 # skip module docstests
117 # skip module docstests
118 __skip_doctest__ = True
118 __skip_doctest__ = True
119
119
120 prompt = 'ipdb> '
120 prompt = 'ipdb> '
121
121
122 # We have to check this directly from sys.argv, config struct not yet available
122 # We have to check this directly from sys.argv, config struct not yet available
123 from pdb import Pdb as OldPdb
123 from pdb import Pdb as OldPdb
124
124
125 # Allow the set_trace code to operate outside of an ipython instance, even if
125 # Allow the set_trace code to operate outside of an ipython instance, even if
126 # it does so with some limitations. The rest of this support is implemented in
126 # it does so with some limitations. The rest of this support is implemented in
127 # the Tracer constructor.
127 # the Tracer constructor.
128
128
129 DEBUGGERSKIP = "__debuggerskip__"
129 DEBUGGERSKIP = "__debuggerskip__"
130
130
131
131
132 def make_arrow(pad):
132 def make_arrow(pad):
133 """generate the leading arrow in front of traceback or debugger"""
133 """generate the leading arrow in front of traceback or debugger"""
134 if pad >= 2:
134 if pad >= 2:
135 return '-'*(pad-2) + '> '
135 return '-'*(pad-2) + '> '
136 elif pad == 1:
136 elif pad == 1:
137 return '>'
137 return '>'
138 return ''
138 return ''
139
139
140
140
141 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
141 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
142 """Exception hook which handles `BdbQuit` exceptions.
142 """Exception hook which handles `BdbQuit` exceptions.
143
143
144 All other exceptions are processed using the `excepthook`
144 All other exceptions are processed using the `excepthook`
145 parameter.
145 parameter.
146 """
146 """
147 raise ValueError(
147 raise ValueError(
148 "`BdbQuit_excepthook` is deprecated since version 5.1",
148 "`BdbQuit_excepthook` is deprecated since version 5.1",
149 )
149 )
150
150
151
151
152 def BdbQuit_IPython_excepthook(self, et, ev, tb, tb_offset=None):
152 def BdbQuit_IPython_excepthook(self, et, ev, tb, tb_offset=None):
153 raise ValueError(
153 raise ValueError(
154 "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
154 "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
155 DeprecationWarning, stacklevel=2)
155 DeprecationWarning, stacklevel=2)
156
156
157
157
158 RGX_EXTRA_INDENT = re.compile(r'(?<=\n)\s+')
158 RGX_EXTRA_INDENT = re.compile(r'(?<=\n)\s+')
159
159
160
160
161 def strip_indentation(multiline_string):
161 def strip_indentation(multiline_string):
162 return RGX_EXTRA_INDENT.sub('', multiline_string)
162 return RGX_EXTRA_INDENT.sub('', multiline_string)
163
163
164
164
165 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
165 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
166 """Make new_fn have old_fn's doc string. This is particularly useful
166 """Make new_fn have old_fn's doc string. This is particularly useful
167 for the ``do_...`` commands that hook into the help system.
167 for the ``do_...`` commands that hook into the help system.
168 Adapted from from a comp.lang.python posting
168 Adapted from from a comp.lang.python posting
169 by Duncan Booth."""
169 by Duncan Booth."""
170 def wrapper(*args, **kw):
170 def wrapper(*args, **kw):
171 return new_fn(*args, **kw)
171 return new_fn(*args, **kw)
172 if old_fn.__doc__:
172 if old_fn.__doc__:
173 wrapper.__doc__ = strip_indentation(old_fn.__doc__) + additional_text
173 wrapper.__doc__ = strip_indentation(old_fn.__doc__) + additional_text
174 return wrapper
174 return wrapper
175
175
176
176
177 class Pdb(OldPdb):
177 class Pdb(OldPdb):
178 """Modified Pdb class, does not load readline.
178 """Modified Pdb class, does not load readline.
179
179
180 for a standalone version that uses prompt_toolkit, see
180 for a standalone version that uses prompt_toolkit, see
181 `IPython.terminal.debugger.TerminalPdb` and
181 `IPython.terminal.debugger.TerminalPdb` and
182 `IPython.terminal.debugger.set_trace()`
182 `IPython.terminal.debugger.set_trace()`
183
183
184
184
185 This debugger can hide and skip frames that are tagged according to some predicates.
185 This debugger can hide and skip frames that are tagged according to some predicates.
186 See the `skip_predicates` commands.
186 See the `skip_predicates` commands.
187
187
188 """
188 """
189
189
190 default_predicates = {
190 default_predicates = {
191 "tbhide": True,
191 "tbhide": True,
192 "readonly": False,
192 "readonly": False,
193 "ipython_internal": True,
193 "ipython_internal": True,
194 "debuggerskip": True,
194 "debuggerskip": True,
195 }
195 }
196
196
197 def __init__(self, completekey=None, stdin=None, stdout=None, context=5, **kwargs):
197 def __init__(self, completekey=None, stdin=None, stdout=None, context=5, **kwargs):
198 """Create a new IPython debugger.
198 """Create a new IPython debugger.
199
199
200 Parameters
200 Parameters
201 ----------
201 ----------
202 completekey : default None
202 completekey : default None
203 Passed to pdb.Pdb.
203 Passed to pdb.Pdb.
204 stdin : default None
204 stdin : default None
205 Passed to pdb.Pdb.
205 Passed to pdb.Pdb.
206 stdout : default None
206 stdout : default None
207 Passed to pdb.Pdb.
207 Passed to pdb.Pdb.
208 context : int
208 context : int
209 Number of lines of source code context to show when
209 Number of lines of source code context to show when
210 displaying stacktrace information.
210 displaying stacktrace information.
211 **kwargs
211 **kwargs
212 Passed to pdb.Pdb.
212 Passed to pdb.Pdb.
213
213
214 Notes
214 Notes
215 -----
215 -----
216 The possibilities are python version dependent, see the python
216 The possibilities are python version dependent, see the python
217 docs for more info.
217 docs for more info.
218 """
218 """
219
219
220 # Parent constructor:
220 # Parent constructor:
221 try:
221 try:
222 self.context = int(context)
222 self.context = int(context)
223 if self.context <= 0:
223 if self.context <= 0:
224 raise ValueError("Context must be a positive integer")
224 raise ValueError("Context must be a positive integer")
225 except (TypeError, ValueError) as e:
225 except (TypeError, ValueError) as e:
226 raise ValueError("Context must be a positive integer") from e
226 raise ValueError("Context must be a positive integer") from e
227
227
228 # `kwargs` ensures full compatibility with stdlib's `pdb.Pdb`.
228 # `kwargs` ensures full compatibility with stdlib's `pdb.Pdb`.
229 OldPdb.__init__(self, completekey, stdin, stdout, **kwargs)
229 OldPdb.__init__(self, completekey, stdin, stdout, **kwargs)
230
230
231 # IPython changes...
231 # IPython changes...
232 self.shell = get_ipython()
232 self.shell = get_ipython()
233
233
234 if self.shell is None:
234 if self.shell is None:
235 save_main = sys.modules['__main__']
235 save_main = sys.modules['__main__']
236 # No IPython instance running, we must create one
236 # No IPython instance running, we must create one
237 from IPython.terminal.interactiveshell import \
237 from IPython.terminal.interactiveshell import \
238 TerminalInteractiveShell
238 TerminalInteractiveShell
239 self.shell = TerminalInteractiveShell.instance()
239 self.shell = TerminalInteractiveShell.instance()
240 # needed by any code which calls __import__("__main__") after
240 # needed by any code which calls __import__("__main__") after
241 # the debugger was entered. See also #9941.
241 # the debugger was entered. See also #9941.
242 sys.modules["__main__"] = save_main
242 sys.modules["__main__"] = save_main
243
243
244
244
245 color_scheme = self.shell.colors
245 color_scheme = self.shell.colors
246
246
247 self.aliases = {}
247 self.aliases = {}
248
248
249 # Create color table: we copy the default one from the traceback
249 # Create color table: we copy the default one from the traceback
250 # module and add a few attributes needed for debugging
250 # module and add a few attributes needed for debugging
251 self.color_scheme_table = exception_colors()
251 self.color_scheme_table = exception_colors()
252
252
253 # shorthands
253 # shorthands
254 C = coloransi.TermColors
254 C = coloransi.TermColors
255 cst = self.color_scheme_table
255 cst = self.color_scheme_table
256
256
257 cst['NoColor'].colors.prompt = C.NoColor
257 cst['NoColor'].colors.prompt = C.NoColor
258 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
258 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
259 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
259 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
260
260
261 cst['Linux'].colors.prompt = C.Green
261 cst['Linux'].colors.prompt = C.Green
262 cst['Linux'].colors.breakpoint_enabled = C.LightRed
262 cst['Linux'].colors.breakpoint_enabled = C.LightRed
263 cst['Linux'].colors.breakpoint_disabled = C.Red
263 cst['Linux'].colors.breakpoint_disabled = C.Red
264
264
265 cst['LightBG'].colors.prompt = C.Blue
265 cst['LightBG'].colors.prompt = C.Blue
266 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
266 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
267 cst['LightBG'].colors.breakpoint_disabled = C.Red
267 cst['LightBG'].colors.breakpoint_disabled = C.Red
268
268
269 cst['Neutral'].colors.prompt = C.Blue
269 cst['Neutral'].colors.prompt = C.Blue
270 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
270 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
271 cst['Neutral'].colors.breakpoint_disabled = C.Red
271 cst['Neutral'].colors.breakpoint_disabled = C.Red
272
272
273 # Add a python parser so we can syntax highlight source while
273 # Add a python parser so we can syntax highlight source while
274 # debugging.
274 # debugging.
275 self.parser = PyColorize.Parser(style=color_scheme)
275 self.parser = PyColorize.Parser(style=color_scheme)
276 self.set_colors(color_scheme)
276 self.set_colors(color_scheme)
277
277
278 # Set the prompt - the default prompt is '(Pdb)'
278 # Set the prompt - the default prompt is '(Pdb)'
279 self.prompt = prompt
279 self.prompt = prompt
280 self.skip_hidden = True
280 self.skip_hidden = True
281 self.report_skipped = True
281 self.report_skipped = True
282
282
283 # list of predicates we use to skip frames
283 # list of predicates we use to skip frames
284 self._predicates = self.default_predicates
284 self._predicates = self.default_predicates
285
285
286 #
286 #
287 def set_colors(self, scheme):
287 def set_colors(self, scheme):
288 """Shorthand access to the color table scheme selector method."""
288 """Shorthand access to the color table scheme selector method."""
289 self.color_scheme_table.set_active_scheme(scheme)
289 self.color_scheme_table.set_active_scheme(scheme)
290 self.parser.style = scheme
290 self.parser.style = scheme
291
291
292 def set_trace(self, frame=None):
292 def set_trace(self, frame=None):
293 if frame is None:
293 if frame is None:
294 frame = sys._getframe().f_back
294 frame = sys._getframe().f_back
295 self.initial_frame = frame
295 self.initial_frame = frame
296 return super().set_trace(frame)
296 return super().set_trace(frame)
297
297
298 def _hidden_predicate(self, frame):
298 def _hidden_predicate(self, frame):
299 """
299 """
300 Given a frame return whether it it should be hidden or not by IPython.
300 Given a frame return whether it it should be hidden or not by IPython.
301 """
301 """
302
302
303 if self._predicates["readonly"]:
303 if self._predicates["readonly"]:
304 fname = frame.f_code.co_filename
304 fname = frame.f_code.co_filename
305 # we need to check for file existence and interactively define
305 # we need to check for file existence and interactively define
306 # function would otherwise appear as RO.
306 # function would otherwise appear as RO.
307 if os.path.isfile(fname) and not os.access(fname, os.W_OK):
307 if os.path.isfile(fname) and not os.access(fname, os.W_OK):
308 return True
308 return True
309
309
310 if self._predicates["tbhide"]:
310 if self._predicates["tbhide"]:
311 if frame in (self.curframe, getattr(self, "initial_frame", None)):
311 if frame in (self.curframe, getattr(self, "initial_frame", None)):
312 return False
312 return False
313 frame_locals = self._get_frame_locals(frame)
313 frame_locals = self._get_frame_locals(frame)
314 if "__tracebackhide__" not in frame_locals:
314 if "__tracebackhide__" not in frame_locals:
315 return False
315 return False
316 return frame_locals["__tracebackhide__"]
316 return frame_locals["__tracebackhide__"]
317 return False
317 return False
318
318
319 def hidden_frames(self, stack):
319 def hidden_frames(self, stack):
320 """
320 """
321 Given an index in the stack return whether it should be skipped.
321 Given an index in the stack return whether it should be skipped.
322
322
323 This is used in up/down and where to skip frames.
323 This is used in up/down and where to skip frames.
324 """
324 """
325 # The f_locals dictionary is updated from the actual frame
325 # The f_locals dictionary is updated from the actual frame
326 # locals whenever the .f_locals accessor is called, so we
326 # locals whenever the .f_locals accessor is called, so we
327 # avoid calling it here to preserve self.curframe_locals.
327 # avoid calling it here to preserve self.curframe_locals.
328 # Furthermore, there is no good reason to hide the current frame.
328 # Furthermore, there is no good reason to hide the current frame.
329 ip_hide = [self._hidden_predicate(s[0]) for s in stack]
329 ip_hide = [self._hidden_predicate(s[0]) for s in stack]
330 ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"]
330 ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"]
331 if ip_start and self._predicates["ipython_internal"]:
331 if ip_start and self._predicates["ipython_internal"]:
332 ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
332 ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
333 return ip_hide
333 return ip_hide
334
334
335 def interaction(self, frame, traceback):
335 def interaction(self, frame, traceback):
336 try:
336 try:
337 OldPdb.interaction(self, frame, traceback)
337 OldPdb.interaction(self, frame, traceback)
338 except KeyboardInterrupt:
338 except KeyboardInterrupt:
339 self.stdout.write("\n" + self.shell.get_exception_only())
339 self.stdout.write("\n" + self.shell.get_exception_only())
340
340
341 def precmd(self, line):
341 def precmd(self, line):
342 """Perform useful escapes on the command before it is executed."""
342 """Perform useful escapes on the command before it is executed."""
343
343
344 if line.endswith("??"):
344 if line.endswith("??"):
345 line = "pinfo2 " + line[:-2]
345 line = "pinfo2 " + line[:-2]
346 elif line.endswith("?"):
346 elif line.endswith("?"):
347 line = "pinfo " + line[:-1]
347 line = "pinfo " + line[:-1]
348
348
349 line = super().precmd(line)
349 line = super().precmd(line)
350
350
351 return line
351 return line
352
352
353 def new_do_frame(self, arg):
353 def new_do_frame(self, arg):
354 OldPdb.do_frame(self, arg)
354 OldPdb.do_frame(self, arg)
355
355
356 def new_do_quit(self, arg):
356 def new_do_quit(self, arg):
357
357
358 if hasattr(self, 'old_all_completions'):
358 if hasattr(self, 'old_all_completions'):
359 self.shell.Completer.all_completions = self.old_all_completions
359 self.shell.Completer.all_completions = self.old_all_completions
360
360
361 return OldPdb.do_quit(self, arg)
361 return OldPdb.do_quit(self, arg)
362
362
363 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
363 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
364
364
365 def new_do_restart(self, arg):
365 def new_do_restart(self, arg):
366 """Restart command. In the context of ipython this is exactly the same
366 """Restart command. In the context of ipython this is exactly the same
367 thing as 'quit'."""
367 thing as 'quit'."""
368 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
368 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
369 return self.do_quit(arg)
369 return self.do_quit(arg)
370
370
371 def print_stack_trace(self, context=None):
371 def print_stack_trace(self, context=None):
372 Colors = self.color_scheme_table.active_colors
372 Colors = self.color_scheme_table.active_colors
373 ColorsNormal = Colors.Normal
373 ColorsNormal = Colors.Normal
374 if context is None:
374 if context is None:
375 context = self.context
375 context = self.context
376 try:
376 try:
377 context = int(context)
377 context = int(context)
378 if context <= 0:
378 if context <= 0:
379 raise ValueError("Context must be a positive integer")
379 raise ValueError("Context must be a positive integer")
380 except (TypeError, ValueError) as e:
380 except (TypeError, ValueError) as e:
381 raise ValueError("Context must be a positive integer") from e
381 raise ValueError("Context must be a positive integer") from e
382 try:
382 try:
383 skipped = 0
383 skipped = 0
384 for hidden, frame_lineno in zip(self.hidden_frames(self.stack), self.stack):
384 for hidden, frame_lineno in zip(self.hidden_frames(self.stack), self.stack):
385 if hidden and self.skip_hidden:
385 if hidden and self.skip_hidden:
386 skipped += 1
386 skipped += 1
387 continue
387 continue
388 if skipped:
388 if skipped:
389 print(
389 print(
390 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
390 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
391 )
391 )
392 skipped = 0
392 skipped = 0
393 self.print_stack_entry(frame_lineno, context=context)
393 self.print_stack_entry(frame_lineno, context=context)
394 if skipped:
394 if skipped:
395 print(
395 print(
396 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
396 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
397 )
397 )
398 except KeyboardInterrupt:
398 except KeyboardInterrupt:
399 pass
399 pass
400
400
401 def print_stack_entry(self, frame_lineno, prompt_prefix='\n-> ',
401 def print_stack_entry(self, frame_lineno, prompt_prefix='\n-> ',
402 context=None):
402 context=None):
403 if context is None:
403 if context is None:
404 context = self.context
404 context = self.context
405 try:
405 try:
406 context = int(context)
406 context = int(context)
407 if context <= 0:
407 if context <= 0:
408 raise ValueError("Context must be a positive integer")
408 raise ValueError("Context must be a positive integer")
409 except (TypeError, ValueError) as e:
409 except (TypeError, ValueError) as e:
410 raise ValueError("Context must be a positive integer") from e
410 raise ValueError("Context must be a positive integer") from e
411 print(self.format_stack_entry(frame_lineno, '', context), file=self.stdout)
411 print(self.format_stack_entry(frame_lineno, '', context), file=self.stdout)
412
412
413 # vds: >>
413 # vds: >>
414 frame, lineno = frame_lineno
414 frame, lineno = frame_lineno
415 filename = frame.f_code.co_filename
415 filename = frame.f_code.co_filename
416 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
416 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
417 # vds: <<
417 # vds: <<
418
418
419 def _get_frame_locals(self, frame):
419 def _get_frame_locals(self, frame):
420 """ "
420 """ "
421 Accessing f_local of current frame reset the namespace, so we want to avoid
421 Accessing f_local of current frame reset the namespace, so we want to avoid
422 that or the following can happen
422 that or the following can happen
423
423
424 ipdb> foo
424 ipdb> foo
425 "old"
425 "old"
426 ipdb> foo = "new"
426 ipdb> foo = "new"
427 ipdb> foo
427 ipdb> foo
428 "new"
428 "new"
429 ipdb> where
429 ipdb> where
430 ipdb> foo
430 ipdb> foo
431 "old"
431 "old"
432
432
433 So if frame is self.current_frame we instead return self.curframe_locals
433 So if frame is self.current_frame we instead return self.curframe_locals
434
434
435 """
435 """
436 if frame is self.curframe:
436 if frame is self.curframe:
437 return self.curframe_locals
437 return self.curframe_locals
438 else:
438 else:
439 return frame.f_locals
439 return frame.f_locals
440
440
441 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
441 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
442 if context is None:
442 if context is None:
443 context = self.context
443 context = self.context
444 try:
444 try:
445 context = int(context)
445 context = int(context)
446 if context <= 0:
446 if context <= 0:
447 print("Context must be a positive integer", file=self.stdout)
447 print("Context must be a positive integer", file=self.stdout)
448 except (TypeError, ValueError):
448 except (TypeError, ValueError):
449 print("Context must be a positive integer", file=self.stdout)
449 print("Context must be a positive integer", file=self.stdout)
450
450
451 import reprlib
451 import reprlib
452
452
453 ret = []
453 ret = []
454
454
455 Colors = self.color_scheme_table.active_colors
455 Colors = self.color_scheme_table.active_colors
456 ColorsNormal = Colors.Normal
456 ColorsNormal = Colors.Normal
457 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
457 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
458 tpl_call = "%s%%s%s%%s%s" % (Colors.vName, Colors.valEm, ColorsNormal)
458 tpl_call = "%s%%s%s%%s%s" % (Colors.vName, Colors.valEm, ColorsNormal)
459 tpl_line = "%%s%s%%s %s%%s" % (Colors.lineno, ColorsNormal)
459 tpl_line = "%%s%s%%s %s%%s" % (Colors.lineno, ColorsNormal)
460 tpl_line_em = "%%s%s%%s %s%%s%s" % (Colors.linenoEm, Colors.line, ColorsNormal)
460 tpl_line_em = "%%s%s%%s %s%%s%s" % (Colors.linenoEm, Colors.line, ColorsNormal)
461
461
462 frame, lineno = frame_lineno
462 frame, lineno = frame_lineno
463
463
464 return_value = ''
464 return_value = ''
465 loc_frame = self._get_frame_locals(frame)
465 loc_frame = self._get_frame_locals(frame)
466 if "__return__" in loc_frame:
466 if "__return__" in loc_frame:
467 rv = loc_frame["__return__"]
467 rv = loc_frame["__return__"]
468 # return_value += '->'
468 # return_value += '->'
469 return_value += reprlib.repr(rv) + "\n"
469 return_value += reprlib.repr(rv) + "\n"
470 ret.append(return_value)
470 ret.append(return_value)
471
471
472 #s = filename + '(' + `lineno` + ')'
472 #s = filename + '(' + `lineno` + ')'
473 filename = self.canonic(frame.f_code.co_filename)
473 filename = self.canonic(frame.f_code.co_filename)
474 link = tpl_link % py3compat.cast_unicode(filename)
474 link = tpl_link % py3compat.cast_unicode(filename)
475
475
476 if frame.f_code.co_name:
476 if frame.f_code.co_name:
477 func = frame.f_code.co_name
477 func = frame.f_code.co_name
478 else:
478 else:
479 func = "<lambda>"
479 func = "<lambda>"
480
480
481 call = ""
481 call = ""
482 if func != "?":
482 if func != "?":
483 if "__args__" in loc_frame:
483 if "__args__" in loc_frame:
484 args = reprlib.repr(loc_frame["__args__"])
484 args = reprlib.repr(loc_frame["__args__"])
485 else:
485 else:
486 args = '()'
486 args = '()'
487 call = tpl_call % (func, args)
487 call = tpl_call % (func, args)
488
488
489 # The level info should be generated in the same format pdb uses, to
489 # The level info should be generated in the same format pdb uses, to
490 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
490 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
491 if frame is self.curframe:
491 if frame is self.curframe:
492 ret.append('> ')
492 ret.append('> ')
493 else:
493 else:
494 ret.append(" ")
494 ret.append(" ")
495 ret.append("%s(%s)%s\n" % (link, lineno, call))
495 ret.append("%s(%s)%s\n" % (link, lineno, call))
496
496
497 start = lineno - 1 - context//2
497 start = lineno - 1 - context//2
498 lines = linecache.getlines(filename)
498 lines = linecache.getlines(filename)
499 start = min(start, len(lines) - context)
499 start = min(start, len(lines) - context)
500 start = max(start, 0)
500 start = max(start, 0)
501 lines = lines[start : start + context]
501 lines = lines[start : start + context]
502
502
503 for i, line in enumerate(lines):
503 for i, line in enumerate(lines):
504 show_arrow = start + 1 + i == lineno
504 show_arrow = start + 1 + i == lineno
505 linetpl = (frame is self.curframe or show_arrow) and tpl_line_em or tpl_line
505 linetpl = (frame is self.curframe or show_arrow) and tpl_line_em or tpl_line
506 ret.append(
506 ret.append(
507 self.__format_line(
507 self.__format_line(
508 linetpl, filename, start + 1 + i, line, arrow=show_arrow
508 linetpl, filename, start + 1 + i, line, arrow=show_arrow
509 )
509 )
510 )
510 )
511 return "".join(ret)
511 return "".join(ret)
512
512
513 def __format_line(self, tpl_line, filename, lineno, line, arrow=False):
513 def __format_line(self, tpl_line, filename, lineno, line, arrow=False):
514 bp_mark = ""
514 bp_mark = ""
515 bp_mark_color = ""
515 bp_mark_color = ""
516
516
517 new_line, err = self.parser.format2(line, 'str')
517 new_line, err = self.parser.format2(line, 'str')
518 if not err:
518 if not err:
519 line = new_line
519 line = new_line
520
520
521 bp = None
521 bp = None
522 if lineno in self.get_file_breaks(filename):
522 if lineno in self.get_file_breaks(filename):
523 bps = self.get_breaks(filename, lineno)
523 bps = self.get_breaks(filename, lineno)
524 bp = bps[-1]
524 bp = bps[-1]
525
525
526 if bp:
526 if bp:
527 Colors = self.color_scheme_table.active_colors
527 Colors = self.color_scheme_table.active_colors
528 bp_mark = str(bp.number)
528 bp_mark = str(bp.number)
529 bp_mark_color = Colors.breakpoint_enabled
529 bp_mark_color = Colors.breakpoint_enabled
530 if not bp.enabled:
530 if not bp.enabled:
531 bp_mark_color = Colors.breakpoint_disabled
531 bp_mark_color = Colors.breakpoint_disabled
532
532
533 numbers_width = 7
533 numbers_width = 7
534 if arrow:
534 if arrow:
535 # This is the line with the error
535 # This is the line with the error
536 pad = numbers_width - len(str(lineno)) - len(bp_mark)
536 pad = numbers_width - len(str(lineno)) - len(bp_mark)
537 num = '%s%s' % (make_arrow(pad), str(lineno))
537 num = '%s%s' % (make_arrow(pad), str(lineno))
538 else:
538 else:
539 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
539 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
540
540
541 return tpl_line % (bp_mark_color + bp_mark, num, line)
541 return tpl_line % (bp_mark_color + bp_mark, num, line)
542
542
543 def print_list_lines(self, filename, first, last):
543 def print_list_lines(self, filename, first, last):
544 """The printing (as opposed to the parsing part of a 'list'
544 """The printing (as opposed to the parsing part of a 'list'
545 command."""
545 command."""
546 try:
546 try:
547 Colors = self.color_scheme_table.active_colors
547 Colors = self.color_scheme_table.active_colors
548 ColorsNormal = Colors.Normal
548 ColorsNormal = Colors.Normal
549 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
549 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
550 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
550 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
551 src = []
551 src = []
552 if filename == "<string>" and hasattr(self, "_exec_filename"):
552 if filename == "<string>" and hasattr(self, "_exec_filename"):
553 filename = self._exec_filename
553 filename = self._exec_filename
554
554
555 for lineno in range(first, last+1):
555 for lineno in range(first, last+1):
556 line = linecache.getline(filename, lineno)
556 line = linecache.getline(filename, lineno)
557 if not line:
557 if not line:
558 break
558 break
559
559
560 if lineno == self.curframe.f_lineno:
560 if lineno == self.curframe.f_lineno:
561 line = self.__format_line(
561 line = self.__format_line(
562 tpl_line_em, filename, lineno, line, arrow=True
562 tpl_line_em, filename, lineno, line, arrow=True
563 )
563 )
564 else:
564 else:
565 line = self.__format_line(
565 line = self.__format_line(
566 tpl_line, filename, lineno, line, arrow=False
566 tpl_line, filename, lineno, line, arrow=False
567 )
567 )
568
568
569 src.append(line)
569 src.append(line)
570 self.lineno = lineno
570 self.lineno = lineno
571
571
572 print(''.join(src), file=self.stdout)
572 print(''.join(src), file=self.stdout)
573
573
574 except KeyboardInterrupt:
574 except KeyboardInterrupt:
575 pass
575 pass
576
576
577 def do_skip_predicates(self, args):
577 def do_skip_predicates(self, args):
578 """
578 """
579 Turn on/off individual predicates as to whether a frame should be hidden/skip.
579 Turn on/off individual predicates as to whether a frame should be hidden/skip.
580
580
581 The global option to skip (or not) hidden frames is set with skip_hidden
581 The global option to skip (or not) hidden frames is set with skip_hidden
582
582
583 To change the value of a predicate
583 To change the value of a predicate
584
584
585 skip_predicates key [true|false]
585 skip_predicates key [true|false]
586
586
587 Call without arguments to see the current values.
587 Call without arguments to see the current values.
588
588
589 To permanently change the value of an option add the corresponding
589 To permanently change the value of an option add the corresponding
590 command to your ``~/.pdbrc`` file. If you are programmatically using the
590 command to your ``~/.pdbrc`` file. If you are programmatically using the
591 Pdb instance you can also change the ``default_predicates`` class
591 Pdb instance you can also change the ``default_predicates`` class
592 attribute.
592 attribute.
593 """
593 """
594 if not args.strip():
594 if not args.strip():
595 print("current predicates:")
595 print("current predicates:")
596 for (p, v) in self._predicates.items():
596 for (p, v) in self._predicates.items():
597 print(" ", p, ":", v)
597 print(" ", p, ":", v)
598 return
598 return
599 type_value = args.strip().split(" ")
599 type_value = args.strip().split(" ")
600 if len(type_value) != 2:
600 if len(type_value) != 2:
601 print(
601 print(
602 f"Usage: skip_predicates <type> <value>, with <type> one of {set(self._predicates.keys())}"
602 f"Usage: skip_predicates <type> <value>, with <type> one of {set(self._predicates.keys())}"
603 )
603 )
604 return
604 return
605
605
606 type_, value = type_value
606 type_, value = type_value
607 if type_ not in self._predicates:
607 if type_ not in self._predicates:
608 print(f"{type_!r} not in {set(self._predicates.keys())}")
608 print(f"{type_!r} not in {set(self._predicates.keys())}")
609 return
609 return
610 if value.lower() not in ("true", "yes", "1", "no", "false", "0"):
610 if value.lower() not in ("true", "yes", "1", "no", "false", "0"):
611 print(
611 print(
612 f"{value!r} is invalid - use one of ('true', 'yes', '1', 'no', 'false', '0')"
612 f"{value!r} is invalid - use one of ('true', 'yes', '1', 'no', 'false', '0')"
613 )
613 )
614 return
614 return
615
615
616 self._predicates[type_] = value.lower() in ("true", "yes", "1")
616 self._predicates[type_] = value.lower() in ("true", "yes", "1")
617 if not any(self._predicates.values()):
617 if not any(self._predicates.values()):
618 print(
618 print(
619 "Warning, all predicates set to False, skip_hidden may not have any effects."
619 "Warning, all predicates set to False, skip_hidden may not have any effects."
620 )
620 )
621
621
622 def do_skip_hidden(self, arg):
622 def do_skip_hidden(self, arg):
623 """
623 """
624 Change whether or not we should skip frames with the
624 Change whether or not we should skip frames with the
625 __tracebackhide__ attribute.
625 __tracebackhide__ attribute.
626 """
626 """
627 if not arg.strip():
627 if not arg.strip():
628 print(
628 print(
629 f"skip_hidden = {self.skip_hidden}, use 'yes','no', 'true', or 'false' to change."
629 f"skip_hidden = {self.skip_hidden}, use 'yes','no', 'true', or 'false' to change."
630 )
630 )
631 elif arg.strip().lower() in ("true", "yes"):
631 elif arg.strip().lower() in ("true", "yes"):
632 self.skip_hidden = True
632 self.skip_hidden = True
633 elif arg.strip().lower() in ("false", "no"):
633 elif arg.strip().lower() in ("false", "no"):
634 self.skip_hidden = False
634 self.skip_hidden = False
635 if not any(self._predicates.values()):
635 if not any(self._predicates.values()):
636 print(
636 print(
637 "Warning, all predicates set to False, skip_hidden may not have any effects."
637 "Warning, all predicates set to False, skip_hidden may not have any effects."
638 )
638 )
639
639
640 def do_list(self, arg):
640 def do_list(self, arg):
641 """Print lines of code from the current stack frame
641 """Print lines of code from the current stack frame
642 """
642 """
643 self.lastcmd = 'list'
643 self.lastcmd = 'list'
644 last = None
644 last = None
645 if arg:
645 if arg:
646 try:
646 try:
647 x = eval(arg, {}, {})
647 x = eval(arg, {}, {})
648 if type(x) == type(()):
648 if type(x) == type(()):
649 first, last = x
649 first, last = x
650 first = int(first)
650 first = int(first)
651 last = int(last)
651 last = int(last)
652 if last < first:
652 if last < first:
653 # Assume it's a count
653 # Assume it's a count
654 last = first + last
654 last = first + last
655 else:
655 else:
656 first = max(1, int(x) - 5)
656 first = max(1, int(x) - 5)
657 except:
657 except:
658 print('*** Error in argument:', repr(arg), file=self.stdout)
658 print('*** Error in argument:', repr(arg), file=self.stdout)
659 return
659 return
660 elif self.lineno is None:
660 elif self.lineno is None:
661 first = max(1, self.curframe.f_lineno - 5)
661 first = max(1, self.curframe.f_lineno - 5)
662 else:
662 else:
663 first = self.lineno + 1
663 first = self.lineno + 1
664 if last is None:
664 if last is None:
665 last = first + 10
665 last = first + 10
666 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
666 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
667
667
668 # vds: >>
668 # vds: >>
669 lineno = first
669 lineno = first
670 filename = self.curframe.f_code.co_filename
670 filename = self.curframe.f_code.co_filename
671 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
671 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
672 # vds: <<
672 # vds: <<
673
673
674 do_l = do_list
674 do_l = do_list
675
675
676 def getsourcelines(self, obj):
676 def getsourcelines(self, obj):
677 lines, lineno = inspect.findsource(obj)
677 lines, lineno = inspect.findsource(obj)
678 if inspect.isframe(obj) and obj.f_globals is self._get_frame_locals(obj):
678 if inspect.isframe(obj) and obj.f_globals is self._get_frame_locals(obj):
679 # must be a module frame: do not try to cut a block out of it
679 # must be a module frame: do not try to cut a block out of it
680 return lines, 1
680 return lines, 1
681 elif inspect.ismodule(obj):
681 elif inspect.ismodule(obj):
682 return lines, 1
682 return lines, 1
683 return inspect.getblock(lines[lineno:]), lineno+1
683 return inspect.getblock(lines[lineno:]), lineno+1
684
684
685 def do_longlist(self, arg):
685 def do_longlist(self, arg):
686 """Print lines of code from the current stack frame.
686 """Print lines of code from the current stack frame.
687
687
688 Shows more lines than 'list' does.
688 Shows more lines than 'list' does.
689 """
689 """
690 self.lastcmd = 'longlist'
690 self.lastcmd = 'longlist'
691 try:
691 try:
692 lines, lineno = self.getsourcelines(self.curframe)
692 lines, lineno = self.getsourcelines(self.curframe)
693 except OSError as err:
693 except OSError as err:
694 self.error(err)
694 self.error(err)
695 return
695 return
696 last = lineno + len(lines)
696 last = lineno + len(lines)
697 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
697 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
698 do_ll = do_longlist
698 do_ll = do_longlist
699
699
700 def do_debug(self, arg):
700 def do_debug(self, arg):
701 """debug code
701 """debug code
702 Enter a recursive debugger that steps through the code
702 Enter a recursive debugger that steps through the code
703 argument (which is an arbitrary expression or statement to be
703 argument (which is an arbitrary expression or statement to be
704 executed in the current environment).
704 executed in the current environment).
705 """
705 """
706 trace_function = sys.gettrace()
706 trace_function = sys.gettrace()
707 sys.settrace(None)
707 sys.settrace(None)
708 globals = self.curframe.f_globals
708 globals = self.curframe.f_globals
709 locals = self.curframe_locals
709 locals = self.curframe_locals
710 p = self.__class__(completekey=self.completekey,
710 p = self.__class__(completekey=self.completekey,
711 stdin=self.stdin, stdout=self.stdout)
711 stdin=self.stdin, stdout=self.stdout)
712 p.use_rawinput = self.use_rawinput
712 p.use_rawinput = self.use_rawinput
713 p.prompt = "(%s) " % self.prompt.strip()
713 p.prompt = "(%s) " % self.prompt.strip()
714 self.message("ENTERING RECURSIVE DEBUGGER")
714 self.message("ENTERING RECURSIVE DEBUGGER")
715 sys.call_tracing(p.run, (arg, globals, locals))
715 sys.call_tracing(p.run, (arg, globals, locals))
716 self.message("LEAVING RECURSIVE DEBUGGER")
716 self.message("LEAVING RECURSIVE DEBUGGER")
717 sys.settrace(trace_function)
717 sys.settrace(trace_function)
718 self.lastcmd = p.lastcmd
718 self.lastcmd = p.lastcmd
719
719
720 def do_pdef(self, arg):
720 def do_pdef(self, arg):
721 """Print the call signature for any callable object.
721 """Print the call signature for any callable object.
722
722
723 The debugger interface to %pdef"""
723 The debugger interface to %pdef"""
724 namespaces = [
724 namespaces = [
725 ("Locals", self.curframe_locals),
725 ("Locals", self.curframe_locals),
726 ("Globals", self.curframe.f_globals),
726 ("Globals", self.curframe.f_globals),
727 ]
727 ]
728 self.shell.find_line_magic("pdef")(arg, namespaces=namespaces)
728 self.shell.find_line_magic("pdef")(arg, namespaces=namespaces)
729
729
730 def do_pdoc(self, arg):
730 def do_pdoc(self, arg):
731 """Print the docstring for an object.
731 """Print the docstring for an object.
732
732
733 The debugger interface to %pdoc."""
733 The debugger interface to %pdoc."""
734 namespaces = [
734 namespaces = [
735 ("Locals", self.curframe_locals),
735 ("Locals", self.curframe_locals),
736 ("Globals", self.curframe.f_globals),
736 ("Globals", self.curframe.f_globals),
737 ]
737 ]
738 self.shell.find_line_magic("pdoc")(arg, namespaces=namespaces)
738 self.shell.find_line_magic("pdoc")(arg, namespaces=namespaces)
739
739
740 def do_pfile(self, arg):
740 def do_pfile(self, arg):
741 """Print (or run through pager) the file where an object is defined.
741 """Print (or run through pager) the file where an object is defined.
742
742
743 The debugger interface to %pfile.
743 The debugger interface to %pfile.
744 """
744 """
745 namespaces = [
745 namespaces = [
746 ("Locals", self.curframe_locals),
746 ("Locals", self.curframe_locals),
747 ("Globals", self.curframe.f_globals),
747 ("Globals", self.curframe.f_globals),
748 ]
748 ]
749 self.shell.find_line_magic("pfile")(arg, namespaces=namespaces)
749 self.shell.find_line_magic("pfile")(arg, namespaces=namespaces)
750
750
751 def do_pinfo(self, arg):
751 def do_pinfo(self, arg):
752 """Provide detailed information about an object.
752 """Provide detailed information about an object.
753
753
754 The debugger interface to %pinfo, i.e., obj?."""
754 The debugger interface to %pinfo, i.e., obj?."""
755 namespaces = [
755 namespaces = [
756 ("Locals", self.curframe_locals),
756 ("Locals", self.curframe_locals),
757 ("Globals", self.curframe.f_globals),
757 ("Globals", self.curframe.f_globals),
758 ]
758 ]
759 self.shell.find_line_magic("pinfo")(arg, namespaces=namespaces)
759 self.shell.find_line_magic("pinfo")(arg, namespaces=namespaces)
760
760
761 def do_pinfo2(self, arg):
761 def do_pinfo2(self, arg):
762 """Provide extra detailed information about an object.
762 """Provide extra detailed information about an object.
763
763
764 The debugger interface to %pinfo2, i.e., obj??."""
764 The debugger interface to %pinfo2, i.e., obj??."""
765 namespaces = [
765 namespaces = [
766 ("Locals", self.curframe_locals),
766 ("Locals", self.curframe_locals),
767 ("Globals", self.curframe.f_globals),
767 ("Globals", self.curframe.f_globals),
768 ]
768 ]
769 self.shell.find_line_magic("pinfo2")(arg, namespaces=namespaces)
769 self.shell.find_line_magic("pinfo2")(arg, namespaces=namespaces)
770
770
771 def do_psource(self, arg):
771 def do_psource(self, arg):
772 """Print (or run through pager) the source code for an object."""
772 """Print (or run through pager) the source code for an object."""
773 namespaces = [
773 namespaces = [
774 ("Locals", self.curframe_locals),
774 ("Locals", self.curframe_locals),
775 ("Globals", self.curframe.f_globals),
775 ("Globals", self.curframe.f_globals),
776 ]
776 ]
777 self.shell.find_line_magic("psource")(arg, namespaces=namespaces)
777 self.shell.find_line_magic("psource")(arg, namespaces=namespaces)
778
778
779 def do_where(self, arg):
779 def do_where(self, arg):
780 """w(here)
780 """w(here)
781 Print a stack trace, with the most recent frame at the bottom.
781 Print a stack trace, with the most recent frame at the bottom.
782 An arrow indicates the "current frame", which determines the
782 An arrow indicates the "current frame", which determines the
783 context of most commands. 'bt' is an alias for this command.
783 context of most commands. 'bt' is an alias for this command.
784
784
785 Take a number as argument as an (optional) number of context line to
785 Take a number as argument as an (optional) number of context line to
786 print"""
786 print"""
787 if arg:
787 if arg:
788 try:
788 try:
789 context = int(arg)
789 context = int(arg)
790 except ValueError as err:
790 except ValueError as err:
791 self.error(err)
791 self.error(err)
792 return
792 return
793 self.print_stack_trace(context)
793 self.print_stack_trace(context)
794 else:
794 else:
795 self.print_stack_trace()
795 self.print_stack_trace()
796
796
797 do_w = do_where
797 do_w = do_where
798
798
799 def break_anywhere(self, frame):
799 def break_anywhere(self, frame):
800 """
800 """
801
802 _stop_in_decorator_internals is overly restrictive, as we may still want
801 _stop_in_decorator_internals is overly restrictive, as we may still want
803 to trace function calls, so we need to also update break_anywhere so
802 to trace function calls, so we need to also update break_anywhere so
804 that is we don't `stop_here`, because of debugger skip, we may still
803 that is we don't `stop_here`, because of debugger skip, we may still
805 stop at any point inside the function
804 stop at any point inside the function
806
805
807 """
806 """
808
807
809 sup = super().break_anywhere(frame)
808 sup = super().break_anywhere(frame)
810 if sup:
809 if sup:
811 return sup
810 return sup
812 if self._predicates["debuggerskip"]:
811 if self._predicates["debuggerskip"]:
813 if DEBUGGERSKIP in frame.f_code.co_varnames:
812 if DEBUGGERSKIP in frame.f_code.co_varnames:
814 return True
813 return True
815 if frame.f_back and self._get_frame_locals(frame.f_back).get(DEBUGGERSKIP):
814 if frame.f_back and self._get_frame_locals(frame.f_back).get(DEBUGGERSKIP):
816 return True
815 return True
817 return False
816 return False
818
817
819 def _is_in_decorator_internal_and_should_skip(self, frame):
818 def _is_in_decorator_internal_and_should_skip(self, frame):
820 """
819 """
821 Utility to tell us whether we are in a decorator internal and should stop.
820 Utility to tell us whether we are in a decorator internal and should stop.
822
821
823
824
825 """
822 """
826
823
827 # if we are disabled don't skip
824 # if we are disabled don't skip
828 if not self._predicates["debuggerskip"]:
825 if not self._predicates["debuggerskip"]:
829 return False
826 return False
830
827
831 # if frame is tagged, skip by default.
828 # if frame is tagged, skip by default.
832 if DEBUGGERSKIP in frame.f_code.co_varnames:
829 if DEBUGGERSKIP in frame.f_code.co_varnames:
833 return True
830 return True
834
831
835 # if one of the parent frame value set to True skip as well.
832 # if one of the parent frame value set to True skip as well.
836
833
837 cframe = frame
834 cframe = frame
838 while getattr(cframe, "f_back", None):
835 while getattr(cframe, "f_back", None):
839 cframe = cframe.f_back
836 cframe = cframe.f_back
840 if self._get_frame_locals(cframe).get(DEBUGGERSKIP):
837 if self._get_frame_locals(cframe).get(DEBUGGERSKIP):
841 return True
838 return True
842
839
843 return False
840 return False
844
841
845 def stop_here(self, frame):
842 def stop_here(self, frame):
846
843
847 if self._is_in_decorator_internal_and_should_skip(frame) is True:
844 if self._is_in_decorator_internal_and_should_skip(frame) is True:
848 return False
845 return False
849
846
850 hidden = False
847 hidden = False
851 if self.skip_hidden:
848 if self.skip_hidden:
852 hidden = self._hidden_predicate(frame)
849 hidden = self._hidden_predicate(frame)
853 if hidden:
850 if hidden:
854 if self.report_skipped:
851 if self.report_skipped:
855 Colors = self.color_scheme_table.active_colors
852 Colors = self.color_scheme_table.active_colors
856 ColorsNormal = Colors.Normal
853 ColorsNormal = Colors.Normal
857 print(
854 print(
858 f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n"
855 f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n"
859 )
856 )
860 return super().stop_here(frame)
857 return super().stop_here(frame)
861
858
862 def do_up(self, arg):
859 def do_up(self, arg):
863 """u(p) [count]
860 """u(p) [count]
864 Move the current frame count (default one) levels up in the
861 Move the current frame count (default one) levels up in the
865 stack trace (to an older frame).
862 stack trace (to an older frame).
866
863
867 Will skip hidden frames.
864 Will skip hidden frames.
868 """
865 """
869 # modified version of upstream that skips
866 # modified version of upstream that skips
870 # frames with __tracebackhide__
867 # frames with __tracebackhide__
871 if self.curindex == 0:
868 if self.curindex == 0:
872 self.error("Oldest frame")
869 self.error("Oldest frame")
873 return
870 return
874 try:
871 try:
875 count = int(arg or 1)
872 count = int(arg or 1)
876 except ValueError:
873 except ValueError:
877 self.error("Invalid frame count (%s)" % arg)
874 self.error("Invalid frame count (%s)" % arg)
878 return
875 return
879 skipped = 0
876 skipped = 0
880 if count < 0:
877 if count < 0:
881 _newframe = 0
878 _newframe = 0
882 else:
879 else:
883 counter = 0
880 counter = 0
884 hidden_frames = self.hidden_frames(self.stack)
881 hidden_frames = self.hidden_frames(self.stack)
885 for i in range(self.curindex - 1, -1, -1):
882 for i in range(self.curindex - 1, -1, -1):
886 if hidden_frames[i] and self.skip_hidden:
883 if hidden_frames[i] and self.skip_hidden:
887 skipped += 1
884 skipped += 1
888 continue
885 continue
889 counter += 1
886 counter += 1
890 if counter >= count:
887 if counter >= count:
891 break
888 break
892 else:
889 else:
893 # if no break occurred.
890 # if no break occurred.
894 self.error(
891 self.error(
895 "all frames above hidden, use `skip_hidden False` to get get into those."
892 "all frames above hidden, use `skip_hidden False` to get get into those."
896 )
893 )
897 return
894 return
898
895
899 Colors = self.color_scheme_table.active_colors
896 Colors = self.color_scheme_table.active_colors
900 ColorsNormal = Colors.Normal
897 ColorsNormal = Colors.Normal
901 _newframe = i
898 _newframe = i
902 self._select_frame(_newframe)
899 self._select_frame(_newframe)
903 if skipped:
900 if skipped:
904 print(
901 print(
905 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
902 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
906 )
903 )
907
904
908 def do_down(self, arg):
905 def do_down(self, arg):
909 """d(own) [count]
906 """d(own) [count]
910 Move the current frame count (default one) levels down in the
907 Move the current frame count (default one) levels down in the
911 stack trace (to a newer frame).
908 stack trace (to a newer frame).
912
909
913 Will skip hidden frames.
910 Will skip hidden frames.
914 """
911 """
915 if self.curindex + 1 == len(self.stack):
912 if self.curindex + 1 == len(self.stack):
916 self.error("Newest frame")
913 self.error("Newest frame")
917 return
914 return
918 try:
915 try:
919 count = int(arg or 1)
916 count = int(arg or 1)
920 except ValueError:
917 except ValueError:
921 self.error("Invalid frame count (%s)" % arg)
918 self.error("Invalid frame count (%s)" % arg)
922 return
919 return
923 if count < 0:
920 if count < 0:
924 _newframe = len(self.stack) - 1
921 _newframe = len(self.stack) - 1
925 else:
922 else:
926 counter = 0
923 counter = 0
927 skipped = 0
924 skipped = 0
928 hidden_frames = self.hidden_frames(self.stack)
925 hidden_frames = self.hidden_frames(self.stack)
929 for i in range(self.curindex + 1, len(self.stack)):
926 for i in range(self.curindex + 1, len(self.stack)):
930 if hidden_frames[i] and self.skip_hidden:
927 if hidden_frames[i] and self.skip_hidden:
931 skipped += 1
928 skipped += 1
932 continue
929 continue
933 counter += 1
930 counter += 1
934 if counter >= count:
931 if counter >= count:
935 break
932 break
936 else:
933 else:
937 self.error(
934 self.error(
938 "all frames below hidden, use `skip_hidden False` to get get into those."
935 "all frames below hidden, use `skip_hidden False` to get get into those."
939 )
936 )
940 return
937 return
941
938
942 Colors = self.color_scheme_table.active_colors
939 Colors = self.color_scheme_table.active_colors
943 ColorsNormal = Colors.Normal
940 ColorsNormal = Colors.Normal
944 if skipped:
941 if skipped:
945 print(
942 print(
946 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
943 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
947 )
944 )
948 _newframe = i
945 _newframe = i
949
946
950 self._select_frame(_newframe)
947 self._select_frame(_newframe)
951
948
952 do_d = do_down
949 do_d = do_down
953 do_u = do_up
950 do_u = do_up
954
951
955 def do_context(self, context):
952 def do_context(self, context):
956 """context number_of_lines
953 """context number_of_lines
957 Set the number of lines of source code to show when displaying
954 Set the number of lines of source code to show when displaying
958 stacktrace information.
955 stacktrace information.
959 """
956 """
960 try:
957 try:
961 new_context = int(context)
958 new_context = int(context)
962 if new_context <= 0:
959 if new_context <= 0:
963 raise ValueError()
960 raise ValueError()
964 self.context = new_context
961 self.context = new_context
965 except ValueError:
962 except ValueError:
966 self.error("The 'context' command requires a positive integer argument.")
963 self.error("The 'context' command requires a positive integer argument.")
967
964
968
965
969 class InterruptiblePdb(Pdb):
966 class InterruptiblePdb(Pdb):
970 """Version of debugger where KeyboardInterrupt exits the debugger altogether."""
967 """Version of debugger where KeyboardInterrupt exits the debugger altogether."""
971
968
972 def cmdloop(self, intro=None):
969 def cmdloop(self, intro=None):
973 """Wrap cmdloop() such that KeyboardInterrupt stops the debugger."""
970 """Wrap cmdloop() such that KeyboardInterrupt stops the debugger."""
974 try:
971 try:
975 return OldPdb.cmdloop(self, intro=intro)
972 return OldPdb.cmdloop(self, intro=intro)
976 except KeyboardInterrupt:
973 except KeyboardInterrupt:
977 self.stop_here = lambda frame: False
974 self.stop_here = lambda frame: False
978 self.do_quit("")
975 self.do_quit("")
979 sys.settrace(None)
976 sys.settrace(None)
980 self.quitting = False
977 self.quitting = False
981 raise
978 raise
982
979
983 def _cmdloop(self):
980 def _cmdloop(self):
984 while True:
981 while True:
985 try:
982 try:
986 # keyboard interrupts allow for an easy way to cancel
983 # keyboard interrupts allow for an easy way to cancel
987 # the current command, so allow them during interactive input
984 # the current command, so allow them during interactive input
988 self.allow_kbdint = True
985 self.allow_kbdint = True
989 self.cmdloop()
986 self.cmdloop()
990 self.allow_kbdint = False
987 self.allow_kbdint = False
991 break
988 break
992 except KeyboardInterrupt:
989 except KeyboardInterrupt:
993 self.message('--KeyboardInterrupt--')
990 self.message('--KeyboardInterrupt--')
994 raise
991 raise
995
992
996
993
997 def set_trace(frame=None):
994 def set_trace(frame=None):
998 """
995 """
999 Start debugging from `frame`.
996 Start debugging from `frame`.
1000
997
1001 If frame is not specified, debugging starts from caller's frame.
998 If frame is not specified, debugging starts from caller's frame.
1002 """
999 """
1003 Pdb().set_trace(frame or sys._getframe().f_back)
1000 Pdb().set_trace(frame or sys._getframe().f_back)
@@ -1,1256 +1,1272 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Top-level display functions for displaying object in different formats."""
2 """Top-level display functions for displaying object in different formats."""
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7
7
8 from binascii import b2a_base64, hexlify
8 from binascii import b2a_base64, hexlify
9 import html
9 import html
10 import json
10 import json
11 import mimetypes
11 import mimetypes
12 import os
12 import os
13 import struct
13 import struct
14 import warnings
14 import warnings
15 from copy import deepcopy
15 from copy import deepcopy
16 from os.path import splitext
16 from os.path import splitext
17 from pathlib import Path, PurePath
17 from pathlib import Path, PurePath
18
18
19 from IPython.utils.py3compat import cast_unicode
19 from IPython.utils.py3compat import cast_unicode
20 from IPython.testing.skipdoctest import skip_doctest
20 from IPython.testing.skipdoctest import skip_doctest
21 from . import display_functions
21 from . import display_functions
22
22
23
23
24 __all__ = ['display_pretty', 'display_html', 'display_markdown',
24 __all__ = ['display_pretty', 'display_html', 'display_markdown',
25 'display_svg', 'display_png', 'display_jpeg', 'display_latex', 'display_json',
25 'display_svg', 'display_png', 'display_jpeg', 'display_latex', 'display_json',
26 'display_javascript', 'display_pdf', 'DisplayObject', 'TextDisplayObject',
26 'display_javascript', 'display_pdf', 'DisplayObject', 'TextDisplayObject',
27 'Pretty', 'HTML', 'Markdown', 'Math', 'Latex', 'SVG', 'ProgressBar', 'JSON',
27 'Pretty', 'HTML', 'Markdown', 'Math', 'Latex', 'SVG', 'ProgressBar', 'JSON',
28 'GeoJSON', 'Javascript', 'Image', 'set_matplotlib_formats',
28 'GeoJSON', 'Javascript', 'Image', 'set_matplotlib_formats',
29 'set_matplotlib_close',
29 'set_matplotlib_close',
30 'Video']
30 'Video']
31
31
32 _deprecated_names = ["display", "clear_output", "publish_display_data", "update_display", "DisplayHandle"]
32 _deprecated_names = ["display", "clear_output", "publish_display_data", "update_display", "DisplayHandle"]
33
33
34 __all__ = __all__ + _deprecated_names
34 __all__ = __all__ + _deprecated_names
35
35
36
36
37 # ----- warn to import from IPython.display -----
37 # ----- warn to import from IPython.display -----
38
38
39 from warnings import warn
39 from warnings import warn
40
40
41
41
42 def __getattr__(name):
42 def __getattr__(name):
43 if name in _deprecated_names:
43 if name in _deprecated_names:
44 warn(f"Importing {name} from IPython.core.display is deprecated since IPython 7.14, please import from IPython display", DeprecationWarning, stacklevel=2)
44 warn(f"Importing {name} from IPython.core.display is deprecated since IPython 7.14, please import from IPython display", DeprecationWarning, stacklevel=2)
45 return getattr(display_functions, name)
45 return getattr(display_functions, name)
46
46
47 if name in globals().keys():
47 if name in globals().keys():
48 return globals()[name]
48 return globals()[name]
49 else:
49 else:
50 raise AttributeError(f"module {__name__} has no attribute {name}")
50 raise AttributeError(f"module {__name__} has no attribute {name}")
51
51
52
52
53 #-----------------------------------------------------------------------------
53 #-----------------------------------------------------------------------------
54 # utility functions
54 # utility functions
55 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
56
56
57 def _safe_exists(path):
57 def _safe_exists(path):
58 """Check path, but don't let exceptions raise"""
58 """Check path, but don't let exceptions raise"""
59 try:
59 try:
60 return os.path.exists(path)
60 return os.path.exists(path)
61 except Exception:
61 except Exception:
62 return False
62 return False
63
63
64
64
65 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
65 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
66 """internal implementation of all display_foo methods
66 """internal implementation of all display_foo methods
67
67
68 Parameters
68 Parameters
69 ----------
69 ----------
70 mimetype : str
70 mimetype : str
71 The mimetype to be published (e.g. 'image/png')
71 The mimetype to be published (e.g. 'image/png')
72 *objs : object
72 *objs : object
73 The Python objects to display, or if raw=True raw text data to
73 The Python objects to display, or if raw=True raw text data to
74 display.
74 display.
75 raw : bool
75 raw : bool
76 Are the data objects raw data or Python objects that need to be
76 Are the data objects raw data or Python objects that need to be
77 formatted before display? [default: False]
77 formatted before display? [default: False]
78 metadata : dict (optional)
78 metadata : dict (optional)
79 Metadata to be associated with the specific mimetype output.
79 Metadata to be associated with the specific mimetype output.
80 """
80 """
81 if metadata:
81 if metadata:
82 metadata = {mimetype: metadata}
82 metadata = {mimetype: metadata}
83 if raw:
83 if raw:
84 # turn list of pngdata into list of { 'image/png': pngdata }
84 # turn list of pngdata into list of { 'image/png': pngdata }
85 objs = [ {mimetype: obj} for obj in objs ]
85 objs = [ {mimetype: obj} for obj in objs ]
86 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
86 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
87
87
88 #-----------------------------------------------------------------------------
88 #-----------------------------------------------------------------------------
89 # Main functions
89 # Main functions
90 #-----------------------------------------------------------------------------
90 #-----------------------------------------------------------------------------
91
91
92
92
93 def display_pretty(*objs, **kwargs):
93 def display_pretty(*objs, **kwargs):
94 """Display the pretty (default) representation of an object.
94 """Display the pretty (default) representation of an object.
95
95
96 Parameters
96 Parameters
97 ----------
97 ----------
98 *objs : object
98 *objs : object
99 The Python objects to display, or if raw=True raw text data to
99 The Python objects to display, or if raw=True raw text data to
100 display.
100 display.
101 raw : bool
101 raw : bool
102 Are the data objects raw data or Python objects that need to be
102 Are the data objects raw data or Python objects that need to be
103 formatted before display? [default: False]
103 formatted before display? [default: False]
104 metadata : dict (optional)
104 metadata : dict (optional)
105 Metadata to be associated with the specific mimetype output.
105 Metadata to be associated with the specific mimetype output.
106 """
106 """
107 _display_mimetype('text/plain', objs, **kwargs)
107 _display_mimetype('text/plain', objs, **kwargs)
108
108
109
109
110 def display_html(*objs, **kwargs):
110 def display_html(*objs, **kwargs):
111 """Display the HTML representation of an object.
111 """Display the HTML representation of an object.
112
112
113 Note: If raw=False and the object does not have a HTML
113 Note: If raw=False and the object does not have a HTML
114 representation, no HTML will be shown.
114 representation, no HTML will be shown.
115
115
116 Parameters
116 Parameters
117 ----------
117 ----------
118 *objs : object
118 *objs : object
119 The Python objects to display, or if raw=True raw HTML data to
119 The Python objects to display, or if raw=True raw HTML data to
120 display.
120 display.
121 raw : bool
121 raw : bool
122 Are the data objects raw data or Python objects that need to be
122 Are the data objects raw data or Python objects that need to be
123 formatted before display? [default: False]
123 formatted before display? [default: False]
124 metadata : dict (optional)
124 metadata : dict (optional)
125 Metadata to be associated with the specific mimetype output.
125 Metadata to be associated with the specific mimetype output.
126 """
126 """
127 _display_mimetype('text/html', objs, **kwargs)
127 _display_mimetype('text/html', objs, **kwargs)
128
128
129
129
130 def display_markdown(*objs, **kwargs):
130 def display_markdown(*objs, **kwargs):
131 """Displays the Markdown representation of an object.
131 """Displays the Markdown representation of an object.
132
132
133 Parameters
133 Parameters
134 ----------
134 ----------
135 *objs : object
135 *objs : object
136 The Python objects to display, or if raw=True raw markdown data to
136 The Python objects to display, or if raw=True raw markdown data to
137 display.
137 display.
138 raw : bool
138 raw : bool
139 Are the data objects raw data or Python objects that need to be
139 Are the data objects raw data or Python objects that need to be
140 formatted before display? [default: False]
140 formatted before display? [default: False]
141 metadata : dict (optional)
141 metadata : dict (optional)
142 Metadata to be associated with the specific mimetype output.
142 Metadata to be associated with the specific mimetype output.
143 """
143 """
144
144
145 _display_mimetype('text/markdown', objs, **kwargs)
145 _display_mimetype('text/markdown', objs, **kwargs)
146
146
147
147
148 def display_svg(*objs, **kwargs):
148 def display_svg(*objs, **kwargs):
149 """Display the SVG representation of an object.
149 """Display the SVG representation of an object.
150
150
151 Parameters
151 Parameters
152 ----------
152 ----------
153 *objs : object
153 *objs : object
154 The Python objects to display, or if raw=True raw svg data to
154 The Python objects to display, or if raw=True raw svg data to
155 display.
155 display.
156 raw : bool
156 raw : bool
157 Are the data objects raw data or Python objects that need to be
157 Are the data objects raw data or Python objects that need to be
158 formatted before display? [default: False]
158 formatted before display? [default: False]
159 metadata : dict (optional)
159 metadata : dict (optional)
160 Metadata to be associated with the specific mimetype output.
160 Metadata to be associated with the specific mimetype output.
161 """
161 """
162 _display_mimetype('image/svg+xml', objs, **kwargs)
162 _display_mimetype('image/svg+xml', objs, **kwargs)
163
163
164
164
165 def display_png(*objs, **kwargs):
165 def display_png(*objs, **kwargs):
166 """Display the PNG representation of an object.
166 """Display the PNG representation of an object.
167
167
168 Parameters
168 Parameters
169 ----------
169 ----------
170 *objs : object
170 *objs : object
171 The Python objects to display, or if raw=True raw png data to
171 The Python objects to display, or if raw=True raw png data to
172 display.
172 display.
173 raw : bool
173 raw : bool
174 Are the data objects raw data or Python objects that need to be
174 Are the data objects raw data or Python objects that need to be
175 formatted before display? [default: False]
175 formatted before display? [default: False]
176 metadata : dict (optional)
176 metadata : dict (optional)
177 Metadata to be associated with the specific mimetype output.
177 Metadata to be associated with the specific mimetype output.
178 """
178 """
179 _display_mimetype('image/png', objs, **kwargs)
179 _display_mimetype('image/png', objs, **kwargs)
180
180
181
181
182 def display_jpeg(*objs, **kwargs):
182 def display_jpeg(*objs, **kwargs):
183 """Display the JPEG representation of an object.
183 """Display the JPEG representation of an object.
184
184
185 Parameters
185 Parameters
186 ----------
186 ----------
187 *objs : object
187 *objs : object
188 The Python objects to display, or if raw=True raw JPEG data to
188 The Python objects to display, or if raw=True raw JPEG data to
189 display.
189 display.
190 raw : bool
190 raw : bool
191 Are the data objects raw data or Python objects that need to be
191 Are the data objects raw data or Python objects that need to be
192 formatted before display? [default: False]
192 formatted before display? [default: False]
193 metadata : dict (optional)
193 metadata : dict (optional)
194 Metadata to be associated with the specific mimetype output.
194 Metadata to be associated with the specific mimetype output.
195 """
195 """
196 _display_mimetype('image/jpeg', objs, **kwargs)
196 _display_mimetype('image/jpeg', objs, **kwargs)
197
197
198
198
199 def display_latex(*objs, **kwargs):
199 def display_latex(*objs, **kwargs):
200 """Display the LaTeX representation of an object.
200 """Display the LaTeX representation of an object.
201
201
202 Parameters
202 Parameters
203 ----------
203 ----------
204 *objs : object
204 *objs : object
205 The Python objects to display, or if raw=True raw latex data to
205 The Python objects to display, or if raw=True raw latex data to
206 display.
206 display.
207 raw : bool
207 raw : bool
208 Are the data objects raw data or Python objects that need to be
208 Are the data objects raw data or Python objects that need to be
209 formatted before display? [default: False]
209 formatted before display? [default: False]
210 metadata : dict (optional)
210 metadata : dict (optional)
211 Metadata to be associated with the specific mimetype output.
211 Metadata to be associated with the specific mimetype output.
212 """
212 """
213 _display_mimetype('text/latex', objs, **kwargs)
213 _display_mimetype('text/latex', objs, **kwargs)
214
214
215
215
216 def display_json(*objs, **kwargs):
216 def display_json(*objs, **kwargs):
217 """Display the JSON representation of an object.
217 """Display the JSON representation of an object.
218
218
219 Note that not many frontends support displaying JSON.
219 Note that not many frontends support displaying JSON.
220
220
221 Parameters
221 Parameters
222 ----------
222 ----------
223 *objs : object
223 *objs : object
224 The Python objects to display, or if raw=True raw json data to
224 The Python objects to display, or if raw=True raw json data to
225 display.
225 display.
226 raw : bool
226 raw : bool
227 Are the data objects raw data or Python objects that need to be
227 Are the data objects raw data or Python objects that need to be
228 formatted before display? [default: False]
228 formatted before display? [default: False]
229 metadata : dict (optional)
229 metadata : dict (optional)
230 Metadata to be associated with the specific mimetype output.
230 Metadata to be associated with the specific mimetype output.
231 """
231 """
232 _display_mimetype('application/json', objs, **kwargs)
232 _display_mimetype('application/json', objs, **kwargs)
233
233
234
234
235 def display_javascript(*objs, **kwargs):
235 def display_javascript(*objs, **kwargs):
236 """Display the Javascript representation of an object.
236 """Display the Javascript representation of an object.
237
237
238 Parameters
238 Parameters
239 ----------
239 ----------
240 *objs : object
240 *objs : object
241 The Python objects to display, or if raw=True raw javascript data to
241 The Python objects to display, or if raw=True raw javascript data to
242 display.
242 display.
243 raw : bool
243 raw : bool
244 Are the data objects raw data or Python objects that need to be
244 Are the data objects raw data or Python objects that need to be
245 formatted before display? [default: False]
245 formatted before display? [default: False]
246 metadata : dict (optional)
246 metadata : dict (optional)
247 Metadata to be associated with the specific mimetype output.
247 Metadata to be associated with the specific mimetype output.
248 """
248 """
249 _display_mimetype('application/javascript', objs, **kwargs)
249 _display_mimetype('application/javascript', objs, **kwargs)
250
250
251
251
252 def display_pdf(*objs, **kwargs):
252 def display_pdf(*objs, **kwargs):
253 """Display the PDF representation of an object.
253 """Display the PDF representation of an object.
254
254
255 Parameters
255 Parameters
256 ----------
256 ----------
257 *objs : object
257 *objs : object
258 The Python objects to display, or if raw=True raw javascript data to
258 The Python objects to display, or if raw=True raw javascript data to
259 display.
259 display.
260 raw : bool
260 raw : bool
261 Are the data objects raw data or Python objects that need to be
261 Are the data objects raw data or Python objects that need to be
262 formatted before display? [default: False]
262 formatted before display? [default: False]
263 metadata : dict (optional)
263 metadata : dict (optional)
264 Metadata to be associated with the specific mimetype output.
264 Metadata to be associated with the specific mimetype output.
265 """
265 """
266 _display_mimetype('application/pdf', objs, **kwargs)
266 _display_mimetype('application/pdf', objs, **kwargs)
267
267
268
268
269 #-----------------------------------------------------------------------------
269 #-----------------------------------------------------------------------------
270 # Smart classes
270 # Smart classes
271 #-----------------------------------------------------------------------------
271 #-----------------------------------------------------------------------------
272
272
273
273
274 class DisplayObject(object):
274 class DisplayObject(object):
275 """An object that wraps data to be displayed."""
275 """An object that wraps data to be displayed."""
276
276
277 _read_flags = 'r'
277 _read_flags = 'r'
278 _show_mem_addr = False
278 _show_mem_addr = False
279 metadata = None
279 metadata = None
280
280
281 def __init__(self, data=None, url=None, filename=None, metadata=None):
281 def __init__(self, data=None, url=None, filename=None, metadata=None):
282 """Create a display object given raw data.
282 """Create a display object given raw data.
283
283
284 When this object is returned by an expression or passed to the
284 When this object is returned by an expression or passed to the
285 display function, it will result in the data being displayed
285 display function, it will result in the data being displayed
286 in the frontend. The MIME type of the data should match the
286 in the frontend. The MIME type of the data should match the
287 subclasses used, so the Png subclass should be used for 'image/png'
287 subclasses used, so the Png subclass should be used for 'image/png'
288 data. If the data is a URL, the data will first be downloaded
288 data. If the data is a URL, the data will first be downloaded
289 and then displayed. If
289 and then displayed. If
290
290
291 Parameters
291 Parameters
292 ----------
292 ----------
293 data : unicode, str or bytes
293 data : unicode, str or bytes
294 The raw data or a URL or file to load the data from
294 The raw data or a URL or file to load the data from
295 url : unicode
295 url : unicode
296 A URL to download the data from.
296 A URL to download the data from.
297 filename : unicode
297 filename : unicode
298 Path to a local file to load the data from.
298 Path to a local file to load the data from.
299 metadata : dict
299 metadata : dict
300 Dict of metadata associated to be the object when displayed
300 Dict of metadata associated to be the object when displayed
301 """
301 """
302 if isinstance(data, (Path, PurePath)):
302 if isinstance(data, (Path, PurePath)):
303 data = str(data)
303 data = str(data)
304
304
305 if data is not None and isinstance(data, str):
305 if data is not None and isinstance(data, str):
306 if data.startswith('http') and url is None:
306 if data.startswith('http') and url is None:
307 url = data
307 url = data
308 filename = None
308 filename = None
309 data = None
309 data = None
310 elif _safe_exists(data) and filename is None:
310 elif _safe_exists(data) and filename is None:
311 url = None
311 url = None
312 filename = data
312 filename = data
313 data = None
313 data = None
314
314
315 self.url = url
315 self.url = url
316 self.filename = filename
316 self.filename = filename
317 # because of @data.setter methods in
317 # because of @data.setter methods in
318 # subclasses ensure url and filename are set
318 # subclasses ensure url and filename are set
319 # before assigning to self.data
319 # before assigning to self.data
320 self.data = data
320 self.data = data
321
321
322 if metadata is not None:
322 if metadata is not None:
323 self.metadata = metadata
323 self.metadata = metadata
324 elif self.metadata is None:
324 elif self.metadata is None:
325 self.metadata = {}
325 self.metadata = {}
326
326
327 self.reload()
327 self.reload()
328 self._check_data()
328 self._check_data()
329
329
330 def __repr__(self):
330 def __repr__(self):
331 if not self._show_mem_addr:
331 if not self._show_mem_addr:
332 cls = self.__class__
332 cls = self.__class__
333 r = "<%s.%s object>" % (cls.__module__, cls.__name__)
333 r = "<%s.%s object>" % (cls.__module__, cls.__name__)
334 else:
334 else:
335 r = super(DisplayObject, self).__repr__()
335 r = super(DisplayObject, self).__repr__()
336 return r
336 return r
337
337
338 def _check_data(self):
338 def _check_data(self):
339 """Override in subclasses if there's something to check."""
339 """Override in subclasses if there's something to check."""
340 pass
340 pass
341
341
342 def _data_and_metadata(self):
342 def _data_and_metadata(self):
343 """shortcut for returning metadata with shape information, if defined"""
343 """shortcut for returning metadata with shape information, if defined"""
344 if self.metadata:
344 if self.metadata:
345 return self.data, deepcopy(self.metadata)
345 return self.data, deepcopy(self.metadata)
346 else:
346 else:
347 return self.data
347 return self.data
348
348
349 def reload(self):
349 def reload(self):
350 """Reload the raw data from file or URL."""
350 """Reload the raw data from file or URL."""
351 if self.filename is not None:
351 if self.filename is not None:
352 with open(self.filename, self._read_flags) as f:
352 with open(self.filename, self._read_flags) as f:
353 self.data = f.read()
353 self.data = f.read()
354 elif self.url is not None:
354 elif self.url is not None:
355 # Deferred import
355 # Deferred import
356 from urllib.request import urlopen
356 from urllib.request import urlopen
357 response = urlopen(self.url)
357 response = urlopen(self.url)
358 data = response.read()
358 data = response.read()
359 # extract encoding from header, if there is one:
359 # extract encoding from header, if there is one:
360 encoding = None
360 encoding = None
361 if 'content-type' in response.headers:
361 if 'content-type' in response.headers:
362 for sub in response.headers['content-type'].split(';'):
362 for sub in response.headers['content-type'].split(';'):
363 sub = sub.strip()
363 sub = sub.strip()
364 if sub.startswith('charset'):
364 if sub.startswith('charset'):
365 encoding = sub.split('=')[-1].strip()
365 encoding = sub.split('=')[-1].strip()
366 break
366 break
367 if 'content-encoding' in response.headers:
367 if 'content-encoding' in response.headers:
368 # TODO: do deflate?
368 # TODO: do deflate?
369 if 'gzip' in response.headers['content-encoding']:
369 if 'gzip' in response.headers['content-encoding']:
370 import gzip
370 import gzip
371 from io import BytesIO
371 from io import BytesIO
372 with gzip.open(BytesIO(data), 'rt', encoding=encoding) as fp:
372 with gzip.open(BytesIO(data), 'rt', encoding=encoding) as fp:
373 encoding = None
373 encoding = None
374 data = fp.read()
374 data = fp.read()
375
375
376 # decode data, if an encoding was specified
376 # decode data, if an encoding was specified
377 # We only touch self.data once since
377 # We only touch self.data once since
378 # subclasses such as SVG have @data.setter methods
378 # subclasses such as SVG have @data.setter methods
379 # that transform self.data into ... well svg.
379 # that transform self.data into ... well svg.
380 if encoding:
380 if encoding:
381 self.data = data.decode(encoding, 'replace')
381 self.data = data.decode(encoding, 'replace')
382 else:
382 else:
383 self.data = data
383 self.data = data
384
384
385
385
386 class TextDisplayObject(DisplayObject):
386 class TextDisplayObject(DisplayObject):
387 """Validate that display data is text"""
387 """Validate that display data is text"""
388 def _check_data(self):
388 def _check_data(self):
389 if self.data is not None and not isinstance(self.data, str):
389 if self.data is not None and not isinstance(self.data, str):
390 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
390 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
391
391
392 class Pretty(TextDisplayObject):
392 class Pretty(TextDisplayObject):
393
393
394 def _repr_pretty_(self, pp, cycle):
394 def _repr_pretty_(self, pp, cycle):
395 return pp.text(self.data)
395 return pp.text(self.data)
396
396
397
397
398 class HTML(TextDisplayObject):
398 class HTML(TextDisplayObject):
399
399
400 def __init__(self, data=None, url=None, filename=None, metadata=None):
400 def __init__(self, data=None, url=None, filename=None, metadata=None):
401 def warn():
401 def warn():
402 if not data:
402 if not data:
403 return False
403 return False
404
404
405 #
405 #
406 # Avoid calling lower() on the entire data, because it could be a
406 # Avoid calling lower() on the entire data, because it could be a
407 # long string and we're only interested in its beginning and end.
407 # long string and we're only interested in its beginning and end.
408 #
408 #
409 prefix = data[:10].lower()
409 prefix = data[:10].lower()
410 suffix = data[-10:].lower()
410 suffix = data[-10:].lower()
411 return prefix.startswith("<iframe ") and suffix.endswith("</iframe>")
411 return prefix.startswith("<iframe ") and suffix.endswith("</iframe>")
412
412
413 if warn():
413 if warn():
414 warnings.warn("Consider using IPython.display.IFrame instead")
414 warnings.warn("Consider using IPython.display.IFrame instead")
415 super(HTML, self).__init__(data=data, url=url, filename=filename, metadata=metadata)
415 super(HTML, self).__init__(data=data, url=url, filename=filename, metadata=metadata)
416
416
417 def _repr_html_(self):
417 def _repr_html_(self):
418 return self._data_and_metadata()
418 return self._data_and_metadata()
419
419
420 def __html__(self):
420 def __html__(self):
421 """
421 """
422 This method exists to inform other HTML-using modules (e.g. Markupsafe,
422 This method exists to inform other HTML-using modules (e.g. Markupsafe,
423 htmltag, etc) that this object is HTML and does not need things like
423 htmltag, etc) that this object is HTML and does not need things like
424 special characters (<>&) escaped.
424 special characters (<>&) escaped.
425 """
425 """
426 return self._repr_html_()
426 return self._repr_html_()
427
427
428
428
429 class Markdown(TextDisplayObject):
429 class Markdown(TextDisplayObject):
430
430
431 def _repr_markdown_(self):
431 def _repr_markdown_(self):
432 return self._data_and_metadata()
432 return self._data_and_metadata()
433
433
434
434
435 class Math(TextDisplayObject):
435 class Math(TextDisplayObject):
436
436
437 def _repr_latex_(self):
437 def _repr_latex_(self):
438 s = r"$\displaystyle %s$" % self.data.strip('$')
438 s = r"$\displaystyle %s$" % self.data.strip('$')
439 if self.metadata:
439 if self.metadata:
440 return s, deepcopy(self.metadata)
440 return s, deepcopy(self.metadata)
441 else:
441 else:
442 return s
442 return s
443
443
444
444
445 class Latex(TextDisplayObject):
445 class Latex(TextDisplayObject):
446
446
447 def _repr_latex_(self):
447 def _repr_latex_(self):
448 return self._data_and_metadata()
448 return self._data_and_metadata()
449
449
450
450
451 class SVG(DisplayObject):
451 class SVG(DisplayObject):
452 """Embed an SVG into the display.
452 """Embed an SVG into the display.
453
453
454 Note if you just want to view a svg image via a URL use `:class:Image` with
454 Note if you just want to view a svg image via a URL use `:class:Image` with
455 a url=URL keyword argument.
455 a url=URL keyword argument.
456 """
456 """
457
457
458 _read_flags = 'rb'
458 _read_flags = 'rb'
459 # wrap data in a property, which extracts the <svg> tag, discarding
459 # wrap data in a property, which extracts the <svg> tag, discarding
460 # document headers
460 # document headers
461 _data = None
461 _data = None
462
462
463 @property
463 @property
464 def data(self):
464 def data(self):
465 return self._data
465 return self._data
466
466
467 @data.setter
467 @data.setter
468 def data(self, svg):
468 def data(self, svg):
469 if svg is None:
469 if svg is None:
470 self._data = None
470 self._data = None
471 return
471 return
472 # parse into dom object
472 # parse into dom object
473 from xml.dom import minidom
473 from xml.dom import minidom
474 x = minidom.parseString(svg)
474 x = minidom.parseString(svg)
475 # get svg tag (should be 1)
475 # get svg tag (should be 1)
476 found_svg = x.getElementsByTagName('svg')
476 found_svg = x.getElementsByTagName('svg')
477 if found_svg:
477 if found_svg:
478 svg = found_svg[0].toxml()
478 svg = found_svg[0].toxml()
479 else:
479 else:
480 # fallback on the input, trust the user
480 # fallback on the input, trust the user
481 # but this is probably an error.
481 # but this is probably an error.
482 pass
482 pass
483 svg = cast_unicode(svg)
483 svg = cast_unicode(svg)
484 self._data = svg
484 self._data = svg
485
485
486 def _repr_svg_(self):
486 def _repr_svg_(self):
487 return self._data_and_metadata()
487 return self._data_and_metadata()
488
488
489 class ProgressBar(DisplayObject):
489 class ProgressBar(DisplayObject):
490 """Progressbar supports displaying a progressbar like element
490 """Progressbar supports displaying a progressbar like element
491 """
491 """
492 def __init__(self, total):
492 def __init__(self, total):
493 """Creates a new progressbar
493 """Creates a new progressbar
494
494
495 Parameters
495 Parameters
496 ----------
496 ----------
497 total : int
497 total : int
498 maximum size of the progressbar
498 maximum size of the progressbar
499 """
499 """
500 self.total = total
500 self.total = total
501 self._progress = 0
501 self._progress = 0
502 self.html_width = '60ex'
502 self.html_width = '60ex'
503 self.text_width = 60
503 self.text_width = 60
504 self._display_id = hexlify(os.urandom(8)).decode('ascii')
504 self._display_id = hexlify(os.urandom(8)).decode('ascii')
505
505
506 def __repr__(self):
506 def __repr__(self):
507 fraction = self.progress / self.total
507 fraction = self.progress / self.total
508 filled = '=' * int(fraction * self.text_width)
508 filled = '=' * int(fraction * self.text_width)
509 rest = ' ' * (self.text_width - len(filled))
509 rest = ' ' * (self.text_width - len(filled))
510 return '[{}{}] {}/{}'.format(
510 return '[{}{}] {}/{}'.format(
511 filled, rest,
511 filled, rest,
512 self.progress, self.total,
512 self.progress, self.total,
513 )
513 )
514
514
515 def _repr_html_(self):
515 def _repr_html_(self):
516 return "<progress style='width:{}' max='{}' value='{}'></progress>".format(
516 return "<progress style='width:{}' max='{}' value='{}'></progress>".format(
517 self.html_width, self.total, self.progress)
517 self.html_width, self.total, self.progress)
518
518
519 def display(self):
519 def display(self):
520 display(self, display_id=self._display_id)
520 display(self, display_id=self._display_id)
521
521
522 def update(self):
522 def update(self):
523 display(self, display_id=self._display_id, update=True)
523 display(self, display_id=self._display_id, update=True)
524
524
525 @property
525 @property
526 def progress(self):
526 def progress(self):
527 return self._progress
527 return self._progress
528
528
529 @progress.setter
529 @progress.setter
530 def progress(self, value):
530 def progress(self, value):
531 self._progress = value
531 self._progress = value
532 self.update()
532 self.update()
533
533
534 def __iter__(self):
534 def __iter__(self):
535 self.display()
535 self.display()
536 self._progress = -1 # First iteration is 0
536 self._progress = -1 # First iteration is 0
537 return self
537 return self
538
538
539 def __next__(self):
539 def __next__(self):
540 """Returns current value and increments display by one."""
540 """Returns current value and increments display by one."""
541 self.progress += 1
541 self.progress += 1
542 if self.progress < self.total:
542 if self.progress < self.total:
543 return self.progress
543 return self.progress
544 else:
544 else:
545 raise StopIteration()
545 raise StopIteration()
546
546
547 class JSON(DisplayObject):
547 class JSON(DisplayObject):
548 """JSON expects a JSON-able dict or list
548 """JSON expects a JSON-able dict or list
549
549
550 not an already-serialized JSON string.
550 not an already-serialized JSON string.
551
551
552 Scalar types (None, number, string) are not allowed, only dict or list containers.
552 Scalar types (None, number, string) are not allowed, only dict or list containers.
553 """
553 """
554 # wrap data in a property, which warns about passing already-serialized JSON
554 # wrap data in a property, which warns about passing already-serialized JSON
555 _data = None
555 _data = None
556 def __init__(self, data=None, url=None, filename=None, expanded=False, metadata=None, root='root', **kwargs):
556 def __init__(self, data=None, url=None, filename=None, expanded=False, metadata=None, root='root', **kwargs):
557 """Create a JSON display object given raw data.
557 """Create a JSON display object given raw data.
558
558
559 Parameters
559 Parameters
560 ----------
560 ----------
561 data : dict or list
561 data : dict or list
562 JSON data to display. Not an already-serialized JSON string.
562 JSON data to display. Not an already-serialized JSON string.
563 Scalar types (None, number, string) are not allowed, only dict
563 Scalar types (None, number, string) are not allowed, only dict
564 or list containers.
564 or list containers.
565 url : unicode
565 url : unicode
566 A URL to download the data from.
566 A URL to download the data from.
567 filename : unicode
567 filename : unicode
568 Path to a local file to load the data from.
568 Path to a local file to load the data from.
569 expanded : boolean
569 expanded : boolean
570 Metadata to control whether a JSON display component is expanded.
570 Metadata to control whether a JSON display component is expanded.
571 metadata : dict
571 metadata : dict
572 Specify extra metadata to attach to the json display object.
572 Specify extra metadata to attach to the json display object.
573 root : str
573 root : str
574 The name of the root element of the JSON tree
574 The name of the root element of the JSON tree
575 """
575 """
576 self.metadata = {
576 self.metadata = {
577 'expanded': expanded,
577 'expanded': expanded,
578 'root': root,
578 'root': root,
579 }
579 }
580 if metadata:
580 if metadata:
581 self.metadata.update(metadata)
581 self.metadata.update(metadata)
582 if kwargs:
582 if kwargs:
583 self.metadata.update(kwargs)
583 self.metadata.update(kwargs)
584 super(JSON, self).__init__(data=data, url=url, filename=filename)
584 super(JSON, self).__init__(data=data, url=url, filename=filename)
585
585
586 def _check_data(self):
586 def _check_data(self):
587 if self.data is not None and not isinstance(self.data, (dict, list)):
587 if self.data is not None and not isinstance(self.data, (dict, list)):
588 raise TypeError("%s expects JSONable dict or list, not %r" % (self.__class__.__name__, self.data))
588 raise TypeError("%s expects JSONable dict or list, not %r" % (self.__class__.__name__, self.data))
589
589
590 @property
590 @property
591 def data(self):
591 def data(self):
592 return self._data
592 return self._data
593
593
594 @data.setter
594 @data.setter
595 def data(self, data):
595 def data(self, data):
596 if isinstance(data, (Path, PurePath)):
596 if isinstance(data, (Path, PurePath)):
597 data = str(data)
597 data = str(data)
598
598
599 if isinstance(data, str):
599 if isinstance(data, str):
600 if self.filename is None and self.url is None:
600 if self.filename is None and self.url is None:
601 warnings.warn("JSON expects JSONable dict or list, not JSON strings")
601 warnings.warn("JSON expects JSONable dict or list, not JSON strings")
602 data = json.loads(data)
602 data = json.loads(data)
603 self._data = data
603 self._data = data
604
604
605 def _data_and_metadata(self):
605 def _data_and_metadata(self):
606 return self.data, self.metadata
606 return self.data, self.metadata
607
607
608 def _repr_json_(self):
608 def _repr_json_(self):
609 return self._data_and_metadata()
609 return self._data_and_metadata()
610
610
611 _css_t = """var link = document.createElement("link");
611 _css_t = """var link = document.createElement("link");
612 link.ref = "stylesheet";
612 link.ref = "stylesheet";
613 link.type = "text/css";
613 link.type = "text/css";
614 link.href = "%s";
614 link.href = "%s";
615 document.head.appendChild(link);
615 document.head.appendChild(link);
616 """
616 """
617
617
618 _lib_t1 = """new Promise(function(resolve, reject) {
618 _lib_t1 = """new Promise(function(resolve, reject) {
619 var script = document.createElement("script");
619 var script = document.createElement("script");
620 script.onload = resolve;
620 script.onload = resolve;
621 script.onerror = reject;
621 script.onerror = reject;
622 script.src = "%s";
622 script.src = "%s";
623 document.head.appendChild(script);
623 document.head.appendChild(script);
624 }).then(() => {
624 }).then(() => {
625 """
625 """
626
626
627 _lib_t2 = """
627 _lib_t2 = """
628 });"""
628 });"""
629
629
630 class GeoJSON(JSON):
630 class GeoJSON(JSON):
631 """GeoJSON expects JSON-able dict
631 """GeoJSON expects JSON-able dict
632
632
633 not an already-serialized JSON string.
633 not an already-serialized JSON string.
634
634
635 Scalar types (None, number, string) are not allowed, only dict containers.
635 Scalar types (None, number, string) are not allowed, only dict containers.
636 """
636 """
637
637
638 def __init__(self, *args, **kwargs):
638 def __init__(self, *args, **kwargs):
639 """Create a GeoJSON display object given raw data.
639 """Create a GeoJSON display object given raw data.
640
640
641 Parameters
641 Parameters
642 ----------
642 ----------
643 data : dict or list
643 data : dict or list
644 VegaLite data. Not an already-serialized JSON string.
644 VegaLite data. Not an already-serialized JSON string.
645 Scalar types (None, number, string) are not allowed, only dict
645 Scalar types (None, number, string) are not allowed, only dict
646 or list containers.
646 or list containers.
647 url_template : string
647 url_template : string
648 Leaflet TileLayer URL template: http://leafletjs.com/reference.html#url-template
648 Leaflet TileLayer URL template: http://leafletjs.com/reference.html#url-template
649 layer_options : dict
649 layer_options : dict
650 Leaflet TileLayer options: http://leafletjs.com/reference.html#tilelayer-options
650 Leaflet TileLayer options: http://leafletjs.com/reference.html#tilelayer-options
651 url : unicode
651 url : unicode
652 A URL to download the data from.
652 A URL to download the data from.
653 filename : unicode
653 filename : unicode
654 Path to a local file to load the data from.
654 Path to a local file to load the data from.
655 metadata : dict
655 metadata : dict
656 Specify extra metadata to attach to the json display object.
656 Specify extra metadata to attach to the json display object.
657
657
658 Examples
658 Examples
659 --------
659 --------
660 The following will display an interactive map of Mars with a point of
660 The following will display an interactive map of Mars with a point of
661 interest on frontend that do support GeoJSON display.
661 interest on frontend that do support GeoJSON display.
662
662
663 >>> from IPython.display import GeoJSON
663 >>> from IPython.display import GeoJSON
664
664
665 >>> GeoJSON(data={
665 >>> GeoJSON(data={
666 ... "type": "Feature",
666 ... "type": "Feature",
667 ... "geometry": {
667 ... "geometry": {
668 ... "type": "Point",
668 ... "type": "Point",
669 ... "coordinates": [-81.327, 296.038]
669 ... "coordinates": [-81.327, 296.038]
670 ... }
670 ... }
671 ... },
671 ... },
672 ... url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
672 ... url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
673 ... layer_options={
673 ... layer_options={
674 ... "basemap_id": "celestia_mars-shaded-16k_global",
674 ... "basemap_id": "celestia_mars-shaded-16k_global",
675 ... "attribution" : "Celestia/praesepe",
675 ... "attribution" : "Celestia/praesepe",
676 ... "minZoom" : 0,
676 ... "minZoom" : 0,
677 ... "maxZoom" : 18,
677 ... "maxZoom" : 18,
678 ... })
678 ... })
679 <IPython.core.display.GeoJSON object>
679 <IPython.core.display.GeoJSON object>
680
680
681 In the terminal IPython, you will only see the text representation of
681 In the terminal IPython, you will only see the text representation of
682 the GeoJSON object.
682 the GeoJSON object.
683
683
684 """
684 """
685
685
686 super(GeoJSON, self).__init__(*args, **kwargs)
686 super(GeoJSON, self).__init__(*args, **kwargs)
687
687
688
688
689 def _ipython_display_(self):
689 def _ipython_display_(self):
690 bundle = {
690 bundle = {
691 'application/geo+json': self.data,
691 'application/geo+json': self.data,
692 'text/plain': '<IPython.display.GeoJSON object>'
692 'text/plain': '<IPython.display.GeoJSON object>'
693 }
693 }
694 metadata = {
694 metadata = {
695 'application/geo+json': self.metadata
695 'application/geo+json': self.metadata
696 }
696 }
697 display(bundle, metadata=metadata, raw=True)
697 display(bundle, metadata=metadata, raw=True)
698
698
699 class Javascript(TextDisplayObject):
699 class Javascript(TextDisplayObject):
700
700
701 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
701 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
702 """Create a Javascript display object given raw data.
702 """Create a Javascript display object given raw data.
703
703
704 When this object is returned by an expression or passed to the
704 When this object is returned by an expression or passed to the
705 display function, it will result in the data being displayed
705 display function, it will result in the data being displayed
706 in the frontend. If the data is a URL, the data will first be
706 in the frontend. If the data is a URL, the data will first be
707 downloaded and then displayed.
707 downloaded and then displayed.
708
708
709 In the Notebook, the containing element will be available as `element`,
709 In the Notebook, the containing element will be available as `element`,
710 and jQuery will be available. Content appended to `element` will be
710 and jQuery will be available. Content appended to `element` will be
711 visible in the output area.
711 visible in the output area.
712
712
713 Parameters
713 Parameters
714 ----------
714 ----------
715 data : unicode, str or bytes
715 data : unicode, str or bytes
716 The Javascript source code or a URL to download it from.
716 The Javascript source code or a URL to download it from.
717 url : unicode
717 url : unicode
718 A URL to download the data from.
718 A URL to download the data from.
719 filename : unicode
719 filename : unicode
720 Path to a local file to load the data from.
720 Path to a local file to load the data from.
721 lib : list or str
721 lib : list or str
722 A sequence of Javascript library URLs to load asynchronously before
722 A sequence of Javascript library URLs to load asynchronously before
723 running the source code. The full URLs of the libraries should
723 running the source code. The full URLs of the libraries should
724 be given. A single Javascript library URL can also be given as a
724 be given. A single Javascript library URL can also be given as a
725 string.
725 string.
726 css : list or str
726 css : list or str
727 A sequence of css files to load before running the source code.
727 A sequence of css files to load before running the source code.
728 The full URLs of the css files should be given. A single css URL
728 The full URLs of the css files should be given. A single css URL
729 can also be given as a string.
729 can also be given as a string.
730 """
730 """
731 if isinstance(lib, str):
731 if isinstance(lib, str):
732 lib = [lib]
732 lib = [lib]
733 elif lib is None:
733 elif lib is None:
734 lib = []
734 lib = []
735 if isinstance(css, str):
735 if isinstance(css, str):
736 css = [css]
736 css = [css]
737 elif css is None:
737 elif css is None:
738 css = []
738 css = []
739 if not isinstance(lib, (list,tuple)):
739 if not isinstance(lib, (list,tuple)):
740 raise TypeError('expected sequence, got: %r' % lib)
740 raise TypeError('expected sequence, got: %r' % lib)
741 if not isinstance(css, (list,tuple)):
741 if not isinstance(css, (list,tuple)):
742 raise TypeError('expected sequence, got: %r' % css)
742 raise TypeError('expected sequence, got: %r' % css)
743 self.lib = lib
743 self.lib = lib
744 self.css = css
744 self.css = css
745 super(Javascript, self).__init__(data=data, url=url, filename=filename)
745 super(Javascript, self).__init__(data=data, url=url, filename=filename)
746
746
747 def _repr_javascript_(self):
747 def _repr_javascript_(self):
748 r = ''
748 r = ''
749 for c in self.css:
749 for c in self.css:
750 r += _css_t % c
750 r += _css_t % c
751 for l in self.lib:
751 for l in self.lib:
752 r += _lib_t1 % l
752 r += _lib_t1 % l
753 r += self.data
753 r += self.data
754 r += _lib_t2*len(self.lib)
754 r += _lib_t2*len(self.lib)
755 return r
755 return r
756
756
757 # constants for identifying png/jpeg data
757 # constants for identifying png/jpeg data
758 _PNG = b'\x89PNG\r\n\x1a\n'
758 _PNG = b'\x89PNG\r\n\x1a\n'
759 _JPEG = b'\xff\xd8'
759 _JPEG = b'\xff\xd8'
760
760
761 def _pngxy(data):
761 def _pngxy(data):
762 """read the (width, height) from a PNG header"""
762 """read the (width, height) from a PNG header"""
763 ihdr = data.index(b'IHDR')
763 ihdr = data.index(b'IHDR')
764 # next 8 bytes are width/height
764 # next 8 bytes are width/height
765 return struct.unpack('>ii', data[ihdr+4:ihdr+12])
765 return struct.unpack('>ii', data[ihdr+4:ihdr+12])
766
766
767 def _jpegxy(data):
767 def _jpegxy(data):
768 """read the (width, height) from a JPEG header"""
768 """read the (width, height) from a JPEG header"""
769 # adapted from http://www.64lines.com/jpeg-width-height
769 # adapted from http://www.64lines.com/jpeg-width-height
770
770
771 idx = 4
771 idx = 4
772 while True:
772 while True:
773 block_size = struct.unpack('>H', data[idx:idx+2])[0]
773 block_size = struct.unpack('>H', data[idx:idx+2])[0]
774 idx = idx + block_size
774 idx = idx + block_size
775 if data[idx:idx+2] == b'\xFF\xC0':
775 if data[idx:idx+2] == b'\xFF\xC0':
776 # found Start of Frame
776 # found Start of Frame
777 iSOF = idx
777 iSOF = idx
778 break
778 break
779 else:
779 else:
780 # read another block
780 # read another block
781 idx += 2
781 idx += 2
782
782
783 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
783 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
784 return w, h
784 return w, h
785
785
786 def _gifxy(data):
786 def _gifxy(data):
787 """read the (width, height) from a GIF header"""
787 """read the (width, height) from a GIF header"""
788 return struct.unpack('<HH', data[6:10])
788 return struct.unpack('<HH', data[6:10])
789
789
790
790
791 class Image(DisplayObject):
791 class Image(DisplayObject):
792
792
793 _read_flags = 'rb'
793 _read_flags = 'rb'
794 _FMT_JPEG = u'jpeg'
794 _FMT_JPEG = u'jpeg'
795 _FMT_PNG = u'png'
795 _FMT_PNG = u'png'
796 _FMT_GIF = u'gif'
796 _FMT_GIF = u'gif'
797 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG, _FMT_GIF]
797 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG, _FMT_GIF]
798 _MIMETYPES = {
798 _MIMETYPES = {
799 _FMT_PNG: 'image/png',
799 _FMT_PNG: 'image/png',
800 _FMT_JPEG: 'image/jpeg',
800 _FMT_JPEG: 'image/jpeg',
801 _FMT_GIF: 'image/gif',
801 _FMT_GIF: 'image/gif',
802 }
802 }
803
803
804 def __init__(
804 def __init__(
805 self,
805 self,
806 data=None,
806 data=None,
807 url=None,
807 url=None,
808 filename=None,
808 filename=None,
809 format=None,
809 format=None,
810 embed=None,
810 embed=None,
811 width=None,
811 width=None,
812 height=None,
812 height=None,
813 retina=False,
813 retina=False,
814 unconfined=False,
814 unconfined=False,
815 metadata=None,
815 metadata=None,
816 alt=None,
816 alt=None,
817 ):
817 ):
818 """Create a PNG/JPEG/GIF image object given raw data.
818 """Create a PNG/JPEG/GIF image object given raw data.
819
819
820 When this object is returned by an input cell or passed to the
820 When this object is returned by an input cell or passed to the
821 display function, it will result in the image being displayed
821 display function, it will result in the image being displayed
822 in the frontend.
822 in the frontend.
823
823
824 Parameters
824 Parameters
825 ----------
825 ----------
826 data : unicode, str or bytes
826 data : unicode, str or bytes
827 The raw image data or a URL or filename to load the data from.
827 The raw image data or a URL or filename to load the data from.
828 This always results in embedded image data.
828 This always results in embedded image data.
829
829 url : unicode
830 url : unicode
830 A URL to download the data from. If you specify `url=`,
831 A URL to download the data from. If you specify `url=`,
831 the image data will not be embedded unless you also specify `embed=True`.
832 the image data will not be embedded unless you also specify `embed=True`.
833
832 filename : unicode
834 filename : unicode
833 Path to a local file to load the data from.
835 Path to a local file to load the data from.
834 Images from a file are always embedded.
836 Images from a file are always embedded.
837
835 format : unicode
838 format : unicode
836 The format of the image data (png/jpeg/jpg/gif). If a filename or URL is given
839 The format of the image data (png/jpeg/jpg/gif). If a filename or URL is given
837 for format will be inferred from the filename extension.
840 for format will be inferred from the filename extension.
841
838 embed : bool
842 embed : bool
839 Should the image data be embedded using a data URI (True) or be
843 Should the image data be embedded using a data URI (True) or be
840 loaded using an <img> tag. Set this to True if you want the image
844 loaded using an <img> tag. Set this to True if you want the image
841 to be viewable later with no internet connection in the notebook.
845 to be viewable later with no internet connection in the notebook.
842
846
843 Default is `True`, unless the keyword argument `url` is set, then
847 Default is `True`, unless the keyword argument `url` is set, then
844 default value is `False`.
848 default value is `False`.
845
849
846 Note that QtConsole is not able to display images if `embed` is set to `False`
850 Note that QtConsole is not able to display images if `embed` is set to `False`
851
847 width : int
852 width : int
848 Width in pixels to which to constrain the image in html
853 Width in pixels to which to constrain the image in html
854
849 height : int
855 height : int
850 Height in pixels to which to constrain the image in html
856 Height in pixels to which to constrain the image in html
857
851 retina : bool
858 retina : bool
852 Automatically set the width and height to half of the measured
859 Automatically set the width and height to half of the measured
853 width and height.
860 width and height.
854 This only works for embedded images because it reads the width/height
861 This only works for embedded images because it reads the width/height
855 from image data.
862 from image data.
856 For non-embedded images, you can just set the desired display width
863 For non-embedded images, you can just set the desired display width
857 and height directly.
864 and height directly.
865
858 unconfined : bool
866 unconfined : bool
859 Set unconfined=True to disable max-width confinement of the image.
867 Set unconfined=True to disable max-width confinement of the image.
868
860 metadata : dict
869 metadata : dict
861 Specify extra metadata to attach to the image.
870 Specify extra metadata to attach to the image.
871
862 alt : unicode
872 alt : unicode
863 Alternative text for the image, for use by screen readers.
873 Alternative text for the image, for use by screen readers.
864
874
865 Examples
875 Examples
866 --------
876 --------
867 embedded image data, works in qtconsole and notebook
877 embedded image data, works in qtconsole and notebook
868 when passed positionally, the first arg can be any of raw image data,
878 when passed positionally, the first arg can be any of raw image data,
869 a URL, or a filename from which to load image data.
879 a URL, or a filename from which to load image data.
870 The result is always embedding image data for inline images.
880 The result is always embedding image data for inline images.
871
881
872 >>> Image('http://www.google.fr/images/srpr/logo3w.png')
882 >>> Image('http://www.google.fr/images/srpr/logo3w.png')
873 <IPython.core.display.Image object>
883 <IPython.core.display.Image object>
874
884
875 >>> Image('/path/to/image.jpg')
885 >>> Image('/path/to/image.jpg')
876 <IPython.core.display.Image object>
886 <IPython.core.display.Image object>
877
887
878 >>> Image(b'RAW_PNG_DATA...')
888 >>> Image(b'RAW_PNG_DATA...')
879 <IPython.core.display.Image object>
889 <IPython.core.display.Image object>
880
890
881 Specifying Image(url=...) does not embed the image data,
891 Specifying Image(url=...) does not embed the image data,
882 it only generates ``<img>`` tag with a link to the source.
892 it only generates ``<img>`` tag with a link to the source.
883 This will not work in the qtconsole or offline.
893 This will not work in the qtconsole or offline.
884
894
885 >>> Image(url='http://www.google.fr/images/srpr/logo3w.png')
895 >>> Image(url='http://www.google.fr/images/srpr/logo3w.png')
886 <IPython.core.display.Image object>
896 <IPython.core.display.Image object>
887
897
888 """
898 """
889 if isinstance(data, (Path, PurePath)):
899 if isinstance(data, (Path, PurePath)):
890 data = str(data)
900 data = str(data)
891
901
892 if filename is not None:
902 if filename is not None:
893 ext = self._find_ext(filename)
903 ext = self._find_ext(filename)
894 elif url is not None:
904 elif url is not None:
895 ext = self._find_ext(url)
905 ext = self._find_ext(url)
896 elif data is None:
906 elif data is None:
897 raise ValueError("No image data found. Expecting filename, url, or data.")
907 raise ValueError("No image data found. Expecting filename, url, or data.")
898 elif isinstance(data, str) and (
908 elif isinstance(data, str) and (
899 data.startswith('http') or _safe_exists(data)
909 data.startswith('http') or _safe_exists(data)
900 ):
910 ):
901 ext = self._find_ext(data)
911 ext = self._find_ext(data)
902 else:
912 else:
903 ext = None
913 ext = None
904
914
905 if format is None:
915 if format is None:
906 if ext is not None:
916 if ext is not None:
907 if ext == u'jpg' or ext == u'jpeg':
917 if ext == u'jpg' or ext == u'jpeg':
908 format = self._FMT_JPEG
918 format = self._FMT_JPEG
909 elif ext == u'png':
919 elif ext == u'png':
910 format = self._FMT_PNG
920 format = self._FMT_PNG
911 elif ext == u'gif':
921 elif ext == u'gif':
912 format = self._FMT_GIF
922 format = self._FMT_GIF
913 else:
923 else:
914 format = ext.lower()
924 format = ext.lower()
915 elif isinstance(data, bytes):
925 elif isinstance(data, bytes):
916 # infer image type from image data header,
926 # infer image type from image data header,
917 # only if format has not been specified.
927 # only if format has not been specified.
918 if data[:2] == _JPEG:
928 if data[:2] == _JPEG:
919 format = self._FMT_JPEG
929 format = self._FMT_JPEG
920
930
921 # failed to detect format, default png
931 # failed to detect format, default png
922 if format is None:
932 if format is None:
923 format = self._FMT_PNG
933 format = self._FMT_PNG
924
934
925 if format.lower() == 'jpg':
935 if format.lower() == 'jpg':
926 # jpg->jpeg
936 # jpg->jpeg
927 format = self._FMT_JPEG
937 format = self._FMT_JPEG
928
938
929 self.format = format.lower()
939 self.format = format.lower()
930 self.embed = embed if embed is not None else (url is None)
940 self.embed = embed if embed is not None else (url is None)
931
941
932 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
942 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
933 raise ValueError("Cannot embed the '%s' image format" % (self.format))
943 raise ValueError("Cannot embed the '%s' image format" % (self.format))
934 if self.embed:
944 if self.embed:
935 self._mimetype = self._MIMETYPES.get(self.format)
945 self._mimetype = self._MIMETYPES.get(self.format)
936
946
937 self.width = width
947 self.width = width
938 self.height = height
948 self.height = height
939 self.retina = retina
949 self.retina = retina
940 self.unconfined = unconfined
950 self.unconfined = unconfined
941 self.alt = alt
951 self.alt = alt
942 super(Image, self).__init__(data=data, url=url, filename=filename,
952 super(Image, self).__init__(data=data, url=url, filename=filename,
943 metadata=metadata)
953 metadata=metadata)
944
954
945 if self.width is None and self.metadata.get('width', {}):
955 if self.width is None and self.metadata.get('width', {}):
946 self.width = metadata['width']
956 self.width = metadata['width']
947
957
948 if self.height is None and self.metadata.get('height', {}):
958 if self.height is None and self.metadata.get('height', {}):
949 self.height = metadata['height']
959 self.height = metadata['height']
950
960
951 if self.alt is None and self.metadata.get("alt", {}):
961 if self.alt is None and self.metadata.get("alt", {}):
952 self.alt = metadata["alt"]
962 self.alt = metadata["alt"]
953
963
954 if retina:
964 if retina:
955 self._retina_shape()
965 self._retina_shape()
956
966
957
967
958 def _retina_shape(self):
968 def _retina_shape(self):
959 """load pixel-doubled width and height from image data"""
969 """load pixel-doubled width and height from image data"""
960 if not self.embed:
970 if not self.embed:
961 return
971 return
962 if self.format == self._FMT_PNG:
972 if self.format == self._FMT_PNG:
963 w, h = _pngxy(self.data)
973 w, h = _pngxy(self.data)
964 elif self.format == self._FMT_JPEG:
974 elif self.format == self._FMT_JPEG:
965 w, h = _jpegxy(self.data)
975 w, h = _jpegxy(self.data)
966 elif self.format == self._FMT_GIF:
976 elif self.format == self._FMT_GIF:
967 w, h = _gifxy(self.data)
977 w, h = _gifxy(self.data)
968 else:
978 else:
969 # retina only supports png
979 # retina only supports png
970 return
980 return
971 self.width = w // 2
981 self.width = w // 2
972 self.height = h // 2
982 self.height = h // 2
973
983
974 def reload(self):
984 def reload(self):
975 """Reload the raw data from file or URL."""
985 """Reload the raw data from file or URL."""
976 if self.embed:
986 if self.embed:
977 super(Image,self).reload()
987 super(Image,self).reload()
978 if self.retina:
988 if self.retina:
979 self._retina_shape()
989 self._retina_shape()
980
990
981 def _repr_html_(self):
991 def _repr_html_(self):
982 if not self.embed:
992 if not self.embed:
983 width = height = klass = alt = ""
993 width = height = klass = alt = ""
984 if self.width:
994 if self.width:
985 width = ' width="%d"' % self.width
995 width = ' width="%d"' % self.width
986 if self.height:
996 if self.height:
987 height = ' height="%d"' % self.height
997 height = ' height="%d"' % self.height
988 if self.unconfined:
998 if self.unconfined:
989 klass = ' class="unconfined"'
999 klass = ' class="unconfined"'
990 if self.alt:
1000 if self.alt:
991 alt = ' alt="%s"' % html.escape(self.alt)
1001 alt = ' alt="%s"' % html.escape(self.alt)
992 return '<img src="{url}"{width}{height}{klass}{alt}/>'.format(
1002 return '<img src="{url}"{width}{height}{klass}{alt}/>'.format(
993 url=self.url,
1003 url=self.url,
994 width=width,
1004 width=width,
995 height=height,
1005 height=height,
996 klass=klass,
1006 klass=klass,
997 alt=alt,
1007 alt=alt,
998 )
1008 )
999
1009
1000 def _repr_mimebundle_(self, include=None, exclude=None):
1010 def _repr_mimebundle_(self, include=None, exclude=None):
1001 """Return the image as a mimebundle
1011 """Return the image as a mimebundle
1002
1012
1003 Any new mimetype support should be implemented here.
1013 Any new mimetype support should be implemented here.
1004 """
1014 """
1005 if self.embed:
1015 if self.embed:
1006 mimetype = self._mimetype
1016 mimetype = self._mimetype
1007 data, metadata = self._data_and_metadata(always_both=True)
1017 data, metadata = self._data_and_metadata(always_both=True)
1008 if metadata:
1018 if metadata:
1009 metadata = {mimetype: metadata}
1019 metadata = {mimetype: metadata}
1010 return {mimetype: data}, metadata
1020 return {mimetype: data}, metadata
1011 else:
1021 else:
1012 return {'text/html': self._repr_html_()}
1022 return {'text/html': self._repr_html_()}
1013
1023
1014 def _data_and_metadata(self, always_both=False):
1024 def _data_and_metadata(self, always_both=False):
1015 """shortcut for returning metadata with shape information, if defined"""
1025 """shortcut for returning metadata with shape information, if defined"""
1016 try:
1026 try:
1017 b64_data = b2a_base64(self.data).decode('ascii')
1027 b64_data = b2a_base64(self.data).decode('ascii')
1018 except TypeError as e:
1028 except TypeError as e:
1019 raise FileNotFoundError(
1029 raise FileNotFoundError(
1020 "No such file or directory: '%s'" % (self.data)) from e
1030 "No such file or directory: '%s'" % (self.data)) from e
1021 md = {}
1031 md = {}
1022 if self.metadata:
1032 if self.metadata:
1023 md.update(self.metadata)
1033 md.update(self.metadata)
1024 if self.width:
1034 if self.width:
1025 md['width'] = self.width
1035 md['width'] = self.width
1026 if self.height:
1036 if self.height:
1027 md['height'] = self.height
1037 md['height'] = self.height
1028 if self.unconfined:
1038 if self.unconfined:
1029 md['unconfined'] = self.unconfined
1039 md['unconfined'] = self.unconfined
1030 if self.alt:
1040 if self.alt:
1031 md["alt"] = self.alt
1041 md["alt"] = self.alt
1032 if md or always_both:
1042 if md or always_both:
1033 return b64_data, md
1043 return b64_data, md
1034 else:
1044 else:
1035 return b64_data
1045 return b64_data
1036
1046
1037 def _repr_png_(self):
1047 def _repr_png_(self):
1038 if self.embed and self.format == self._FMT_PNG:
1048 if self.embed and self.format == self._FMT_PNG:
1039 return self._data_and_metadata()
1049 return self._data_and_metadata()
1040
1050
1041 def _repr_jpeg_(self):
1051 def _repr_jpeg_(self):
1042 if self.embed and self.format == self._FMT_JPEG:
1052 if self.embed and self.format == self._FMT_JPEG:
1043 return self._data_and_metadata()
1053 return self._data_and_metadata()
1044
1054
1045 def _find_ext(self, s):
1055 def _find_ext(self, s):
1046 base, ext = splitext(s)
1056 base, ext = splitext(s)
1047
1057
1048 if not ext:
1058 if not ext:
1049 return base
1059 return base
1050
1060
1051 # `splitext` includes leading period, so we skip it
1061 # `splitext` includes leading period, so we skip it
1052 return ext[1:].lower()
1062 return ext[1:].lower()
1053
1063
1054
1064
1055 class Video(DisplayObject):
1065 class Video(DisplayObject):
1056
1066
1057 def __init__(self, data=None, url=None, filename=None, embed=False,
1067 def __init__(self, data=None, url=None, filename=None, embed=False,
1058 mimetype=None, width=None, height=None, html_attributes="controls"):
1068 mimetype=None, width=None, height=None, html_attributes="controls"):
1059 """Create a video object given raw data or an URL.
1069 """Create a video object given raw data or an URL.
1060
1070
1061 When this object is returned by an input cell or passed to the
1071 When this object is returned by an input cell or passed to the
1062 display function, it will result in the video being displayed
1072 display function, it will result in the video being displayed
1063 in the frontend.
1073 in the frontend.
1064
1074
1065 Parameters
1075 Parameters
1066 ----------
1076 ----------
1067 data : unicode, str or bytes
1077 data : unicode, str or bytes
1068 The raw video data or a URL or filename to load the data from.
1078 The raw video data or a URL or filename to load the data from.
1069 Raw data will require passing ``embed=True``.
1079 Raw data will require passing ``embed=True``.
1080
1070 url : unicode
1081 url : unicode
1071 A URL for the video. If you specify ``url=``,
1082 A URL for the video. If you specify ``url=``,
1072 the image data will not be embedded.
1083 the image data will not be embedded.
1084
1073 filename : unicode
1085 filename : unicode
1074 Path to a local file containing the video.
1086 Path to a local file containing the video.
1075 Will be interpreted as a local URL unless ``embed=True``.
1087 Will be interpreted as a local URL unless ``embed=True``.
1088
1076 embed : bool
1089 embed : bool
1077 Should the video be embedded using a data URI (True) or be
1090 Should the video be embedded using a data URI (True) or be
1078 loaded using a <video> tag (False).
1091 loaded using a <video> tag (False).
1079
1092
1080 Since videos are large, embedding them should be avoided, if possible.
1093 Since videos are large, embedding them should be avoided, if possible.
1081 You must confirm embedding as your intention by passing ``embed=True``.
1094 You must confirm embedding as your intention by passing ``embed=True``.
1082
1095
1083 Local files can be displayed with URLs without embedding the content, via::
1096 Local files can be displayed with URLs without embedding the content, via::
1084
1097
1085 Video('./video.mp4')
1098 Video('./video.mp4')
1099
1086 mimetype : unicode
1100 mimetype : unicode
1087 Specify the mimetype for embedded videos.
1101 Specify the mimetype for embedded videos.
1088 Default will be guessed from file extension, if available.
1102 Default will be guessed from file extension, if available.
1103
1089 width : int
1104 width : int
1090 Width in pixels to which to constrain the video in HTML.
1105 Width in pixels to which to constrain the video in HTML.
1091 If not supplied, defaults to the width of the video.
1106 If not supplied, defaults to the width of the video.
1107
1092 height : int
1108 height : int
1093 Height in pixels to which to constrain the video in html.
1109 Height in pixels to which to constrain the video in html.
1094 If not supplied, defaults to the height of the video.
1110 If not supplied, defaults to the height of the video.
1111
1095 html_attributes : str
1112 html_attributes : str
1096 Attributes for the HTML ``<video>`` block.
1113 Attributes for the HTML ``<video>`` block.
1097 Default: ``"controls"`` to get video controls.
1114 Default: ``"controls"`` to get video controls.
1098 Other examples: ``"controls muted"`` for muted video with controls,
1115 Other examples: ``"controls muted"`` for muted video with controls,
1099 ``"loop autoplay"`` for looping autoplaying video without controls.
1116 ``"loop autoplay"`` for looping autoplaying video without controls.
1100
1117
1101 Examples
1118 Examples
1102 --------
1119 --------
1103 ::
1120 ::
1104
1121
1105 Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4')
1122 Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4')
1106 Video('path/to/video.mp4')
1123 Video('path/to/video.mp4')
1107 Video('path/to/video.mp4', embed=True)
1124 Video('path/to/video.mp4', embed=True)
1108 Video('path/to/video.mp4', embed=True, html_attributes="controls muted autoplay")
1125 Video('path/to/video.mp4', embed=True, html_attributes="controls muted autoplay")
1109 Video(b'raw-videodata', embed=True)
1126 Video(b'raw-videodata', embed=True)
1110 """
1127 """
1111 if isinstance(data, (Path, PurePath)):
1128 if isinstance(data, (Path, PurePath)):
1112 data = str(data)
1129 data = str(data)
1113
1130
1114 if url is None and isinstance(data, str) and data.startswith(('http:', 'https:')):
1131 if url is None and isinstance(data, str) and data.startswith(('http:', 'https:')):
1115 url = data
1132 url = data
1116 data = None
1133 data = None
1117 elif data is not None and os.path.exists(data):
1134 elif data is not None and os.path.exists(data):
1118 filename = data
1135 filename = data
1119 data = None
1136 data = None
1120
1137
1121 if data and not embed:
1138 if data and not embed:
1122 msg = ''.join([
1139 msg = ''.join([
1123 "To embed videos, you must pass embed=True ",
1140 "To embed videos, you must pass embed=True ",
1124 "(this may make your notebook files huge)\n",
1141 "(this may make your notebook files huge)\n",
1125 "Consider passing Video(url='...')",
1142 "Consider passing Video(url='...')",
1126 ])
1143 ])
1127 raise ValueError(msg)
1144 raise ValueError(msg)
1128
1145
1129 self.mimetype = mimetype
1146 self.mimetype = mimetype
1130 self.embed = embed
1147 self.embed = embed
1131 self.width = width
1148 self.width = width
1132 self.height = height
1149 self.height = height
1133 self.html_attributes = html_attributes
1150 self.html_attributes = html_attributes
1134 super(Video, self).__init__(data=data, url=url, filename=filename)
1151 super(Video, self).__init__(data=data, url=url, filename=filename)
1135
1152
1136 def _repr_html_(self):
1153 def _repr_html_(self):
1137 width = height = ''
1154 width = height = ''
1138 if self.width:
1155 if self.width:
1139 width = ' width="%d"' % self.width
1156 width = ' width="%d"' % self.width
1140 if self.height:
1157 if self.height:
1141 height = ' height="%d"' % self.height
1158 height = ' height="%d"' % self.height
1142
1159
1143 # External URLs and potentially local files are not embedded into the
1160 # External URLs and potentially local files are not embedded into the
1144 # notebook output.
1161 # notebook output.
1145 if not self.embed:
1162 if not self.embed:
1146 url = self.url if self.url is not None else self.filename
1163 url = self.url if self.url is not None else self.filename
1147 output = """<video src="{0}" {1} {2} {3}>
1164 output = """<video src="{0}" {1} {2} {3}>
1148 Your browser does not support the <code>video</code> element.
1165 Your browser does not support the <code>video</code> element.
1149 </video>""".format(url, self.html_attributes, width, height)
1166 </video>""".format(url, self.html_attributes, width, height)
1150 return output
1167 return output
1151
1168
1152 # Embedded videos are base64-encoded.
1169 # Embedded videos are base64-encoded.
1153 mimetype = self.mimetype
1170 mimetype = self.mimetype
1154 if self.filename is not None:
1171 if self.filename is not None:
1155 if not mimetype:
1172 if not mimetype:
1156 mimetype, _ = mimetypes.guess_type(self.filename)
1173 mimetype, _ = mimetypes.guess_type(self.filename)
1157
1174
1158 with open(self.filename, 'rb') as f:
1175 with open(self.filename, 'rb') as f:
1159 video = f.read()
1176 video = f.read()
1160 else:
1177 else:
1161 video = self.data
1178 video = self.data
1162 if isinstance(video, str):
1179 if isinstance(video, str):
1163 # unicode input is already b64-encoded
1180 # unicode input is already b64-encoded
1164 b64_video = video
1181 b64_video = video
1165 else:
1182 else:
1166 b64_video = b2a_base64(video).decode('ascii').rstrip()
1183 b64_video = b2a_base64(video).decode('ascii').rstrip()
1167
1184
1168 output = """<video {0} {1} {2}>
1185 output = """<video {0} {1} {2}>
1169 <source src="data:{3};base64,{4}" type="{3}">
1186 <source src="data:{3};base64,{4}" type="{3}">
1170 Your browser does not support the video tag.
1187 Your browser does not support the video tag.
1171 </video>""".format(self.html_attributes, width, height, mimetype, b64_video)
1188 </video>""".format(self.html_attributes, width, height, mimetype, b64_video)
1172 return output
1189 return output
1173
1190
1174 def reload(self):
1191 def reload(self):
1175 # TODO
1192 # TODO
1176 pass
1193 pass
1177
1194
1178
1195
1179 @skip_doctest
1196 @skip_doctest
1180 def set_matplotlib_formats(*formats, **kwargs):
1197 def set_matplotlib_formats(*formats, **kwargs):
1181 """
1198 """
1182 .. deprecated:: 7.23
1199 .. deprecated:: 7.23
1183
1200
1184 use `matplotlib_inline.backend_inline.set_matplotlib_formats()`
1201 use `matplotlib_inline.backend_inline.set_matplotlib_formats()`
1185
1202
1186 Select figure formats for the inline backend. Optionally pass quality for JPEG.
1203 Select figure formats for the inline backend. Optionally pass quality for JPEG.
1187
1204
1188 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
1205 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
1189
1206
1190 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
1207 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
1191
1208
1192 To set this in your config files use the following::
1209 To set this in your config files use the following::
1193
1210
1194 c.InlineBackend.figure_formats = {'png', 'jpeg'}
1211 c.InlineBackend.figure_formats = {'png', 'jpeg'}
1195 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
1212 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
1196
1213
1197 Parameters
1214 Parameters
1198 ----------
1215 ----------
1199 *formats : strs
1216 *formats : strs
1200 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
1217 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
1201 **kwargs
1218 **kwargs
1202 Keyword args will be relayed to ``figure.canvas.print_figure``.
1219 Keyword args will be relayed to ``figure.canvas.print_figure``.
1203 """
1220 """
1204 warnings.warn(
1221 warnings.warn(
1205 "`set_matplotlib_formats` is deprecated since IPython 7.23, directly "
1222 "`set_matplotlib_formats` is deprecated since IPython 7.23, directly "
1206 "use `matplotlib_inline.backend_inline.set_matplotlib_formats()`",
1223 "use `matplotlib_inline.backend_inline.set_matplotlib_formats()`",
1207 DeprecationWarning,
1224 DeprecationWarning,
1208 stacklevel=2,
1225 stacklevel=2,
1209 )
1226 )
1210
1227
1211 from matplotlib_inline.backend_inline import (
1228 from matplotlib_inline.backend_inline import (
1212 set_matplotlib_formats as set_matplotlib_formats_orig,
1229 set_matplotlib_formats as set_matplotlib_formats_orig,
1213 )
1230 )
1214
1231
1215 set_matplotlib_formats_orig(*formats, **kwargs)
1232 set_matplotlib_formats_orig(*formats, **kwargs)
1216
1233
1217 @skip_doctest
1234 @skip_doctest
1218 def set_matplotlib_close(close=True):
1235 def set_matplotlib_close(close=True):
1219 """
1236 """
1220 .. deprecated:: 7.23
1237 .. deprecated:: 7.23
1221
1238
1222 use `matplotlib_inline.backend_inline.set_matplotlib_close()`
1239 use `matplotlib_inline.backend_inline.set_matplotlib_close()`
1223
1240
1224
1225 Set whether the inline backend closes all figures automatically or not.
1241 Set whether the inline backend closes all figures automatically or not.
1226
1242
1227 By default, the inline backend used in the IPython Notebook will close all
1243 By default, the inline backend used in the IPython Notebook will close all
1228 matplotlib figures automatically after each cell is run. This means that
1244 matplotlib figures automatically after each cell is run. This means that
1229 plots in different cells won't interfere. Sometimes, you may want to make
1245 plots in different cells won't interfere. Sometimes, you may want to make
1230 a plot in one cell and then refine it in later cells. This can be accomplished
1246 a plot in one cell and then refine it in later cells. This can be accomplished
1231 by::
1247 by::
1232
1248
1233 In [1]: set_matplotlib_close(False)
1249 In [1]: set_matplotlib_close(False)
1234
1250
1235 To set this in your config files use the following::
1251 To set this in your config files use the following::
1236
1252
1237 c.InlineBackend.close_figures = False
1253 c.InlineBackend.close_figures = False
1238
1254
1239 Parameters
1255 Parameters
1240 ----------
1256 ----------
1241 close : bool
1257 close : bool
1242 Should all matplotlib figures be automatically closed after each cell is
1258 Should all matplotlib figures be automatically closed after each cell is
1243 run?
1259 run?
1244 """
1260 """
1245 warnings.warn(
1261 warnings.warn(
1246 "`set_matplotlib_close` is deprecated since IPython 7.23, directly "
1262 "`set_matplotlib_close` is deprecated since IPython 7.23, directly "
1247 "use `matplotlib_inline.backend_inline.set_matplotlib_close()`",
1263 "use `matplotlib_inline.backend_inline.set_matplotlib_close()`",
1248 DeprecationWarning,
1264 DeprecationWarning,
1249 stacklevel=2,
1265 stacklevel=2,
1250 )
1266 )
1251
1267
1252 from matplotlib_inline.backend_inline import (
1268 from matplotlib_inline.backend_inline import (
1253 set_matplotlib_close as set_matplotlib_close_orig,
1269 set_matplotlib_close as set_matplotlib_close_orig,
1254 )
1270 )
1255
1271
1256 set_matplotlib_close_orig(close)
1272 set_matplotlib_close_orig(close)
@@ -1,1024 +1,1027 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Display formatters.
2 """Display formatters.
3
3
4 Inheritance diagram:
4 Inheritance diagram:
5
5
6 .. inheritance-diagram:: IPython.core.formatters
6 .. inheritance-diagram:: IPython.core.formatters
7 :parts: 3
7 :parts: 3
8 """
8 """
9
9
10 # Copyright (c) IPython Development Team.
10 # Copyright (c) IPython Development Team.
11 # Distributed under the terms of the Modified BSD License.
11 # Distributed under the terms of the Modified BSD License.
12
12
13 import abc
13 import abc
14 import json
14 import json
15 import sys
15 import sys
16 import traceback
16 import traceback
17 import warnings
17 import warnings
18 from io import StringIO
18 from io import StringIO
19
19
20 from decorator import decorator
20 from decorator import decorator
21
21
22 from traitlets.config.configurable import Configurable
22 from traitlets.config.configurable import Configurable
23 from .getipython import get_ipython
23 from .getipython import get_ipython
24 from ..utils.sentinel import Sentinel
24 from ..utils.sentinel import Sentinel
25 from ..utils.dir2 import get_real_method
25 from ..utils.dir2 import get_real_method
26 from ..lib import pretty
26 from ..lib import pretty
27 from traitlets import (
27 from traitlets import (
28 Bool, Dict, Integer, Unicode, CUnicode, ObjectName, List,
28 Bool, Dict, Integer, Unicode, CUnicode, ObjectName, List,
29 ForwardDeclaredInstance,
29 ForwardDeclaredInstance,
30 default, observe,
30 default, observe,
31 )
31 )
32
32
33
33
34 class DisplayFormatter(Configurable):
34 class DisplayFormatter(Configurable):
35
35
36 active_types = List(Unicode(),
36 active_types = List(Unicode(),
37 help="""List of currently active mime-types to display.
37 help="""List of currently active mime-types to display.
38 You can use this to set a white-list for formats to display.
38 You can use this to set a white-list for formats to display.
39
39
40 Most users will not need to change this value.
40 Most users will not need to change this value.
41 """).tag(config=True)
41 """).tag(config=True)
42
42
43 @default('active_types')
43 @default('active_types')
44 def _active_types_default(self):
44 def _active_types_default(self):
45 return self.format_types
45 return self.format_types
46
46
47 @observe('active_types')
47 @observe('active_types')
48 def _active_types_changed(self, change):
48 def _active_types_changed(self, change):
49 for key, formatter in self.formatters.items():
49 for key, formatter in self.formatters.items():
50 if key in change['new']:
50 if key in change['new']:
51 formatter.enabled = True
51 formatter.enabled = True
52 else:
52 else:
53 formatter.enabled = False
53 formatter.enabled = False
54
54
55 ipython_display_formatter = ForwardDeclaredInstance('FormatterABC')
55 ipython_display_formatter = ForwardDeclaredInstance('FormatterABC')
56 @default('ipython_display_formatter')
56 @default('ipython_display_formatter')
57 def _default_formatter(self):
57 def _default_formatter(self):
58 return IPythonDisplayFormatter(parent=self)
58 return IPythonDisplayFormatter(parent=self)
59
59
60 mimebundle_formatter = ForwardDeclaredInstance('FormatterABC')
60 mimebundle_formatter = ForwardDeclaredInstance('FormatterABC')
61 @default('mimebundle_formatter')
61 @default('mimebundle_formatter')
62 def _default_mime_formatter(self):
62 def _default_mime_formatter(self):
63 return MimeBundleFormatter(parent=self)
63 return MimeBundleFormatter(parent=self)
64
64
65 # A dict of formatter whose keys are format types (MIME types) and whose
65 # A dict of formatter whose keys are format types (MIME types) and whose
66 # values are subclasses of BaseFormatter.
66 # values are subclasses of BaseFormatter.
67 formatters = Dict()
67 formatters = Dict()
68 @default('formatters')
68 @default('formatters')
69 def _formatters_default(self):
69 def _formatters_default(self):
70 """Activate the default formatters."""
70 """Activate the default formatters."""
71 formatter_classes = [
71 formatter_classes = [
72 PlainTextFormatter,
72 PlainTextFormatter,
73 HTMLFormatter,
73 HTMLFormatter,
74 MarkdownFormatter,
74 MarkdownFormatter,
75 SVGFormatter,
75 SVGFormatter,
76 PNGFormatter,
76 PNGFormatter,
77 PDFFormatter,
77 PDFFormatter,
78 JPEGFormatter,
78 JPEGFormatter,
79 LatexFormatter,
79 LatexFormatter,
80 JSONFormatter,
80 JSONFormatter,
81 JavascriptFormatter
81 JavascriptFormatter
82 ]
82 ]
83 d = {}
83 d = {}
84 for cls in formatter_classes:
84 for cls in formatter_classes:
85 f = cls(parent=self)
85 f = cls(parent=self)
86 d[f.format_type] = f
86 d[f.format_type] = f
87 return d
87 return d
88
88
89 def format(self, obj, include=None, exclude=None):
89 def format(self, obj, include=None, exclude=None):
90 """Return a format data dict for an object.
90 """Return a format data dict for an object.
91
91
92 By default all format types will be computed.
92 By default all format types will be computed.
93
93
94 The following MIME types are usually implemented:
94 The following MIME types are usually implemented:
95
95
96 * text/plain
96 * text/plain
97 * text/html
97 * text/html
98 * text/markdown
98 * text/markdown
99 * text/latex
99 * text/latex
100 * application/json
100 * application/json
101 * application/javascript
101 * application/javascript
102 * application/pdf
102 * application/pdf
103 * image/png
103 * image/png
104 * image/jpeg
104 * image/jpeg
105 * image/svg+xml
105 * image/svg+xml
106
106
107 Parameters
107 Parameters
108 ----------
108 ----------
109 obj : object
109 obj : object
110 The Python object whose format data will be computed.
110 The Python object whose format data will be computed.
111 include : list, tuple or set; optional
111 include : list, tuple or set; optional
112 A list of format type strings (MIME types) to include in the
112 A list of format type strings (MIME types) to include in the
113 format data dict. If this is set *only* the format types included
113 format data dict. If this is set *only* the format types included
114 in this list will be computed.
114 in this list will be computed.
115 exclude : list, tuple or set; optional
115 exclude : list, tuple or set; optional
116 A list of format type string (MIME types) to exclude in the format
116 A list of format type string (MIME types) to exclude in the format
117 data dict. If this is set all format types will be computed,
117 data dict. If this is set all format types will be computed,
118 except for those included in this argument.
118 except for those included in this argument.
119 Mimetypes present in exclude will take precedence over the ones in include
119 Mimetypes present in exclude will take precedence over the ones in include
120
120
121 Returns
121 Returns
122 -------
122 -------
123 (format_dict, metadata_dict) : tuple of two dicts
123 (format_dict, metadata_dict) : tuple of two dicts
124 format_dict is a dictionary of key/value pairs, one of each format that was
124 format_dict is a dictionary of key/value pairs, one of each format that was
125 generated for the object. The keys are the format types, which
125 generated for the object. The keys are the format types, which
126 will usually be MIME type strings and the values and JSON'able
126 will usually be MIME type strings and the values and JSON'able
127 data structure containing the raw data for the representation in
127 data structure containing the raw data for the representation in
128 that format.
128 that format.
129
129
130 metadata_dict is a dictionary of metadata about each mime-type output.
130 metadata_dict is a dictionary of metadata about each mime-type output.
131 Its keys will be a strict subset of the keys in format_dict.
131 Its keys will be a strict subset of the keys in format_dict.
132
132
133 Notes
133 Notes
134 -----
134 -----
135 If an object implement `_repr_mimebundle_` as well as various
135 If an object implement `_repr_mimebundle_` as well as various
136 `_repr_*_`, the data returned by `_repr_mimebundle_` will take
136 `_repr_*_`, the data returned by `_repr_mimebundle_` will take
137 precedence and the corresponding `_repr_*_` for this mimetype will
137 precedence and the corresponding `_repr_*_` for this mimetype will
138 not be called.
138 not be called.
139
139
140 """
140 """
141 format_dict = {}
141 format_dict = {}
142 md_dict = {}
142 md_dict = {}
143
143
144 if self.ipython_display_formatter(obj):
144 if self.ipython_display_formatter(obj):
145 # object handled itself, don't proceed
145 # object handled itself, don't proceed
146 return {}, {}
146 return {}, {}
147
147
148 format_dict, md_dict = self.mimebundle_formatter(obj, include=include, exclude=exclude)
148 format_dict, md_dict = self.mimebundle_formatter(obj, include=include, exclude=exclude)
149
149
150 if format_dict or md_dict:
150 if format_dict or md_dict:
151 if include:
151 if include:
152 format_dict = {k:v for k,v in format_dict.items() if k in include}
152 format_dict = {k:v for k,v in format_dict.items() if k in include}
153 md_dict = {k:v for k,v in md_dict.items() if k in include}
153 md_dict = {k:v for k,v in md_dict.items() if k in include}
154 if exclude:
154 if exclude:
155 format_dict = {k:v for k,v in format_dict.items() if k not in exclude}
155 format_dict = {k:v for k,v in format_dict.items() if k not in exclude}
156 md_dict = {k:v for k,v in md_dict.items() if k not in exclude}
156 md_dict = {k:v for k,v in md_dict.items() if k not in exclude}
157
157
158 for format_type, formatter in self.formatters.items():
158 for format_type, formatter in self.formatters.items():
159 if format_type in format_dict:
159 if format_type in format_dict:
160 # already got it from mimebundle, maybe don't render again.
160 # already got it from mimebundle, maybe don't render again.
161 # exception: manually registered per-mime renderer
161 # exception: manually registered per-mime renderer
162 # check priority:
162 # check priority:
163 # 1. user-registered per-mime formatter
163 # 1. user-registered per-mime formatter
164 # 2. mime-bundle (user-registered or repr method)
164 # 2. mime-bundle (user-registered or repr method)
165 # 3. default per-mime formatter (e.g. repr method)
165 # 3. default per-mime formatter (e.g. repr method)
166 try:
166 try:
167 formatter.lookup(obj)
167 formatter.lookup(obj)
168 except KeyError:
168 except KeyError:
169 # no special formatter, use mime-bundle-provided value
169 # no special formatter, use mime-bundle-provided value
170 continue
170 continue
171 if include and format_type not in include:
171 if include and format_type not in include:
172 continue
172 continue
173 if exclude and format_type in exclude:
173 if exclude and format_type in exclude:
174 continue
174 continue
175
175
176 md = None
176 md = None
177 try:
177 try:
178 data = formatter(obj)
178 data = formatter(obj)
179 except:
179 except:
180 # FIXME: log the exception
180 # FIXME: log the exception
181 raise
181 raise
182
182
183 # formatters can return raw data or (data, metadata)
183 # formatters can return raw data or (data, metadata)
184 if isinstance(data, tuple) and len(data) == 2:
184 if isinstance(data, tuple) and len(data) == 2:
185 data, md = data
185 data, md = data
186
186
187 if data is not None:
187 if data is not None:
188 format_dict[format_type] = data
188 format_dict[format_type] = data
189 if md is not None:
189 if md is not None:
190 md_dict[format_type] = md
190 md_dict[format_type] = md
191 return format_dict, md_dict
191 return format_dict, md_dict
192
192
193 @property
193 @property
194 def format_types(self):
194 def format_types(self):
195 """Return the format types (MIME types) of the active formatters."""
195 """Return the format types (MIME types) of the active formatters."""
196 return list(self.formatters.keys())
196 return list(self.formatters.keys())
197
197
198
198
199 #-----------------------------------------------------------------------------
199 #-----------------------------------------------------------------------------
200 # Formatters for specific format types (text, html, svg, etc.)
200 # Formatters for specific format types (text, html, svg, etc.)
201 #-----------------------------------------------------------------------------
201 #-----------------------------------------------------------------------------
202
202
203
203
204 def _safe_repr(obj):
204 def _safe_repr(obj):
205 """Try to return a repr of an object
205 """Try to return a repr of an object
206
206
207 always returns a string, at least.
207 always returns a string, at least.
208 """
208 """
209 try:
209 try:
210 return repr(obj)
210 return repr(obj)
211 except Exception as e:
211 except Exception as e:
212 return "un-repr-able object (%r)" % e
212 return "un-repr-able object (%r)" % e
213
213
214
214
215 class FormatterWarning(UserWarning):
215 class FormatterWarning(UserWarning):
216 """Warning class for errors in formatters"""
216 """Warning class for errors in formatters"""
217
217
218 @decorator
218 @decorator
219 def catch_format_error(method, self, *args, **kwargs):
219 def catch_format_error(method, self, *args, **kwargs):
220 """show traceback on failed format call"""
220 """show traceback on failed format call"""
221 try:
221 try:
222 r = method(self, *args, **kwargs)
222 r = method(self, *args, **kwargs)
223 except NotImplementedError:
223 except NotImplementedError:
224 # don't warn on NotImplementedErrors
224 # don't warn on NotImplementedErrors
225 return self._check_return(None, args[0])
225 return self._check_return(None, args[0])
226 except Exception:
226 except Exception:
227 exc_info = sys.exc_info()
227 exc_info = sys.exc_info()
228 ip = get_ipython()
228 ip = get_ipython()
229 if ip is not None:
229 if ip is not None:
230 ip.showtraceback(exc_info)
230 ip.showtraceback(exc_info)
231 else:
231 else:
232 traceback.print_exception(*exc_info)
232 traceback.print_exception(*exc_info)
233 return self._check_return(None, args[0])
233 return self._check_return(None, args[0])
234 return self._check_return(r, args[0])
234 return self._check_return(r, args[0])
235
235
236
236
237 class FormatterABC(metaclass=abc.ABCMeta):
237 class FormatterABC(metaclass=abc.ABCMeta):
238 """ Abstract base class for Formatters.
238 """ Abstract base class for Formatters.
239
239
240 A formatter is a callable class that is responsible for computing the
240 A formatter is a callable class that is responsible for computing the
241 raw format data for a particular format type (MIME type). For example,
241 raw format data for a particular format type (MIME type). For example,
242 an HTML formatter would have a format type of `text/html` and would return
242 an HTML formatter would have a format type of `text/html` and would return
243 the HTML representation of the object when called.
243 the HTML representation of the object when called.
244 """
244 """
245
245
246 # The format type of the data returned, usually a MIME type.
246 # The format type of the data returned, usually a MIME type.
247 format_type = 'text/plain'
247 format_type = 'text/plain'
248
248
249 # Is the formatter enabled...
249 # Is the formatter enabled...
250 enabled = True
250 enabled = True
251
251
252 @abc.abstractmethod
252 @abc.abstractmethod
253 def __call__(self, obj):
253 def __call__(self, obj):
254 """Return a JSON'able representation of the object.
254 """Return a JSON'able representation of the object.
255
255
256 If the object cannot be formatted by this formatter,
256 If the object cannot be formatted by this formatter,
257 warn and return None.
257 warn and return None.
258 """
258 """
259 return repr(obj)
259 return repr(obj)
260
260
261
261
262 def _mod_name_key(typ):
262 def _mod_name_key(typ):
263 """Return a (__module__, __name__) tuple for a type.
263 """Return a (__module__, __name__) tuple for a type.
264
264
265 Used as key in Formatter.deferred_printers.
265 Used as key in Formatter.deferred_printers.
266 """
266 """
267 module = getattr(typ, '__module__', None)
267 module = getattr(typ, '__module__', None)
268 name = getattr(typ, '__name__', None)
268 name = getattr(typ, '__name__', None)
269 return (module, name)
269 return (module, name)
270
270
271
271
272 def _get_type(obj):
272 def _get_type(obj):
273 """Return the type of an instance (old and new-style)"""
273 """Return the type of an instance (old and new-style)"""
274 return getattr(obj, '__class__', None) or type(obj)
274 return getattr(obj, '__class__', None) or type(obj)
275
275
276
276
277 _raise_key_error = Sentinel('_raise_key_error', __name__,
277 _raise_key_error = Sentinel('_raise_key_error', __name__,
278 """
278 """
279 Special value to raise a KeyError
279 Special value to raise a KeyError
280
280
281 Raise KeyError in `BaseFormatter.pop` if passed as the default value to `pop`
281 Raise KeyError in `BaseFormatter.pop` if passed as the default value to `pop`
282 """)
282 """)
283
283
284
284
285 class BaseFormatter(Configurable):
285 class BaseFormatter(Configurable):
286 """A base formatter class that is configurable.
286 """A base formatter class that is configurable.
287
287
288 This formatter should usually be used as the base class of all formatters.
288 This formatter should usually be used as the base class of all formatters.
289 It is a traited :class:`Configurable` class and includes an extensible
289 It is a traited :class:`Configurable` class and includes an extensible
290 API for users to determine how their objects are formatted. The following
290 API for users to determine how their objects are formatted. The following
291 logic is used to find a function to format an given object.
291 logic is used to find a function to format an given object.
292
292
293 1. The object is introspected to see if it has a method with the name
293 1. The object is introspected to see if it has a method with the name
294 :attr:`print_method`. If is does, that object is passed to that method
294 :attr:`print_method`. If is does, that object is passed to that method
295 for formatting.
295 for formatting.
296 2. If no print method is found, three internal dictionaries are consulted
296 2. If no print method is found, three internal dictionaries are consulted
297 to find print method: :attr:`singleton_printers`, :attr:`type_printers`
297 to find print method: :attr:`singleton_printers`, :attr:`type_printers`
298 and :attr:`deferred_printers`.
298 and :attr:`deferred_printers`.
299
299
300 Users should use these dictionaries to register functions that will be
300 Users should use these dictionaries to register functions that will be
301 used to compute the format data for their objects (if those objects don't
301 used to compute the format data for their objects (if those objects don't
302 have the special print methods). The easiest way of using these
302 have the special print methods). The easiest way of using these
303 dictionaries is through the :meth:`for_type` and :meth:`for_type_by_name`
303 dictionaries is through the :meth:`for_type` and :meth:`for_type_by_name`
304 methods.
304 methods.
305
305
306 If no function/callable is found to compute the format data, ``None`` is
306 If no function/callable is found to compute the format data, ``None`` is
307 returned and this format type is not used.
307 returned and this format type is not used.
308 """
308 """
309
309
310 format_type = Unicode('text/plain')
310 format_type = Unicode('text/plain')
311 _return_type = str
311 _return_type = str
312
312
313 enabled = Bool(True).tag(config=True)
313 enabled = Bool(True).tag(config=True)
314
314
315 print_method = ObjectName('__repr__')
315 print_method = ObjectName('__repr__')
316
316
317 # The singleton printers.
317 # The singleton printers.
318 # Maps the IDs of the builtin singleton objects to the format functions.
318 # Maps the IDs of the builtin singleton objects to the format functions.
319 singleton_printers = Dict().tag(config=True)
319 singleton_printers = Dict().tag(config=True)
320
320
321 # The type-specific printers.
321 # The type-specific printers.
322 # Map type objects to the format functions.
322 # Map type objects to the format functions.
323 type_printers = Dict().tag(config=True)
323 type_printers = Dict().tag(config=True)
324
324
325 # The deferred-import type-specific printers.
325 # The deferred-import type-specific printers.
326 # Map (modulename, classname) pairs to the format functions.
326 # Map (modulename, classname) pairs to the format functions.
327 deferred_printers = Dict().tag(config=True)
327 deferred_printers = Dict().tag(config=True)
328
328
329 @catch_format_error
329 @catch_format_error
330 def __call__(self, obj):
330 def __call__(self, obj):
331 """Compute the format for an object."""
331 """Compute the format for an object."""
332 if self.enabled:
332 if self.enabled:
333 # lookup registered printer
333 # lookup registered printer
334 try:
334 try:
335 printer = self.lookup(obj)
335 printer = self.lookup(obj)
336 except KeyError:
336 except KeyError:
337 pass
337 pass
338 else:
338 else:
339 return printer(obj)
339 return printer(obj)
340 # Finally look for special method names
340 # Finally look for special method names
341 method = get_real_method(obj, self.print_method)
341 method = get_real_method(obj, self.print_method)
342 if method is not None:
342 if method is not None:
343 return method()
343 return method()
344 return None
344 return None
345 else:
345 else:
346 return None
346 return None
347
347
348 def __contains__(self, typ):
348 def __contains__(self, typ):
349 """map in to lookup_by_type"""
349 """map in to lookup_by_type"""
350 try:
350 try:
351 self.lookup_by_type(typ)
351 self.lookup_by_type(typ)
352 except KeyError:
352 except KeyError:
353 return False
353 return False
354 else:
354 else:
355 return True
355 return True
356
356
357 def _check_return(self, r, obj):
357 def _check_return(self, r, obj):
358 """Check that a return value is appropriate
358 """Check that a return value is appropriate
359
359
360 Return the value if so, None otherwise, warning if invalid.
360 Return the value if so, None otherwise, warning if invalid.
361 """
361 """
362 if r is None or isinstance(r, self._return_type) or \
362 if r is None or isinstance(r, self._return_type) or \
363 (isinstance(r, tuple) and r and isinstance(r[0], self._return_type)):
363 (isinstance(r, tuple) and r and isinstance(r[0], self._return_type)):
364 return r
364 return r
365 else:
365 else:
366 warnings.warn(
366 warnings.warn(
367 "%s formatter returned invalid type %s (expected %s) for object: %s" % \
367 "%s formatter returned invalid type %s (expected %s) for object: %s" % \
368 (self.format_type, type(r), self._return_type, _safe_repr(obj)),
368 (self.format_type, type(r), self._return_type, _safe_repr(obj)),
369 FormatterWarning
369 FormatterWarning
370 )
370 )
371
371
372 def lookup(self, obj):
372 def lookup(self, obj):
373 """Look up the formatter for a given instance.
373 """Look up the formatter for a given instance.
374
374
375 Parameters
375 Parameters
376 ----------
376 ----------
377 obj : object instance
377 obj : object instance
378
378
379 Returns
379 Returns
380 -------
380 -------
381 f : callable
381 f : callable
382 The registered formatting callable for the type.
382 The registered formatting callable for the type.
383
383
384 Raises
384 Raises
385 ------
385 ------
386 KeyError if the type has not been registered.
386 KeyError if the type has not been registered.
387 """
387 """
388 # look for singleton first
388 # look for singleton first
389 obj_id = id(obj)
389 obj_id = id(obj)
390 if obj_id in self.singleton_printers:
390 if obj_id in self.singleton_printers:
391 return self.singleton_printers[obj_id]
391 return self.singleton_printers[obj_id]
392 # then lookup by type
392 # then lookup by type
393 return self.lookup_by_type(_get_type(obj))
393 return self.lookup_by_type(_get_type(obj))
394
394
395 def lookup_by_type(self, typ):
395 def lookup_by_type(self, typ):
396 """Look up the registered formatter for a type.
396 """Look up the registered formatter for a type.
397
397
398 Parameters
398 Parameters
399 ----------
399 ----------
400 typ : type or '__module__.__name__' string for a type
400 typ : type or '__module__.__name__' string for a type
401
401
402 Returns
402 Returns
403 -------
403 -------
404 f : callable
404 f : callable
405 The registered formatting callable for the type.
405 The registered formatting callable for the type.
406
406
407 Raises
407 Raises
408 ------
408 ------
409 KeyError if the type has not been registered.
409 KeyError if the type has not been registered.
410 """
410 """
411 if isinstance(typ, str):
411 if isinstance(typ, str):
412 typ_key = tuple(typ.rsplit('.',1))
412 typ_key = tuple(typ.rsplit('.',1))
413 if typ_key not in self.deferred_printers:
413 if typ_key not in self.deferred_printers:
414 # We may have it cached in the type map. We will have to
414 # We may have it cached in the type map. We will have to
415 # iterate over all of the types to check.
415 # iterate over all of the types to check.
416 for cls in self.type_printers:
416 for cls in self.type_printers:
417 if _mod_name_key(cls) == typ_key:
417 if _mod_name_key(cls) == typ_key:
418 return self.type_printers[cls]
418 return self.type_printers[cls]
419 else:
419 else:
420 return self.deferred_printers[typ_key]
420 return self.deferred_printers[typ_key]
421 else:
421 else:
422 for cls in pretty._get_mro(typ):
422 for cls in pretty._get_mro(typ):
423 if cls in self.type_printers or self._in_deferred_types(cls):
423 if cls in self.type_printers or self._in_deferred_types(cls):
424 return self.type_printers[cls]
424 return self.type_printers[cls]
425
425
426 # If we have reached here, the lookup failed.
426 # If we have reached here, the lookup failed.
427 raise KeyError("No registered printer for {0!r}".format(typ))
427 raise KeyError("No registered printer for {0!r}".format(typ))
428
428
429 def for_type(self, typ, func=None):
429 def for_type(self, typ, func=None):
430 """Add a format function for a given type.
430 """Add a format function for a given type.
431
431
432 Parameters
432 Parameters
433 ----------
433 ----------
434 typ : type or '__module__.__name__' string for a type
434 typ : type or '__module__.__name__' string for a type
435 The class of the object that will be formatted using `func`.
435 The class of the object that will be formatted using `func`.
436
436 func : callable
437 func : callable
437 A callable for computing the format data.
438 A callable for computing the format data.
438 `func` will be called with the object to be formatted,
439 `func` will be called with the object to be formatted,
439 and will return the raw data in this formatter's format.
440 and will return the raw data in this formatter's format.
440 Subclasses may use a different call signature for the
441 Subclasses may use a different call signature for the
441 `func` argument.
442 `func` argument.
442
443
443 If `func` is None or not specified, there will be no change,
444 If `func` is None or not specified, there will be no change,
444 only returning the current value.
445 only returning the current value.
445
446
446 Returns
447 Returns
447 -------
448 -------
448 oldfunc : callable
449 oldfunc : callable
449 The currently registered callable.
450 The currently registered callable.
450 If you are registering a new formatter,
451 If you are registering a new formatter,
451 this will be the previous value (to enable restoring later).
452 this will be the previous value (to enable restoring later).
452 """
453 """
453 # if string given, interpret as 'pkg.module.class_name'
454 # if string given, interpret as 'pkg.module.class_name'
454 if isinstance(typ, str):
455 if isinstance(typ, str):
455 type_module, type_name = typ.rsplit('.', 1)
456 type_module, type_name = typ.rsplit('.', 1)
456 return self.for_type_by_name(type_module, type_name, func)
457 return self.for_type_by_name(type_module, type_name, func)
457
458
458 try:
459 try:
459 oldfunc = self.lookup_by_type(typ)
460 oldfunc = self.lookup_by_type(typ)
460 except KeyError:
461 except KeyError:
461 oldfunc = None
462 oldfunc = None
462
463
463 if func is not None:
464 if func is not None:
464 self.type_printers[typ] = func
465 self.type_printers[typ] = func
465
466
466 return oldfunc
467 return oldfunc
467
468
468 def for_type_by_name(self, type_module, type_name, func=None):
469 def for_type_by_name(self, type_module, type_name, func=None):
469 """Add a format function for a type specified by the full dotted
470 """Add a format function for a type specified by the full dotted
470 module and name of the type, rather than the type of the object.
471 module and name of the type, rather than the type of the object.
471
472
472 Parameters
473 Parameters
473 ----------
474 ----------
474 type_module : str
475 type_module : str
475 The full dotted name of the module the type is defined in, like
476 The full dotted name of the module the type is defined in, like
476 ``numpy``.
477 ``numpy``.
478
477 type_name : str
479 type_name : str
478 The name of the type (the class name), like ``dtype``
480 The name of the type (the class name), like ``dtype``
481
479 func : callable
482 func : callable
480 A callable for computing the format data.
483 A callable for computing the format data.
481 `func` will be called with the object to be formatted,
484 `func` will be called with the object to be formatted,
482 and will return the raw data in this formatter's format.
485 and will return the raw data in this formatter's format.
483 Subclasses may use a different call signature for the
486 Subclasses may use a different call signature for the
484 `func` argument.
487 `func` argument.
485
488
486 If `func` is None or unspecified, there will be no change,
489 If `func` is None or unspecified, there will be no change,
487 only returning the current value.
490 only returning the current value.
488
491
489 Returns
492 Returns
490 -------
493 -------
491 oldfunc : callable
494 oldfunc : callable
492 The currently registered callable.
495 The currently registered callable.
493 If you are registering a new formatter,
496 If you are registering a new formatter,
494 this will be the previous value (to enable restoring later).
497 this will be the previous value (to enable restoring later).
495 """
498 """
496 key = (type_module, type_name)
499 key = (type_module, type_name)
497
500
498 try:
501 try:
499 oldfunc = self.lookup_by_type("%s.%s" % key)
502 oldfunc = self.lookup_by_type("%s.%s" % key)
500 except KeyError:
503 except KeyError:
501 oldfunc = None
504 oldfunc = None
502
505
503 if func is not None:
506 if func is not None:
504 self.deferred_printers[key] = func
507 self.deferred_printers[key] = func
505 return oldfunc
508 return oldfunc
506
509
507 def pop(self, typ, default=_raise_key_error):
510 def pop(self, typ, default=_raise_key_error):
508 """Pop a formatter for the given type.
511 """Pop a formatter for the given type.
509
512
510 Parameters
513 Parameters
511 ----------
514 ----------
512 typ : type or '__module__.__name__' string for a type
515 typ : type or '__module__.__name__' string for a type
513 default : object
516 default : object
514 value to be returned if no formatter is registered for typ.
517 value to be returned if no formatter is registered for typ.
515
518
516 Returns
519 Returns
517 -------
520 -------
518 obj : object
521 obj : object
519 The last registered object for the type.
522 The last registered object for the type.
520
523
521 Raises
524 Raises
522 ------
525 ------
523 KeyError if the type is not registered and default is not specified.
526 KeyError if the type is not registered and default is not specified.
524 """
527 """
525
528
526 if isinstance(typ, str):
529 if isinstance(typ, str):
527 typ_key = tuple(typ.rsplit('.',1))
530 typ_key = tuple(typ.rsplit('.',1))
528 if typ_key not in self.deferred_printers:
531 if typ_key not in self.deferred_printers:
529 # We may have it cached in the type map. We will have to
532 # We may have it cached in the type map. We will have to
530 # iterate over all of the types to check.
533 # iterate over all of the types to check.
531 for cls in self.type_printers:
534 for cls in self.type_printers:
532 if _mod_name_key(cls) == typ_key:
535 if _mod_name_key(cls) == typ_key:
533 old = self.type_printers.pop(cls)
536 old = self.type_printers.pop(cls)
534 break
537 break
535 else:
538 else:
536 old = default
539 old = default
537 else:
540 else:
538 old = self.deferred_printers.pop(typ_key)
541 old = self.deferred_printers.pop(typ_key)
539 else:
542 else:
540 if typ in self.type_printers:
543 if typ in self.type_printers:
541 old = self.type_printers.pop(typ)
544 old = self.type_printers.pop(typ)
542 else:
545 else:
543 old = self.deferred_printers.pop(_mod_name_key(typ), default)
546 old = self.deferred_printers.pop(_mod_name_key(typ), default)
544 if old is _raise_key_error:
547 if old is _raise_key_error:
545 raise KeyError("No registered value for {0!r}".format(typ))
548 raise KeyError("No registered value for {0!r}".format(typ))
546 return old
549 return old
547
550
548 def _in_deferred_types(self, cls):
551 def _in_deferred_types(self, cls):
549 """
552 """
550 Check if the given class is specified in the deferred type registry.
553 Check if the given class is specified in the deferred type registry.
551
554
552 Successful matches will be moved to the regular type registry for future use.
555 Successful matches will be moved to the regular type registry for future use.
553 """
556 """
554 mod = getattr(cls, '__module__', None)
557 mod = getattr(cls, '__module__', None)
555 name = getattr(cls, '__name__', None)
558 name = getattr(cls, '__name__', None)
556 key = (mod, name)
559 key = (mod, name)
557 if key in self.deferred_printers:
560 if key in self.deferred_printers:
558 # Move the printer over to the regular registry.
561 # Move the printer over to the regular registry.
559 printer = self.deferred_printers.pop(key)
562 printer = self.deferred_printers.pop(key)
560 self.type_printers[cls] = printer
563 self.type_printers[cls] = printer
561 return True
564 return True
562 return False
565 return False
563
566
564
567
565 class PlainTextFormatter(BaseFormatter):
568 class PlainTextFormatter(BaseFormatter):
566 """The default pretty-printer.
569 """The default pretty-printer.
567
570
568 This uses :mod:`IPython.lib.pretty` to compute the format data of
571 This uses :mod:`IPython.lib.pretty` to compute the format data of
569 the object. If the object cannot be pretty printed, :func:`repr` is used.
572 the object. If the object cannot be pretty printed, :func:`repr` is used.
570 See the documentation of :mod:`IPython.lib.pretty` for details on
573 See the documentation of :mod:`IPython.lib.pretty` for details on
571 how to write pretty printers. Here is a simple example::
574 how to write pretty printers. Here is a simple example::
572
575
573 def dtype_pprinter(obj, p, cycle):
576 def dtype_pprinter(obj, p, cycle):
574 if cycle:
577 if cycle:
575 return p.text('dtype(...)')
578 return p.text('dtype(...)')
576 if hasattr(obj, 'fields'):
579 if hasattr(obj, 'fields'):
577 if obj.fields is None:
580 if obj.fields is None:
578 p.text(repr(obj))
581 p.text(repr(obj))
579 else:
582 else:
580 p.begin_group(7, 'dtype([')
583 p.begin_group(7, 'dtype([')
581 for i, field in enumerate(obj.descr):
584 for i, field in enumerate(obj.descr):
582 if i > 0:
585 if i > 0:
583 p.text(',')
586 p.text(',')
584 p.breakable()
587 p.breakable()
585 p.pretty(field)
588 p.pretty(field)
586 p.end_group(7, '])')
589 p.end_group(7, '])')
587 """
590 """
588
591
589 # The format type of data returned.
592 # The format type of data returned.
590 format_type = Unicode('text/plain')
593 format_type = Unicode('text/plain')
591
594
592 # This subclass ignores this attribute as it always need to return
595 # This subclass ignores this attribute as it always need to return
593 # something.
596 # something.
594 enabled = Bool(True).tag(config=False)
597 enabled = Bool(True).tag(config=False)
595
598
596 max_seq_length = Integer(pretty.MAX_SEQ_LENGTH,
599 max_seq_length = Integer(pretty.MAX_SEQ_LENGTH,
597 help="""Truncate large collections (lists, dicts, tuples, sets) to this size.
600 help="""Truncate large collections (lists, dicts, tuples, sets) to this size.
598
601
599 Set to 0 to disable truncation.
602 Set to 0 to disable truncation.
600 """
603 """
601 ).tag(config=True)
604 ).tag(config=True)
602
605
603 # Look for a _repr_pretty_ methods to use for pretty printing.
606 # Look for a _repr_pretty_ methods to use for pretty printing.
604 print_method = ObjectName('_repr_pretty_')
607 print_method = ObjectName('_repr_pretty_')
605
608
606 # Whether to pretty-print or not.
609 # Whether to pretty-print or not.
607 pprint = Bool(True).tag(config=True)
610 pprint = Bool(True).tag(config=True)
608
611
609 # Whether to be verbose or not.
612 # Whether to be verbose or not.
610 verbose = Bool(False).tag(config=True)
613 verbose = Bool(False).tag(config=True)
611
614
612 # The maximum width.
615 # The maximum width.
613 max_width = Integer(79).tag(config=True)
616 max_width = Integer(79).tag(config=True)
614
617
615 # The newline character.
618 # The newline character.
616 newline = Unicode('\n').tag(config=True)
619 newline = Unicode('\n').tag(config=True)
617
620
618 # format-string for pprinting floats
621 # format-string for pprinting floats
619 float_format = Unicode('%r')
622 float_format = Unicode('%r')
620 # setter for float precision, either int or direct format-string
623 # setter for float precision, either int or direct format-string
621 float_precision = CUnicode('').tag(config=True)
624 float_precision = CUnicode('').tag(config=True)
622
625
623 @observe('float_precision')
626 @observe('float_precision')
624 def _float_precision_changed(self, change):
627 def _float_precision_changed(self, change):
625 """float_precision changed, set float_format accordingly.
628 """float_precision changed, set float_format accordingly.
626
629
627 float_precision can be set by int or str.
630 float_precision can be set by int or str.
628 This will set float_format, after interpreting input.
631 This will set float_format, after interpreting input.
629 If numpy has been imported, numpy print precision will also be set.
632 If numpy has been imported, numpy print precision will also be set.
630
633
631 integer `n` sets format to '%.nf', otherwise, format set directly.
634 integer `n` sets format to '%.nf', otherwise, format set directly.
632
635
633 An empty string returns to defaults (repr for float, 8 for numpy).
636 An empty string returns to defaults (repr for float, 8 for numpy).
634
637
635 This parameter can be set via the '%precision' magic.
638 This parameter can be set via the '%precision' magic.
636 """
639 """
637 new = change['new']
640 new = change['new']
638 if '%' in new:
641 if '%' in new:
639 # got explicit format string
642 # got explicit format string
640 fmt = new
643 fmt = new
641 try:
644 try:
642 fmt%3.14159
645 fmt%3.14159
643 except Exception as e:
646 except Exception as e:
644 raise ValueError("Precision must be int or format string, not %r"%new) from e
647 raise ValueError("Precision must be int or format string, not %r"%new) from e
645 elif new:
648 elif new:
646 # otherwise, should be an int
649 # otherwise, should be an int
647 try:
650 try:
648 i = int(new)
651 i = int(new)
649 assert i >= 0
652 assert i >= 0
650 except ValueError as e:
653 except ValueError as e:
651 raise ValueError("Precision must be int or format string, not %r"%new) from e
654 raise ValueError("Precision must be int or format string, not %r"%new) from e
652 except AssertionError as e:
655 except AssertionError as e:
653 raise ValueError("int precision must be non-negative, not %r"%i) from e
656 raise ValueError("int precision must be non-negative, not %r"%i) from e
654
657
655 fmt = '%%.%if'%i
658 fmt = '%%.%if'%i
656 if 'numpy' in sys.modules:
659 if 'numpy' in sys.modules:
657 # set numpy precision if it has been imported
660 # set numpy precision if it has been imported
658 import numpy
661 import numpy
659 numpy.set_printoptions(precision=i)
662 numpy.set_printoptions(precision=i)
660 else:
663 else:
661 # default back to repr
664 # default back to repr
662 fmt = '%r'
665 fmt = '%r'
663 if 'numpy' in sys.modules:
666 if 'numpy' in sys.modules:
664 import numpy
667 import numpy
665 # numpy default is 8
668 # numpy default is 8
666 numpy.set_printoptions(precision=8)
669 numpy.set_printoptions(precision=8)
667 self.float_format = fmt
670 self.float_format = fmt
668
671
669 # Use the default pretty printers from IPython.lib.pretty.
672 # Use the default pretty printers from IPython.lib.pretty.
670 @default('singleton_printers')
673 @default('singleton_printers')
671 def _singleton_printers_default(self):
674 def _singleton_printers_default(self):
672 return pretty._singleton_pprinters.copy()
675 return pretty._singleton_pprinters.copy()
673
676
674 @default('type_printers')
677 @default('type_printers')
675 def _type_printers_default(self):
678 def _type_printers_default(self):
676 d = pretty._type_pprinters.copy()
679 d = pretty._type_pprinters.copy()
677 d[float] = lambda obj,p,cycle: p.text(self.float_format%obj)
680 d[float] = lambda obj,p,cycle: p.text(self.float_format%obj)
678 # if NumPy is used, set precision for its float64 type
681 # if NumPy is used, set precision for its float64 type
679 if "numpy" in sys.modules:
682 if "numpy" in sys.modules:
680 import numpy
683 import numpy
681
684
682 d[numpy.float64] = lambda obj, p, cycle: p.text(self.float_format % obj)
685 d[numpy.float64] = lambda obj, p, cycle: p.text(self.float_format % obj)
683 return d
686 return d
684
687
685 @default('deferred_printers')
688 @default('deferred_printers')
686 def _deferred_printers_default(self):
689 def _deferred_printers_default(self):
687 return pretty._deferred_type_pprinters.copy()
690 return pretty._deferred_type_pprinters.copy()
688
691
689 #### FormatterABC interface ####
692 #### FormatterABC interface ####
690
693
691 @catch_format_error
694 @catch_format_error
692 def __call__(self, obj):
695 def __call__(self, obj):
693 """Compute the pretty representation of the object."""
696 """Compute the pretty representation of the object."""
694 if not self.pprint:
697 if not self.pprint:
695 return repr(obj)
698 return repr(obj)
696 else:
699 else:
697 stream = StringIO()
700 stream = StringIO()
698 printer = pretty.RepresentationPrinter(stream, self.verbose,
701 printer = pretty.RepresentationPrinter(stream, self.verbose,
699 self.max_width, self.newline,
702 self.max_width, self.newline,
700 max_seq_length=self.max_seq_length,
703 max_seq_length=self.max_seq_length,
701 singleton_pprinters=self.singleton_printers,
704 singleton_pprinters=self.singleton_printers,
702 type_pprinters=self.type_printers,
705 type_pprinters=self.type_printers,
703 deferred_pprinters=self.deferred_printers)
706 deferred_pprinters=self.deferred_printers)
704 printer.pretty(obj)
707 printer.pretty(obj)
705 printer.flush()
708 printer.flush()
706 return stream.getvalue()
709 return stream.getvalue()
707
710
708
711
709 class HTMLFormatter(BaseFormatter):
712 class HTMLFormatter(BaseFormatter):
710 """An HTML formatter.
713 """An HTML formatter.
711
714
712 To define the callables that compute the HTML representation of your
715 To define the callables that compute the HTML representation of your
713 objects, define a :meth:`_repr_html_` method or use the :meth:`for_type`
716 objects, define a :meth:`_repr_html_` method or use the :meth:`for_type`
714 or :meth:`for_type_by_name` methods to register functions that handle
717 or :meth:`for_type_by_name` methods to register functions that handle
715 this.
718 this.
716
719
717 The return value of this formatter should be a valid HTML snippet that
720 The return value of this formatter should be a valid HTML snippet that
718 could be injected into an existing DOM. It should *not* include the
721 could be injected into an existing DOM. It should *not* include the
719 ```<html>`` or ```<body>`` tags.
722 ```<html>`` or ```<body>`` tags.
720 """
723 """
721 format_type = Unicode('text/html')
724 format_type = Unicode('text/html')
722
725
723 print_method = ObjectName('_repr_html_')
726 print_method = ObjectName('_repr_html_')
724
727
725
728
726 class MarkdownFormatter(BaseFormatter):
729 class MarkdownFormatter(BaseFormatter):
727 """A Markdown formatter.
730 """A Markdown formatter.
728
731
729 To define the callables that compute the Markdown representation of your
732 To define the callables that compute the Markdown representation of your
730 objects, define a :meth:`_repr_markdown_` method or use the :meth:`for_type`
733 objects, define a :meth:`_repr_markdown_` method or use the :meth:`for_type`
731 or :meth:`for_type_by_name` methods to register functions that handle
734 or :meth:`for_type_by_name` methods to register functions that handle
732 this.
735 this.
733
736
734 The return value of this formatter should be a valid Markdown.
737 The return value of this formatter should be a valid Markdown.
735 """
738 """
736 format_type = Unicode('text/markdown')
739 format_type = Unicode('text/markdown')
737
740
738 print_method = ObjectName('_repr_markdown_')
741 print_method = ObjectName('_repr_markdown_')
739
742
740 class SVGFormatter(BaseFormatter):
743 class SVGFormatter(BaseFormatter):
741 """An SVG formatter.
744 """An SVG formatter.
742
745
743 To define the callables that compute the SVG representation of your
746 To define the callables that compute the SVG representation of your
744 objects, define a :meth:`_repr_svg_` method or use the :meth:`for_type`
747 objects, define a :meth:`_repr_svg_` method or use the :meth:`for_type`
745 or :meth:`for_type_by_name` methods to register functions that handle
748 or :meth:`for_type_by_name` methods to register functions that handle
746 this.
749 this.
747
750
748 The return value of this formatter should be valid SVG enclosed in
751 The return value of this formatter should be valid SVG enclosed in
749 ```<svg>``` tags, that could be injected into an existing DOM. It should
752 ```<svg>``` tags, that could be injected into an existing DOM. It should
750 *not* include the ```<html>`` or ```<body>`` tags.
753 *not* include the ```<html>`` or ```<body>`` tags.
751 """
754 """
752 format_type = Unicode('image/svg+xml')
755 format_type = Unicode('image/svg+xml')
753
756
754 print_method = ObjectName('_repr_svg_')
757 print_method = ObjectName('_repr_svg_')
755
758
756
759
757 class PNGFormatter(BaseFormatter):
760 class PNGFormatter(BaseFormatter):
758 """A PNG formatter.
761 """A PNG formatter.
759
762
760 To define the callables that compute the PNG representation of your
763 To define the callables that compute the PNG representation of your
761 objects, define a :meth:`_repr_png_` method or use the :meth:`for_type`
764 objects, define a :meth:`_repr_png_` method or use the :meth:`for_type`
762 or :meth:`for_type_by_name` methods to register functions that handle
765 or :meth:`for_type_by_name` methods to register functions that handle
763 this.
766 this.
764
767
765 The return value of this formatter should be raw PNG data, *not*
768 The return value of this formatter should be raw PNG data, *not*
766 base64 encoded.
769 base64 encoded.
767 """
770 """
768 format_type = Unicode('image/png')
771 format_type = Unicode('image/png')
769
772
770 print_method = ObjectName('_repr_png_')
773 print_method = ObjectName('_repr_png_')
771
774
772 _return_type = (bytes, str)
775 _return_type = (bytes, str)
773
776
774
777
775 class JPEGFormatter(BaseFormatter):
778 class JPEGFormatter(BaseFormatter):
776 """A JPEG formatter.
779 """A JPEG formatter.
777
780
778 To define the callables that compute the JPEG representation of your
781 To define the callables that compute the JPEG representation of your
779 objects, define a :meth:`_repr_jpeg_` method or use the :meth:`for_type`
782 objects, define a :meth:`_repr_jpeg_` method or use the :meth:`for_type`
780 or :meth:`for_type_by_name` methods to register functions that handle
783 or :meth:`for_type_by_name` methods to register functions that handle
781 this.
784 this.
782
785
783 The return value of this formatter should be raw JPEG data, *not*
786 The return value of this formatter should be raw JPEG data, *not*
784 base64 encoded.
787 base64 encoded.
785 """
788 """
786 format_type = Unicode('image/jpeg')
789 format_type = Unicode('image/jpeg')
787
790
788 print_method = ObjectName('_repr_jpeg_')
791 print_method = ObjectName('_repr_jpeg_')
789
792
790 _return_type = (bytes, str)
793 _return_type = (bytes, str)
791
794
792
795
793 class LatexFormatter(BaseFormatter):
796 class LatexFormatter(BaseFormatter):
794 """A LaTeX formatter.
797 """A LaTeX formatter.
795
798
796 To define the callables that compute the LaTeX representation of your
799 To define the callables that compute the LaTeX representation of your
797 objects, define a :meth:`_repr_latex_` method or use the :meth:`for_type`
800 objects, define a :meth:`_repr_latex_` method or use the :meth:`for_type`
798 or :meth:`for_type_by_name` methods to register functions that handle
801 or :meth:`for_type_by_name` methods to register functions that handle
799 this.
802 this.
800
803
801 The return value of this formatter should be a valid LaTeX equation,
804 The return value of this formatter should be a valid LaTeX equation,
802 enclosed in either ```$```, ```$$``` or another LaTeX equation
805 enclosed in either ```$```, ```$$``` or another LaTeX equation
803 environment.
806 environment.
804 """
807 """
805 format_type = Unicode('text/latex')
808 format_type = Unicode('text/latex')
806
809
807 print_method = ObjectName('_repr_latex_')
810 print_method = ObjectName('_repr_latex_')
808
811
809
812
810 class JSONFormatter(BaseFormatter):
813 class JSONFormatter(BaseFormatter):
811 """A JSON string formatter.
814 """A JSON string formatter.
812
815
813 To define the callables that compute the JSONable representation of
816 To define the callables that compute the JSONable representation of
814 your objects, define a :meth:`_repr_json_` method or use the :meth:`for_type`
817 your objects, define a :meth:`_repr_json_` method or use the :meth:`for_type`
815 or :meth:`for_type_by_name` methods to register functions that handle
818 or :meth:`for_type_by_name` methods to register functions that handle
816 this.
819 this.
817
820
818 The return value of this formatter should be a JSONable list or dict.
821 The return value of this formatter should be a JSONable list or dict.
819 JSON scalars (None, number, string) are not allowed, only dict or list containers.
822 JSON scalars (None, number, string) are not allowed, only dict or list containers.
820 """
823 """
821 format_type = Unicode('application/json')
824 format_type = Unicode('application/json')
822 _return_type = (list, dict)
825 _return_type = (list, dict)
823
826
824 print_method = ObjectName('_repr_json_')
827 print_method = ObjectName('_repr_json_')
825
828
826 def _check_return(self, r, obj):
829 def _check_return(self, r, obj):
827 """Check that a return value is appropriate
830 """Check that a return value is appropriate
828
831
829 Return the value if so, None otherwise, warning if invalid.
832 Return the value if so, None otherwise, warning if invalid.
830 """
833 """
831 if r is None:
834 if r is None:
832 return
835 return
833 md = None
836 md = None
834 if isinstance(r, tuple):
837 if isinstance(r, tuple):
835 # unpack data, metadata tuple for type checking on first element
838 # unpack data, metadata tuple for type checking on first element
836 r, md = r
839 r, md = r
837
840
838 assert not isinstance(
841 assert not isinstance(
839 r, str
842 r, str
840 ), "JSON-as-string has been deprecated since IPython < 3"
843 ), "JSON-as-string has been deprecated since IPython < 3"
841
844
842 if md is not None:
845 if md is not None:
843 # put the tuple back together
846 # put the tuple back together
844 r = (r, md)
847 r = (r, md)
845 return super(JSONFormatter, self)._check_return(r, obj)
848 return super(JSONFormatter, self)._check_return(r, obj)
846
849
847
850
848 class JavascriptFormatter(BaseFormatter):
851 class JavascriptFormatter(BaseFormatter):
849 """A Javascript formatter.
852 """A Javascript formatter.
850
853
851 To define the callables that compute the Javascript representation of
854 To define the callables that compute the Javascript representation of
852 your objects, define a :meth:`_repr_javascript_` method or use the
855 your objects, define a :meth:`_repr_javascript_` method or use the
853 :meth:`for_type` or :meth:`for_type_by_name` methods to register functions
856 :meth:`for_type` or :meth:`for_type_by_name` methods to register functions
854 that handle this.
857 that handle this.
855
858
856 The return value of this formatter should be valid Javascript code and
859 The return value of this formatter should be valid Javascript code and
857 should *not* be enclosed in ```<script>``` tags.
860 should *not* be enclosed in ```<script>``` tags.
858 """
861 """
859 format_type = Unicode('application/javascript')
862 format_type = Unicode('application/javascript')
860
863
861 print_method = ObjectName('_repr_javascript_')
864 print_method = ObjectName('_repr_javascript_')
862
865
863
866
864 class PDFFormatter(BaseFormatter):
867 class PDFFormatter(BaseFormatter):
865 """A PDF formatter.
868 """A PDF formatter.
866
869
867 To define the callables that compute the PDF representation of your
870 To define the callables that compute the PDF representation of your
868 objects, define a :meth:`_repr_pdf_` method or use the :meth:`for_type`
871 objects, define a :meth:`_repr_pdf_` method or use the :meth:`for_type`
869 or :meth:`for_type_by_name` methods to register functions that handle
872 or :meth:`for_type_by_name` methods to register functions that handle
870 this.
873 this.
871
874
872 The return value of this formatter should be raw PDF data, *not*
875 The return value of this formatter should be raw PDF data, *not*
873 base64 encoded.
876 base64 encoded.
874 """
877 """
875 format_type = Unicode('application/pdf')
878 format_type = Unicode('application/pdf')
876
879
877 print_method = ObjectName('_repr_pdf_')
880 print_method = ObjectName('_repr_pdf_')
878
881
879 _return_type = (bytes, str)
882 _return_type = (bytes, str)
880
883
881 class IPythonDisplayFormatter(BaseFormatter):
884 class IPythonDisplayFormatter(BaseFormatter):
882 """An escape-hatch Formatter for objects that know how to display themselves.
885 """An escape-hatch Formatter for objects that know how to display themselves.
883
886
884 To define the callables that compute the representation of your
887 To define the callables that compute the representation of your
885 objects, define a :meth:`_ipython_display_` method or use the :meth:`for_type`
888 objects, define a :meth:`_ipython_display_` method or use the :meth:`for_type`
886 or :meth:`for_type_by_name` methods to register functions that handle
889 or :meth:`for_type_by_name` methods to register functions that handle
887 this. Unlike mime-type displays, this method should not return anything,
890 this. Unlike mime-type displays, this method should not return anything,
888 instead calling any appropriate display methods itself.
891 instead calling any appropriate display methods itself.
889
892
890 This display formatter has highest priority.
893 This display formatter has highest priority.
891 If it fires, no other display formatter will be called.
894 If it fires, no other display formatter will be called.
892
895
893 Prior to IPython 6.1, `_ipython_display_` was the only way to display custom mime-types
896 Prior to IPython 6.1, `_ipython_display_` was the only way to display custom mime-types
894 without registering a new Formatter.
897 without registering a new Formatter.
895
898
896 IPython 6.1 introduces `_repr_mimebundle_` for displaying custom mime-types,
899 IPython 6.1 introduces `_repr_mimebundle_` for displaying custom mime-types,
897 so `_ipython_display_` should only be used for objects that require unusual
900 so `_ipython_display_` should only be used for objects that require unusual
898 display patterns, such as multiple display calls.
901 display patterns, such as multiple display calls.
899 """
902 """
900 print_method = ObjectName('_ipython_display_')
903 print_method = ObjectName('_ipython_display_')
901 _return_type = (type(None), bool)
904 _return_type = (type(None), bool)
902
905
903 @catch_format_error
906 @catch_format_error
904 def __call__(self, obj):
907 def __call__(self, obj):
905 """Compute the format for an object."""
908 """Compute the format for an object."""
906 if self.enabled:
909 if self.enabled:
907 # lookup registered printer
910 # lookup registered printer
908 try:
911 try:
909 printer = self.lookup(obj)
912 printer = self.lookup(obj)
910 except KeyError:
913 except KeyError:
911 pass
914 pass
912 else:
915 else:
913 printer(obj)
916 printer(obj)
914 return True
917 return True
915 # Finally look for special method names
918 # Finally look for special method names
916 method = get_real_method(obj, self.print_method)
919 method = get_real_method(obj, self.print_method)
917 if method is not None:
920 if method is not None:
918 method()
921 method()
919 return True
922 return True
920
923
921
924
922 class MimeBundleFormatter(BaseFormatter):
925 class MimeBundleFormatter(BaseFormatter):
923 """A Formatter for arbitrary mime-types.
926 """A Formatter for arbitrary mime-types.
924
927
925 Unlike other `_repr_<mimetype>_` methods,
928 Unlike other `_repr_<mimetype>_` methods,
926 `_repr_mimebundle_` should return mime-bundle data,
929 `_repr_mimebundle_` should return mime-bundle data,
927 either the mime-keyed `data` dictionary or the tuple `(data, metadata)`.
930 either the mime-keyed `data` dictionary or the tuple `(data, metadata)`.
928 Any mime-type is valid.
931 Any mime-type is valid.
929
932
930 To define the callables that compute the mime-bundle representation of your
933 To define the callables that compute the mime-bundle representation of your
931 objects, define a :meth:`_repr_mimebundle_` method or use the :meth:`for_type`
934 objects, define a :meth:`_repr_mimebundle_` method or use the :meth:`for_type`
932 or :meth:`for_type_by_name` methods to register functions that handle
935 or :meth:`for_type_by_name` methods to register functions that handle
933 this.
936 this.
934
937
935 .. versionadded:: 6.1
938 .. versionadded:: 6.1
936 """
939 """
937 print_method = ObjectName('_repr_mimebundle_')
940 print_method = ObjectName('_repr_mimebundle_')
938 _return_type = dict
941 _return_type = dict
939
942
940 def _check_return(self, r, obj):
943 def _check_return(self, r, obj):
941 r = super(MimeBundleFormatter, self)._check_return(r, obj)
944 r = super(MimeBundleFormatter, self)._check_return(r, obj)
942 # always return (data, metadata):
945 # always return (data, metadata):
943 if r is None:
946 if r is None:
944 return {}, {}
947 return {}, {}
945 if not isinstance(r, tuple):
948 if not isinstance(r, tuple):
946 return r, {}
949 return r, {}
947 return r
950 return r
948
951
949 @catch_format_error
952 @catch_format_error
950 def __call__(self, obj, include=None, exclude=None):
953 def __call__(self, obj, include=None, exclude=None):
951 """Compute the format for an object.
954 """Compute the format for an object.
952
955
953 Identical to parent's method but we pass extra parameters to the method.
956 Identical to parent's method but we pass extra parameters to the method.
954
957
955 Unlike other _repr_*_ `_repr_mimebundle_` should allow extra kwargs, in
958 Unlike other _repr_*_ `_repr_mimebundle_` should allow extra kwargs, in
956 particular `include` and `exclude`.
959 particular `include` and `exclude`.
957 """
960 """
958 if self.enabled:
961 if self.enabled:
959 # lookup registered printer
962 # lookup registered printer
960 try:
963 try:
961 printer = self.lookup(obj)
964 printer = self.lookup(obj)
962 except KeyError:
965 except KeyError:
963 pass
966 pass
964 else:
967 else:
965 return printer(obj)
968 return printer(obj)
966 # Finally look for special method names
969 # Finally look for special method names
967 method = get_real_method(obj, self.print_method)
970 method = get_real_method(obj, self.print_method)
968
971
969 if method is not None:
972 if method is not None:
970 return method(include=include, exclude=exclude)
973 return method(include=include, exclude=exclude)
971 return None
974 return None
972 else:
975 else:
973 return None
976 return None
974
977
975
978
976 FormatterABC.register(BaseFormatter)
979 FormatterABC.register(BaseFormatter)
977 FormatterABC.register(PlainTextFormatter)
980 FormatterABC.register(PlainTextFormatter)
978 FormatterABC.register(HTMLFormatter)
981 FormatterABC.register(HTMLFormatter)
979 FormatterABC.register(MarkdownFormatter)
982 FormatterABC.register(MarkdownFormatter)
980 FormatterABC.register(SVGFormatter)
983 FormatterABC.register(SVGFormatter)
981 FormatterABC.register(PNGFormatter)
984 FormatterABC.register(PNGFormatter)
982 FormatterABC.register(PDFFormatter)
985 FormatterABC.register(PDFFormatter)
983 FormatterABC.register(JPEGFormatter)
986 FormatterABC.register(JPEGFormatter)
984 FormatterABC.register(LatexFormatter)
987 FormatterABC.register(LatexFormatter)
985 FormatterABC.register(JSONFormatter)
988 FormatterABC.register(JSONFormatter)
986 FormatterABC.register(JavascriptFormatter)
989 FormatterABC.register(JavascriptFormatter)
987 FormatterABC.register(IPythonDisplayFormatter)
990 FormatterABC.register(IPythonDisplayFormatter)
988 FormatterABC.register(MimeBundleFormatter)
991 FormatterABC.register(MimeBundleFormatter)
989
992
990
993
991 def format_display_data(obj, include=None, exclude=None):
994 def format_display_data(obj, include=None, exclude=None):
992 """Return a format data dict for an object.
995 """Return a format data dict for an object.
993
996
994 By default all format types will be computed.
997 By default all format types will be computed.
995
998
996 Parameters
999 Parameters
997 ----------
1000 ----------
998 obj : object
1001 obj : object
999 The Python object whose format data will be computed.
1002 The Python object whose format data will be computed.
1000
1003
1001 Returns
1004 Returns
1002 -------
1005 -------
1003 format_dict : dict
1006 format_dict : dict
1004 A dictionary of key/value pairs, one or each format that was
1007 A dictionary of key/value pairs, one or each format that was
1005 generated for the object. The keys are the format types, which
1008 generated for the object. The keys are the format types, which
1006 will usually be MIME type strings and the values and JSON'able
1009 will usually be MIME type strings and the values and JSON'able
1007 data structure containing the raw data for the representation in
1010 data structure containing the raw data for the representation in
1008 that format.
1011 that format.
1009 include : list or tuple, optional
1012 include : list or tuple, optional
1010 A list of format type strings (MIME types) to include in the
1013 A list of format type strings (MIME types) to include in the
1011 format data dict. If this is set *only* the format types included
1014 format data dict. If this is set *only* the format types included
1012 in this list will be computed.
1015 in this list will be computed.
1013 exclude : list or tuple, optional
1016 exclude : list or tuple, optional
1014 A list of format type string (MIME types) to exclude in the format
1017 A list of format type string (MIME types) to exclude in the format
1015 data dict. If this is set all format types will be computed,
1018 data dict. If this is set all format types will be computed,
1016 except for those included in this argument.
1019 except for those included in this argument.
1017 """
1020 """
1018 from .interactiveshell import InteractiveShell
1021 from .interactiveshell import InteractiveShell
1019
1022
1020 return InteractiveShell.instance().display_formatter.format(
1023 return InteractiveShell.instance().display_formatter.format(
1021 obj,
1024 obj,
1022 include,
1025 include,
1023 exclude
1026 exclude
1024 )
1027 )
@@ -1,24 +1,24 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Simple function to call to get the current InteractiveShell instance
2 """Simple function to call to get the current InteractiveShell instance
3 """
3 """
4
4
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 # Copyright (C) 2013 The IPython Development Team
6 # Copyright (C) 2013 The IPython Development Team
7 #
7 #
8 # Distributed under the terms of the BSD License. The full license is in
8 # Distributed under the terms of the BSD License. The full license is in
9 # the file COPYING, distributed as part of this software.
9 # the file COPYING, distributed as part of this software.
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Classes and functions
13 # Classes and functions
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16
16
17 def get_ipython():
17 def get_ipython():
18 """Get the global InteractiveShell instance.
18 """Get the global InteractiveShell instance.
19
19
20 Returns None if no InteractiveShell instance is registered.
20 Returns None if no InteractiveShell instance is registered.
21 """
21 """
22 from IPython.core.interactiveshell import InteractiveShell
22 from IPython.core.interactiveshell import InteractiveShell
23 if InteractiveShell.initialized():
23 if InteractiveShell.initialized():
24 return InteractiveShell.instance()
24 return InteractiveShell.instance()
@@ -1,913 +1,907 b''
1 """ History related magics and functionality """
1 """ History related magics and functionality """
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6
6
7 import atexit
7 import atexit
8 import datetime
8 import datetime
9 from pathlib import Path
9 from pathlib import Path
10 import re
10 import re
11 import sqlite3
11 import sqlite3
12 import threading
12 import threading
13
13
14 from traitlets.config.configurable import LoggingConfigurable
14 from traitlets.config.configurable import LoggingConfigurable
15 from decorator import decorator
15 from decorator import decorator
16 from IPython.utils.decorators import undoc
16 from IPython.utils.decorators import undoc
17 from IPython.paths import locate_profile
17 from IPython.paths import locate_profile
18 from traitlets import (
18 from traitlets import (
19 Any,
19 Any,
20 Bool,
20 Bool,
21 Dict,
21 Dict,
22 Instance,
22 Instance,
23 Integer,
23 Integer,
24 List,
24 List,
25 Unicode,
25 Unicode,
26 Union,
26 Union,
27 TraitError,
27 TraitError,
28 default,
28 default,
29 observe,
29 observe,
30 )
30 )
31
31
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33 # Classes and functions
33 # Classes and functions
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35
35
36 @undoc
36 @undoc
37 class DummyDB(object):
37 class DummyDB(object):
38 """Dummy DB that will act as a black hole for history.
38 """Dummy DB that will act as a black hole for history.
39
39
40 Only used in the absence of sqlite"""
40 Only used in the absence of sqlite"""
41 def execute(*args, **kwargs):
41 def execute(*args, **kwargs):
42 return []
42 return []
43
43
44 def commit(self, *args, **kwargs):
44 def commit(self, *args, **kwargs):
45 pass
45 pass
46
46
47 def __enter__(self, *args, **kwargs):
47 def __enter__(self, *args, **kwargs):
48 pass
48 pass
49
49
50 def __exit__(self, *args, **kwargs):
50 def __exit__(self, *args, **kwargs):
51 pass
51 pass
52
52
53
53
54 @decorator
54 @decorator
55 def only_when_enabled(f, self, *a, **kw):
55 def only_when_enabled(f, self, *a, **kw):
56 """Decorator: return an empty list in the absence of sqlite."""
56 """Decorator: return an empty list in the absence of sqlite."""
57 if not self.enabled:
57 if not self.enabled:
58 return []
58 return []
59 else:
59 else:
60 return f(self, *a, **kw)
60 return f(self, *a, **kw)
61
61
62
62
63 # use 16kB as threshold for whether a corrupt history db should be saved
63 # use 16kB as threshold for whether a corrupt history db should be saved
64 # that should be at least 100 entries or so
64 # that should be at least 100 entries or so
65 _SAVE_DB_SIZE = 16384
65 _SAVE_DB_SIZE = 16384
66
66
67 @decorator
67 @decorator
68 def catch_corrupt_db(f, self, *a, **kw):
68 def catch_corrupt_db(f, self, *a, **kw):
69 """A decorator which wraps HistoryAccessor method calls to catch errors from
69 """A decorator which wraps HistoryAccessor method calls to catch errors from
70 a corrupt SQLite database, move the old database out of the way, and create
70 a corrupt SQLite database, move the old database out of the way, and create
71 a new one.
71 a new one.
72
72
73 We avoid clobbering larger databases because this may be triggered due to filesystem issues,
73 We avoid clobbering larger databases because this may be triggered due to filesystem issues,
74 not just a corrupt file.
74 not just a corrupt file.
75 """
75 """
76 try:
76 try:
77 return f(self, *a, **kw)
77 return f(self, *a, **kw)
78 except (sqlite3.DatabaseError, sqlite3.OperationalError) as e:
78 except (sqlite3.DatabaseError, sqlite3.OperationalError) as e:
79 self._corrupt_db_counter += 1
79 self._corrupt_db_counter += 1
80 self.log.error("Failed to open SQLite history %s (%s).", self.hist_file, e)
80 self.log.error("Failed to open SQLite history %s (%s).", self.hist_file, e)
81 if self.hist_file != ':memory:':
81 if self.hist_file != ':memory:':
82 if self._corrupt_db_counter > self._corrupt_db_limit:
82 if self._corrupt_db_counter > self._corrupt_db_limit:
83 self.hist_file = ':memory:'
83 self.hist_file = ':memory:'
84 self.log.error("Failed to load history too many times, history will not be saved.")
84 self.log.error("Failed to load history too many times, history will not be saved.")
85 elif self.hist_file.is_file():
85 elif self.hist_file.is_file():
86 # move the file out of the way
86 # move the file out of the way
87 base = str(self.hist_file.parent / self.hist_file.stem)
87 base = str(self.hist_file.parent / self.hist_file.stem)
88 ext = self.hist_file.suffix
88 ext = self.hist_file.suffix
89 size = self.hist_file.stat().st_size
89 size = self.hist_file.stat().st_size
90 if size >= _SAVE_DB_SIZE:
90 if size >= _SAVE_DB_SIZE:
91 # if there's significant content, avoid clobbering
91 # if there's significant content, avoid clobbering
92 now = datetime.datetime.now().isoformat().replace(':', '.')
92 now = datetime.datetime.now().isoformat().replace(':', '.')
93 newpath = base + '-corrupt-' + now + ext
93 newpath = base + '-corrupt-' + now + ext
94 # don't clobber previous corrupt backups
94 # don't clobber previous corrupt backups
95 for i in range(100):
95 for i in range(100):
96 if not Path(newpath).exists():
96 if not Path(newpath).exists():
97 break
97 break
98 else:
98 else:
99 newpath = base + '-corrupt-' + now + (u'-%i' % i) + ext
99 newpath = base + '-corrupt-' + now + (u'-%i' % i) + ext
100 else:
100 else:
101 # not much content, possibly empty; don't worry about clobbering
101 # not much content, possibly empty; don't worry about clobbering
102 # maybe we should just delete it?
102 # maybe we should just delete it?
103 newpath = base + '-corrupt' + ext
103 newpath = base + '-corrupt' + ext
104 self.hist_file.rename(newpath)
104 self.hist_file.rename(newpath)
105 self.log.error("History file was moved to %s and a new file created.", newpath)
105 self.log.error("History file was moved to %s and a new file created.", newpath)
106 self.init_db()
106 self.init_db()
107 return []
107 return []
108 else:
108 else:
109 # Failed with :memory:, something serious is wrong
109 # Failed with :memory:, something serious is wrong
110 raise
110 raise
111
111
112
112
113 class HistoryAccessorBase(LoggingConfigurable):
113 class HistoryAccessorBase(LoggingConfigurable):
114 """An abstract class for History Accessors """
114 """An abstract class for History Accessors """
115
115
116 def get_tail(self, n=10, raw=True, output=False, include_latest=False):
116 def get_tail(self, n=10, raw=True, output=False, include_latest=False):
117 raise NotImplementedError
117 raise NotImplementedError
118
118
119 def search(self, pattern="*", raw=True, search_raw=True,
119 def search(self, pattern="*", raw=True, search_raw=True,
120 output=False, n=None, unique=False):
120 output=False, n=None, unique=False):
121 raise NotImplementedError
121 raise NotImplementedError
122
122
123 def get_range(self, session, start=1, stop=None, raw=True,output=False):
123 def get_range(self, session, start=1, stop=None, raw=True,output=False):
124 raise NotImplementedError
124 raise NotImplementedError
125
125
126 def get_range_by_str(self, rangestr, raw=True, output=False):
126 def get_range_by_str(self, rangestr, raw=True, output=False):
127 raise NotImplementedError
127 raise NotImplementedError
128
128
129
129
130 class HistoryAccessor(HistoryAccessorBase):
130 class HistoryAccessor(HistoryAccessorBase):
131 """Access the history database without adding to it.
131 """Access the history database without adding to it.
132
132
133 This is intended for use by standalone history tools. IPython shells use
133 This is intended for use by standalone history tools. IPython shells use
134 HistoryManager, below, which is a subclass of this."""
134 HistoryManager, below, which is a subclass of this."""
135
135
136 # counter for init_db retries, so we don't keep trying over and over
136 # counter for init_db retries, so we don't keep trying over and over
137 _corrupt_db_counter = 0
137 _corrupt_db_counter = 0
138 # after two failures, fallback on :memory:
138 # after two failures, fallback on :memory:
139 _corrupt_db_limit = 2
139 _corrupt_db_limit = 2
140
140
141 # String holding the path to the history file
141 # String holding the path to the history file
142 hist_file = Union(
142 hist_file = Union(
143 [Instance(Path), Unicode()],
143 [Instance(Path), Unicode()],
144 help="""Path to file to use for SQLite history database.
144 help="""Path to file to use for SQLite history database.
145
145
146 By default, IPython will put the history database in the IPython
146 By default, IPython will put the history database in the IPython
147 profile directory. If you would rather share one history among
147 profile directory. If you would rather share one history among
148 profiles, you can set this value in each, so that they are consistent.
148 profiles, you can set this value in each, so that they are consistent.
149
149
150 Due to an issue with fcntl, SQLite is known to misbehave on some NFS
150 Due to an issue with fcntl, SQLite is known to misbehave on some NFS
151 mounts. If you see IPython hanging, try setting this to something on a
151 mounts. If you see IPython hanging, try setting this to something on a
152 local disk, e.g::
152 local disk, e.g::
153
153
154 ipython --HistoryManager.hist_file=/tmp/ipython_hist.sqlite
154 ipython --HistoryManager.hist_file=/tmp/ipython_hist.sqlite
155
155
156 you can also use the specific value `:memory:` (including the colon
156 you can also use the specific value `:memory:` (including the colon
157 at both end but not the back ticks), to avoid creating an history file.
157 at both end but not the back ticks), to avoid creating an history file.
158
158
159 """,
159 """,
160 ).tag(config=True)
160 ).tag(config=True)
161
161
162 enabled = Bool(True,
162 enabled = Bool(True,
163 help="""enable the SQLite history
163 help="""enable the SQLite history
164
164
165 set enabled=False to disable the SQLite history,
165 set enabled=False to disable the SQLite history,
166 in which case there will be no stored history, no SQLite connection,
166 in which case there will be no stored history, no SQLite connection,
167 and no background saving thread. This may be necessary in some
167 and no background saving thread. This may be necessary in some
168 threaded environments where IPython is embedded.
168 threaded environments where IPython is embedded.
169 """
169 """
170 ).tag(config=True)
170 ).tag(config=True)
171
171
172 connection_options = Dict(
172 connection_options = Dict(
173 help="""Options for configuring the SQLite connection
173 help="""Options for configuring the SQLite connection
174
174
175 These options are passed as keyword args to sqlite3.connect
175 These options are passed as keyword args to sqlite3.connect
176 when establishing database connections.
176 when establishing database connections.
177 """
177 """
178 ).tag(config=True)
178 ).tag(config=True)
179
179
180 # The SQLite database
180 # The SQLite database
181 db = Any()
181 db = Any()
182 @observe('db')
182 @observe('db')
183 def _db_changed(self, change):
183 def _db_changed(self, change):
184 """validate the db, since it can be an Instance of two different types"""
184 """validate the db, since it can be an Instance of two different types"""
185 new = change['new']
185 new = change['new']
186 connection_types = (DummyDB, sqlite3.Connection)
186 connection_types = (DummyDB, sqlite3.Connection)
187 if not isinstance(new, connection_types):
187 if not isinstance(new, connection_types):
188 msg = "%s.db must be sqlite3 Connection or DummyDB, not %r" % \
188 msg = "%s.db must be sqlite3 Connection or DummyDB, not %r" % \
189 (self.__class__.__name__, new)
189 (self.__class__.__name__, new)
190 raise TraitError(msg)
190 raise TraitError(msg)
191
191
192 def __init__(self, profile="default", hist_file="", **traits):
192 def __init__(self, profile="default", hist_file="", **traits):
193 """Create a new history accessor.
193 """Create a new history accessor.
194
194
195 Parameters
195 Parameters
196 ----------
196 ----------
197 profile : str
197 profile : str
198 The name of the profile from which to open history.
198 The name of the profile from which to open history.
199 hist_file : str
199 hist_file : str
200 Path to an SQLite history database stored by IPython. If specified,
200 Path to an SQLite history database stored by IPython. If specified,
201 hist_file overrides profile.
201 hist_file overrides profile.
202 config : :class:`~traitlets.config.loader.Config`
202 config : :class:`~traitlets.config.loader.Config`
203 Config object. hist_file can also be set through this.
203 Config object. hist_file can also be set through this.
204 """
204 """
205 # We need a pointer back to the shell for various tasks.
205 # We need a pointer back to the shell for various tasks.
206 super(HistoryAccessor, self).__init__(**traits)
206 super(HistoryAccessor, self).__init__(**traits)
207 # defer setting hist_file from kwarg until after init,
207 # defer setting hist_file from kwarg until after init,
208 # otherwise the default kwarg value would clobber any value
208 # otherwise the default kwarg value would clobber any value
209 # set by config
209 # set by config
210 if hist_file:
210 if hist_file:
211 self.hist_file = hist_file
211 self.hist_file = hist_file
212
212
213 try:
213 try:
214 self.hist_file
214 self.hist_file
215 except TraitError:
215 except TraitError:
216 # No one has set the hist_file, yet.
216 # No one has set the hist_file, yet.
217 self.hist_file = self._get_hist_file_name(profile)
217 self.hist_file = self._get_hist_file_name(profile)
218
218
219 self.init_db()
219 self.init_db()
220
220
221 def _get_hist_file_name(self, profile='default'):
221 def _get_hist_file_name(self, profile='default'):
222 """Find the history file for the given profile name.
222 """Find the history file for the given profile name.
223
223
224 This is overridden by the HistoryManager subclass, to use the shell's
224 This is overridden by the HistoryManager subclass, to use the shell's
225 active profile.
225 active profile.
226
226
227 Parameters
227 Parameters
228 ----------
228 ----------
229 profile : str
229 profile : str
230 The name of a profile which has a history file.
230 The name of a profile which has a history file.
231 """
231 """
232 return Path(locate_profile(profile)) / "history.sqlite"
232 return Path(locate_profile(profile)) / "history.sqlite"
233
233
234 @catch_corrupt_db
234 @catch_corrupt_db
235 def init_db(self):
235 def init_db(self):
236 """Connect to the database, and create tables if necessary."""
236 """Connect to the database, and create tables if necessary."""
237 if not self.enabled:
237 if not self.enabled:
238 self.db = DummyDB()
238 self.db = DummyDB()
239 return
239 return
240
240
241 # use detect_types so that timestamps return datetime objects
241 # use detect_types so that timestamps return datetime objects
242 kwargs = dict(detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)
242 kwargs = dict(detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)
243 kwargs.update(self.connection_options)
243 kwargs.update(self.connection_options)
244 self.db = sqlite3.connect(str(self.hist_file), **kwargs)
244 self.db = sqlite3.connect(str(self.hist_file), **kwargs)
245 self.db.execute("""CREATE TABLE IF NOT EXISTS sessions (session integer
245 self.db.execute("""CREATE TABLE IF NOT EXISTS sessions (session integer
246 primary key autoincrement, start timestamp,
246 primary key autoincrement, start timestamp,
247 end timestamp, num_cmds integer, remark text)""")
247 end timestamp, num_cmds integer, remark text)""")
248 self.db.execute("""CREATE TABLE IF NOT EXISTS history
248 self.db.execute("""CREATE TABLE IF NOT EXISTS history
249 (session integer, line integer, source text, source_raw text,
249 (session integer, line integer, source text, source_raw text,
250 PRIMARY KEY (session, line))""")
250 PRIMARY KEY (session, line))""")
251 # Output history is optional, but ensure the table's there so it can be
251 # Output history is optional, but ensure the table's there so it can be
252 # enabled later.
252 # enabled later.
253 self.db.execute("""CREATE TABLE IF NOT EXISTS output_history
253 self.db.execute("""CREATE TABLE IF NOT EXISTS output_history
254 (session integer, line integer, output text,
254 (session integer, line integer, output text,
255 PRIMARY KEY (session, line))""")
255 PRIMARY KEY (session, line))""")
256 self.db.commit()
256 self.db.commit()
257 # success! reset corrupt db count
257 # success! reset corrupt db count
258 self._corrupt_db_counter = 0
258 self._corrupt_db_counter = 0
259
259
260 def writeout_cache(self):
260 def writeout_cache(self):
261 """Overridden by HistoryManager to dump the cache before certain
261 """Overridden by HistoryManager to dump the cache before certain
262 database lookups."""
262 database lookups."""
263 pass
263 pass
264
264
265 ## -------------------------------
265 ## -------------------------------
266 ## Methods for retrieving history:
266 ## Methods for retrieving history:
267 ## -------------------------------
267 ## -------------------------------
268 def _run_sql(self, sql, params, raw=True, output=False, latest=False):
268 def _run_sql(self, sql, params, raw=True, output=False, latest=False):
269 """Prepares and runs an SQL query for the history database.
269 """Prepares and runs an SQL query for the history database.
270
270
271 Parameters
271 Parameters
272 ----------
272 ----------
273 sql : str
273 sql : str
274 Any filtering expressions to go after SELECT ... FROM ...
274 Any filtering expressions to go after SELECT ... FROM ...
275 params : tuple
275 params : tuple
276 Parameters passed to the SQL query (to replace "?")
276 Parameters passed to the SQL query (to replace "?")
277 raw, output : bool
277 raw, output : bool
278 See :meth:`get_range`
278 See :meth:`get_range`
279 latest : bool
279 latest : bool
280 Select rows with max (session, line)
280 Select rows with max (session, line)
281
281
282 Returns
282 Returns
283 -------
283 -------
284 Tuples as :meth:`get_range`
284 Tuples as :meth:`get_range`
285 """
285 """
286 toget = 'source_raw' if raw else 'source'
286 toget = 'source_raw' if raw else 'source'
287 sqlfrom = "history"
287 sqlfrom = "history"
288 if output:
288 if output:
289 sqlfrom = "history LEFT JOIN output_history USING (session, line)"
289 sqlfrom = "history LEFT JOIN output_history USING (session, line)"
290 toget = "history.%s, output_history.output" % toget
290 toget = "history.%s, output_history.output" % toget
291 if latest:
291 if latest:
292 toget += ", MAX(session * 128 * 1024 + line)"
292 toget += ", MAX(session * 128 * 1024 + line)"
293 cur = self.db.execute("SELECT session, line, %s FROM %s " %\
293 cur = self.db.execute("SELECT session, line, %s FROM %s " %\
294 (toget, sqlfrom) + sql, params)
294 (toget, sqlfrom) + sql, params)
295 if latest:
295 if latest:
296 cur = (row[:-1] for row in cur)
296 cur = (row[:-1] for row in cur)
297 if output: # Regroup into 3-tuples, and parse JSON
297 if output: # Regroup into 3-tuples, and parse JSON
298 return ((ses, lin, (inp, out)) for ses, lin, inp, out in cur)
298 return ((ses, lin, (inp, out)) for ses, lin, inp, out in cur)
299 return cur
299 return cur
300
300
301 @only_when_enabled
301 @only_when_enabled
302 @catch_corrupt_db
302 @catch_corrupt_db
303 def get_session_info(self, session):
303 def get_session_info(self, session):
304 """Get info about a session.
304 """Get info about a session.
305
305
306 Parameters
306 Parameters
307 ----------
307 ----------
308
309 session : int
308 session : int
310 Session number to retrieve.
309 Session number to retrieve.
311
310
312 Returns
311 Returns
313 -------
312 -------
314
315 session_id : int
313 session_id : int
316 Session ID number
314 Session ID number
317 start : datetime
315 start : datetime
318 Timestamp for the start of the session.
316 Timestamp for the start of the session.
319 end : datetime
317 end : datetime
320 Timestamp for the end of the session, or None if IPython crashed.
318 Timestamp for the end of the session, or None if IPython crashed.
321 num_cmds : int
319 num_cmds : int
322 Number of commands run, or None if IPython crashed.
320 Number of commands run, or None if IPython crashed.
323 remark : unicode
321 remark : unicode
324 A manually set description.
322 A manually set description.
325 """
323 """
326 query = "SELECT * from sessions where session == ?"
324 query = "SELECT * from sessions where session == ?"
327 return self.db.execute(query, (session,)).fetchone()
325 return self.db.execute(query, (session,)).fetchone()
328
326
329 @catch_corrupt_db
327 @catch_corrupt_db
330 def get_last_session_id(self):
328 def get_last_session_id(self):
331 """Get the last session ID currently in the database.
329 """Get the last session ID currently in the database.
332
330
333 Within IPython, this should be the same as the value stored in
331 Within IPython, this should be the same as the value stored in
334 :attr:`HistoryManager.session_number`.
332 :attr:`HistoryManager.session_number`.
335 """
333 """
336 for record in self.get_tail(n=1, include_latest=True):
334 for record in self.get_tail(n=1, include_latest=True):
337 return record[0]
335 return record[0]
338
336
339 @catch_corrupt_db
337 @catch_corrupt_db
340 def get_tail(self, n=10, raw=True, output=False, include_latest=False):
338 def get_tail(self, n=10, raw=True, output=False, include_latest=False):
341 """Get the last n lines from the history database.
339 """Get the last n lines from the history database.
342
340
343 Parameters
341 Parameters
344 ----------
342 ----------
345 n : int
343 n : int
346 The number of lines to get
344 The number of lines to get
347 raw, output : bool
345 raw, output : bool
348 See :meth:`get_range`
346 See :meth:`get_range`
349 include_latest : bool
347 include_latest : bool
350 If False (default), n+1 lines are fetched, and the latest one
348 If False (default), n+1 lines are fetched, and the latest one
351 is discarded. This is intended to be used where the function
349 is discarded. This is intended to be used where the function
352 is called by a user command, which it should not return.
350 is called by a user command, which it should not return.
353
351
354 Returns
352 Returns
355 -------
353 -------
356 Tuples as :meth:`get_range`
354 Tuples as :meth:`get_range`
357 """
355 """
358 self.writeout_cache()
356 self.writeout_cache()
359 if not include_latest:
357 if not include_latest:
360 n += 1
358 n += 1
361 cur = self._run_sql("ORDER BY session DESC, line DESC LIMIT ?",
359 cur = self._run_sql("ORDER BY session DESC, line DESC LIMIT ?",
362 (n,), raw=raw, output=output)
360 (n,), raw=raw, output=output)
363 if not include_latest:
361 if not include_latest:
364 return reversed(list(cur)[1:])
362 return reversed(list(cur)[1:])
365 return reversed(list(cur))
363 return reversed(list(cur))
366
364
367 @catch_corrupt_db
365 @catch_corrupt_db
368 def search(self, pattern="*", raw=True, search_raw=True,
366 def search(self, pattern="*", raw=True, search_raw=True,
369 output=False, n=None, unique=False):
367 output=False, n=None, unique=False):
370 """Search the database using unix glob-style matching (wildcards
368 """Search the database using unix glob-style matching (wildcards
371 * and ?).
369 * and ?).
372
370
373 Parameters
371 Parameters
374 ----------
372 ----------
375 pattern : str
373 pattern : str
376 The wildcarded pattern to match when searching
374 The wildcarded pattern to match when searching
377 search_raw : bool
375 search_raw : bool
378 If True, search the raw input, otherwise, the parsed input
376 If True, search the raw input, otherwise, the parsed input
379 raw, output : bool
377 raw, output : bool
380 See :meth:`get_range`
378 See :meth:`get_range`
381 n : None or int
379 n : None or int
382 If an integer is given, it defines the limit of
380 If an integer is given, it defines the limit of
383 returned entries.
381 returned entries.
384 unique : bool
382 unique : bool
385 When it is true, return only unique entries.
383 When it is true, return only unique entries.
386
384
387 Returns
385 Returns
388 -------
386 -------
389 Tuples as :meth:`get_range`
387 Tuples as :meth:`get_range`
390 """
388 """
391 tosearch = "source_raw" if search_raw else "source"
389 tosearch = "source_raw" if search_raw else "source"
392 if output:
390 if output:
393 tosearch = "history." + tosearch
391 tosearch = "history." + tosearch
394 self.writeout_cache()
392 self.writeout_cache()
395 sqlform = "WHERE %s GLOB ?" % tosearch
393 sqlform = "WHERE %s GLOB ?" % tosearch
396 params = (pattern,)
394 params = (pattern,)
397 if unique:
395 if unique:
398 sqlform += ' GROUP BY {0}'.format(tosearch)
396 sqlform += ' GROUP BY {0}'.format(tosearch)
399 if n is not None:
397 if n is not None:
400 sqlform += " ORDER BY session DESC, line DESC LIMIT ?"
398 sqlform += " ORDER BY session DESC, line DESC LIMIT ?"
401 params += (n,)
399 params += (n,)
402 elif unique:
400 elif unique:
403 sqlform += " ORDER BY session, line"
401 sqlform += " ORDER BY session, line"
404 cur = self._run_sql(sqlform, params, raw=raw, output=output, latest=unique)
402 cur = self._run_sql(sqlform, params, raw=raw, output=output, latest=unique)
405 if n is not None:
403 if n is not None:
406 return reversed(list(cur))
404 return reversed(list(cur))
407 return cur
405 return cur
408
406
409 @catch_corrupt_db
407 @catch_corrupt_db
410 def get_range(self, session, start=1, stop=None, raw=True,output=False):
408 def get_range(self, session, start=1, stop=None, raw=True,output=False):
411 """Retrieve input by session.
409 """Retrieve input by session.
412
410
413 Parameters
411 Parameters
414 ----------
412 ----------
415 session : int
413 session : int
416 Session number to retrieve.
414 Session number to retrieve.
417 start : int
415 start : int
418 First line to retrieve.
416 First line to retrieve.
419 stop : int
417 stop : int
420 End of line range (excluded from output itself). If None, retrieve
418 End of line range (excluded from output itself). If None, retrieve
421 to the end of the session.
419 to the end of the session.
422 raw : bool
420 raw : bool
423 If True, return untranslated input
421 If True, return untranslated input
424 output : bool
422 output : bool
425 If True, attempt to include output. This will be 'real' Python
423 If True, attempt to include output. This will be 'real' Python
426 objects for the current session, or text reprs from previous
424 objects for the current session, or text reprs from previous
427 sessions if db_log_output was enabled at the time. Where no output
425 sessions if db_log_output was enabled at the time. Where no output
428 is found, None is used.
426 is found, None is used.
429
427
430 Returns
428 Returns
431 -------
429 -------
432 entries
430 entries
433 An iterator over the desired lines. Each line is a 3-tuple, either
431 An iterator over the desired lines. Each line is a 3-tuple, either
434 (session, line, input) if output is False, or
432 (session, line, input) if output is False, or
435 (session, line, (input, output)) if output is True.
433 (session, line, (input, output)) if output is True.
436 """
434 """
437 if stop:
435 if stop:
438 lineclause = "line >= ? AND line < ?"
436 lineclause = "line >= ? AND line < ?"
439 params = (session, start, stop)
437 params = (session, start, stop)
440 else:
438 else:
441 lineclause = "line>=?"
439 lineclause = "line>=?"
442 params = (session, start)
440 params = (session, start)
443
441
444 return self._run_sql("WHERE session==? AND %s" % lineclause,
442 return self._run_sql("WHERE session==? AND %s" % lineclause,
445 params, raw=raw, output=output)
443 params, raw=raw, output=output)
446
444
447 def get_range_by_str(self, rangestr, raw=True, output=False):
445 def get_range_by_str(self, rangestr, raw=True, output=False):
448 """Get lines of history from a string of ranges, as used by magic
446 """Get lines of history from a string of ranges, as used by magic
449 commands %hist, %save, %macro, etc.
447 commands %hist, %save, %macro, etc.
450
448
451 Parameters
449 Parameters
452 ----------
450 ----------
453 rangestr : str
451 rangestr : str
454 A string specifying ranges, e.g. "5 ~2/1-4". If empty string is used,
452 A string specifying ranges, e.g. "5 ~2/1-4". If empty string is used,
455 this will return everything from current session's history.
453 this will return everything from current session's history.
456
454
457 See the documentation of :func:`%history` for the full details.
455 See the documentation of :func:`%history` for the full details.
458
456
459 raw, output : bool
457 raw, output : bool
460 As :meth:`get_range`
458 As :meth:`get_range`
461
459
462 Returns
460 Returns
463 -------
461 -------
464 Tuples as :meth:`get_range`
462 Tuples as :meth:`get_range`
465 """
463 """
466 for sess, s, e in extract_hist_ranges(rangestr):
464 for sess, s, e in extract_hist_ranges(rangestr):
467 for line in self.get_range(sess, s, e, raw=raw, output=output):
465 for line in self.get_range(sess, s, e, raw=raw, output=output):
468 yield line
466 yield line
469
467
470
468
471 class HistoryManager(HistoryAccessor):
469 class HistoryManager(HistoryAccessor):
472 """A class to organize all history-related functionality in one place.
470 """A class to organize all history-related functionality in one place.
473 """
471 """
474 # Public interface
472 # Public interface
475
473
476 # An instance of the IPython shell we are attached to
474 # An instance of the IPython shell we are attached to
477 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC',
475 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC',
478 allow_none=True)
476 allow_none=True)
479 # Lists to hold processed and raw history. These start with a blank entry
477 # Lists to hold processed and raw history. These start with a blank entry
480 # so that we can index them starting from 1
478 # so that we can index them starting from 1
481 input_hist_parsed = List([""])
479 input_hist_parsed = List([""])
482 input_hist_raw = List([""])
480 input_hist_raw = List([""])
483 # A list of directories visited during session
481 # A list of directories visited during session
484 dir_hist = List()
482 dir_hist = List()
485 @default('dir_hist')
483 @default('dir_hist')
486 def _dir_hist_default(self):
484 def _dir_hist_default(self):
487 try:
485 try:
488 return [Path.cwd()]
486 return [Path.cwd()]
489 except OSError:
487 except OSError:
490 return []
488 return []
491
489
492 # A dict of output history, keyed with ints from the shell's
490 # A dict of output history, keyed with ints from the shell's
493 # execution count.
491 # execution count.
494 output_hist = Dict()
492 output_hist = Dict()
495 # The text/plain repr of outputs.
493 # The text/plain repr of outputs.
496 output_hist_reprs = Dict()
494 output_hist_reprs = Dict()
497
495
498 # The number of the current session in the history database
496 # The number of the current session in the history database
499 session_number = Integer()
497 session_number = Integer()
500
498
501 db_log_output = Bool(False,
499 db_log_output = Bool(False,
502 help="Should the history database include output? (default: no)"
500 help="Should the history database include output? (default: no)"
503 ).tag(config=True)
501 ).tag(config=True)
504 db_cache_size = Integer(0,
502 db_cache_size = Integer(0,
505 help="Write to database every x commands (higher values save disk access & power).\n"
503 help="Write to database every x commands (higher values save disk access & power).\n"
506 "Values of 1 or less effectively disable caching."
504 "Values of 1 or less effectively disable caching."
507 ).tag(config=True)
505 ).tag(config=True)
508 # The input and output caches
506 # The input and output caches
509 db_input_cache = List()
507 db_input_cache = List()
510 db_output_cache = List()
508 db_output_cache = List()
511
509
512 # History saving in separate thread
510 # History saving in separate thread
513 save_thread = Instance('IPython.core.history.HistorySavingThread',
511 save_thread = Instance('IPython.core.history.HistorySavingThread',
514 allow_none=True)
512 allow_none=True)
515 save_flag = Instance(threading.Event, allow_none=True)
513 save_flag = Instance(threading.Event, allow_none=True)
516
514
517 # Private interface
515 # Private interface
518 # Variables used to store the three last inputs from the user. On each new
516 # Variables used to store the three last inputs from the user. On each new
519 # history update, we populate the user's namespace with these, shifted as
517 # history update, we populate the user's namespace with these, shifted as
520 # necessary.
518 # necessary.
521 _i00 = Unicode(u'')
519 _i00 = Unicode(u'')
522 _i = Unicode(u'')
520 _i = Unicode(u'')
523 _ii = Unicode(u'')
521 _ii = Unicode(u'')
524 _iii = Unicode(u'')
522 _iii = Unicode(u'')
525
523
526 # A regex matching all forms of the exit command, so that we don't store
524 # A regex matching all forms of the exit command, so that we don't store
527 # them in the history (it's annoying to rewind the first entry and land on
525 # them in the history (it's annoying to rewind the first entry and land on
528 # an exit call).
526 # an exit call).
529 _exit_re = re.compile(r"(exit|quit)(\s*\(.*\))?$")
527 _exit_re = re.compile(r"(exit|quit)(\s*\(.*\))?$")
530
528
531 def __init__(self, shell=None, config=None, **traits):
529 def __init__(self, shell=None, config=None, **traits):
532 """Create a new history manager associated with a shell instance.
530 """Create a new history manager associated with a shell instance.
533 """
531 """
534 # We need a pointer back to the shell for various tasks.
532 # We need a pointer back to the shell for various tasks.
535 super(HistoryManager, self).__init__(shell=shell, config=config,
533 super(HistoryManager, self).__init__(shell=shell, config=config,
536 **traits)
534 **traits)
537 self.save_flag = threading.Event()
535 self.save_flag = threading.Event()
538 self.db_input_cache_lock = threading.Lock()
536 self.db_input_cache_lock = threading.Lock()
539 self.db_output_cache_lock = threading.Lock()
537 self.db_output_cache_lock = threading.Lock()
540
538
541 try:
539 try:
542 self.new_session()
540 self.new_session()
543 except sqlite3.OperationalError:
541 except sqlite3.OperationalError:
544 self.log.error("Failed to create history session in %s. History will not be saved.",
542 self.log.error("Failed to create history session in %s. History will not be saved.",
545 self.hist_file, exc_info=True)
543 self.hist_file, exc_info=True)
546 self.hist_file = ':memory:'
544 self.hist_file = ':memory:'
547
545
548 if self.enabled and self.hist_file != ':memory:':
546 if self.enabled and self.hist_file != ':memory:':
549 self.save_thread = HistorySavingThread(self)
547 self.save_thread = HistorySavingThread(self)
550 self.save_thread.start()
548 self.save_thread.start()
551
549
552 def _get_hist_file_name(self, profile=None):
550 def _get_hist_file_name(self, profile=None):
553 """Get default history file name based on the Shell's profile.
551 """Get default history file name based on the Shell's profile.
554
552
555 The profile parameter is ignored, but must exist for compatibility with
553 The profile parameter is ignored, but must exist for compatibility with
556 the parent class."""
554 the parent class."""
557 profile_dir = self.shell.profile_dir.location
555 profile_dir = self.shell.profile_dir.location
558 return Path(profile_dir) / "history.sqlite"
556 return Path(profile_dir) / "history.sqlite"
559
557
560 @only_when_enabled
558 @only_when_enabled
561 def new_session(self, conn=None):
559 def new_session(self, conn=None):
562 """Get a new session number."""
560 """Get a new session number."""
563 if conn is None:
561 if conn is None:
564 conn = self.db
562 conn = self.db
565
563
566 with conn:
564 with conn:
567 cur = conn.execute("""INSERT INTO sessions VALUES (NULL, ?, NULL,
565 cur = conn.execute("""INSERT INTO sessions VALUES (NULL, ?, NULL,
568 NULL, "") """, (datetime.datetime.now(),))
566 NULL, "") """, (datetime.datetime.now(),))
569 self.session_number = cur.lastrowid
567 self.session_number = cur.lastrowid
570
568
571 def end_session(self):
569 def end_session(self):
572 """Close the database session, filling in the end time and line count."""
570 """Close the database session, filling in the end time and line count."""
573 self.writeout_cache()
571 self.writeout_cache()
574 with self.db:
572 with self.db:
575 self.db.execute("""UPDATE sessions SET end=?, num_cmds=? WHERE
573 self.db.execute("""UPDATE sessions SET end=?, num_cmds=? WHERE
576 session==?""", (datetime.datetime.now(),
574 session==?""", (datetime.datetime.now(),
577 len(self.input_hist_parsed)-1, self.session_number))
575 len(self.input_hist_parsed)-1, self.session_number))
578 self.session_number = 0
576 self.session_number = 0
579
577
580 def name_session(self, name):
578 def name_session(self, name):
581 """Give the current session a name in the history database."""
579 """Give the current session a name in the history database."""
582 with self.db:
580 with self.db:
583 self.db.execute("UPDATE sessions SET remark=? WHERE session==?",
581 self.db.execute("UPDATE sessions SET remark=? WHERE session==?",
584 (name, self.session_number))
582 (name, self.session_number))
585
583
586 def reset(self, new_session=True):
584 def reset(self, new_session=True):
587 """Clear the session history, releasing all object references, and
585 """Clear the session history, releasing all object references, and
588 optionally open a new session."""
586 optionally open a new session."""
589 self.output_hist.clear()
587 self.output_hist.clear()
590 # The directory history can't be completely empty
588 # The directory history can't be completely empty
591 self.dir_hist[:] = [Path.cwd()]
589 self.dir_hist[:] = [Path.cwd()]
592
590
593 if new_session:
591 if new_session:
594 if self.session_number:
592 if self.session_number:
595 self.end_session()
593 self.end_session()
596 self.input_hist_parsed[:] = [""]
594 self.input_hist_parsed[:] = [""]
597 self.input_hist_raw[:] = [""]
595 self.input_hist_raw[:] = [""]
598 self.new_session()
596 self.new_session()
599
597
600 # ------------------------------
598 # ------------------------------
601 # Methods for retrieving history
599 # Methods for retrieving history
602 # ------------------------------
600 # ------------------------------
603 def get_session_info(self, session=0):
601 def get_session_info(self, session=0):
604 """Get info about a session.
602 """Get info about a session.
605
603
606 Parameters
604 Parameters
607 ----------
605 ----------
608
609 session : int
606 session : int
610 Session number to retrieve. The current session is 0, and negative
607 Session number to retrieve. The current session is 0, and negative
611 numbers count back from current session, so -1 is the previous session.
608 numbers count back from current session, so -1 is the previous session.
612
609
613 Returns
610 Returns
614 -------
611 -------
615
616 session_id : int
612 session_id : int
617 Session ID number
613 Session ID number
618 start : datetime
614 start : datetime
619 Timestamp for the start of the session.
615 Timestamp for the start of the session.
620 end : datetime
616 end : datetime
621 Timestamp for the end of the session, or None if IPython crashed.
617 Timestamp for the end of the session, or None if IPython crashed.
622 num_cmds : int
618 num_cmds : int
623 Number of commands run, or None if IPython crashed.
619 Number of commands run, or None if IPython crashed.
624 remark : unicode
620 remark : unicode
625 A manually set description.
621 A manually set description.
626 """
622 """
627 if session <= 0:
623 if session <= 0:
628 session += self.session_number
624 session += self.session_number
629
625
630 return super(HistoryManager, self).get_session_info(session=session)
626 return super(HistoryManager, self).get_session_info(session=session)
631
627
632 def _get_range_session(self, start=1, stop=None, raw=True, output=False):
628 def _get_range_session(self, start=1, stop=None, raw=True, output=False):
633 """Get input and output history from the current session. Called by
629 """Get input and output history from the current session. Called by
634 get_range, and takes similar parameters."""
630 get_range, and takes similar parameters."""
635 input_hist = self.input_hist_raw if raw else self.input_hist_parsed
631 input_hist = self.input_hist_raw if raw else self.input_hist_parsed
636
632
637 n = len(input_hist)
633 n = len(input_hist)
638 if start < 0:
634 if start < 0:
639 start += n
635 start += n
640 if not stop or (stop > n):
636 if not stop or (stop > n):
641 stop = n
637 stop = n
642 elif stop < 0:
638 elif stop < 0:
643 stop += n
639 stop += n
644
640
645 for i in range(start, stop):
641 for i in range(start, stop):
646 if output:
642 if output:
647 line = (input_hist[i], self.output_hist_reprs.get(i))
643 line = (input_hist[i], self.output_hist_reprs.get(i))
648 else:
644 else:
649 line = input_hist[i]
645 line = input_hist[i]
650 yield (0, i, line)
646 yield (0, i, line)
651
647
652 def get_range(self, session=0, start=1, stop=None, raw=True,output=False):
648 def get_range(self, session=0, start=1, stop=None, raw=True,output=False):
653 """Retrieve input by session.
649 """Retrieve input by session.
654
650
655 Parameters
651 Parameters
656 ----------
652 ----------
657 session : int
653 session : int
658 Session number to retrieve. The current session is 0, and negative
654 Session number to retrieve. The current session is 0, and negative
659 numbers count back from current session, so -1 is previous session.
655 numbers count back from current session, so -1 is previous session.
660 start : int
656 start : int
661 First line to retrieve.
657 First line to retrieve.
662 stop : int
658 stop : int
663 End of line range (excluded from output itself). If None, retrieve
659 End of line range (excluded from output itself). If None, retrieve
664 to the end of the session.
660 to the end of the session.
665 raw : bool
661 raw : bool
666 If True, return untranslated input
662 If True, return untranslated input
667 output : bool
663 output : bool
668 If True, attempt to include output. This will be 'real' Python
664 If True, attempt to include output. This will be 'real' Python
669 objects for the current session, or text reprs from previous
665 objects for the current session, or text reprs from previous
670 sessions if db_log_output was enabled at the time. Where no output
666 sessions if db_log_output was enabled at the time. Where no output
671 is found, None is used.
667 is found, None is used.
672
668
673 Returns
669 Returns
674 -------
670 -------
675 entries
671 entries
676 An iterator over the desired lines. Each line is a 3-tuple, either
672 An iterator over the desired lines. Each line is a 3-tuple, either
677 (session, line, input) if output is False, or
673 (session, line, input) if output is False, or
678 (session, line, (input, output)) if output is True.
674 (session, line, (input, output)) if output is True.
679 """
675 """
680 if session <= 0:
676 if session <= 0:
681 session += self.session_number
677 session += self.session_number
682 if session==self.session_number: # Current session
678 if session==self.session_number: # Current session
683 return self._get_range_session(start, stop, raw, output)
679 return self._get_range_session(start, stop, raw, output)
684 return super(HistoryManager, self).get_range(session, start, stop, raw,
680 return super(HistoryManager, self).get_range(session, start, stop, raw,
685 output)
681 output)
686
682
687 ## ----------------------------
683 ## ----------------------------
688 ## Methods for storing history:
684 ## Methods for storing history:
689 ## ----------------------------
685 ## ----------------------------
690 def store_inputs(self, line_num, source, source_raw=None):
686 def store_inputs(self, line_num, source, source_raw=None):
691 """Store source and raw input in history and create input cache
687 """Store source and raw input in history and create input cache
692 variables ``_i*``.
688 variables ``_i*``.
693
689
694 Parameters
690 Parameters
695 ----------
691 ----------
696 line_num : int
692 line_num : int
697 The prompt number of this input.
693 The prompt number of this input.
698
699 source : str
694 source : str
700 Python input.
695 Python input.
701
702 source_raw : str, optional
696 source_raw : str, optional
703 If given, this is the raw input without any IPython transformations
697 If given, this is the raw input without any IPython transformations
704 applied to it. If not given, ``source`` is used.
698 applied to it. If not given, ``source`` is used.
705 """
699 """
706 if source_raw is None:
700 if source_raw is None:
707 source_raw = source
701 source_raw = source
708 source = source.rstrip('\n')
702 source = source.rstrip('\n')
709 source_raw = source_raw.rstrip('\n')
703 source_raw = source_raw.rstrip('\n')
710
704
711 # do not store exit/quit commands
705 # do not store exit/quit commands
712 if self._exit_re.match(source_raw.strip()):
706 if self._exit_re.match(source_raw.strip()):
713 return
707 return
714
708
715 self.input_hist_parsed.append(source)
709 self.input_hist_parsed.append(source)
716 self.input_hist_raw.append(source_raw)
710 self.input_hist_raw.append(source_raw)
717
711
718 with self.db_input_cache_lock:
712 with self.db_input_cache_lock:
719 self.db_input_cache.append((line_num, source, source_raw))
713 self.db_input_cache.append((line_num, source, source_raw))
720 # Trigger to flush cache and write to DB.
714 # Trigger to flush cache and write to DB.
721 if len(self.db_input_cache) >= self.db_cache_size:
715 if len(self.db_input_cache) >= self.db_cache_size:
722 self.save_flag.set()
716 self.save_flag.set()
723
717
724 # update the auto _i variables
718 # update the auto _i variables
725 self._iii = self._ii
719 self._iii = self._ii
726 self._ii = self._i
720 self._ii = self._i
727 self._i = self._i00
721 self._i = self._i00
728 self._i00 = source_raw
722 self._i00 = source_raw
729
723
730 # hackish access to user namespace to create _i1,_i2... dynamically
724 # hackish access to user namespace to create _i1,_i2... dynamically
731 new_i = '_i%s' % line_num
725 new_i = '_i%s' % line_num
732 to_main = {'_i': self._i,
726 to_main = {'_i': self._i,
733 '_ii': self._ii,
727 '_ii': self._ii,
734 '_iii': self._iii,
728 '_iii': self._iii,
735 new_i : self._i00 }
729 new_i : self._i00 }
736
730
737 if self.shell is not None:
731 if self.shell is not None:
738 self.shell.push(to_main, interactive=False)
732 self.shell.push(to_main, interactive=False)
739
733
740 def store_output(self, line_num):
734 def store_output(self, line_num):
741 """If database output logging is enabled, this saves all the
735 """If database output logging is enabled, this saves all the
742 outputs from the indicated prompt number to the database. It's
736 outputs from the indicated prompt number to the database. It's
743 called by run_cell after code has been executed.
737 called by run_cell after code has been executed.
744
738
745 Parameters
739 Parameters
746 ----------
740 ----------
747 line_num : int
741 line_num : int
748 The line number from which to save outputs
742 The line number from which to save outputs
749 """
743 """
750 if (not self.db_log_output) or (line_num not in self.output_hist_reprs):
744 if (not self.db_log_output) or (line_num not in self.output_hist_reprs):
751 return
745 return
752 output = self.output_hist_reprs[line_num]
746 output = self.output_hist_reprs[line_num]
753
747
754 with self.db_output_cache_lock:
748 with self.db_output_cache_lock:
755 self.db_output_cache.append((line_num, output))
749 self.db_output_cache.append((line_num, output))
756 if self.db_cache_size <= 1:
750 if self.db_cache_size <= 1:
757 self.save_flag.set()
751 self.save_flag.set()
758
752
759 def _writeout_input_cache(self, conn):
753 def _writeout_input_cache(self, conn):
760 with conn:
754 with conn:
761 for line in self.db_input_cache:
755 for line in self.db_input_cache:
762 conn.execute("INSERT INTO history VALUES (?, ?, ?, ?)",
756 conn.execute("INSERT INTO history VALUES (?, ?, ?, ?)",
763 (self.session_number,)+line)
757 (self.session_number,)+line)
764
758
765 def _writeout_output_cache(self, conn):
759 def _writeout_output_cache(self, conn):
766 with conn:
760 with conn:
767 for line in self.db_output_cache:
761 for line in self.db_output_cache:
768 conn.execute("INSERT INTO output_history VALUES (?, ?, ?)",
762 conn.execute("INSERT INTO output_history VALUES (?, ?, ?)",
769 (self.session_number,)+line)
763 (self.session_number,)+line)
770
764
771 @only_when_enabled
765 @only_when_enabled
772 def writeout_cache(self, conn=None):
766 def writeout_cache(self, conn=None):
773 """Write any entries in the cache to the database."""
767 """Write any entries in the cache to the database."""
774 if conn is None:
768 if conn is None:
775 conn = self.db
769 conn = self.db
776
770
777 with self.db_input_cache_lock:
771 with self.db_input_cache_lock:
778 try:
772 try:
779 self._writeout_input_cache(conn)
773 self._writeout_input_cache(conn)
780 except sqlite3.IntegrityError:
774 except sqlite3.IntegrityError:
781 self.new_session(conn)
775 self.new_session(conn)
782 print("ERROR! Session/line number was not unique in",
776 print("ERROR! Session/line number was not unique in",
783 "database. History logging moved to new session",
777 "database. History logging moved to new session",
784 self.session_number)
778 self.session_number)
785 try:
779 try:
786 # Try writing to the new session. If this fails, don't
780 # Try writing to the new session. If this fails, don't
787 # recurse
781 # recurse
788 self._writeout_input_cache(conn)
782 self._writeout_input_cache(conn)
789 except sqlite3.IntegrityError:
783 except sqlite3.IntegrityError:
790 pass
784 pass
791 finally:
785 finally:
792 self.db_input_cache = []
786 self.db_input_cache = []
793
787
794 with self.db_output_cache_lock:
788 with self.db_output_cache_lock:
795 try:
789 try:
796 self._writeout_output_cache(conn)
790 self._writeout_output_cache(conn)
797 except sqlite3.IntegrityError:
791 except sqlite3.IntegrityError:
798 print("!! Session/line number for output was not unique",
792 print("!! Session/line number for output was not unique",
799 "in database. Output will not be stored.")
793 "in database. Output will not be stored.")
800 finally:
794 finally:
801 self.db_output_cache = []
795 self.db_output_cache = []
802
796
803
797
804 class HistorySavingThread(threading.Thread):
798 class HistorySavingThread(threading.Thread):
805 """This thread takes care of writing history to the database, so that
799 """This thread takes care of writing history to the database, so that
806 the UI isn't held up while that happens.
800 the UI isn't held up while that happens.
807
801
808 It waits for the HistoryManager's save_flag to be set, then writes out
802 It waits for the HistoryManager's save_flag to be set, then writes out
809 the history cache. The main thread is responsible for setting the flag when
803 the history cache. The main thread is responsible for setting the flag when
810 the cache size reaches a defined threshold."""
804 the cache size reaches a defined threshold."""
811 daemon = True
805 daemon = True
812 stop_now = False
806 stop_now = False
813 enabled = True
807 enabled = True
814 def __init__(self, history_manager):
808 def __init__(self, history_manager):
815 super(HistorySavingThread, self).__init__(name="IPythonHistorySavingThread")
809 super(HistorySavingThread, self).__init__(name="IPythonHistorySavingThread")
816 self.history_manager = history_manager
810 self.history_manager = history_manager
817 self.enabled = history_manager.enabled
811 self.enabled = history_manager.enabled
818 atexit.register(self.stop)
812 atexit.register(self.stop)
819
813
820 @only_when_enabled
814 @only_when_enabled
821 def run(self):
815 def run(self):
822 # We need a separate db connection per thread:
816 # We need a separate db connection per thread:
823 try:
817 try:
824 self.db = sqlite3.connect(
818 self.db = sqlite3.connect(
825 str(self.history_manager.hist_file),
819 str(self.history_manager.hist_file),
826 **self.history_manager.connection_options,
820 **self.history_manager.connection_options,
827 )
821 )
828 while True:
822 while True:
829 self.history_manager.save_flag.wait()
823 self.history_manager.save_flag.wait()
830 if self.stop_now:
824 if self.stop_now:
831 self.db.close()
825 self.db.close()
832 return
826 return
833 self.history_manager.save_flag.clear()
827 self.history_manager.save_flag.clear()
834 self.history_manager.writeout_cache(self.db)
828 self.history_manager.writeout_cache(self.db)
835 except Exception as e:
829 except Exception as e:
836 print(("The history saving thread hit an unexpected error (%s)."
830 print(("The history saving thread hit an unexpected error (%s)."
837 "History will not be written to the database.") % repr(e))
831 "History will not be written to the database.") % repr(e))
838
832
839 def stop(self):
833 def stop(self):
840 """This can be called from the main thread to safely stop this thread.
834 """This can be called from the main thread to safely stop this thread.
841
835
842 Note that it does not attempt to write out remaining history before
836 Note that it does not attempt to write out remaining history before
843 exiting. That should be done by calling the HistoryManager's
837 exiting. That should be done by calling the HistoryManager's
844 end_session method."""
838 end_session method."""
845 self.stop_now = True
839 self.stop_now = True
846 self.history_manager.save_flag.set()
840 self.history_manager.save_flag.set()
847 self.join()
841 self.join()
848
842
849
843
850 # To match, e.g. ~5/8-~2/3
844 # To match, e.g. ~5/8-~2/3
851 range_re = re.compile(r"""
845 range_re = re.compile(r"""
852 ((?P<startsess>~?\d+)/)?
846 ((?P<startsess>~?\d+)/)?
853 (?P<start>\d+)?
847 (?P<start>\d+)?
854 ((?P<sep>[\-:])
848 ((?P<sep>[\-:])
855 ((?P<endsess>~?\d+)/)?
849 ((?P<endsess>~?\d+)/)?
856 (?P<end>\d+))?
850 (?P<end>\d+))?
857 $""", re.VERBOSE)
851 $""", re.VERBOSE)
858
852
859
853
860 def extract_hist_ranges(ranges_str):
854 def extract_hist_ranges(ranges_str):
861 """Turn a string of history ranges into 3-tuples of (session, start, stop).
855 """Turn a string of history ranges into 3-tuples of (session, start, stop).
862
856
863 Empty string results in a `[(0, 1, None)]`, i.e. "everything from current
857 Empty string results in a `[(0, 1, None)]`, i.e. "everything from current
864 session".
858 session".
865
859
866 Examples
860 Examples
867 --------
861 --------
868 >>> list(extract_hist_ranges("~8/5-~7/4 2"))
862 >>> list(extract_hist_ranges("~8/5-~7/4 2"))
869 [(-8, 5, None), (-7, 1, 5), (0, 2, 3)]
863 [(-8, 5, None), (-7, 1, 5), (0, 2, 3)]
870 """
864 """
871 if ranges_str == "":
865 if ranges_str == "":
872 yield (0, 1, None) # Everything from current session
866 yield (0, 1, None) # Everything from current session
873 return
867 return
874
868
875 for range_str in ranges_str.split():
869 for range_str in ranges_str.split():
876 rmatch = range_re.match(range_str)
870 rmatch = range_re.match(range_str)
877 if not rmatch:
871 if not rmatch:
878 continue
872 continue
879 start = rmatch.group("start")
873 start = rmatch.group("start")
880 if start:
874 if start:
881 start = int(start)
875 start = int(start)
882 end = rmatch.group("end")
876 end = rmatch.group("end")
883 # If no end specified, get (a, a + 1)
877 # If no end specified, get (a, a + 1)
884 end = int(end) if end else start + 1
878 end = int(end) if end else start + 1
885 else: # start not specified
879 else: # start not specified
886 if not rmatch.group('startsess'): # no startsess
880 if not rmatch.group('startsess'): # no startsess
887 continue
881 continue
888 start = 1
882 start = 1
889 end = None # provide the entire session hist
883 end = None # provide the entire session hist
890
884
891 if rmatch.group("sep") == "-": # 1-3 == 1:4 --> [1, 2, 3]
885 if rmatch.group("sep") == "-": # 1-3 == 1:4 --> [1, 2, 3]
892 end += 1
886 end += 1
893 startsess = rmatch.group("startsess") or "0"
887 startsess = rmatch.group("startsess") or "0"
894 endsess = rmatch.group("endsess") or startsess
888 endsess = rmatch.group("endsess") or startsess
895 startsess = int(startsess.replace("~","-"))
889 startsess = int(startsess.replace("~","-"))
896 endsess = int(endsess.replace("~","-"))
890 endsess = int(endsess.replace("~","-"))
897 assert endsess >= startsess, "start session must be earlier than end session"
891 assert endsess >= startsess, "start session must be earlier than end session"
898
892
899 if endsess == startsess:
893 if endsess == startsess:
900 yield (startsess, start, end)
894 yield (startsess, start, end)
901 continue
895 continue
902 # Multiple sessions in one range:
896 # Multiple sessions in one range:
903 yield (startsess, start, None)
897 yield (startsess, start, None)
904 for sess in range(startsess+1, endsess):
898 for sess in range(startsess+1, endsess):
905 yield (sess, 1, None)
899 yield (sess, 1, None)
906 yield (endsess, 1, end)
900 yield (endsess, 1, end)
907
901
908
902
909 def _format_lineno(session, line):
903 def _format_lineno(session, line):
910 """Helper function to format line numbers properly."""
904 """Helper function to format line numbers properly."""
911 if session == 0:
905 if session == 0:
912 return str(line)
906 return str(line)
913 return "%s#%s" % (session, line)
907 return "%s#%s" % (session, line)
@@ -1,772 +1,772 b''
1 """DEPRECATED: Input handling and transformation machinery.
1 """DEPRECATED: Input handling and transformation machinery.
2
2
3 This module was deprecated in IPython 7.0, in favour of inputtransformer2.
3 This module was deprecated in IPython 7.0, in favour of inputtransformer2.
4
4
5 The first class in this module, :class:`InputSplitter`, is designed to tell when
5 The first class in this module, :class:`InputSplitter`, is designed to tell when
6 input from a line-oriented frontend is complete and should be executed, and when
6 input from a line-oriented frontend is complete and should be executed, and when
7 the user should be prompted for another line of code instead. The name 'input
7 the user should be prompted for another line of code instead. The name 'input
8 splitter' is largely for historical reasons.
8 splitter' is largely for historical reasons.
9
9
10 A companion, :class:`IPythonInputSplitter`, provides the same functionality but
10 A companion, :class:`IPythonInputSplitter`, provides the same functionality but
11 with full support for the extended IPython syntax (magics, system calls, etc).
11 with full support for the extended IPython syntax (magics, system calls, etc).
12 The code to actually do these transformations is in :mod:`IPython.core.inputtransformer`.
12 The code to actually do these transformations is in :mod:`IPython.core.inputtransformer`.
13 :class:`IPythonInputSplitter` feeds the raw code to the transformers in order
13 :class:`IPythonInputSplitter` feeds the raw code to the transformers in order
14 and stores the results.
14 and stores the results.
15
15
16 For more details, see the class docstrings below.
16 For more details, see the class docstrings below.
17 """
17 """
18
18
19 from warnings import warn
19 from warnings import warn
20
20
21 warn('IPython.core.inputsplitter is deprecated since IPython 7 in favor of `IPython.core.inputtransformer2`',
21 warn('IPython.core.inputsplitter is deprecated since IPython 7 in favor of `IPython.core.inputtransformer2`',
22 DeprecationWarning)
22 DeprecationWarning)
23
23
24 # Copyright (c) IPython Development Team.
24 # Copyright (c) IPython Development Team.
25 # Distributed under the terms of the Modified BSD License.
25 # Distributed under the terms of the Modified BSD License.
26 import ast
26 import ast
27 import codeop
27 import codeop
28 import io
28 import io
29 import re
29 import re
30 import sys
30 import sys
31 import tokenize
31 import tokenize
32 import warnings
32 import warnings
33
33
34 from IPython.core.inputtransformer import (leading_indent,
34 from IPython.core.inputtransformer import (leading_indent,
35 classic_prompt,
35 classic_prompt,
36 ipy_prompt,
36 ipy_prompt,
37 cellmagic,
37 cellmagic,
38 assemble_logical_lines,
38 assemble_logical_lines,
39 help_end,
39 help_end,
40 escaped_commands,
40 escaped_commands,
41 assign_from_magic,
41 assign_from_magic,
42 assign_from_system,
42 assign_from_system,
43 assemble_python_lines,
43 assemble_python_lines,
44 )
44 )
45
45
46 # These are available in this module for backwards compatibility.
46 # These are available in this module for backwards compatibility.
47 from IPython.core.inputtransformer import (ESC_SHELL, ESC_SH_CAP, ESC_HELP,
47 from IPython.core.inputtransformer import (ESC_SHELL, ESC_SH_CAP, ESC_HELP,
48 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,
48 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,
49 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN, ESC_SEQUENCES)
49 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN, ESC_SEQUENCES)
50
50
51 #-----------------------------------------------------------------------------
51 #-----------------------------------------------------------------------------
52 # Utilities
52 # Utilities
53 #-----------------------------------------------------------------------------
53 #-----------------------------------------------------------------------------
54
54
55 # FIXME: These are general-purpose utilities that later can be moved to the
55 # FIXME: These are general-purpose utilities that later can be moved to the
56 # general ward. Kept here for now because we're being very strict about test
56 # general ward. Kept here for now because we're being very strict about test
57 # coverage with this code, and this lets us ensure that we keep 100% coverage
57 # coverage with this code, and this lets us ensure that we keep 100% coverage
58 # while developing.
58 # while developing.
59
59
60 # compiled regexps for autoindent management
60 # compiled regexps for autoindent management
61 dedent_re = re.compile('|'.join([
61 dedent_re = re.compile('|'.join([
62 r'^\s+raise(\s.*)?$', # raise statement (+ space + other stuff, maybe)
62 r'^\s+raise(\s.*)?$', # raise statement (+ space + other stuff, maybe)
63 r'^\s+raise\([^\)]*\).*$', # wacky raise with immediate open paren
63 r'^\s+raise\([^\)]*\).*$', # wacky raise with immediate open paren
64 r'^\s+return(\s.*)?$', # normal return (+ space + other stuff, maybe)
64 r'^\s+return(\s.*)?$', # normal return (+ space + other stuff, maybe)
65 r'^\s+return\([^\)]*\).*$', # wacky return with immediate open paren
65 r'^\s+return\([^\)]*\).*$', # wacky return with immediate open paren
66 r'^\s+pass\s*$', # pass (optionally followed by trailing spaces)
66 r'^\s+pass\s*$', # pass (optionally followed by trailing spaces)
67 r'^\s+break\s*$', # break (optionally followed by trailing spaces)
67 r'^\s+break\s*$', # break (optionally followed by trailing spaces)
68 r'^\s+continue\s*$', # continue (optionally followed by trailing spaces)
68 r'^\s+continue\s*$', # continue (optionally followed by trailing spaces)
69 ]))
69 ]))
70 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
70 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
71
71
72 # regexp to match pure comment lines so we don't accidentally insert 'if 1:'
72 # regexp to match pure comment lines so we don't accidentally insert 'if 1:'
73 # before pure comments
73 # before pure comments
74 comment_line_re = re.compile(r'^\s*\#')
74 comment_line_re = re.compile(r'^\s*\#')
75
75
76
76
77 def num_ini_spaces(s):
77 def num_ini_spaces(s):
78 """Return the number of initial spaces in a string.
78 """Return the number of initial spaces in a string.
79
79
80 Note that tabs are counted as a single space. For now, we do *not* support
80 Note that tabs are counted as a single space. For now, we do *not* support
81 mixing of tabs and spaces in the user's input.
81 mixing of tabs and spaces in the user's input.
82
82
83 Parameters
83 Parameters
84 ----------
84 ----------
85 s : string
85 s : string
86
86
87 Returns
87 Returns
88 -------
88 -------
89 n : int
89 n : int
90 """
90 """
91
91
92 ini_spaces = ini_spaces_re.match(s)
92 ini_spaces = ini_spaces_re.match(s)
93 if ini_spaces:
93 if ini_spaces:
94 return ini_spaces.end()
94 return ini_spaces.end()
95 else:
95 else:
96 return 0
96 return 0
97
97
98 # Fake token types for partial_tokenize:
98 # Fake token types for partial_tokenize:
99 INCOMPLETE_STRING = tokenize.N_TOKENS
99 INCOMPLETE_STRING = tokenize.N_TOKENS
100 IN_MULTILINE_STATEMENT = tokenize.N_TOKENS + 1
100 IN_MULTILINE_STATEMENT = tokenize.N_TOKENS + 1
101
101
102 # The 2 classes below have the same API as TokenInfo, but don't try to look up
102 # The 2 classes below have the same API as TokenInfo, but don't try to look up
103 # a token type name that they won't find.
103 # a token type name that they won't find.
104 class IncompleteString:
104 class IncompleteString:
105 type = exact_type = INCOMPLETE_STRING
105 type = exact_type = INCOMPLETE_STRING
106 def __init__(self, s, start, end, line):
106 def __init__(self, s, start, end, line):
107 self.s = s
107 self.s = s
108 self.start = start
108 self.start = start
109 self.end = end
109 self.end = end
110 self.line = line
110 self.line = line
111
111
112 class InMultilineStatement:
112 class InMultilineStatement:
113 type = exact_type = IN_MULTILINE_STATEMENT
113 type = exact_type = IN_MULTILINE_STATEMENT
114 def __init__(self, pos, line):
114 def __init__(self, pos, line):
115 self.s = ''
115 self.s = ''
116 self.start = self.end = pos
116 self.start = self.end = pos
117 self.line = line
117 self.line = line
118
118
119 def partial_tokens(s):
119 def partial_tokens(s):
120 """Iterate over tokens from a possibly-incomplete string of code.
120 """Iterate over tokens from a possibly-incomplete string of code.
121
121
122 This adds two special token types: INCOMPLETE_STRING and
122 This adds two special token types: INCOMPLETE_STRING and
123 IN_MULTILINE_STATEMENT. These can only occur as the last token yielded, and
123 IN_MULTILINE_STATEMENT. These can only occur as the last token yielded, and
124 represent the two main ways for code to be incomplete.
124 represent the two main ways for code to be incomplete.
125 """
125 """
126 readline = io.StringIO(s).readline
126 readline = io.StringIO(s).readline
127 token = tokenize.TokenInfo(tokenize.NEWLINE, '', (1, 0), (1, 0), '')
127 token = tokenize.TokenInfo(tokenize.NEWLINE, '', (1, 0), (1, 0), '')
128 try:
128 try:
129 for token in tokenize.generate_tokens(readline):
129 for token in tokenize.generate_tokens(readline):
130 yield token
130 yield token
131 except tokenize.TokenError as e:
131 except tokenize.TokenError as e:
132 # catch EOF error
132 # catch EOF error
133 lines = s.splitlines(keepends=True)
133 lines = s.splitlines(keepends=True)
134 end = len(lines), len(lines[-1])
134 end = len(lines), len(lines[-1])
135 if 'multi-line string' in e.args[0]:
135 if 'multi-line string' in e.args[0]:
136 l, c = start = token.end
136 l, c = start = token.end
137 s = lines[l-1][c:] + ''.join(lines[l:])
137 s = lines[l-1][c:] + ''.join(lines[l:])
138 yield IncompleteString(s, start, end, lines[-1])
138 yield IncompleteString(s, start, end, lines[-1])
139 elif 'multi-line statement' in e.args[0]:
139 elif 'multi-line statement' in e.args[0]:
140 yield InMultilineStatement(end, lines[-1])
140 yield InMultilineStatement(end, lines[-1])
141 else:
141 else:
142 raise
142 raise
143
143
144 def find_next_indent(code):
144 def find_next_indent(code):
145 """Find the number of spaces for the next line of indentation"""
145 """Find the number of spaces for the next line of indentation"""
146 tokens = list(partial_tokens(code))
146 tokens = list(partial_tokens(code))
147 if tokens[-1].type == tokenize.ENDMARKER:
147 if tokens[-1].type == tokenize.ENDMARKER:
148 tokens.pop()
148 tokens.pop()
149 if not tokens:
149 if not tokens:
150 return 0
150 return 0
151 while (tokens[-1].type in {tokenize.DEDENT, tokenize.NEWLINE, tokenize.COMMENT}):
151 while (tokens[-1].type in {tokenize.DEDENT, tokenize.NEWLINE, tokenize.COMMENT}):
152 tokens.pop()
152 tokens.pop()
153
153
154 if tokens[-1].type == INCOMPLETE_STRING:
154 if tokens[-1].type == INCOMPLETE_STRING:
155 # Inside a multiline string
155 # Inside a multiline string
156 return 0
156 return 0
157
157
158 # Find the indents used before
158 # Find the indents used before
159 prev_indents = [0]
159 prev_indents = [0]
160 def _add_indent(n):
160 def _add_indent(n):
161 if n != prev_indents[-1]:
161 if n != prev_indents[-1]:
162 prev_indents.append(n)
162 prev_indents.append(n)
163
163
164 tokiter = iter(tokens)
164 tokiter = iter(tokens)
165 for tok in tokiter:
165 for tok in tokiter:
166 if tok.type in {tokenize.INDENT, tokenize.DEDENT}:
166 if tok.type in {tokenize.INDENT, tokenize.DEDENT}:
167 _add_indent(tok.end[1])
167 _add_indent(tok.end[1])
168 elif (tok.type == tokenize.NL):
168 elif (tok.type == tokenize.NL):
169 try:
169 try:
170 _add_indent(next(tokiter).start[1])
170 _add_indent(next(tokiter).start[1])
171 except StopIteration:
171 except StopIteration:
172 break
172 break
173
173
174 last_indent = prev_indents.pop()
174 last_indent = prev_indents.pop()
175
175
176 # If we've just opened a multiline statement (e.g. 'a = ['), indent more
176 # If we've just opened a multiline statement (e.g. 'a = ['), indent more
177 if tokens[-1].type == IN_MULTILINE_STATEMENT:
177 if tokens[-1].type == IN_MULTILINE_STATEMENT:
178 if tokens[-2].exact_type in {tokenize.LPAR, tokenize.LSQB, tokenize.LBRACE}:
178 if tokens[-2].exact_type in {tokenize.LPAR, tokenize.LSQB, tokenize.LBRACE}:
179 return last_indent + 4
179 return last_indent + 4
180 return last_indent
180 return last_indent
181
181
182 if tokens[-1].exact_type == tokenize.COLON:
182 if tokens[-1].exact_type == tokenize.COLON:
183 # Line ends with colon - indent
183 # Line ends with colon - indent
184 return last_indent + 4
184 return last_indent + 4
185
185
186 if last_indent:
186 if last_indent:
187 # Examine the last line for dedent cues - statements like return or
187 # Examine the last line for dedent cues - statements like return or
188 # raise which normally end a block of code.
188 # raise which normally end a block of code.
189 last_line_starts = 0
189 last_line_starts = 0
190 for i, tok in enumerate(tokens):
190 for i, tok in enumerate(tokens):
191 if tok.type == tokenize.NEWLINE:
191 if tok.type == tokenize.NEWLINE:
192 last_line_starts = i + 1
192 last_line_starts = i + 1
193
193
194 last_line_tokens = tokens[last_line_starts:]
194 last_line_tokens = tokens[last_line_starts:]
195 names = [t.string for t in last_line_tokens if t.type == tokenize.NAME]
195 names = [t.string for t in last_line_tokens if t.type == tokenize.NAME]
196 if names and names[0] in {'raise', 'return', 'pass', 'break', 'continue'}:
196 if names and names[0] in {'raise', 'return', 'pass', 'break', 'continue'}:
197 # Find the most recent indentation less than the current level
197 # Find the most recent indentation less than the current level
198 for indent in reversed(prev_indents):
198 for indent in reversed(prev_indents):
199 if indent < last_indent:
199 if indent < last_indent:
200 return indent
200 return indent
201
201
202 return last_indent
202 return last_indent
203
203
204
204
205 def last_blank(src):
205 def last_blank(src):
206 """Determine if the input source ends in a blank.
206 """Determine if the input source ends in a blank.
207
207
208 A blank is either a newline or a line consisting of whitespace.
208 A blank is either a newline or a line consisting of whitespace.
209
209
210 Parameters
210 Parameters
211 ----------
211 ----------
212 src : string
212 src : string
213 A single or multiline string.
213 A single or multiline string.
214 """
214 """
215 if not src: return False
215 if not src: return False
216 ll = src.splitlines()[-1]
216 ll = src.splitlines()[-1]
217 return (ll == '') or ll.isspace()
217 return (ll == '') or ll.isspace()
218
218
219
219
220 last_two_blanks_re = re.compile(r'\n\s*\n\s*$', re.MULTILINE)
220 last_two_blanks_re = re.compile(r'\n\s*\n\s*$', re.MULTILINE)
221 last_two_blanks_re2 = re.compile(r'.+\n\s*\n\s+$', re.MULTILINE)
221 last_two_blanks_re2 = re.compile(r'.+\n\s*\n\s+$', re.MULTILINE)
222
222
223 def last_two_blanks(src):
223 def last_two_blanks(src):
224 """Determine if the input source ends in two blanks.
224 """Determine if the input source ends in two blanks.
225
225
226 A blank is either a newline or a line consisting of whitespace.
226 A blank is either a newline or a line consisting of whitespace.
227
227
228 Parameters
228 Parameters
229 ----------
229 ----------
230 src : string
230 src : string
231 A single or multiline string.
231 A single or multiline string.
232 """
232 """
233 if not src: return False
233 if not src: return False
234 # The logic here is tricky: I couldn't get a regexp to work and pass all
234 # The logic here is tricky: I couldn't get a regexp to work and pass all
235 # the tests, so I took a different approach: split the source by lines,
235 # the tests, so I took a different approach: split the source by lines,
236 # grab the last two and prepend '###\n' as a stand-in for whatever was in
236 # grab the last two and prepend '###\n' as a stand-in for whatever was in
237 # the body before the last two lines. Then, with that structure, it's
237 # the body before the last two lines. Then, with that structure, it's
238 # possible to analyze with two regexps. Not the most elegant solution, but
238 # possible to analyze with two regexps. Not the most elegant solution, but
239 # it works. If anyone tries to change this logic, make sure to validate
239 # it works. If anyone tries to change this logic, make sure to validate
240 # the whole test suite first!
240 # the whole test suite first!
241 new_src = '\n'.join(['###\n'] + src.splitlines()[-2:])
241 new_src = '\n'.join(['###\n'] + src.splitlines()[-2:])
242 return (bool(last_two_blanks_re.match(new_src)) or
242 return (bool(last_two_blanks_re.match(new_src)) or
243 bool(last_two_blanks_re2.match(new_src)) )
243 bool(last_two_blanks_re2.match(new_src)) )
244
244
245
245
246 def remove_comments(src):
246 def remove_comments(src):
247 """Remove all comments from input source.
247 """Remove all comments from input source.
248
248
249 Note: comments are NOT recognized inside of strings!
249 Note: comments are NOT recognized inside of strings!
250
250
251 Parameters
251 Parameters
252 ----------
252 ----------
253 src : string
253 src : string
254 A single or multiline input string.
254 A single or multiline input string.
255
255
256 Returns
256 Returns
257 -------
257 -------
258 String with all Python comments removed.
258 String with all Python comments removed.
259 """
259 """
260
260
261 return re.sub('#.*', '', src)
261 return re.sub('#.*', '', src)
262
262
263
263
264 def get_input_encoding():
264 def get_input_encoding():
265 """Return the default standard input encoding.
265 """Return the default standard input encoding.
266
266
267 If sys.stdin has no encoding, 'ascii' is returned."""
267 If sys.stdin has no encoding, 'ascii' is returned."""
268 # There are strange environments for which sys.stdin.encoding is None. We
268 # There are strange environments for which sys.stdin.encoding is None. We
269 # ensure that a valid encoding is returned.
269 # ensure that a valid encoding is returned.
270 encoding = getattr(sys.stdin, 'encoding', None)
270 encoding = getattr(sys.stdin, 'encoding', None)
271 if encoding is None:
271 if encoding is None:
272 encoding = 'ascii'
272 encoding = 'ascii'
273 return encoding
273 return encoding
274
274
275 #-----------------------------------------------------------------------------
275 #-----------------------------------------------------------------------------
276 # Classes and functions for normal Python syntax handling
276 # Classes and functions for normal Python syntax handling
277 #-----------------------------------------------------------------------------
277 #-----------------------------------------------------------------------------
278
278
279 class InputSplitter(object):
279 class InputSplitter(object):
280 r"""An object that can accumulate lines of Python source before execution.
280 r"""An object that can accumulate lines of Python source before execution.
281
281
282 This object is designed to be fed python source line-by-line, using
282 This object is designed to be fed python source line-by-line, using
283 :meth:`push`. It will return on each push whether the currently pushed
283 :meth:`push`. It will return on each push whether the currently pushed
284 code could be executed already. In addition, it provides a method called
284 code could be executed already. In addition, it provides a method called
285 :meth:`push_accepts_more` that can be used to query whether more input
285 :meth:`push_accepts_more` that can be used to query whether more input
286 can be pushed into a single interactive block.
286 can be pushed into a single interactive block.
287
287
288 This is a simple example of how an interactive terminal-based client can use
288 This is a simple example of how an interactive terminal-based client can use
289 this tool::
289 this tool::
290
290
291 isp = InputSplitter()
291 isp = InputSplitter()
292 while isp.push_accepts_more():
292 while isp.push_accepts_more():
293 indent = ' '*isp.indent_spaces
293 indent = ' '*isp.indent_spaces
294 prompt = '>>> ' + indent
294 prompt = '>>> ' + indent
295 line = indent + raw_input(prompt)
295 line = indent + raw_input(prompt)
296 isp.push(line)
296 isp.push(line)
297 print 'Input source was:\n', isp.source_reset(),
297 print 'Input source was:\n', isp.source_reset(),
298 """
298 """
299 # A cache for storing the current indentation
299 # A cache for storing the current indentation
300 # The first value stores the most recently processed source input
300 # The first value stores the most recently processed source input
301 # The second value is the number of spaces for the current indentation
301 # The second value is the number of spaces for the current indentation
302 # If self.source matches the first value, the second value is a valid
302 # If self.source matches the first value, the second value is a valid
303 # current indentation. Otherwise, the cache is invalid and the indentation
303 # current indentation. Otherwise, the cache is invalid and the indentation
304 # must be recalculated.
304 # must be recalculated.
305 _indent_spaces_cache = None, None
305 _indent_spaces_cache = None, None
306 # String, indicating the default input encoding. It is computed by default
306 # String, indicating the default input encoding. It is computed by default
307 # at initialization time via get_input_encoding(), but it can be reset by a
307 # at initialization time via get_input_encoding(), but it can be reset by a
308 # client with specific knowledge of the encoding.
308 # client with specific knowledge of the encoding.
309 encoding = ''
309 encoding = ''
310 # String where the current full source input is stored, properly encoded.
310 # String where the current full source input is stored, properly encoded.
311 # Reading this attribute is the normal way of querying the currently pushed
311 # Reading this attribute is the normal way of querying the currently pushed
312 # source code, that has been properly encoded.
312 # source code, that has been properly encoded.
313 source = ''
313 source = ''
314 # Code object corresponding to the current source. It is automatically
314 # Code object corresponding to the current source. It is automatically
315 # synced to the source, so it can be queried at any time to obtain the code
315 # synced to the source, so it can be queried at any time to obtain the code
316 # object; it will be None if the source doesn't compile to valid Python.
316 # object; it will be None if the source doesn't compile to valid Python.
317 code = None
317 code = None
318
318
319 # Private attributes
319 # Private attributes
320
320
321 # List with lines of input accumulated so far
321 # List with lines of input accumulated so far
322 _buffer = None
322 _buffer = None
323 # Command compiler
323 # Command compiler
324 _compile = None
324 _compile = None
325 # Boolean indicating whether the current block is complete
325 # Boolean indicating whether the current block is complete
326 _is_complete = None
326 _is_complete = None
327 # Boolean indicating whether the current block has an unrecoverable syntax error
327 # Boolean indicating whether the current block has an unrecoverable syntax error
328 _is_invalid = False
328 _is_invalid = False
329
329
330 def __init__(self):
330 def __init__(self):
331 """Create a new InputSplitter instance.
331 """Create a new InputSplitter instance.
332 """
332 """
333 self._buffer = []
333 self._buffer = []
334 self._compile = codeop.CommandCompiler()
334 self._compile = codeop.CommandCompiler()
335 self.encoding = get_input_encoding()
335 self.encoding = get_input_encoding()
336
336
337 def reset(self):
337 def reset(self):
338 """Reset the input buffer and associated state."""
338 """Reset the input buffer and associated state."""
339 self._buffer[:] = []
339 self._buffer[:] = []
340 self.source = ''
340 self.source = ''
341 self.code = None
341 self.code = None
342 self._is_complete = False
342 self._is_complete = False
343 self._is_invalid = False
343 self._is_invalid = False
344
344
345 def source_reset(self):
345 def source_reset(self):
346 """Return the input source and perform a full reset.
346 """Return the input source and perform a full reset.
347 """
347 """
348 out = self.source
348 out = self.source
349 self.reset()
349 self.reset()
350 return out
350 return out
351
351
352 def check_complete(self, source):
352 def check_complete(self, source):
353 """Return whether a block of code is ready to execute, or should be continued
353 """Return whether a block of code is ready to execute, or should be continued
354
354
355 This is a non-stateful API, and will reset the state of this InputSplitter.
355 This is a non-stateful API, and will reset the state of this InputSplitter.
356
356
357 Parameters
357 Parameters
358 ----------
358 ----------
359 source : string
359 source : string
360 Python input code, which can be multiline.
360 Python input code, which can be multiline.
361
361
362 Returns
362 Returns
363 -------
363 -------
364 status : str
364 status : str
365 One of 'complete', 'incomplete', or 'invalid' if source is not a
365 One of 'complete', 'incomplete', or 'invalid' if source is not a
366 prefix of valid code.
366 prefix of valid code.
367 indent_spaces : int or None
367 indent_spaces : int or None
368 The number of spaces by which to indent the next line of code. If
368 The number of spaces by which to indent the next line of code. If
369 status is not 'incomplete', this is None.
369 status is not 'incomplete', this is None.
370 """
370 """
371 self.reset()
371 self.reset()
372 try:
372 try:
373 self.push(source)
373 self.push(source)
374 except SyntaxError:
374 except SyntaxError:
375 # Transformers in IPythonInputSplitter can raise SyntaxError,
375 # Transformers in IPythonInputSplitter can raise SyntaxError,
376 # which push() will not catch.
376 # which push() will not catch.
377 return 'invalid', None
377 return 'invalid', None
378 else:
378 else:
379 if self._is_invalid:
379 if self._is_invalid:
380 return 'invalid', None
380 return 'invalid', None
381 elif self.push_accepts_more():
381 elif self.push_accepts_more():
382 return 'incomplete', self.get_indent_spaces()
382 return 'incomplete', self.get_indent_spaces()
383 else:
383 else:
384 return 'complete', None
384 return 'complete', None
385 finally:
385 finally:
386 self.reset()
386 self.reset()
387
387
388 def push(self, lines:str) -> bool:
388 def push(self, lines:str) -> bool:
389 """Push one or more lines of input.
389 """Push one or more lines of input.
390
390
391 This stores the given lines and returns a status code indicating
391 This stores the given lines and returns a status code indicating
392 whether the code forms a complete Python block or not.
392 whether the code forms a complete Python block or not.
393
393
394 Any exceptions generated in compilation are swallowed, but if an
394 Any exceptions generated in compilation are swallowed, but if an
395 exception was produced, the method returns True.
395 exception was produced, the method returns True.
396
396
397 Parameters
397 Parameters
398 ----------
398 ----------
399 lines : string
399 lines : string
400 One or more lines of Python input.
400 One or more lines of Python input.
401
401
402 Returns
402 Returns
403 -------
403 -------
404 is_complete : boolean
404 is_complete : boolean
405 True if the current input source (the result of the current input
405 True if the current input source (the result of the current input
406 plus prior inputs) forms a complete Python execution block. Note that
406 plus prior inputs) forms a complete Python execution block. Note that
407 this value is also stored as a private attribute (``_is_complete``), so it
407 this value is also stored as a private attribute (``_is_complete``), so it
408 can be queried at any time.
408 can be queried at any time.
409 """
409 """
410 assert isinstance(lines, str)
410 assert isinstance(lines, str)
411 self._store(lines)
411 self._store(lines)
412 source = self.source
412 source = self.source
413
413
414 # Before calling _compile(), reset the code object to None so that if an
414 # Before calling _compile(), reset the code object to None so that if an
415 # exception is raised in compilation, we don't mislead by having
415 # exception is raised in compilation, we don't mislead by having
416 # inconsistent code/source attributes.
416 # inconsistent code/source attributes.
417 self.code, self._is_complete = None, None
417 self.code, self._is_complete = None, None
418 self._is_invalid = False
418 self._is_invalid = False
419
419
420 # Honor termination lines properly
420 # Honor termination lines properly
421 if source.endswith('\\\n'):
421 if source.endswith('\\\n'):
422 return False
422 return False
423
423
424 try:
424 try:
425 with warnings.catch_warnings():
425 with warnings.catch_warnings():
426 warnings.simplefilter('error', SyntaxWarning)
426 warnings.simplefilter('error', SyntaxWarning)
427 self.code = self._compile(source, symbol="exec")
427 self.code = self._compile(source, symbol="exec")
428 # Invalid syntax can produce any of a number of different errors from
428 # Invalid syntax can produce any of a number of different errors from
429 # inside the compiler, so we have to catch them all. Syntax errors
429 # inside the compiler, so we have to catch them all. Syntax errors
430 # immediately produce a 'ready' block, so the invalid Python can be
430 # immediately produce a 'ready' block, so the invalid Python can be
431 # sent to the kernel for evaluation with possible ipython
431 # sent to the kernel for evaluation with possible ipython
432 # special-syntax conversion.
432 # special-syntax conversion.
433 except (SyntaxError, OverflowError, ValueError, TypeError,
433 except (SyntaxError, OverflowError, ValueError, TypeError,
434 MemoryError, SyntaxWarning):
434 MemoryError, SyntaxWarning):
435 self._is_complete = True
435 self._is_complete = True
436 self._is_invalid = True
436 self._is_invalid = True
437 else:
437 else:
438 # Compilation didn't produce any exceptions (though it may not have
438 # Compilation didn't produce any exceptions (though it may not have
439 # given a complete code object)
439 # given a complete code object)
440 self._is_complete = self.code is not None
440 self._is_complete = self.code is not None
441
441
442 return self._is_complete
442 return self._is_complete
443
443
444 def push_accepts_more(self):
444 def push_accepts_more(self):
445 """Return whether a block of interactive input can accept more input.
445 """Return whether a block of interactive input can accept more input.
446
446
447 This method is meant to be used by line-oriented frontends, who need to
447 This method is meant to be used by line-oriented frontends, who need to
448 guess whether a block is complete or not based solely on prior and
448 guess whether a block is complete or not based solely on prior and
449 current input lines. The InputSplitter considers it has a complete
449 current input lines. The InputSplitter considers it has a complete
450 interactive block and will not accept more input when either:
450 interactive block and will not accept more input when either:
451
451
452 * A SyntaxError is raised
452 * A SyntaxError is raised
453
453
454 * The code is complete and consists of a single line or a single
454 * The code is complete and consists of a single line or a single
455 non-compound statement
455 non-compound statement
456
456
457 * The code is complete and has a blank line at the end
457 * The code is complete and has a blank line at the end
458
458
459 If the current input produces a syntax error, this method immediately
459 If the current input produces a syntax error, this method immediately
460 returns False but does *not* raise the syntax error exception, as
460 returns False but does *not* raise the syntax error exception, as
461 typically clients will want to send invalid syntax to an execution
461 typically clients will want to send invalid syntax to an execution
462 backend which might convert the invalid syntax into valid Python via
462 backend which might convert the invalid syntax into valid Python via
463 one of the dynamic IPython mechanisms.
463 one of the dynamic IPython mechanisms.
464 """
464 """
465
465
466 # With incomplete input, unconditionally accept more
466 # With incomplete input, unconditionally accept more
467 # A syntax error also sets _is_complete to True - see push()
467 # A syntax error also sets _is_complete to True - see push()
468 if not self._is_complete:
468 if not self._is_complete:
469 #print("Not complete") # debug
469 #print("Not complete") # debug
470 return True
470 return True
471
471
472 # The user can make any (complete) input execute by leaving a blank line
472 # The user can make any (complete) input execute by leaving a blank line
473 last_line = self.source.splitlines()[-1]
473 last_line = self.source.splitlines()[-1]
474 if (not last_line) or last_line.isspace():
474 if (not last_line) or last_line.isspace():
475 #print("Blank line") # debug
475 #print("Blank line") # debug
476 return False
476 return False
477
477
478 # If there's just a single line or AST node, and we're flush left, as is
478 # If there's just a single line or AST node, and we're flush left, as is
479 # the case after a simple statement such as 'a=1', we want to execute it
479 # the case after a simple statement such as 'a=1', we want to execute it
480 # straight away.
480 # straight away.
481 if self.get_indent_spaces() == 0:
481 if self.get_indent_spaces() == 0:
482 if len(self.source.splitlines()) <= 1:
482 if len(self.source.splitlines()) <= 1:
483 return False
483 return False
484
484
485 try:
485 try:
486 code_ast = ast.parse(u''.join(self._buffer))
486 code_ast = ast.parse(u''.join(self._buffer))
487 except Exception:
487 except Exception:
488 #print("Can't parse AST") # debug
488 #print("Can't parse AST") # debug
489 return False
489 return False
490 else:
490 else:
491 if len(code_ast.body) == 1 and \
491 if len(code_ast.body) == 1 and \
492 not hasattr(code_ast.body[0], 'body'):
492 not hasattr(code_ast.body[0], 'body'):
493 #print("Simple statement") # debug
493 #print("Simple statement") # debug
494 return False
494 return False
495
495
496 # General fallback - accept more code
496 # General fallback - accept more code
497 return True
497 return True
498
498
499 def get_indent_spaces(self):
499 def get_indent_spaces(self):
500 sourcefor, n = self._indent_spaces_cache
500 sourcefor, n = self._indent_spaces_cache
501 if sourcefor == self.source:
501 if sourcefor == self.source:
502 return n
502 return n
503
503
504 # self.source always has a trailing newline
504 # self.source always has a trailing newline
505 n = find_next_indent(self.source[:-1])
505 n = find_next_indent(self.source[:-1])
506 self._indent_spaces_cache = (self.source, n)
506 self._indent_spaces_cache = (self.source, n)
507 return n
507 return n
508
508
509 # Backwards compatibility. I think all code that used .indent_spaces was
509 # Backwards compatibility. I think all code that used .indent_spaces was
510 # inside IPython, but we can leave this here until IPython 7 in case any
510 # inside IPython, but we can leave this here until IPython 7 in case any
511 # other modules are using it. -TK, November 2017
511 # other modules are using it. -TK, November 2017
512 indent_spaces = property(get_indent_spaces)
512 indent_spaces = property(get_indent_spaces)
513
513
514 def _store(self, lines, buffer=None, store='source'):
514 def _store(self, lines, buffer=None, store='source'):
515 """Store one or more lines of input.
515 """Store one or more lines of input.
516
516
517 If input lines are not newline-terminated, a newline is automatically
517 If input lines are not newline-terminated, a newline is automatically
518 appended."""
518 appended."""
519
519
520 if buffer is None:
520 if buffer is None:
521 buffer = self._buffer
521 buffer = self._buffer
522
522
523 if lines.endswith('\n'):
523 if lines.endswith('\n'):
524 buffer.append(lines)
524 buffer.append(lines)
525 else:
525 else:
526 buffer.append(lines+'\n')
526 buffer.append(lines+'\n')
527 setattr(self, store, self._set_source(buffer))
527 setattr(self, store, self._set_source(buffer))
528
528
529 def _set_source(self, buffer):
529 def _set_source(self, buffer):
530 return u''.join(buffer)
530 return u''.join(buffer)
531
531
532
532
533 class IPythonInputSplitter(InputSplitter):
533 class IPythonInputSplitter(InputSplitter):
534 """An input splitter that recognizes all of IPython's special syntax."""
534 """An input splitter that recognizes all of IPython's special syntax."""
535
535
536 # String with raw, untransformed input.
536 # String with raw, untransformed input.
537 source_raw = ''
537 source_raw = ''
538
538
539 # Flag to track when a transformer has stored input that it hasn't given
539 # Flag to track when a transformer has stored input that it hasn't given
540 # back yet.
540 # back yet.
541 transformer_accumulating = False
541 transformer_accumulating = False
542
542
543 # Flag to track when assemble_python_lines has stored input that it hasn't
543 # Flag to track when assemble_python_lines has stored input that it hasn't
544 # given back yet.
544 # given back yet.
545 within_python_line = False
545 within_python_line = False
546
546
547 # Private attributes
547 # Private attributes
548
548
549 # List with lines of raw input accumulated so far.
549 # List with lines of raw input accumulated so far.
550 _buffer_raw = None
550 _buffer_raw = None
551
551
552 def __init__(self, line_input_checker=True, physical_line_transforms=None,
552 def __init__(self, line_input_checker=True, physical_line_transforms=None,
553 logical_line_transforms=None, python_line_transforms=None):
553 logical_line_transforms=None, python_line_transforms=None):
554 super(IPythonInputSplitter, self).__init__()
554 super(IPythonInputSplitter, self).__init__()
555 self._buffer_raw = []
555 self._buffer_raw = []
556 self._validate = True
556 self._validate = True
557
557
558 if physical_line_transforms is not None:
558 if physical_line_transforms is not None:
559 self.physical_line_transforms = physical_line_transforms
559 self.physical_line_transforms = physical_line_transforms
560 else:
560 else:
561 self.physical_line_transforms = [
561 self.physical_line_transforms = [
562 leading_indent(),
562 leading_indent(),
563 classic_prompt(),
563 classic_prompt(),
564 ipy_prompt(),
564 ipy_prompt(),
565 cellmagic(end_on_blank_line=line_input_checker),
565 cellmagic(end_on_blank_line=line_input_checker),
566 ]
566 ]
567
567
568 self.assemble_logical_lines = assemble_logical_lines()
568 self.assemble_logical_lines = assemble_logical_lines()
569 if logical_line_transforms is not None:
569 if logical_line_transforms is not None:
570 self.logical_line_transforms = logical_line_transforms
570 self.logical_line_transforms = logical_line_transforms
571 else:
571 else:
572 self.logical_line_transforms = [
572 self.logical_line_transforms = [
573 help_end(),
573 help_end(),
574 escaped_commands(),
574 escaped_commands(),
575 assign_from_magic(),
575 assign_from_magic(),
576 assign_from_system(),
576 assign_from_system(),
577 ]
577 ]
578
578
579 self.assemble_python_lines = assemble_python_lines()
579 self.assemble_python_lines = assemble_python_lines()
580 if python_line_transforms is not None:
580 if python_line_transforms is not None:
581 self.python_line_transforms = python_line_transforms
581 self.python_line_transforms = python_line_transforms
582 else:
582 else:
583 # We don't use any of these at present
583 # We don't use any of these at present
584 self.python_line_transforms = []
584 self.python_line_transforms = []
585
585
586 @property
586 @property
587 def transforms(self):
587 def transforms(self):
588 "Quick access to all transformers."
588 "Quick access to all transformers."
589 return self.physical_line_transforms + \
589 return self.physical_line_transforms + \
590 [self.assemble_logical_lines] + self.logical_line_transforms + \
590 [self.assemble_logical_lines] + self.logical_line_transforms + \
591 [self.assemble_python_lines] + self.python_line_transforms
591 [self.assemble_python_lines] + self.python_line_transforms
592
592
593 @property
593 @property
594 def transforms_in_use(self):
594 def transforms_in_use(self):
595 """Transformers, excluding logical line transformers if we're in a
595 """Transformers, excluding logical line transformers if we're in a
596 Python line."""
596 Python line."""
597 t = self.physical_line_transforms[:]
597 t = self.physical_line_transforms[:]
598 if not self.within_python_line:
598 if not self.within_python_line:
599 t += [self.assemble_logical_lines] + self.logical_line_transforms
599 t += [self.assemble_logical_lines] + self.logical_line_transforms
600 return t + [self.assemble_python_lines] + self.python_line_transforms
600 return t + [self.assemble_python_lines] + self.python_line_transforms
601
601
602 def reset(self):
602 def reset(self):
603 """Reset the input buffer and associated state."""
603 """Reset the input buffer and associated state."""
604 super(IPythonInputSplitter, self).reset()
604 super(IPythonInputSplitter, self).reset()
605 self._buffer_raw[:] = []
605 self._buffer_raw[:] = []
606 self.source_raw = ''
606 self.source_raw = ''
607 self.transformer_accumulating = False
607 self.transformer_accumulating = False
608 self.within_python_line = False
608 self.within_python_line = False
609
609
610 for t in self.transforms:
610 for t in self.transforms:
611 try:
611 try:
612 t.reset()
612 t.reset()
613 except SyntaxError:
613 except SyntaxError:
614 # Nothing that calls reset() expects to handle transformer
614 # Nothing that calls reset() expects to handle transformer
615 # errors
615 # errors
616 pass
616 pass
617
617
618 def flush_transformers(self):
618 def flush_transformers(self):
619 def _flush(transform, outs):
619 def _flush(transform, outs):
620 """yield transformed lines
620 """yield transformed lines
621
621
622 always strings, never None
622 always strings, never None
623
623
624 transform: the current transform
624 transform: the current transform
625 outs: an iterable of previously transformed inputs.
625 outs: an iterable of previously transformed inputs.
626 Each may be multiline, which will be passed
626 Each may be multiline, which will be passed
627 one line at a time to transform.
627 one line at a time to transform.
628 """
628 """
629 for out in outs:
629 for out in outs:
630 for line in out.splitlines():
630 for line in out.splitlines():
631 # push one line at a time
631 # push one line at a time
632 tmp = transform.push(line)
632 tmp = transform.push(line)
633 if tmp is not None:
633 if tmp is not None:
634 yield tmp
634 yield tmp
635
635
636 # reset the transform
636 # reset the transform
637 tmp = transform.reset()
637 tmp = transform.reset()
638 if tmp is not None:
638 if tmp is not None:
639 yield tmp
639 yield tmp
640
640
641 out = []
641 out = []
642 for t in self.transforms_in_use:
642 for t in self.transforms_in_use:
643 out = _flush(t, out)
643 out = _flush(t, out)
644
644
645 out = list(out)
645 out = list(out)
646 if out:
646 if out:
647 self._store('\n'.join(out))
647 self._store('\n'.join(out))
648
648
649 def raw_reset(self):
649 def raw_reset(self):
650 """Return raw input only and perform a full reset.
650 """Return raw input only and perform a full reset.
651 """
651 """
652 out = self.source_raw
652 out = self.source_raw
653 self.reset()
653 self.reset()
654 return out
654 return out
655
655
656 def source_reset(self):
656 def source_reset(self):
657 try:
657 try:
658 self.flush_transformers()
658 self.flush_transformers()
659 return self.source
659 return self.source
660 finally:
660 finally:
661 self.reset()
661 self.reset()
662
662
663 def push_accepts_more(self):
663 def push_accepts_more(self):
664 if self.transformer_accumulating:
664 if self.transformer_accumulating:
665 return True
665 return True
666 else:
666 else:
667 return super(IPythonInputSplitter, self).push_accepts_more()
667 return super(IPythonInputSplitter, self).push_accepts_more()
668
668
669 def transform_cell(self, cell):
669 def transform_cell(self, cell):
670 """Process and translate a cell of input.
670 """Process and translate a cell of input.
671 """
671 """
672 self.reset()
672 self.reset()
673 try:
673 try:
674 self.push(cell)
674 self.push(cell)
675 self.flush_transformers()
675 self.flush_transformers()
676 return self.source
676 return self.source
677 finally:
677 finally:
678 self.reset()
678 self.reset()
679
679
680 def push(self, lines:str) -> bool:
680 def push(self, lines:str) -> bool:
681 """Push one or more lines of IPython input.
681 """Push one or more lines of IPython input.
682
682
683 This stores the given lines and returns a status code indicating
683 This stores the given lines and returns a status code indicating
684 whether the code forms a complete Python block or not, after processing
684 whether the code forms a complete Python block or not, after processing
685 all input lines for special IPython syntax.
685 all input lines for special IPython syntax.
686
686
687 Any exceptions generated in compilation are swallowed, but if an
687 Any exceptions generated in compilation are swallowed, but if an
688 exception was produced, the method returns True.
688 exception was produced, the method returns True.
689
689
690 Parameters
690 Parameters
691 ----------
691 ----------
692 lines : string
692 lines : string
693 One or more lines of Python input.
693 One or more lines of Python input.
694
694
695 Returns
695 Returns
696 -------
696 -------
697 is_complete : boolean
697 is_complete : boolean
698 True if the current input source (the result of the current input
698 True if the current input source (the result of the current input
699 plus prior inputs) forms a complete Python execution block. Note that
699 plus prior inputs) forms a complete Python execution block. Note that
700 this value is also stored as a private attribute (_is_complete), so it
700 this value is also stored as a private attribute (_is_complete), so it
701 can be queried at any time.
701 can be queried at any time.
702 """
702 """
703 assert isinstance(lines, str)
703 assert isinstance(lines, str)
704 # We must ensure all input is pure unicode
704 # We must ensure all input is pure unicode
705 # ''.splitlines() --> [], but we need to push the empty line to transformers
705 # ''.splitlines() --> [], but we need to push the empty line to transformers
706 lines_list = lines.splitlines()
706 lines_list = lines.splitlines()
707 if not lines_list:
707 if not lines_list:
708 lines_list = ['']
708 lines_list = ['']
709
709
710 # Store raw source before applying any transformations to it. Note
710 # Store raw source before applying any transformations to it. Note
711 # that this must be done *after* the reset() call that would otherwise
711 # that this must be done *after* the reset() call that would otherwise
712 # flush the buffer.
712 # flush the buffer.
713 self._store(lines, self._buffer_raw, 'source_raw')
713 self._store(lines, self._buffer_raw, 'source_raw')
714
714
715 transformed_lines_list = []
715 transformed_lines_list = []
716 for line in lines_list:
716 for line in lines_list:
717 transformed = self._transform_line(line)
717 transformed = self._transform_line(line)
718 if transformed is not None:
718 if transformed is not None:
719 transformed_lines_list.append(transformed)
719 transformed_lines_list.append(transformed)
720
720
721 if transformed_lines_list:
721 if transformed_lines_list:
722 transformed_lines = '\n'.join(transformed_lines_list)
722 transformed_lines = '\n'.join(transformed_lines_list)
723 return super(IPythonInputSplitter, self).push(transformed_lines)
723 return super(IPythonInputSplitter, self).push(transformed_lines)
724 else:
724 else:
725 # Got nothing back from transformers - they must be waiting for
725 # Got nothing back from transformers - they must be waiting for
726 # more input.
726 # more input.
727 return False
727 return False
728
728
729 def _transform_line(self, line):
729 def _transform_line(self, line):
730 """Push a line of input code through the various transformers.
730 """Push a line of input code through the various transformers.
731
731
732 Returns any output from the transformers, or None if a transformer
732 Returns any output from the transformers, or None if a transformer
733 is accumulating lines.
733 is accumulating lines.
734
734
735 Sets self.transformer_accumulating as a side effect.
735 Sets self.transformer_accumulating as a side effect.
736 """
736 """
737 def _accumulating(dbg):
737 def _accumulating(dbg):
738 #print(dbg)
738 #print(dbg)
739 self.transformer_accumulating = True
739 self.transformer_accumulating = True
740 return None
740 return None
741
741
742 for transformer in self.physical_line_transforms:
742 for transformer in self.physical_line_transforms:
743 line = transformer.push(line)
743 line = transformer.push(line)
744 if line is None:
744 if line is None:
745 return _accumulating(transformer)
745 return _accumulating(transformer)
746
746
747 if not self.within_python_line:
747 if not self.within_python_line:
748 line = self.assemble_logical_lines.push(line)
748 line = self.assemble_logical_lines.push(line)
749 if line is None:
749 if line is None:
750 return _accumulating('acc logical line')
750 return _accumulating('acc logical line')
751
751
752 for transformer in self.logical_line_transforms:
752 for transformer in self.logical_line_transforms:
753 line = transformer.push(line)
753 line = transformer.push(line)
754 if line is None:
754 if line is None:
755 return _accumulating(transformer)
755 return _accumulating(transformer)
756
756
757 line = self.assemble_python_lines.push(line)
757 line = self.assemble_python_lines.push(line)
758 if line is None:
758 if line is None:
759 self.within_python_line = True
759 self.within_python_line = True
760 return _accumulating('acc python line')
760 return _accumulating('acc python line')
761 else:
761 else:
762 self.within_python_line = False
762 self.within_python_line = False
763
763
764 for transformer in self.python_line_transforms:
764 for transformer in self.python_line_transforms:
765 line = transformer.push(line)
765 line = transformer.push(line)
766 if line is None:
766 if line is None:
767 return _accumulating(transformer)
767 return _accumulating(transformer)
768
768
769 #print("transformers clear") #debug
769 #print("transformers clear") #debug
770 self.transformer_accumulating = False
770 self.transformer_accumulating = False
771 return line
771 return line
772
772
@@ -1,536 +1,535 b''
1 """DEPRECATED: Input transformer classes to support IPython special syntax.
1 """DEPRECATED: Input transformer classes to support IPython special syntax.
2
2
3 This module was deprecated in IPython 7.0, in favour of inputtransformer2.
3 This module was deprecated in IPython 7.0, in favour of inputtransformer2.
4
4
5 This includes the machinery to recognise and transform ``%magic`` commands,
5 This includes the machinery to recognise and transform ``%magic`` commands,
6 ``!system`` commands, ``help?`` querying, prompt stripping, and so forth.
6 ``!system`` commands, ``help?`` querying, prompt stripping, and so forth.
7 """
7 """
8 import abc
8 import abc
9 import functools
9 import functools
10 import re
10 import re
11 import tokenize
11 import tokenize
12 from tokenize import generate_tokens, untokenize, TokenError
12 from tokenize import generate_tokens, untokenize, TokenError
13 from io import StringIO
13 from io import StringIO
14
14
15 from IPython.core.splitinput import LineInfo
15 from IPython.core.splitinput import LineInfo
16
16
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18 # Globals
18 # Globals
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20
20
21 # The escape sequences that define the syntax transformations IPython will
21 # The escape sequences that define the syntax transformations IPython will
22 # apply to user input. These can NOT be just changed here: many regular
22 # apply to user input. These can NOT be just changed here: many regular
23 # expressions and other parts of the code may use their hardcoded values, and
23 # expressions and other parts of the code may use their hardcoded values, and
24 # for all intents and purposes they constitute the 'IPython syntax', so they
24 # for all intents and purposes they constitute the 'IPython syntax', so they
25 # should be considered fixed.
25 # should be considered fixed.
26
26
27 ESC_SHELL = '!' # Send line to underlying system shell
27 ESC_SHELL = '!' # Send line to underlying system shell
28 ESC_SH_CAP = '!!' # Send line to system shell and capture output
28 ESC_SH_CAP = '!!' # Send line to system shell and capture output
29 ESC_HELP = '?' # Find information about object
29 ESC_HELP = '?' # Find information about object
30 ESC_HELP2 = '??' # Find extra-detailed information about object
30 ESC_HELP2 = '??' # Find extra-detailed information about object
31 ESC_MAGIC = '%' # Call magic function
31 ESC_MAGIC = '%' # Call magic function
32 ESC_MAGIC2 = '%%' # Call cell-magic function
32 ESC_MAGIC2 = '%%' # Call cell-magic function
33 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
33 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
34 ESC_QUOTE2 = ';' # Quote all args as a single string, call
34 ESC_QUOTE2 = ';' # Quote all args as a single string, call
35 ESC_PAREN = '/' # Call first argument with rest of line as arguments
35 ESC_PAREN = '/' # Call first argument with rest of line as arguments
36
36
37 ESC_SEQUENCES = [ESC_SHELL, ESC_SH_CAP, ESC_HELP ,\
37 ESC_SEQUENCES = [ESC_SHELL, ESC_SH_CAP, ESC_HELP ,\
38 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,\
38 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,\
39 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN ]
39 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN ]
40
40
41
41
42 class InputTransformer(metaclass=abc.ABCMeta):
42 class InputTransformer(metaclass=abc.ABCMeta):
43 """Abstract base class for line-based input transformers."""
43 """Abstract base class for line-based input transformers."""
44
44
45 @abc.abstractmethod
45 @abc.abstractmethod
46 def push(self, line):
46 def push(self, line):
47 """Send a line of input to the transformer, returning the transformed
47 """Send a line of input to the transformer, returning the transformed
48 input or None if the transformer is waiting for more input.
48 input or None if the transformer is waiting for more input.
49
49
50 Must be overridden by subclasses.
50 Must be overridden by subclasses.
51
51
52 Implementations may raise ``SyntaxError`` if the input is invalid. No
52 Implementations may raise ``SyntaxError`` if the input is invalid. No
53 other exceptions may be raised.
53 other exceptions may be raised.
54 """
54 """
55 pass
55 pass
56
56
57 @abc.abstractmethod
57 @abc.abstractmethod
58 def reset(self):
58 def reset(self):
59 """Return, transformed any lines that the transformer has accumulated,
59 """Return, transformed any lines that the transformer has accumulated,
60 and reset its internal state.
60 and reset its internal state.
61
61
62 Must be overridden by subclasses.
62 Must be overridden by subclasses.
63 """
63 """
64 pass
64 pass
65
65
66 @classmethod
66 @classmethod
67 def wrap(cls, func):
67 def wrap(cls, func):
68 """Can be used by subclasses as a decorator, to return a factory that
68 """Can be used by subclasses as a decorator, to return a factory that
69 will allow instantiation with the decorated object.
69 will allow instantiation with the decorated object.
70 """
70 """
71 @functools.wraps(func)
71 @functools.wraps(func)
72 def transformer_factory(**kwargs):
72 def transformer_factory(**kwargs):
73 return cls(func, **kwargs)
73 return cls(func, **kwargs)
74
74
75 return transformer_factory
75 return transformer_factory
76
76
77 class StatelessInputTransformer(InputTransformer):
77 class StatelessInputTransformer(InputTransformer):
78 """Wrapper for a stateless input transformer implemented as a function."""
78 """Wrapper for a stateless input transformer implemented as a function."""
79 def __init__(self, func):
79 def __init__(self, func):
80 self.func = func
80 self.func = func
81
81
82 def __repr__(self):
82 def __repr__(self):
83 return "StatelessInputTransformer(func={0!r})".format(self.func)
83 return "StatelessInputTransformer(func={0!r})".format(self.func)
84
84
85 def push(self, line):
85 def push(self, line):
86 """Send a line of input to the transformer, returning the
86 """Send a line of input to the transformer, returning the
87 transformed input."""
87 transformed input."""
88 return self.func(line)
88 return self.func(line)
89
89
90 def reset(self):
90 def reset(self):
91 """No-op - exists for compatibility."""
91 """No-op - exists for compatibility."""
92 pass
92 pass
93
93
94 class CoroutineInputTransformer(InputTransformer):
94 class CoroutineInputTransformer(InputTransformer):
95 """Wrapper for an input transformer implemented as a coroutine."""
95 """Wrapper for an input transformer implemented as a coroutine."""
96 def __init__(self, coro, **kwargs):
96 def __init__(self, coro, **kwargs):
97 # Prime it
97 # Prime it
98 self.coro = coro(**kwargs)
98 self.coro = coro(**kwargs)
99 next(self.coro)
99 next(self.coro)
100
100
101 def __repr__(self):
101 def __repr__(self):
102 return "CoroutineInputTransformer(coro={0!r})".format(self.coro)
102 return "CoroutineInputTransformer(coro={0!r})".format(self.coro)
103
103
104 def push(self, line):
104 def push(self, line):
105 """Send a line of input to the transformer, returning the
105 """Send a line of input to the transformer, returning the
106 transformed input or None if the transformer is waiting for more
106 transformed input or None if the transformer is waiting for more
107 input.
107 input.
108 """
108 """
109 return self.coro.send(line)
109 return self.coro.send(line)
110
110
111 def reset(self):
111 def reset(self):
112 """Return, transformed any lines that the transformer has
112 """Return, transformed any lines that the transformer has
113 accumulated, and reset its internal state.
113 accumulated, and reset its internal state.
114 """
114 """
115 return self.coro.send(None)
115 return self.coro.send(None)
116
116
117 class TokenInputTransformer(InputTransformer):
117 class TokenInputTransformer(InputTransformer):
118 """Wrapper for a token-based input transformer.
118 """Wrapper for a token-based input transformer.
119
119
120 func should accept a list of tokens (5-tuples, see tokenize docs), and
120 func should accept a list of tokens (5-tuples, see tokenize docs), and
121 return an iterable which can be passed to tokenize.untokenize().
121 return an iterable which can be passed to tokenize.untokenize().
122 """
122 """
123 def __init__(self, func):
123 def __init__(self, func):
124 self.func = func
124 self.func = func
125 self.buf = []
125 self.buf = []
126 self.reset_tokenizer()
126 self.reset_tokenizer()
127
127
128 def reset_tokenizer(self):
128 def reset_tokenizer(self):
129 it = iter(self.buf)
129 it = iter(self.buf)
130 self.tokenizer = generate_tokens(it.__next__)
130 self.tokenizer = generate_tokens(it.__next__)
131
131
132 def push(self, line):
132 def push(self, line):
133 self.buf.append(line + '\n')
133 self.buf.append(line + '\n')
134 if all(l.isspace() for l in self.buf):
134 if all(l.isspace() for l in self.buf):
135 return self.reset()
135 return self.reset()
136
136
137 tokens = []
137 tokens = []
138 stop_at_NL = False
138 stop_at_NL = False
139 try:
139 try:
140 for intok in self.tokenizer:
140 for intok in self.tokenizer:
141 tokens.append(intok)
141 tokens.append(intok)
142 t = intok[0]
142 t = intok[0]
143 if t == tokenize.NEWLINE or (stop_at_NL and t == tokenize.NL):
143 if t == tokenize.NEWLINE or (stop_at_NL and t == tokenize.NL):
144 # Stop before we try to pull a line we don't have yet
144 # Stop before we try to pull a line we don't have yet
145 break
145 break
146 elif t == tokenize.ERRORTOKEN:
146 elif t == tokenize.ERRORTOKEN:
147 stop_at_NL = True
147 stop_at_NL = True
148 except TokenError:
148 except TokenError:
149 # Multi-line statement - stop and try again with the next line
149 # Multi-line statement - stop and try again with the next line
150 self.reset_tokenizer()
150 self.reset_tokenizer()
151 return None
151 return None
152
152
153 return self.output(tokens)
153 return self.output(tokens)
154
154
155 def output(self, tokens):
155 def output(self, tokens):
156 self.buf.clear()
156 self.buf.clear()
157 self.reset_tokenizer()
157 self.reset_tokenizer()
158 return untokenize(self.func(tokens)).rstrip('\n')
158 return untokenize(self.func(tokens)).rstrip('\n')
159
159
160 def reset(self):
160 def reset(self):
161 l = ''.join(self.buf)
161 l = ''.join(self.buf)
162 self.buf.clear()
162 self.buf.clear()
163 self.reset_tokenizer()
163 self.reset_tokenizer()
164 if l:
164 if l:
165 return l.rstrip('\n')
165 return l.rstrip('\n')
166
166
167 class assemble_python_lines(TokenInputTransformer):
167 class assemble_python_lines(TokenInputTransformer):
168 def __init__(self):
168 def __init__(self):
169 super(assemble_python_lines, self).__init__(None)
169 super(assemble_python_lines, self).__init__(None)
170
170
171 def output(self, tokens):
171 def output(self, tokens):
172 return self.reset()
172 return self.reset()
173
173
174 @CoroutineInputTransformer.wrap
174 @CoroutineInputTransformer.wrap
175 def assemble_logical_lines():
175 def assemble_logical_lines():
176 r"""Join lines following explicit line continuations (\)"""
176 r"""Join lines following explicit line continuations (\)"""
177 line = ''
177 line = ''
178 while True:
178 while True:
179 line = (yield line)
179 line = (yield line)
180 if not line or line.isspace():
180 if not line or line.isspace():
181 continue
181 continue
182
182
183 parts = []
183 parts = []
184 while line is not None:
184 while line is not None:
185 if line.endswith('\\') and (not has_comment(line)):
185 if line.endswith('\\') and (not has_comment(line)):
186 parts.append(line[:-1])
186 parts.append(line[:-1])
187 line = (yield None) # Get another line
187 line = (yield None) # Get another line
188 else:
188 else:
189 parts.append(line)
189 parts.append(line)
190 break
190 break
191
191
192 # Output
192 # Output
193 line = ''.join(parts)
193 line = ''.join(parts)
194
194
195 # Utilities
195 # Utilities
196 def _make_help_call(target, esc, lspace, next_input=None):
196 def _make_help_call(target, esc, lspace, next_input=None):
197 """Prepares a pinfo(2)/psearch call from a target name and the escape
197 """Prepares a pinfo(2)/psearch call from a target name and the escape
198 (i.e. ? or ??)"""
198 (i.e. ? or ??)"""
199 method = 'pinfo2' if esc == '??' \
199 method = 'pinfo2' if esc == '??' \
200 else 'psearch' if '*' in target \
200 else 'psearch' if '*' in target \
201 else 'pinfo'
201 else 'pinfo'
202 arg = " ".join([method, target])
202 arg = " ".join([method, target])
203 #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args)
203 #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args)
204 t_magic_name, _, t_magic_arg_s = arg.partition(' ')
204 t_magic_name, _, t_magic_arg_s = arg.partition(' ')
205 t_magic_name = t_magic_name.lstrip(ESC_MAGIC)
205 t_magic_name = t_magic_name.lstrip(ESC_MAGIC)
206 if next_input is None:
206 if next_input is None:
207 return '%sget_ipython().run_line_magic(%r, %r)' % (lspace, t_magic_name, t_magic_arg_s)
207 return '%sget_ipython().run_line_magic(%r, %r)' % (lspace, t_magic_name, t_magic_arg_s)
208 else:
208 else:
209 return '%sget_ipython().set_next_input(%r);get_ipython().run_line_magic(%r, %r)' % \
209 return '%sget_ipython().set_next_input(%r);get_ipython().run_line_magic(%r, %r)' % \
210 (lspace, next_input, t_magic_name, t_magic_arg_s)
210 (lspace, next_input, t_magic_name, t_magic_arg_s)
211
211
212 # These define the transformations for the different escape characters.
212 # These define the transformations for the different escape characters.
213 def _tr_system(line_info):
213 def _tr_system(line_info):
214 "Translate lines escaped with: !"
214 "Translate lines escaped with: !"
215 cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
215 cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
216 return '%sget_ipython().system(%r)' % (line_info.pre, cmd)
216 return '%sget_ipython().system(%r)' % (line_info.pre, cmd)
217
217
218 def _tr_system2(line_info):
218 def _tr_system2(line_info):
219 "Translate lines escaped with: !!"
219 "Translate lines escaped with: !!"
220 cmd = line_info.line.lstrip()[2:]
220 cmd = line_info.line.lstrip()[2:]
221 return '%sget_ipython().getoutput(%r)' % (line_info.pre, cmd)
221 return '%sget_ipython().getoutput(%r)' % (line_info.pre, cmd)
222
222
223 def _tr_help(line_info):
223 def _tr_help(line_info):
224 "Translate lines escaped with: ?/??"
224 "Translate lines escaped with: ?/??"
225 # A naked help line should just fire the intro help screen
225 # A naked help line should just fire the intro help screen
226 if not line_info.line[1:]:
226 if not line_info.line[1:]:
227 return 'get_ipython().show_usage()'
227 return 'get_ipython().show_usage()'
228
228
229 return _make_help_call(line_info.ifun, line_info.esc, line_info.pre)
229 return _make_help_call(line_info.ifun, line_info.esc, line_info.pre)
230
230
231 def _tr_magic(line_info):
231 def _tr_magic(line_info):
232 "Translate lines escaped with: %"
232 "Translate lines escaped with: %"
233 tpl = '%sget_ipython().run_line_magic(%r, %r)'
233 tpl = '%sget_ipython().run_line_magic(%r, %r)'
234 if line_info.line.startswith(ESC_MAGIC2):
234 if line_info.line.startswith(ESC_MAGIC2):
235 return line_info.line
235 return line_info.line
236 cmd = ' '.join([line_info.ifun, line_info.the_rest]).strip()
236 cmd = ' '.join([line_info.ifun, line_info.the_rest]).strip()
237 #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args)
237 #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args)
238 t_magic_name, _, t_magic_arg_s = cmd.partition(' ')
238 t_magic_name, _, t_magic_arg_s = cmd.partition(' ')
239 t_magic_name = t_magic_name.lstrip(ESC_MAGIC)
239 t_magic_name = t_magic_name.lstrip(ESC_MAGIC)
240 return tpl % (line_info.pre, t_magic_name, t_magic_arg_s)
240 return tpl % (line_info.pre, t_magic_name, t_magic_arg_s)
241
241
242 def _tr_quote(line_info):
242 def _tr_quote(line_info):
243 "Translate lines escaped with: ,"
243 "Translate lines escaped with: ,"
244 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
244 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
245 '", "'.join(line_info.the_rest.split()) )
245 '", "'.join(line_info.the_rest.split()) )
246
246
247 def _tr_quote2(line_info):
247 def _tr_quote2(line_info):
248 "Translate lines escaped with: ;"
248 "Translate lines escaped with: ;"
249 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
249 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
250 line_info.the_rest)
250 line_info.the_rest)
251
251
252 def _tr_paren(line_info):
252 def _tr_paren(line_info):
253 "Translate lines escaped with: /"
253 "Translate lines escaped with: /"
254 return '%s%s(%s)' % (line_info.pre, line_info.ifun,
254 return '%s%s(%s)' % (line_info.pre, line_info.ifun,
255 ", ".join(line_info.the_rest.split()))
255 ", ".join(line_info.the_rest.split()))
256
256
257 tr = { ESC_SHELL : _tr_system,
257 tr = { ESC_SHELL : _tr_system,
258 ESC_SH_CAP : _tr_system2,
258 ESC_SH_CAP : _tr_system2,
259 ESC_HELP : _tr_help,
259 ESC_HELP : _tr_help,
260 ESC_HELP2 : _tr_help,
260 ESC_HELP2 : _tr_help,
261 ESC_MAGIC : _tr_magic,
261 ESC_MAGIC : _tr_magic,
262 ESC_QUOTE : _tr_quote,
262 ESC_QUOTE : _tr_quote,
263 ESC_QUOTE2 : _tr_quote2,
263 ESC_QUOTE2 : _tr_quote2,
264 ESC_PAREN : _tr_paren }
264 ESC_PAREN : _tr_paren }
265
265
266 @StatelessInputTransformer.wrap
266 @StatelessInputTransformer.wrap
267 def escaped_commands(line):
267 def escaped_commands(line):
268 """Transform escaped commands - %magic, !system, ?help + various autocalls.
268 """Transform escaped commands - %magic, !system, ?help + various autocalls.
269 """
269 """
270 if not line or line.isspace():
270 if not line or line.isspace():
271 return line
271 return line
272 lineinf = LineInfo(line)
272 lineinf = LineInfo(line)
273 if lineinf.esc not in tr:
273 if lineinf.esc not in tr:
274 return line
274 return line
275
275
276 return tr[lineinf.esc](lineinf)
276 return tr[lineinf.esc](lineinf)
277
277
278 _initial_space_re = re.compile(r'\s*')
278 _initial_space_re = re.compile(r'\s*')
279
279
280 _help_end_re = re.compile(r"""(%{0,2}
280 _help_end_re = re.compile(r"""(%{0,2}
281 (?!\d)[\w*]+ # Variable name
281 (?!\d)[\w*]+ # Variable name
282 (\.(?!\d)[\w*]+)* # .etc.etc
282 (\.(?!\d)[\w*]+)* # .etc.etc
283 )
283 )
284 (\?\??)$ # ? or ??
284 (\?\??)$ # ? or ??
285 """,
285 """,
286 re.VERBOSE)
286 re.VERBOSE)
287
287
288 # Extra pseudotokens for multiline strings and data structures
288 # Extra pseudotokens for multiline strings and data structures
289 _MULTILINE_STRING = object()
289 _MULTILINE_STRING = object()
290 _MULTILINE_STRUCTURE = object()
290 _MULTILINE_STRUCTURE = object()
291
291
292 def _line_tokens(line):
292 def _line_tokens(line):
293 """Helper for has_comment and ends_in_comment_or_string."""
293 """Helper for has_comment and ends_in_comment_or_string."""
294 readline = StringIO(line).readline
294 readline = StringIO(line).readline
295 toktypes = set()
295 toktypes = set()
296 try:
296 try:
297 for t in generate_tokens(readline):
297 for t in generate_tokens(readline):
298 toktypes.add(t[0])
298 toktypes.add(t[0])
299 except TokenError as e:
299 except TokenError as e:
300 # There are only two cases where a TokenError is raised.
300 # There are only two cases where a TokenError is raised.
301 if 'multi-line string' in e.args[0]:
301 if 'multi-line string' in e.args[0]:
302 toktypes.add(_MULTILINE_STRING)
302 toktypes.add(_MULTILINE_STRING)
303 else:
303 else:
304 toktypes.add(_MULTILINE_STRUCTURE)
304 toktypes.add(_MULTILINE_STRUCTURE)
305 return toktypes
305 return toktypes
306
306
307 def has_comment(src):
307 def has_comment(src):
308 """Indicate whether an input line has (i.e. ends in, or is) a comment.
308 """Indicate whether an input line has (i.e. ends in, or is) a comment.
309
309
310 This uses tokenize, so it can distinguish comments from # inside strings.
310 This uses tokenize, so it can distinguish comments from # inside strings.
311
311
312 Parameters
312 Parameters
313 ----------
313 ----------
314 src : string
314 src : string
315 A single line input string.
315 A single line input string.
316
316
317 Returns
317 Returns
318 -------
318 -------
319 comment : bool
319 comment : bool
320 True if source has a comment.
320 True if source has a comment.
321 """
321 """
322 return (tokenize.COMMENT in _line_tokens(src))
322 return (tokenize.COMMENT in _line_tokens(src))
323
323
324 def ends_in_comment_or_string(src):
324 def ends_in_comment_or_string(src):
325 """Indicates whether or not an input line ends in a comment or within
325 """Indicates whether or not an input line ends in a comment or within
326 a multiline string.
326 a multiline string.
327
327
328 Parameters
328 Parameters
329 ----------
329 ----------
330 src : string
330 src : string
331 A single line input string.
331 A single line input string.
332
332
333 Returns
333 Returns
334 -------
334 -------
335 comment : bool
335 comment : bool
336 True if source ends in a comment or multiline string.
336 True if source ends in a comment or multiline string.
337 """
337 """
338 toktypes = _line_tokens(src)
338 toktypes = _line_tokens(src)
339 return (tokenize.COMMENT in toktypes) or (_MULTILINE_STRING in toktypes)
339 return (tokenize.COMMENT in toktypes) or (_MULTILINE_STRING in toktypes)
340
340
341
341
342 @StatelessInputTransformer.wrap
342 @StatelessInputTransformer.wrap
343 def help_end(line):
343 def help_end(line):
344 """Translate lines with ?/?? at the end"""
344 """Translate lines with ?/?? at the end"""
345 m = _help_end_re.search(line)
345 m = _help_end_re.search(line)
346 if m is None or ends_in_comment_or_string(line):
346 if m is None or ends_in_comment_or_string(line):
347 return line
347 return line
348 target = m.group(1)
348 target = m.group(1)
349 esc = m.group(3)
349 esc = m.group(3)
350 lspace = _initial_space_re.match(line).group(0)
350 lspace = _initial_space_re.match(line).group(0)
351
351
352 # If we're mid-command, put it back on the next prompt for the user.
352 # If we're mid-command, put it back on the next prompt for the user.
353 next_input = line.rstrip('?') if line.strip() != m.group(0) else None
353 next_input = line.rstrip('?') if line.strip() != m.group(0) else None
354
354
355 return _make_help_call(target, esc, lspace, next_input)
355 return _make_help_call(target, esc, lspace, next_input)
356
356
357
357
358 @CoroutineInputTransformer.wrap
358 @CoroutineInputTransformer.wrap
359 def cellmagic(end_on_blank_line=False):
359 def cellmagic(end_on_blank_line=False):
360 """Captures & transforms cell magics.
360 """Captures & transforms cell magics.
361
361
362 After a cell magic is started, this stores up any lines it gets until it is
362 After a cell magic is started, this stores up any lines it gets until it is
363 reset (sent None).
363 reset (sent None).
364 """
364 """
365 tpl = 'get_ipython().run_cell_magic(%r, %r, %r)'
365 tpl = 'get_ipython().run_cell_magic(%r, %r, %r)'
366 cellmagic_help_re = re.compile(r'%%\w+\?')
366 cellmagic_help_re = re.compile(r'%%\w+\?')
367 line = ''
367 line = ''
368 while True:
368 while True:
369 line = (yield line)
369 line = (yield line)
370 # consume leading empty lines
370 # consume leading empty lines
371 while not line:
371 while not line:
372 line = (yield line)
372 line = (yield line)
373
373
374 if not line.startswith(ESC_MAGIC2):
374 if not line.startswith(ESC_MAGIC2):
375 # This isn't a cell magic, idle waiting for reset then start over
375 # This isn't a cell magic, idle waiting for reset then start over
376 while line is not None:
376 while line is not None:
377 line = (yield line)
377 line = (yield line)
378 continue
378 continue
379
379
380 if cellmagic_help_re.match(line):
380 if cellmagic_help_re.match(line):
381 # This case will be handled by help_end
381 # This case will be handled by help_end
382 continue
382 continue
383
383
384 first = line
384 first = line
385 body = []
385 body = []
386 line = (yield None)
386 line = (yield None)
387 while (line is not None) and \
387 while (line is not None) and \
388 ((line.strip() != '') or not end_on_blank_line):
388 ((line.strip() != '') or not end_on_blank_line):
389 body.append(line)
389 body.append(line)
390 line = (yield None)
390 line = (yield None)
391
391
392 # Output
392 # Output
393 magic_name, _, first = first.partition(' ')
393 magic_name, _, first = first.partition(' ')
394 magic_name = magic_name.lstrip(ESC_MAGIC2)
394 magic_name = magic_name.lstrip(ESC_MAGIC2)
395 line = tpl % (magic_name, first, u'\n'.join(body))
395 line = tpl % (magic_name, first, u'\n'.join(body))
396
396
397
397
398 def _strip_prompts(prompt_re, initial_re=None, turnoff_re=None):
398 def _strip_prompts(prompt_re, initial_re=None, turnoff_re=None):
399 """Remove matching input prompts from a block of input.
399 """Remove matching input prompts from a block of input.
400
400
401 Parameters
401 Parameters
402 ----------
402 ----------
403 prompt_re : regular expression
403 prompt_re : regular expression
404 A regular expression matching any input prompt (including continuation)
404 A regular expression matching any input prompt (including continuation)
405 initial_re : regular expression, optional
405 initial_re : regular expression, optional
406 A regular expression matching only the initial prompt, but not continuation.
406 A regular expression matching only the initial prompt, but not continuation.
407 If no initial expression is given, prompt_re will be used everywhere.
407 If no initial expression is given, prompt_re will be used everywhere.
408 Used mainly for plain Python prompts, where the continuation prompt
408 Used mainly for plain Python prompts, where the continuation prompt
409 ``...`` is a valid Python expression in Python 3, so shouldn't be stripped.
409 ``...`` is a valid Python expression in Python 3, so shouldn't be stripped.
410
411 If initial_re and prompt_re differ,
410 If initial_re and prompt_re differ,
412 only initial_re will be tested against the first line.
411 only initial_re will be tested against the first line.
413 If any prompt is found on the first two lines,
412 If any prompt is found on the first two lines,
414 prompts will be stripped from the rest of the block.
413 prompts will be stripped from the rest of the block.
415 """
414 """
416 if initial_re is None:
415 if initial_re is None:
417 initial_re = prompt_re
416 initial_re = prompt_re
418 line = ''
417 line = ''
419 while True:
418 while True:
420 line = (yield line)
419 line = (yield line)
421
420
422 # First line of cell
421 # First line of cell
423 if line is None:
422 if line is None:
424 continue
423 continue
425 out, n1 = initial_re.subn('', line, count=1)
424 out, n1 = initial_re.subn('', line, count=1)
426 if turnoff_re and not n1:
425 if turnoff_re and not n1:
427 if turnoff_re.match(line):
426 if turnoff_re.match(line):
428 # We're in e.g. a cell magic; disable this transformer for
427 # We're in e.g. a cell magic; disable this transformer for
429 # the rest of the cell.
428 # the rest of the cell.
430 while line is not None:
429 while line is not None:
431 line = (yield line)
430 line = (yield line)
432 continue
431 continue
433
432
434 line = (yield out)
433 line = (yield out)
435
434
436 if line is None:
435 if line is None:
437 continue
436 continue
438 # check for any prompt on the second line of the cell,
437 # check for any prompt on the second line of the cell,
439 # because people often copy from just after the first prompt,
438 # because people often copy from just after the first prompt,
440 # so we might not see it in the first line.
439 # so we might not see it in the first line.
441 out, n2 = prompt_re.subn('', line, count=1)
440 out, n2 = prompt_re.subn('', line, count=1)
442 line = (yield out)
441 line = (yield out)
443
442
444 if n1 or n2:
443 if n1 or n2:
445 # Found a prompt in the first two lines - check for it in
444 # Found a prompt in the first two lines - check for it in
446 # the rest of the cell as well.
445 # the rest of the cell as well.
447 while line is not None:
446 while line is not None:
448 line = (yield prompt_re.sub('', line, count=1))
447 line = (yield prompt_re.sub('', line, count=1))
449
448
450 else:
449 else:
451 # Prompts not in input - wait for reset
450 # Prompts not in input - wait for reset
452 while line is not None:
451 while line is not None:
453 line = (yield line)
452 line = (yield line)
454
453
455 @CoroutineInputTransformer.wrap
454 @CoroutineInputTransformer.wrap
456 def classic_prompt():
455 def classic_prompt():
457 """Strip the >>>/... prompts of the Python interactive shell."""
456 """Strip the >>>/... prompts of the Python interactive shell."""
458 # FIXME: non-capturing version (?:...) usable?
457 # FIXME: non-capturing version (?:...) usable?
459 prompt_re = re.compile(r'^(>>>|\.\.\.)( |$)')
458 prompt_re = re.compile(r'^(>>>|\.\.\.)( |$)')
460 initial_re = re.compile(r'^>>>( |$)')
459 initial_re = re.compile(r'^>>>( |$)')
461 # Any %magic/!system is IPython syntax, so we needn't look for >>> prompts
460 # Any %magic/!system is IPython syntax, so we needn't look for >>> prompts
462 turnoff_re = re.compile(r'^[%!]')
461 turnoff_re = re.compile(r'^[%!]')
463 return _strip_prompts(prompt_re, initial_re, turnoff_re)
462 return _strip_prompts(prompt_re, initial_re, turnoff_re)
464
463
465 @CoroutineInputTransformer.wrap
464 @CoroutineInputTransformer.wrap
466 def ipy_prompt():
465 def ipy_prompt():
467 """Strip IPython's In [1]:/...: prompts."""
466 """Strip IPython's In [1]:/...: prompts."""
468 # FIXME: non-capturing version (?:...) usable?
467 # FIXME: non-capturing version (?:...) usable?
469 prompt_re = re.compile(r'^(In \[\d+\]: |\s*\.{3,}: ?)')
468 prompt_re = re.compile(r'^(In \[\d+\]: |\s*\.{3,}: ?)')
470 # Disable prompt stripping inside cell magics
469 # Disable prompt stripping inside cell magics
471 turnoff_re = re.compile(r'^%%')
470 turnoff_re = re.compile(r'^%%')
472 return _strip_prompts(prompt_re, turnoff_re=turnoff_re)
471 return _strip_prompts(prompt_re, turnoff_re=turnoff_re)
473
472
474
473
475 @CoroutineInputTransformer.wrap
474 @CoroutineInputTransformer.wrap
476 def leading_indent():
475 def leading_indent():
477 """Remove leading indentation.
476 """Remove leading indentation.
478
477
479 If the first line starts with a spaces or tabs, the same whitespace will be
478 If the first line starts with a spaces or tabs, the same whitespace will be
480 removed from each following line until it is reset.
479 removed from each following line until it is reset.
481 """
480 """
482 space_re = re.compile(r'^[ \t]+')
481 space_re = re.compile(r'^[ \t]+')
483 line = ''
482 line = ''
484 while True:
483 while True:
485 line = (yield line)
484 line = (yield line)
486
485
487 if line is None:
486 if line is None:
488 continue
487 continue
489
488
490 m = space_re.match(line)
489 m = space_re.match(line)
491 if m:
490 if m:
492 space = m.group(0)
491 space = m.group(0)
493 while line is not None:
492 while line is not None:
494 if line.startswith(space):
493 if line.startswith(space):
495 line = line[len(space):]
494 line = line[len(space):]
496 line = (yield line)
495 line = (yield line)
497 else:
496 else:
498 # No leading spaces - wait for reset
497 # No leading spaces - wait for reset
499 while line is not None:
498 while line is not None:
500 line = (yield line)
499 line = (yield line)
501
500
502
501
503 _assign_pat = \
502 _assign_pat = \
504 r'''(?P<lhs>(\s*)
503 r'''(?P<lhs>(\s*)
505 ([\w\.]+) # Initial identifier
504 ([\w\.]+) # Initial identifier
506 (\s*,\s*
505 (\s*,\s*
507 \*?[\w\.]+)* # Further identifiers for unpacking
506 \*?[\w\.]+)* # Further identifiers for unpacking
508 \s*?,? # Trailing comma
507 \s*?,? # Trailing comma
509 )
508 )
510 \s*=\s*
509 \s*=\s*
511 '''
510 '''
512
511
513 assign_system_re = re.compile(r'{}!\s*(?P<cmd>.*)'.format(_assign_pat), re.VERBOSE)
512 assign_system_re = re.compile(r'{}!\s*(?P<cmd>.*)'.format(_assign_pat), re.VERBOSE)
514 assign_system_template = '%s = get_ipython().getoutput(%r)'
513 assign_system_template = '%s = get_ipython().getoutput(%r)'
515 @StatelessInputTransformer.wrap
514 @StatelessInputTransformer.wrap
516 def assign_from_system(line):
515 def assign_from_system(line):
517 """Transform assignment from system commands (e.g. files = !ls)"""
516 """Transform assignment from system commands (e.g. files = !ls)"""
518 m = assign_system_re.match(line)
517 m = assign_system_re.match(line)
519 if m is None:
518 if m is None:
520 return line
519 return line
521
520
522 return assign_system_template % m.group('lhs', 'cmd')
521 return assign_system_template % m.group('lhs', 'cmd')
523
522
524 assign_magic_re = re.compile(r'{}%\s*(?P<cmd>.*)'.format(_assign_pat), re.VERBOSE)
523 assign_magic_re = re.compile(r'{}%\s*(?P<cmd>.*)'.format(_assign_pat), re.VERBOSE)
525 assign_magic_template = '%s = get_ipython().run_line_magic(%r, %r)'
524 assign_magic_template = '%s = get_ipython().run_line_magic(%r, %r)'
526 @StatelessInputTransformer.wrap
525 @StatelessInputTransformer.wrap
527 def assign_from_magic(line):
526 def assign_from_magic(line):
528 """Transform assignment from magic commands (e.g. a = %who_ls)"""
527 """Transform assignment from magic commands (e.g. a = %who_ls)"""
529 m = assign_magic_re.match(line)
528 m = assign_magic_re.match(line)
530 if m is None:
529 if m is None:
531 return line
530 return line
532 #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args)
531 #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args)
533 m_lhs, m_cmd = m.group('lhs', 'cmd')
532 m_lhs, m_cmd = m.group('lhs', 'cmd')
534 t_magic_name, _, t_magic_arg_s = m_cmd.partition(' ')
533 t_magic_name, _, t_magic_arg_s = m_cmd.partition(' ')
535 t_magic_name = t_magic_name.lstrip(ESC_MAGIC)
534 t_magic_name = t_magic_name.lstrip(ESC_MAGIC)
536 return assign_magic_template % (m_lhs, t_magic_name, t_magic_arg_s)
535 return assign_magic_template % (m_lhs, t_magic_name, t_magic_arg_s)
@@ -1,796 +1,796 b''
1 """Input transformer machinery to support IPython special syntax.
1 """Input transformer machinery to support IPython special syntax.
2
2
3 This includes the machinery to recognise and transform ``%magic`` commands,
3 This includes the machinery to recognise and transform ``%magic`` commands,
4 ``!system`` commands, ``help?`` querying, prompt stripping, and so forth.
4 ``!system`` commands, ``help?`` querying, prompt stripping, and so forth.
5
5
6 Added: IPython 7.0. Replaces inputsplitter and inputtransformer which were
6 Added: IPython 7.0. Replaces inputsplitter and inputtransformer which were
7 deprecated in 7.0.
7 deprecated in 7.0.
8 """
8 """
9
9
10 # Copyright (c) IPython Development Team.
10 # Copyright (c) IPython Development Team.
11 # Distributed under the terms of the Modified BSD License.
11 # Distributed under the terms of the Modified BSD License.
12
12
13 import ast
13 import ast
14 import sys
14 import sys
15 from codeop import CommandCompiler, Compile
15 from codeop import CommandCompiler, Compile
16 import re
16 import re
17 import tokenize
17 import tokenize
18 from typing import List, Tuple, Optional, Any
18 from typing import List, Tuple, Optional, Any
19 import warnings
19 import warnings
20
20
21 _indent_re = re.compile(r'^[ \t]+')
21 _indent_re = re.compile(r'^[ \t]+')
22
22
23 def leading_empty_lines(lines):
23 def leading_empty_lines(lines):
24 """Remove leading empty lines
24 """Remove leading empty lines
25
25
26 If the leading lines are empty or contain only whitespace, they will be
26 If the leading lines are empty or contain only whitespace, they will be
27 removed.
27 removed.
28 """
28 """
29 if not lines:
29 if not lines:
30 return lines
30 return lines
31 for i, line in enumerate(lines):
31 for i, line in enumerate(lines):
32 if line and not line.isspace():
32 if line and not line.isspace():
33 return lines[i:]
33 return lines[i:]
34 return lines
34 return lines
35
35
36 def leading_indent(lines):
36 def leading_indent(lines):
37 """Remove leading indentation.
37 """Remove leading indentation.
38
38
39 If the first line starts with a spaces or tabs, the same whitespace will be
39 If the first line starts with a spaces or tabs, the same whitespace will be
40 removed from each following line in the cell.
40 removed from each following line in the cell.
41 """
41 """
42 if not lines:
42 if not lines:
43 return lines
43 return lines
44 m = _indent_re.match(lines[0])
44 m = _indent_re.match(lines[0])
45 if not m:
45 if not m:
46 return lines
46 return lines
47 space = m.group(0)
47 space = m.group(0)
48 n = len(space)
48 n = len(space)
49 return [l[n:] if l.startswith(space) else l
49 return [l[n:] if l.startswith(space) else l
50 for l in lines]
50 for l in lines]
51
51
52 class PromptStripper:
52 class PromptStripper:
53 """Remove matching input prompts from a block of input.
53 """Remove matching input prompts from a block of input.
54
54
55 Parameters
55 Parameters
56 ----------
56 ----------
57 prompt_re : regular expression
57 prompt_re : regular expression
58 A regular expression matching any input prompt (including continuation,
58 A regular expression matching any input prompt (including continuation,
59 e.g. ``...``)
59 e.g. ``...``)
60 initial_re : regular expression, optional
60 initial_re : regular expression, optional
61 A regular expression matching only the initial prompt, but not continuation.
61 A regular expression matching only the initial prompt, but not continuation.
62 If no initial expression is given, prompt_re will be used everywhere.
62 If no initial expression is given, prompt_re will be used everywhere.
63 Used mainly for plain Python prompts (``>>>``), where the continuation prompt
63 Used mainly for plain Python prompts (``>>>``), where the continuation prompt
64 ``...`` is a valid Python expression in Python 3, so shouldn't be stripped.
64 ``...`` is a valid Python expression in Python 3, so shouldn't be stripped.
65
65
66 Notes
66 Notes
67 -----
67 -----
68
68
69 If initial_re and prompt_re differ,
69 If initial_re and prompt_re differ,
70 only initial_re will be tested against the first line.
70 only initial_re will be tested against the first line.
71 If any prompt is found on the first two lines,
71 If any prompt is found on the first two lines,
72 prompts will be stripped from the rest of the block.
72 prompts will be stripped from the rest of the block.
73 """
73 """
74 def __init__(self, prompt_re, initial_re=None):
74 def __init__(self, prompt_re, initial_re=None):
75 self.prompt_re = prompt_re
75 self.prompt_re = prompt_re
76 self.initial_re = initial_re or prompt_re
76 self.initial_re = initial_re or prompt_re
77
77
78 def _strip(self, lines):
78 def _strip(self, lines):
79 return [self.prompt_re.sub('', l, count=1) for l in lines]
79 return [self.prompt_re.sub('', l, count=1) for l in lines]
80
80
81 def __call__(self, lines):
81 def __call__(self, lines):
82 if not lines:
82 if not lines:
83 return lines
83 return lines
84 if self.initial_re.match(lines[0]) or \
84 if self.initial_re.match(lines[0]) or \
85 (len(lines) > 1 and self.prompt_re.match(lines[1])):
85 (len(lines) > 1 and self.prompt_re.match(lines[1])):
86 return self._strip(lines)
86 return self._strip(lines)
87 return lines
87 return lines
88
88
89 classic_prompt = PromptStripper(
89 classic_prompt = PromptStripper(
90 prompt_re=re.compile(r'^(>>>|\.\.\.)( |$)'),
90 prompt_re=re.compile(r'^(>>>|\.\.\.)( |$)'),
91 initial_re=re.compile(r'^>>>( |$)')
91 initial_re=re.compile(r'^>>>( |$)')
92 )
92 )
93
93
94 ipython_prompt = PromptStripper(
94 ipython_prompt = PromptStripper(
95 re.compile(
95 re.compile(
96 r"""
96 r"""
97 ^( # Match from the beginning of a line, either:
97 ^( # Match from the beginning of a line, either:
98
98
99 # 1. First-line prompt:
99 # 1. First-line prompt:
100 ((\[nav\]|\[ins\])?\ )? # Vi editing mode prompt, if it's there
100 ((\[nav\]|\[ins\])?\ )? # Vi editing mode prompt, if it's there
101 In\ # The 'In' of the prompt, with a space
101 In\ # The 'In' of the prompt, with a space
102 \[\d+\]: # Command index, as displayed in the prompt
102 \[\d+\]: # Command index, as displayed in the prompt
103 \ # With a mandatory trailing space
103 \ # With a mandatory trailing space
104
104
105 | # ... or ...
105 | # ... or ...
106
106
107 # 2. The three dots of the multiline prompt
107 # 2. The three dots of the multiline prompt
108 \s* # All leading whitespace characters
108 \s* # All leading whitespace characters
109 \.{3,}: # The three (or more) dots
109 \.{3,}: # The three (or more) dots
110 \ ? # With an optional trailing space
110 \ ? # With an optional trailing space
111
111
112 )
112 )
113 """,
113 """,
114 re.VERBOSE,
114 re.VERBOSE,
115 )
115 )
116 )
116 )
117
117
118
118
119 def cell_magic(lines):
119 def cell_magic(lines):
120 if not lines or not lines[0].startswith('%%'):
120 if not lines or not lines[0].startswith('%%'):
121 return lines
121 return lines
122 if re.match(r'%%\w+\?', lines[0]):
122 if re.match(r'%%\w+\?', lines[0]):
123 # This case will be handled by help_end
123 # This case will be handled by help_end
124 return lines
124 return lines
125 magic_name, _, first_line = lines[0][2:].rstrip().partition(' ')
125 magic_name, _, first_line = lines[0][2:].rstrip().partition(' ')
126 body = ''.join(lines[1:])
126 body = ''.join(lines[1:])
127 return ['get_ipython().run_cell_magic(%r, %r, %r)\n'
127 return ['get_ipython().run_cell_magic(%r, %r, %r)\n'
128 % (magic_name, first_line, body)]
128 % (magic_name, first_line, body)]
129
129
130
130
131 def _find_assign_op(token_line) -> Optional[int]:
131 def _find_assign_op(token_line) -> Optional[int]:
132 """Get the index of the first assignment in the line ('=' not inside brackets)
132 """Get the index of the first assignment in the line ('=' not inside brackets)
133
133
134 Note: We don't try to support multiple special assignment (a = b = %foo)
134 Note: We don't try to support multiple special assignment (a = b = %foo)
135 """
135 """
136 paren_level = 0
136 paren_level = 0
137 for i, ti in enumerate(token_line):
137 for i, ti in enumerate(token_line):
138 s = ti.string
138 s = ti.string
139 if s == '=' and paren_level == 0:
139 if s == '=' and paren_level == 0:
140 return i
140 return i
141 if s in {'(','[','{'}:
141 if s in {'(','[','{'}:
142 paren_level += 1
142 paren_level += 1
143 elif s in {')', ']', '}'}:
143 elif s in {')', ']', '}'}:
144 if paren_level > 0:
144 if paren_level > 0:
145 paren_level -= 1
145 paren_level -= 1
146 return None
146 return None
147
147
148 def find_end_of_continued_line(lines, start_line: int):
148 def find_end_of_continued_line(lines, start_line: int):
149 """Find the last line of a line explicitly extended using backslashes.
149 """Find the last line of a line explicitly extended using backslashes.
150
150
151 Uses 0-indexed line numbers.
151 Uses 0-indexed line numbers.
152 """
152 """
153 end_line = start_line
153 end_line = start_line
154 while lines[end_line].endswith('\\\n'):
154 while lines[end_line].endswith('\\\n'):
155 end_line += 1
155 end_line += 1
156 if end_line >= len(lines):
156 if end_line >= len(lines):
157 break
157 break
158 return end_line
158 return end_line
159
159
160 def assemble_continued_line(lines, start: Tuple[int, int], end_line: int):
160 def assemble_continued_line(lines, start: Tuple[int, int], end_line: int):
161 r"""Assemble a single line from multiple continued line pieces
161 r"""Assemble a single line from multiple continued line pieces
162
162
163 Continued lines are lines ending in ``\``, and the line following the last
163 Continued lines are lines ending in ``\``, and the line following the last
164 ``\`` in the block.
164 ``\`` in the block.
165
165
166 For example, this code continues over multiple lines::
166 For example, this code continues over multiple lines::
167
167
168 if (assign_ix is not None) \
168 if (assign_ix is not None) \
169 and (len(line) >= assign_ix + 2) \
169 and (len(line) >= assign_ix + 2) \
170 and (line[assign_ix+1].string == '%') \
170 and (line[assign_ix+1].string == '%') \
171 and (line[assign_ix+2].type == tokenize.NAME):
171 and (line[assign_ix+2].type == tokenize.NAME):
172
172
173 This statement contains four continued line pieces.
173 This statement contains four continued line pieces.
174 Assembling these pieces into a single line would give::
174 Assembling these pieces into a single line would give::
175
175
176 if (assign_ix is not None) and (len(line) >= assign_ix + 2) and (line[...
176 if (assign_ix is not None) and (len(line) >= assign_ix + 2) and (line[...
177
177
178 This uses 0-indexed line numbers. *start* is (lineno, colno).
178 This uses 0-indexed line numbers. *start* is (lineno, colno).
179
179
180 Used to allow ``%magic`` and ``!system`` commands to be continued over
180 Used to allow ``%magic`` and ``!system`` commands to be continued over
181 multiple lines.
181 multiple lines.
182 """
182 """
183 parts = [lines[start[0]][start[1]:]] + lines[start[0]+1:end_line+1]
183 parts = [lines[start[0]][start[1]:]] + lines[start[0]+1:end_line+1]
184 return ' '.join([p.rstrip()[:-1] for p in parts[:-1]] # Strip backslash+newline
184 return ' '.join([p.rstrip()[:-1] for p in parts[:-1]] # Strip backslash+newline
185 + [parts[-1].rstrip()]) # Strip newline from last line
185 + [parts[-1].rstrip()]) # Strip newline from last line
186
186
187 class TokenTransformBase:
187 class TokenTransformBase:
188 """Base class for transformations which examine tokens.
188 """Base class for transformations which examine tokens.
189
189
190 Special syntax should not be transformed when it occurs inside strings or
190 Special syntax should not be transformed when it occurs inside strings or
191 comments. This is hard to reliably avoid with regexes. The solution is to
191 comments. This is hard to reliably avoid with regexes. The solution is to
192 tokenise the code as Python, and recognise the special syntax in the tokens.
192 tokenise the code as Python, and recognise the special syntax in the tokens.
193
193
194 IPython's special syntax is not valid Python syntax, so tokenising may go
194 IPython's special syntax is not valid Python syntax, so tokenising may go
195 wrong after the special syntax starts. These classes therefore find and
195 wrong after the special syntax starts. These classes therefore find and
196 transform *one* instance of special syntax at a time into regular Python
196 transform *one* instance of special syntax at a time into regular Python
197 syntax. After each transformation, tokens are regenerated to find the next
197 syntax. After each transformation, tokens are regenerated to find the next
198 piece of special syntax.
198 piece of special syntax.
199
199
200 Subclasses need to implement one class method (find)
200 Subclasses need to implement one class method (find)
201 and one regular method (transform).
201 and one regular method (transform).
202
202
203 The priority attribute can select which transformation to apply if multiple
203 The priority attribute can select which transformation to apply if multiple
204 transformers match in the same place. Lower numbers have higher priority.
204 transformers match in the same place. Lower numbers have higher priority.
205 This allows "%magic?" to be turned into a help call rather than a magic call.
205 This allows "%magic?" to be turned into a help call rather than a magic call.
206 """
206 """
207 # Lower numbers -> higher priority (for matches in the same location)
207 # Lower numbers -> higher priority (for matches in the same location)
208 priority = 10
208 priority = 10
209
209
210 def sortby(self):
210 def sortby(self):
211 return self.start_line, self.start_col, self.priority
211 return self.start_line, self.start_col, self.priority
212
212
213 def __init__(self, start):
213 def __init__(self, start):
214 self.start_line = start[0] - 1 # Shift from 1-index to 0-index
214 self.start_line = start[0] - 1 # Shift from 1-index to 0-index
215 self.start_col = start[1]
215 self.start_col = start[1]
216
216
217 @classmethod
217 @classmethod
218 def find(cls, tokens_by_line):
218 def find(cls, tokens_by_line):
219 """Find one instance of special syntax in the provided tokens.
219 """Find one instance of special syntax in the provided tokens.
220
220
221 Tokens are grouped into logical lines for convenience,
221 Tokens are grouped into logical lines for convenience,
222 so it is easy to e.g. look at the first token of each line.
222 so it is easy to e.g. look at the first token of each line.
223 *tokens_by_line* is a list of lists of tokenize.TokenInfo objects.
223 *tokens_by_line* is a list of lists of tokenize.TokenInfo objects.
224
224
225 This should return an instance of its class, pointing to the start
225 This should return an instance of its class, pointing to the start
226 position it has found, or None if it found no match.
226 position it has found, or None if it found no match.
227 """
227 """
228 raise NotImplementedError
228 raise NotImplementedError
229
229
230 def transform(self, lines: List[str]):
230 def transform(self, lines: List[str]):
231 """Transform one instance of special syntax found by ``find()``
231 """Transform one instance of special syntax found by ``find()``
232
232
233 Takes a list of strings representing physical lines,
233 Takes a list of strings representing physical lines,
234 returns a similar list of transformed lines.
234 returns a similar list of transformed lines.
235 """
235 """
236 raise NotImplementedError
236 raise NotImplementedError
237
237
238 class MagicAssign(TokenTransformBase):
238 class MagicAssign(TokenTransformBase):
239 """Transformer for assignments from magics (a = %foo)"""
239 """Transformer for assignments from magics (a = %foo)"""
240 @classmethod
240 @classmethod
241 def find(cls, tokens_by_line):
241 def find(cls, tokens_by_line):
242 """Find the first magic assignment (a = %foo) in the cell.
242 """Find the first magic assignment (a = %foo) in the cell.
243 """
243 """
244 for line in tokens_by_line:
244 for line in tokens_by_line:
245 assign_ix = _find_assign_op(line)
245 assign_ix = _find_assign_op(line)
246 if (assign_ix is not None) \
246 if (assign_ix is not None) \
247 and (len(line) >= assign_ix + 2) \
247 and (len(line) >= assign_ix + 2) \
248 and (line[assign_ix+1].string == '%') \
248 and (line[assign_ix+1].string == '%') \
249 and (line[assign_ix+2].type == tokenize.NAME):
249 and (line[assign_ix+2].type == tokenize.NAME):
250 return cls(line[assign_ix+1].start)
250 return cls(line[assign_ix+1].start)
251
251
252 def transform(self, lines: List[str]):
252 def transform(self, lines: List[str]):
253 """Transform a magic assignment found by the ``find()`` classmethod.
253 """Transform a magic assignment found by the ``find()`` classmethod.
254 """
254 """
255 start_line, start_col = self.start_line, self.start_col
255 start_line, start_col = self.start_line, self.start_col
256 lhs = lines[start_line][:start_col]
256 lhs = lines[start_line][:start_col]
257 end_line = find_end_of_continued_line(lines, start_line)
257 end_line = find_end_of_continued_line(lines, start_line)
258 rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
258 rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
259 assert rhs.startswith('%'), rhs
259 assert rhs.startswith('%'), rhs
260 magic_name, _, args = rhs[1:].partition(' ')
260 magic_name, _, args = rhs[1:].partition(' ')
261
261
262 lines_before = lines[:start_line]
262 lines_before = lines[:start_line]
263 call = "get_ipython().run_line_magic({!r}, {!r})".format(magic_name, args)
263 call = "get_ipython().run_line_magic({!r}, {!r})".format(magic_name, args)
264 new_line = lhs + call + '\n'
264 new_line = lhs + call + '\n'
265 lines_after = lines[end_line+1:]
265 lines_after = lines[end_line+1:]
266
266
267 return lines_before + [new_line] + lines_after
267 return lines_before + [new_line] + lines_after
268
268
269
269
270 class SystemAssign(TokenTransformBase):
270 class SystemAssign(TokenTransformBase):
271 """Transformer for assignments from system commands (a = !foo)"""
271 """Transformer for assignments from system commands (a = !foo)"""
272 @classmethod
272 @classmethod
273 def find(cls, tokens_by_line):
273 def find(cls, tokens_by_line):
274 """Find the first system assignment (a = !foo) in the cell.
274 """Find the first system assignment (a = !foo) in the cell.
275 """
275 """
276 for line in tokens_by_line:
276 for line in tokens_by_line:
277 assign_ix = _find_assign_op(line)
277 assign_ix = _find_assign_op(line)
278 if (assign_ix is not None) \
278 if (assign_ix is not None) \
279 and not line[assign_ix].line.strip().startswith('=') \
279 and not line[assign_ix].line.strip().startswith('=') \
280 and (len(line) >= assign_ix + 2) \
280 and (len(line) >= assign_ix + 2) \
281 and (line[assign_ix + 1].type == tokenize.ERRORTOKEN):
281 and (line[assign_ix + 1].type == tokenize.ERRORTOKEN):
282 ix = assign_ix + 1
282 ix = assign_ix + 1
283
283
284 while ix < len(line) and line[ix].type == tokenize.ERRORTOKEN:
284 while ix < len(line) and line[ix].type == tokenize.ERRORTOKEN:
285 if line[ix].string == '!':
285 if line[ix].string == '!':
286 return cls(line[ix].start)
286 return cls(line[ix].start)
287 elif not line[ix].string.isspace():
287 elif not line[ix].string.isspace():
288 break
288 break
289 ix += 1
289 ix += 1
290
290
291 def transform(self, lines: List[str]):
291 def transform(self, lines: List[str]):
292 """Transform a system assignment found by the ``find()`` classmethod.
292 """Transform a system assignment found by the ``find()`` classmethod.
293 """
293 """
294 start_line, start_col = self.start_line, self.start_col
294 start_line, start_col = self.start_line, self.start_col
295
295
296 lhs = lines[start_line][:start_col]
296 lhs = lines[start_line][:start_col]
297 end_line = find_end_of_continued_line(lines, start_line)
297 end_line = find_end_of_continued_line(lines, start_line)
298 rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
298 rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
299 assert rhs.startswith('!'), rhs
299 assert rhs.startswith('!'), rhs
300 cmd = rhs[1:]
300 cmd = rhs[1:]
301
301
302 lines_before = lines[:start_line]
302 lines_before = lines[:start_line]
303 call = "get_ipython().getoutput({!r})".format(cmd)
303 call = "get_ipython().getoutput({!r})".format(cmd)
304 new_line = lhs + call + '\n'
304 new_line = lhs + call + '\n'
305 lines_after = lines[end_line + 1:]
305 lines_after = lines[end_line + 1:]
306
306
307 return lines_before + [new_line] + lines_after
307 return lines_before + [new_line] + lines_after
308
308
309 # The escape sequences that define the syntax transformations IPython will
309 # The escape sequences that define the syntax transformations IPython will
310 # apply to user input. These can NOT be just changed here: many regular
310 # apply to user input. These can NOT be just changed here: many regular
311 # expressions and other parts of the code may use their hardcoded values, and
311 # expressions and other parts of the code may use their hardcoded values, and
312 # for all intents and purposes they constitute the 'IPython syntax', so they
312 # for all intents and purposes they constitute the 'IPython syntax', so they
313 # should be considered fixed.
313 # should be considered fixed.
314
314
315 ESC_SHELL = '!' # Send line to underlying system shell
315 ESC_SHELL = '!' # Send line to underlying system shell
316 ESC_SH_CAP = '!!' # Send line to system shell and capture output
316 ESC_SH_CAP = '!!' # Send line to system shell and capture output
317 ESC_HELP = '?' # Find information about object
317 ESC_HELP = '?' # Find information about object
318 ESC_HELP2 = '??' # Find extra-detailed information about object
318 ESC_HELP2 = '??' # Find extra-detailed information about object
319 ESC_MAGIC = '%' # Call magic function
319 ESC_MAGIC = '%' # Call magic function
320 ESC_MAGIC2 = '%%' # Call cell-magic function
320 ESC_MAGIC2 = '%%' # Call cell-magic function
321 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
321 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
322 ESC_QUOTE2 = ';' # Quote all args as a single string, call
322 ESC_QUOTE2 = ';' # Quote all args as a single string, call
323 ESC_PAREN = '/' # Call first argument with rest of line as arguments
323 ESC_PAREN = '/' # Call first argument with rest of line as arguments
324
324
325 ESCAPE_SINGLES = {'!', '?', '%', ',', ';', '/'}
325 ESCAPE_SINGLES = {'!', '?', '%', ',', ';', '/'}
326 ESCAPE_DOUBLES = {'!!', '??'} # %% (cell magic) is handled separately
326 ESCAPE_DOUBLES = {'!!', '??'} # %% (cell magic) is handled separately
327
327
328 def _make_help_call(target, esc, next_input=None):
328 def _make_help_call(target, esc, next_input=None):
329 """Prepares a pinfo(2)/psearch call from a target name and the escape
329 """Prepares a pinfo(2)/psearch call from a target name and the escape
330 (i.e. ? or ??)"""
330 (i.e. ? or ??)"""
331 method = 'pinfo2' if esc == '??' \
331 method = 'pinfo2' if esc == '??' \
332 else 'psearch' if '*' in target \
332 else 'psearch' if '*' in target \
333 else 'pinfo'
333 else 'pinfo'
334 arg = " ".join([method, target])
334 arg = " ".join([method, target])
335 #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args)
335 #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args)
336 t_magic_name, _, t_magic_arg_s = arg.partition(' ')
336 t_magic_name, _, t_magic_arg_s = arg.partition(' ')
337 t_magic_name = t_magic_name.lstrip(ESC_MAGIC)
337 t_magic_name = t_magic_name.lstrip(ESC_MAGIC)
338 if next_input is None:
338 if next_input is None:
339 return 'get_ipython().run_line_magic(%r, %r)' % (t_magic_name, t_magic_arg_s)
339 return 'get_ipython().run_line_magic(%r, %r)' % (t_magic_name, t_magic_arg_s)
340 else:
340 else:
341 return 'get_ipython().set_next_input(%r);get_ipython().run_line_magic(%r, %r)' % \
341 return 'get_ipython().set_next_input(%r);get_ipython().run_line_magic(%r, %r)' % \
342 (next_input, t_magic_name, t_magic_arg_s)
342 (next_input, t_magic_name, t_magic_arg_s)
343
343
344 def _tr_help(content):
344 def _tr_help(content):
345 """Translate lines escaped with: ?
345 """Translate lines escaped with: ?
346
346
347 A naked help line should fire the intro help screen (shell.show_usage())
347 A naked help line should fire the intro help screen (shell.show_usage())
348 """
348 """
349 if not content:
349 if not content:
350 return 'get_ipython().show_usage()'
350 return 'get_ipython().show_usage()'
351
351
352 return _make_help_call(content, '?')
352 return _make_help_call(content, '?')
353
353
354 def _tr_help2(content):
354 def _tr_help2(content):
355 """Translate lines escaped with: ??
355 """Translate lines escaped with: ??
356
356
357 A naked help line should fire the intro help screen (shell.show_usage())
357 A naked help line should fire the intro help screen (shell.show_usage())
358 """
358 """
359 if not content:
359 if not content:
360 return 'get_ipython().show_usage()'
360 return 'get_ipython().show_usage()'
361
361
362 return _make_help_call(content, '??')
362 return _make_help_call(content, '??')
363
363
364 def _tr_magic(content):
364 def _tr_magic(content):
365 "Translate lines escaped with a percent sign: %"
365 "Translate lines escaped with a percent sign: %"
366 name, _, args = content.partition(' ')
366 name, _, args = content.partition(' ')
367 return 'get_ipython().run_line_magic(%r, %r)' % (name, args)
367 return 'get_ipython().run_line_magic(%r, %r)' % (name, args)
368
368
369 def _tr_quote(content):
369 def _tr_quote(content):
370 "Translate lines escaped with a comma: ,"
370 "Translate lines escaped with a comma: ,"
371 name, _, args = content.partition(' ')
371 name, _, args = content.partition(' ')
372 return '%s("%s")' % (name, '", "'.join(args.split()) )
372 return '%s("%s")' % (name, '", "'.join(args.split()) )
373
373
374 def _tr_quote2(content):
374 def _tr_quote2(content):
375 "Translate lines escaped with a semicolon: ;"
375 "Translate lines escaped with a semicolon: ;"
376 name, _, args = content.partition(' ')
376 name, _, args = content.partition(' ')
377 return '%s("%s")' % (name, args)
377 return '%s("%s")' % (name, args)
378
378
379 def _tr_paren(content):
379 def _tr_paren(content):
380 "Translate lines escaped with a slash: /"
380 "Translate lines escaped with a slash: /"
381 name, _, args = content.partition(' ')
381 name, _, args = content.partition(' ')
382 return '%s(%s)' % (name, ", ".join(args.split()))
382 return '%s(%s)' % (name, ", ".join(args.split()))
383
383
384 tr = { ESC_SHELL : 'get_ipython().system({!r})'.format,
384 tr = { ESC_SHELL : 'get_ipython().system({!r})'.format,
385 ESC_SH_CAP : 'get_ipython().getoutput({!r})'.format,
385 ESC_SH_CAP : 'get_ipython().getoutput({!r})'.format,
386 ESC_HELP : _tr_help,
386 ESC_HELP : _tr_help,
387 ESC_HELP2 : _tr_help2,
387 ESC_HELP2 : _tr_help2,
388 ESC_MAGIC : _tr_magic,
388 ESC_MAGIC : _tr_magic,
389 ESC_QUOTE : _tr_quote,
389 ESC_QUOTE : _tr_quote,
390 ESC_QUOTE2 : _tr_quote2,
390 ESC_QUOTE2 : _tr_quote2,
391 ESC_PAREN : _tr_paren }
391 ESC_PAREN : _tr_paren }
392
392
393 class EscapedCommand(TokenTransformBase):
393 class EscapedCommand(TokenTransformBase):
394 """Transformer for escaped commands like %foo, !foo, or /foo"""
394 """Transformer for escaped commands like %foo, !foo, or /foo"""
395 @classmethod
395 @classmethod
396 def find(cls, tokens_by_line):
396 def find(cls, tokens_by_line):
397 """Find the first escaped command (%foo, !foo, etc.) in the cell.
397 """Find the first escaped command (%foo, !foo, etc.) in the cell.
398 """
398 """
399 for line in tokens_by_line:
399 for line in tokens_by_line:
400 if not line:
400 if not line:
401 continue
401 continue
402 ix = 0
402 ix = 0
403 ll = len(line)
403 ll = len(line)
404 while ll > ix and line[ix].type in {tokenize.INDENT, tokenize.DEDENT}:
404 while ll > ix and line[ix].type in {tokenize.INDENT, tokenize.DEDENT}:
405 ix += 1
405 ix += 1
406 if ix >= ll:
406 if ix >= ll:
407 continue
407 continue
408 if line[ix].string in ESCAPE_SINGLES:
408 if line[ix].string in ESCAPE_SINGLES:
409 return cls(line[ix].start)
409 return cls(line[ix].start)
410
410
411 def transform(self, lines):
411 def transform(self, lines):
412 """Transform an escaped line found by the ``find()`` classmethod.
412 """Transform an escaped line found by the ``find()`` classmethod.
413 """
413 """
414 start_line, start_col = self.start_line, self.start_col
414 start_line, start_col = self.start_line, self.start_col
415
415
416 indent = lines[start_line][:start_col]
416 indent = lines[start_line][:start_col]
417 end_line = find_end_of_continued_line(lines, start_line)
417 end_line = find_end_of_continued_line(lines, start_line)
418 line = assemble_continued_line(lines, (start_line, start_col), end_line)
418 line = assemble_continued_line(lines, (start_line, start_col), end_line)
419
419
420 if len(line) > 1 and line[:2] in ESCAPE_DOUBLES:
420 if len(line) > 1 and line[:2] in ESCAPE_DOUBLES:
421 escape, content = line[:2], line[2:]
421 escape, content = line[:2], line[2:]
422 else:
422 else:
423 escape, content = line[:1], line[1:]
423 escape, content = line[:1], line[1:]
424
424
425 if escape in tr:
425 if escape in tr:
426 call = tr[escape](content)
426 call = tr[escape](content)
427 else:
427 else:
428 call = ''
428 call = ''
429
429
430 lines_before = lines[:start_line]
430 lines_before = lines[:start_line]
431 new_line = indent + call + '\n'
431 new_line = indent + call + '\n'
432 lines_after = lines[end_line + 1:]
432 lines_after = lines[end_line + 1:]
433
433
434 return lines_before + [new_line] + lines_after
434 return lines_before + [new_line] + lines_after
435
435
436 _help_end_re = re.compile(r"""(%{0,2}
436 _help_end_re = re.compile(r"""(%{0,2}
437 (?!\d)[\w*]+ # Variable name
437 (?!\d)[\w*]+ # Variable name
438 (\.(?!\d)[\w*]+)* # .etc.etc
438 (\.(?!\d)[\w*]+)* # .etc.etc
439 )
439 )
440 (\?\??)$ # ? or ??
440 (\?\??)$ # ? or ??
441 """,
441 """,
442 re.VERBOSE)
442 re.VERBOSE)
443
443
444 class HelpEnd(TokenTransformBase):
444 class HelpEnd(TokenTransformBase):
445 """Transformer for help syntax: obj? and obj??"""
445 """Transformer for help syntax: obj? and obj??"""
446 # This needs to be higher priority (lower number) than EscapedCommand so
446 # This needs to be higher priority (lower number) than EscapedCommand so
447 # that inspecting magics (%foo?) works.
447 # that inspecting magics (%foo?) works.
448 priority = 5
448 priority = 5
449
449
450 def __init__(self, start, q_locn):
450 def __init__(self, start, q_locn):
451 super().__init__(start)
451 super().__init__(start)
452 self.q_line = q_locn[0] - 1 # Shift from 1-indexed to 0-indexed
452 self.q_line = q_locn[0] - 1 # Shift from 1-indexed to 0-indexed
453 self.q_col = q_locn[1]
453 self.q_col = q_locn[1]
454
454
455 @classmethod
455 @classmethod
456 def find(cls, tokens_by_line):
456 def find(cls, tokens_by_line):
457 """Find the first help command (foo?) in the cell.
457 """Find the first help command (foo?) in the cell.
458 """
458 """
459 for line in tokens_by_line:
459 for line in tokens_by_line:
460 # Last token is NEWLINE; look at last but one
460 # Last token is NEWLINE; look at last but one
461 if len(line) > 2 and line[-2].string == '?':
461 if len(line) > 2 and line[-2].string == '?':
462 # Find the first token that's not INDENT/DEDENT
462 # Find the first token that's not INDENT/DEDENT
463 ix = 0
463 ix = 0
464 while line[ix].type in {tokenize.INDENT, tokenize.DEDENT}:
464 while line[ix].type in {tokenize.INDENT, tokenize.DEDENT}:
465 ix += 1
465 ix += 1
466 return cls(line[ix].start, line[-2].start)
466 return cls(line[ix].start, line[-2].start)
467
467
468 def transform(self, lines):
468 def transform(self, lines):
469 """Transform a help command found by the ``find()`` classmethod.
469 """Transform a help command found by the ``find()`` classmethod.
470 """
470 """
471 piece = ''.join(lines[self.start_line:self.q_line+1])
471 piece = ''.join(lines[self.start_line:self.q_line+1])
472 indent, content = piece[:self.start_col], piece[self.start_col:]
472 indent, content = piece[:self.start_col], piece[self.start_col:]
473 lines_before = lines[:self.start_line]
473 lines_before = lines[:self.start_line]
474 lines_after = lines[self.q_line + 1:]
474 lines_after = lines[self.q_line + 1:]
475
475
476 m = _help_end_re.search(content)
476 m = _help_end_re.search(content)
477 if not m:
477 if not m:
478 raise SyntaxError(content)
478 raise SyntaxError(content)
479 assert m is not None, content
479 assert m is not None, content
480 target = m.group(1)
480 target = m.group(1)
481 esc = m.group(3)
481 esc = m.group(3)
482
482
483 # If we're mid-command, put it back on the next prompt for the user.
483 # If we're mid-command, put it back on the next prompt for the user.
484 next_input = None
484 next_input = None
485 if (not lines_before) and (not lines_after) \
485 if (not lines_before) and (not lines_after) \
486 and content.strip() != m.group(0):
486 and content.strip() != m.group(0):
487 next_input = content.rstrip('?\n')
487 next_input = content.rstrip('?\n')
488
488
489 call = _make_help_call(target, esc, next_input=next_input)
489 call = _make_help_call(target, esc, next_input=next_input)
490 new_line = indent + call + '\n'
490 new_line = indent + call + '\n'
491
491
492 return lines_before + [new_line] + lines_after
492 return lines_before + [new_line] + lines_after
493
493
494 def make_tokens_by_line(lines:List[str]):
494 def make_tokens_by_line(lines:List[str]):
495 """Tokenize a series of lines and group tokens by line.
495 """Tokenize a series of lines and group tokens by line.
496
496
497 The tokens for a multiline Python string or expression are grouped as one
497 The tokens for a multiline Python string or expression are grouped as one
498 line. All lines except the last lines should keep their line ending ('\\n',
498 line. All lines except the last lines should keep their line ending ('\\n',
499 '\\r\\n') for this to properly work. Use `.splitlines(keeplineending=True)`
499 '\\r\\n') for this to properly work. Use `.splitlines(keeplineending=True)`
500 for example when passing block of text to this function.
500 for example when passing block of text to this function.
501
501
502 """
502 """
503 # NL tokens are used inside multiline expressions, but also after blank
503 # NL tokens are used inside multiline expressions, but also after blank
504 # lines or comments. This is intentional - see https://bugs.python.org/issue17061
504 # lines or comments. This is intentional - see https://bugs.python.org/issue17061
505 # We want to group the former case together but split the latter, so we
505 # We want to group the former case together but split the latter, so we
506 # track parentheses level, similar to the internals of tokenize.
506 # track parentheses level, similar to the internals of tokenize.
507
507
508 # reexported from token on 3.7+
508 # reexported from token on 3.7+
509 NEWLINE, NL = tokenize.NEWLINE, tokenize.NL # type: ignore
509 NEWLINE, NL = tokenize.NEWLINE, tokenize.NL # type: ignore
510 tokens_by_line:List[List[Any]] = [[]]
510 tokens_by_line:List[List[Any]] = [[]]
511 if len(lines) > 1 and not lines[0].endswith(('\n', '\r', '\r\n', '\x0b', '\x0c')):
511 if len(lines) > 1 and not lines[0].endswith(('\n', '\r', '\r\n', '\x0b', '\x0c')):
512 warnings.warn("`make_tokens_by_line` received a list of lines which do not have lineending markers ('\\n', '\\r', '\\r\\n', '\\x0b', '\\x0c'), behavior will be unspecified")
512 warnings.warn("`make_tokens_by_line` received a list of lines which do not have lineending markers ('\\n', '\\r', '\\r\\n', '\\x0b', '\\x0c'), behavior will be unspecified")
513 parenlev = 0
513 parenlev = 0
514 try:
514 try:
515 for token in tokenize.generate_tokens(iter(lines).__next__):
515 for token in tokenize.generate_tokens(iter(lines).__next__):
516 tokens_by_line[-1].append(token)
516 tokens_by_line[-1].append(token)
517 if (token.type == NEWLINE) \
517 if (token.type == NEWLINE) \
518 or ((token.type == NL) and (parenlev <= 0)):
518 or ((token.type == NL) and (parenlev <= 0)):
519 tokens_by_line.append([])
519 tokens_by_line.append([])
520 elif token.string in {'(', '[', '{'}:
520 elif token.string in {'(', '[', '{'}:
521 parenlev += 1
521 parenlev += 1
522 elif token.string in {')', ']', '}'}:
522 elif token.string in {')', ']', '}'}:
523 if parenlev > 0:
523 if parenlev > 0:
524 parenlev -= 1
524 parenlev -= 1
525 except tokenize.TokenError:
525 except tokenize.TokenError:
526 # Input ended in a multiline string or expression. That's OK for us.
526 # Input ended in a multiline string or expression. That's OK for us.
527 pass
527 pass
528
528
529
529
530 if not tokens_by_line[-1]:
530 if not tokens_by_line[-1]:
531 tokens_by_line.pop()
531 tokens_by_line.pop()
532
532
533
533
534 return tokens_by_line
534 return tokens_by_line
535
535
536
536
537 def has_sunken_brackets(tokens: List[tokenize.TokenInfo]):
537 def has_sunken_brackets(tokens: List[tokenize.TokenInfo]):
538 """Check if the depth of brackets in the list of tokens drops below 0"""
538 """Check if the depth of brackets in the list of tokens drops below 0"""
539 parenlev = 0
539 parenlev = 0
540 for token in tokens:
540 for token in tokens:
541 if token.string in {"(", "[", "{"}:
541 if token.string in {"(", "[", "{"}:
542 parenlev += 1
542 parenlev += 1
543 elif token.string in {")", "]", "}"}:
543 elif token.string in {")", "]", "}"}:
544 parenlev -= 1
544 parenlev -= 1
545 if parenlev < 0:
545 if parenlev < 0:
546 return True
546 return True
547 return False
547 return False
548
548
549
549
550 def show_linewise_tokens(s: str):
550 def show_linewise_tokens(s: str):
551 """For investigation and debugging"""
551 """For investigation and debugging"""
552 if not s.endswith('\n'):
552 if not s.endswith('\n'):
553 s += '\n'
553 s += '\n'
554 lines = s.splitlines(keepends=True)
554 lines = s.splitlines(keepends=True)
555 for line in make_tokens_by_line(lines):
555 for line in make_tokens_by_line(lines):
556 print("Line -------")
556 print("Line -------")
557 for tokinfo in line:
557 for tokinfo in line:
558 print(" ", tokinfo)
558 print(" ", tokinfo)
559
559
560 # Arbitrary limit to prevent getting stuck in infinite loops
560 # Arbitrary limit to prevent getting stuck in infinite loops
561 TRANSFORM_LOOP_LIMIT = 500
561 TRANSFORM_LOOP_LIMIT = 500
562
562
563 class TransformerManager:
563 class TransformerManager:
564 """Applies various transformations to a cell or code block.
564 """Applies various transformations to a cell or code block.
565
565
566 The key methods for external use are ``transform_cell()``
566 The key methods for external use are ``transform_cell()``
567 and ``check_complete()``.
567 and ``check_complete()``.
568 """
568 """
569 def __init__(self):
569 def __init__(self):
570 self.cleanup_transforms = [
570 self.cleanup_transforms = [
571 leading_empty_lines,
571 leading_empty_lines,
572 leading_indent,
572 leading_indent,
573 classic_prompt,
573 classic_prompt,
574 ipython_prompt,
574 ipython_prompt,
575 ]
575 ]
576 self.line_transforms = [
576 self.line_transforms = [
577 cell_magic,
577 cell_magic,
578 ]
578 ]
579 self.token_transformers = [
579 self.token_transformers = [
580 MagicAssign,
580 MagicAssign,
581 SystemAssign,
581 SystemAssign,
582 EscapedCommand,
582 EscapedCommand,
583 HelpEnd,
583 HelpEnd,
584 ]
584 ]
585
585
586 def do_one_token_transform(self, lines):
586 def do_one_token_transform(self, lines):
587 """Find and run the transform earliest in the code.
587 """Find and run the transform earliest in the code.
588
588
589 Returns (changed, lines).
589 Returns (changed, lines).
590
590
591 This method is called repeatedly until changed is False, indicating
591 This method is called repeatedly until changed is False, indicating
592 that all available transformations are complete.
592 that all available transformations are complete.
593
593
594 The tokens following IPython special syntax might not be valid, so
594 The tokens following IPython special syntax might not be valid, so
595 the transformed code is retokenised every time to identify the next
595 the transformed code is retokenised every time to identify the next
596 piece of special syntax. Hopefully long code cells are mostly valid
596 piece of special syntax. Hopefully long code cells are mostly valid
597 Python, not using lots of IPython special syntax, so this shouldn't be
597 Python, not using lots of IPython special syntax, so this shouldn't be
598 a performance issue.
598 a performance issue.
599 """
599 """
600 tokens_by_line = make_tokens_by_line(lines)
600 tokens_by_line = make_tokens_by_line(lines)
601 candidates = []
601 candidates = []
602 for transformer_cls in self.token_transformers:
602 for transformer_cls in self.token_transformers:
603 transformer = transformer_cls.find(tokens_by_line)
603 transformer = transformer_cls.find(tokens_by_line)
604 if transformer:
604 if transformer:
605 candidates.append(transformer)
605 candidates.append(transformer)
606
606
607 if not candidates:
607 if not candidates:
608 # Nothing to transform
608 # Nothing to transform
609 return False, lines
609 return False, lines
610 ordered_transformers = sorted(candidates, key=TokenTransformBase.sortby)
610 ordered_transformers = sorted(candidates, key=TokenTransformBase.sortby)
611 for transformer in ordered_transformers:
611 for transformer in ordered_transformers:
612 try:
612 try:
613 return True, transformer.transform(lines)
613 return True, transformer.transform(lines)
614 except SyntaxError:
614 except SyntaxError:
615 pass
615 pass
616 return False, lines
616 return False, lines
617
617
618 def do_token_transforms(self, lines):
618 def do_token_transforms(self, lines):
619 for _ in range(TRANSFORM_LOOP_LIMIT):
619 for _ in range(TRANSFORM_LOOP_LIMIT):
620 changed, lines = self.do_one_token_transform(lines)
620 changed, lines = self.do_one_token_transform(lines)
621 if not changed:
621 if not changed:
622 return lines
622 return lines
623
623
624 raise RuntimeError("Input transformation still changing after "
624 raise RuntimeError("Input transformation still changing after "
625 "%d iterations. Aborting." % TRANSFORM_LOOP_LIMIT)
625 "%d iterations. Aborting." % TRANSFORM_LOOP_LIMIT)
626
626
627 def transform_cell(self, cell: str) -> str:
627 def transform_cell(self, cell: str) -> str:
628 """Transforms a cell of input code"""
628 """Transforms a cell of input code"""
629 if not cell.endswith('\n'):
629 if not cell.endswith('\n'):
630 cell += '\n' # Ensure the cell has a trailing newline
630 cell += '\n' # Ensure the cell has a trailing newline
631 lines = cell.splitlines(keepends=True)
631 lines = cell.splitlines(keepends=True)
632 for transform in self.cleanup_transforms + self.line_transforms:
632 for transform in self.cleanup_transforms + self.line_transforms:
633 lines = transform(lines)
633 lines = transform(lines)
634
634
635 lines = self.do_token_transforms(lines)
635 lines = self.do_token_transforms(lines)
636 return ''.join(lines)
636 return ''.join(lines)
637
637
638 def check_complete(self, cell: str):
638 def check_complete(self, cell: str):
639 """Return whether a block of code is ready to execute, or should be continued
639 """Return whether a block of code is ready to execute, or should be continued
640
640
641 Parameters
641 Parameters
642 ----------
642 ----------
643 source : string
643 cell : string
644 Python input code, which can be multiline.
644 Python input code, which can be multiline.
645
645
646 Returns
646 Returns
647 -------
647 -------
648 status : str
648 status : str
649 One of 'complete', 'incomplete', or 'invalid' if source is not a
649 One of 'complete', 'incomplete', or 'invalid' if source is not a
650 prefix of valid code.
650 prefix of valid code.
651 indent_spaces : int or None
651 indent_spaces : int or None
652 The number of spaces by which to indent the next line of code. If
652 The number of spaces by which to indent the next line of code. If
653 status is not 'incomplete', this is None.
653 status is not 'incomplete', this is None.
654 """
654 """
655 # Remember if the lines ends in a new line.
655 # Remember if the lines ends in a new line.
656 ends_with_newline = False
656 ends_with_newline = False
657 for character in reversed(cell):
657 for character in reversed(cell):
658 if character == '\n':
658 if character == '\n':
659 ends_with_newline = True
659 ends_with_newline = True
660 break
660 break
661 elif character.strip():
661 elif character.strip():
662 break
662 break
663 else:
663 else:
664 continue
664 continue
665
665
666 if not ends_with_newline:
666 if not ends_with_newline:
667 # Append an newline for consistent tokenization
667 # Append an newline for consistent tokenization
668 # See https://bugs.python.org/issue33899
668 # See https://bugs.python.org/issue33899
669 cell += '\n'
669 cell += '\n'
670
670
671 lines = cell.splitlines(keepends=True)
671 lines = cell.splitlines(keepends=True)
672
672
673 if not lines:
673 if not lines:
674 return 'complete', None
674 return 'complete', None
675
675
676 if lines[-1].endswith('\\'):
676 if lines[-1].endswith('\\'):
677 # Explicit backslash continuation
677 # Explicit backslash continuation
678 return 'incomplete', find_last_indent(lines)
678 return 'incomplete', find_last_indent(lines)
679
679
680 try:
680 try:
681 for transform in self.cleanup_transforms:
681 for transform in self.cleanup_transforms:
682 if not getattr(transform, 'has_side_effects', False):
682 if not getattr(transform, 'has_side_effects', False):
683 lines = transform(lines)
683 lines = transform(lines)
684 except SyntaxError:
684 except SyntaxError:
685 return 'invalid', None
685 return 'invalid', None
686
686
687 if lines[0].startswith('%%'):
687 if lines[0].startswith('%%'):
688 # Special case for cell magics - completion marked by blank line
688 # Special case for cell magics - completion marked by blank line
689 if lines[-1].strip():
689 if lines[-1].strip():
690 return 'incomplete', find_last_indent(lines)
690 return 'incomplete', find_last_indent(lines)
691 else:
691 else:
692 return 'complete', None
692 return 'complete', None
693
693
694 try:
694 try:
695 for transform in self.line_transforms:
695 for transform in self.line_transforms:
696 if not getattr(transform, 'has_side_effects', False):
696 if not getattr(transform, 'has_side_effects', False):
697 lines = transform(lines)
697 lines = transform(lines)
698 lines = self.do_token_transforms(lines)
698 lines = self.do_token_transforms(lines)
699 except SyntaxError:
699 except SyntaxError:
700 return 'invalid', None
700 return 'invalid', None
701
701
702 tokens_by_line = make_tokens_by_line(lines)
702 tokens_by_line = make_tokens_by_line(lines)
703
703
704 # Bail if we got one line and there are more closing parentheses than
704 # Bail if we got one line and there are more closing parentheses than
705 # the opening ones
705 # the opening ones
706 if (
706 if (
707 len(lines) == 1
707 len(lines) == 1
708 and tokens_by_line
708 and tokens_by_line
709 and has_sunken_brackets(tokens_by_line[0])
709 and has_sunken_brackets(tokens_by_line[0])
710 ):
710 ):
711 return "invalid", None
711 return "invalid", None
712
712
713 if not tokens_by_line:
713 if not tokens_by_line:
714 return 'incomplete', find_last_indent(lines)
714 return 'incomplete', find_last_indent(lines)
715
715
716 if tokens_by_line[-1][-1].type != tokenize.ENDMARKER:
716 if tokens_by_line[-1][-1].type != tokenize.ENDMARKER:
717 # We're in a multiline string or expression
717 # We're in a multiline string or expression
718 return 'incomplete', find_last_indent(lines)
718 return 'incomplete', find_last_indent(lines)
719
719
720 newline_types = {tokenize.NEWLINE, tokenize.COMMENT, tokenize.ENDMARKER} # type: ignore
720 newline_types = {tokenize.NEWLINE, tokenize.COMMENT, tokenize.ENDMARKER} # type: ignore
721
721
722 # Pop the last line which only contains DEDENTs and ENDMARKER
722 # Pop the last line which only contains DEDENTs and ENDMARKER
723 last_token_line = None
723 last_token_line = None
724 if {t.type for t in tokens_by_line[-1]} in [
724 if {t.type for t in tokens_by_line[-1]} in [
725 {tokenize.DEDENT, tokenize.ENDMARKER},
725 {tokenize.DEDENT, tokenize.ENDMARKER},
726 {tokenize.ENDMARKER}
726 {tokenize.ENDMARKER}
727 ] and len(tokens_by_line) > 1:
727 ] and len(tokens_by_line) > 1:
728 last_token_line = tokens_by_line.pop()
728 last_token_line = tokens_by_line.pop()
729
729
730 while tokens_by_line[-1] and tokens_by_line[-1][-1].type in newline_types:
730 while tokens_by_line[-1] and tokens_by_line[-1][-1].type in newline_types:
731 tokens_by_line[-1].pop()
731 tokens_by_line[-1].pop()
732
732
733 if not tokens_by_line[-1]:
733 if not tokens_by_line[-1]:
734 return 'incomplete', find_last_indent(lines)
734 return 'incomplete', find_last_indent(lines)
735
735
736 if tokens_by_line[-1][-1].string == ':':
736 if tokens_by_line[-1][-1].string == ':':
737 # The last line starts a block (e.g. 'if foo:')
737 # The last line starts a block (e.g. 'if foo:')
738 ix = 0
738 ix = 0
739 while tokens_by_line[-1][ix].type in {tokenize.INDENT, tokenize.DEDENT}:
739 while tokens_by_line[-1][ix].type in {tokenize.INDENT, tokenize.DEDENT}:
740 ix += 1
740 ix += 1
741
741
742 indent = tokens_by_line[-1][ix].start[1]
742 indent = tokens_by_line[-1][ix].start[1]
743 return 'incomplete', indent + 4
743 return 'incomplete', indent + 4
744
744
745 if tokens_by_line[-1][0].line.endswith('\\'):
745 if tokens_by_line[-1][0].line.endswith('\\'):
746 return 'incomplete', None
746 return 'incomplete', None
747
747
748 # At this point, our checks think the code is complete (or invalid).
748 # At this point, our checks think the code is complete (or invalid).
749 # We'll use codeop.compile_command to check this with the real parser
749 # We'll use codeop.compile_command to check this with the real parser
750 try:
750 try:
751 with warnings.catch_warnings():
751 with warnings.catch_warnings():
752 warnings.simplefilter('error', SyntaxWarning)
752 warnings.simplefilter('error', SyntaxWarning)
753 res = compile_command(''.join(lines), symbol='exec')
753 res = compile_command(''.join(lines), symbol='exec')
754 except (SyntaxError, OverflowError, ValueError, TypeError,
754 except (SyntaxError, OverflowError, ValueError, TypeError,
755 MemoryError, SyntaxWarning):
755 MemoryError, SyntaxWarning):
756 return 'invalid', None
756 return 'invalid', None
757 else:
757 else:
758 if res is None:
758 if res is None:
759 return 'incomplete', find_last_indent(lines)
759 return 'incomplete', find_last_indent(lines)
760
760
761 if last_token_line and last_token_line[0].type == tokenize.DEDENT:
761 if last_token_line and last_token_line[0].type == tokenize.DEDENT:
762 if ends_with_newline:
762 if ends_with_newline:
763 return 'complete', None
763 return 'complete', None
764 return 'incomplete', find_last_indent(lines)
764 return 'incomplete', find_last_indent(lines)
765
765
766 # If there's a blank line at the end, assume we're ready to execute
766 # If there's a blank line at the end, assume we're ready to execute
767 if not lines[-1].strip():
767 if not lines[-1].strip():
768 return 'complete', None
768 return 'complete', None
769
769
770 return 'complete', None
770 return 'complete', None
771
771
772
772
773 def find_last_indent(lines):
773 def find_last_indent(lines):
774 m = _indent_re.match(lines[-1])
774 m = _indent_re.match(lines[-1])
775 if not m:
775 if not m:
776 return 0
776 return 0
777 return len(m.group(0).replace('\t', ' '*4))
777 return len(m.group(0).replace('\t', ' '*4))
778
778
779
779
780 class MaybeAsyncCompile(Compile):
780 class MaybeAsyncCompile(Compile):
781 def __init__(self, extra_flags=0):
781 def __init__(self, extra_flags=0):
782 super().__init__()
782 super().__init__()
783 self.flags |= extra_flags
783 self.flags |= extra_flags
784
784
785 def __call__(self, *args, **kwds):
785 def __call__(self, *args, **kwds):
786 return compile(*args, **kwds)
786 return compile(*args, **kwds)
787
787
788
788
789 class MaybeAsyncCommandCompiler(CommandCompiler):
789 class MaybeAsyncCommandCompiler(CommandCompiler):
790 def __init__(self, extra_flags=0):
790 def __init__(self, extra_flags=0):
791 self.compiler = MaybeAsyncCompile(extra_flags=extra_flags)
791 self.compiler = MaybeAsyncCompile(extra_flags=extra_flags)
792
792
793
793
794 _extra_flags = ast.PyCF_ALLOW_TOP_LEVEL_AWAIT
794 _extra_flags = ast.PyCF_ALLOW_TOP_LEVEL_AWAIT
795
795
796 compile_command = MaybeAsyncCommandCompiler(extra_flags=_extra_flags)
796 compile_command = MaybeAsyncCommandCompiler(extra_flags=_extra_flags)
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,712 +1,700 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Magic functions for InteractiveShell.
2 """Magic functions for InteractiveShell.
3 """
3 """
4
4
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and
6 # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and
7 # Copyright (C) 2001 Fernando Perez <fperez@colorado.edu>
7 # Copyright (C) 2001 Fernando Perez <fperez@colorado.edu>
8 # Copyright (C) 2008 The IPython Development Team
8 # Copyright (C) 2008 The IPython Development Team
9
9
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 import os
14 import os
15 import re
15 import re
16 import sys
16 import sys
17 from getopt import getopt, GetoptError
17 from getopt import getopt, GetoptError
18
18
19 from traitlets.config.configurable import Configurable
19 from traitlets.config.configurable import Configurable
20 from . import oinspect
20 from . import oinspect
21 from .error import UsageError
21 from .error import UsageError
22 from .inputtransformer2 import ESC_MAGIC, ESC_MAGIC2
22 from .inputtransformer2 import ESC_MAGIC, ESC_MAGIC2
23 from ..utils.ipstruct import Struct
23 from ..utils.ipstruct import Struct
24 from ..utils.process import arg_split
24 from ..utils.process import arg_split
25 from ..utils.text import dedent
25 from ..utils.text import dedent
26 from traitlets import Bool, Dict, Instance, observe
26 from traitlets import Bool, Dict, Instance, observe
27 from logging import error
27 from logging import error
28
28
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30 # Globals
30 # Globals
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32
32
33 # A dict we'll use for each class that has magics, used as temporary storage to
33 # A dict we'll use for each class that has magics, used as temporary storage to
34 # pass information between the @line/cell_magic method decorators and the
34 # pass information between the @line/cell_magic method decorators and the
35 # @magics_class class decorator, because the method decorators have no
35 # @magics_class class decorator, because the method decorators have no
36 # access to the class when they run. See for more details:
36 # access to the class when they run. See for more details:
37 # http://stackoverflow.com/questions/2366713/can-a-python-decorator-of-an-instance-method-access-the-class
37 # http://stackoverflow.com/questions/2366713/can-a-python-decorator-of-an-instance-method-access-the-class
38
38
39 magics = dict(line={}, cell={})
39 magics = dict(line={}, cell={})
40
40
41 magic_kinds = ('line', 'cell')
41 magic_kinds = ('line', 'cell')
42 magic_spec = ('line', 'cell', 'line_cell')
42 magic_spec = ('line', 'cell', 'line_cell')
43 magic_escapes = dict(line=ESC_MAGIC, cell=ESC_MAGIC2)
43 magic_escapes = dict(line=ESC_MAGIC, cell=ESC_MAGIC2)
44
44
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46 # Utility classes and functions
46 # Utility classes and functions
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48
48
49 class Bunch: pass
49 class Bunch: pass
50
50
51
51
52 def on_off(tag):
52 def on_off(tag):
53 """Return an ON/OFF string for a 1/0 input. Simple utility function."""
53 """Return an ON/OFF string for a 1/0 input. Simple utility function."""
54 return ['OFF','ON'][tag]
54 return ['OFF','ON'][tag]
55
55
56
56
57 def compress_dhist(dh):
57 def compress_dhist(dh):
58 """Compress a directory history into a new one with at most 20 entries.
58 """Compress a directory history into a new one with at most 20 entries.
59
59
60 Return a new list made from the first and last 10 elements of dhist after
60 Return a new list made from the first and last 10 elements of dhist after
61 removal of duplicates.
61 removal of duplicates.
62 """
62 """
63 head, tail = dh[:-10], dh[-10:]
63 head, tail = dh[:-10], dh[-10:]
64
64
65 newhead = []
65 newhead = []
66 done = set()
66 done = set()
67 for h in head:
67 for h in head:
68 if h in done:
68 if h in done:
69 continue
69 continue
70 newhead.append(h)
70 newhead.append(h)
71 done.add(h)
71 done.add(h)
72
72
73 return newhead + tail
73 return newhead + tail
74
74
75
75
76 def needs_local_scope(func):
76 def needs_local_scope(func):
77 """Decorator to mark magic functions which need to local scope to run."""
77 """Decorator to mark magic functions which need to local scope to run."""
78 func.needs_local_scope = True
78 func.needs_local_scope = True
79 return func
79 return func
80
80
81 #-----------------------------------------------------------------------------
81 #-----------------------------------------------------------------------------
82 # Class and method decorators for registering magics
82 # Class and method decorators for registering magics
83 #-----------------------------------------------------------------------------
83 #-----------------------------------------------------------------------------
84
84
85 def magics_class(cls):
85 def magics_class(cls):
86 """Class decorator for all subclasses of the main Magics class.
86 """Class decorator for all subclasses of the main Magics class.
87
87
88 Any class that subclasses Magics *must* also apply this decorator, to
88 Any class that subclasses Magics *must* also apply this decorator, to
89 ensure that all the methods that have been decorated as line/cell magics
89 ensure that all the methods that have been decorated as line/cell magics
90 get correctly registered in the class instance. This is necessary because
90 get correctly registered in the class instance. This is necessary because
91 when method decorators run, the class does not exist yet, so they
91 when method decorators run, the class does not exist yet, so they
92 temporarily store their information into a module global. Application of
92 temporarily store their information into a module global. Application of
93 this class decorator copies that global data to the class instance and
93 this class decorator copies that global data to the class instance and
94 clears the global.
94 clears the global.
95
95
96 Obviously, this mechanism is not thread-safe, which means that the
96 Obviously, this mechanism is not thread-safe, which means that the
97 *creation* of subclasses of Magic should only be done in a single-thread
97 *creation* of subclasses of Magic should only be done in a single-thread
98 context. Instantiation of the classes has no restrictions. Given that
98 context. Instantiation of the classes has no restrictions. Given that
99 these classes are typically created at IPython startup time and before user
99 these classes are typically created at IPython startup time and before user
100 application code becomes active, in practice this should not pose any
100 application code becomes active, in practice this should not pose any
101 problems.
101 problems.
102 """
102 """
103 cls.registered = True
103 cls.registered = True
104 cls.magics = dict(line = magics['line'],
104 cls.magics = dict(line = magics['line'],
105 cell = magics['cell'])
105 cell = magics['cell'])
106 magics['line'] = {}
106 magics['line'] = {}
107 magics['cell'] = {}
107 magics['cell'] = {}
108 return cls
108 return cls
109
109
110
110
111 def record_magic(dct, magic_kind, magic_name, func):
111 def record_magic(dct, magic_kind, magic_name, func):
112 """Utility function to store a function as a magic of a specific kind.
112 """Utility function to store a function as a magic of a specific kind.
113
113
114 Parameters
114 Parameters
115 ----------
115 ----------
116 dct : dict
116 dct : dict
117 A dictionary with 'line' and 'cell' subdicts.
117 A dictionary with 'line' and 'cell' subdicts.
118
119 magic_kind : str
118 magic_kind : str
120 Kind of magic to be stored.
119 Kind of magic to be stored.
121
122 magic_name : str
120 magic_name : str
123 Key to store the magic as.
121 Key to store the magic as.
124
125 func : function
122 func : function
126 Callable object to store.
123 Callable object to store.
127 """
124 """
128 if magic_kind == 'line_cell':
125 if magic_kind == 'line_cell':
129 dct['line'][magic_name] = dct['cell'][magic_name] = func
126 dct['line'][magic_name] = dct['cell'][magic_name] = func
130 else:
127 else:
131 dct[magic_kind][magic_name] = func
128 dct[magic_kind][magic_name] = func
132
129
133
130
134 def validate_type(magic_kind):
131 def validate_type(magic_kind):
135 """Ensure that the given magic_kind is valid.
132 """Ensure that the given magic_kind is valid.
136
133
137 Check that the given magic_kind is one of the accepted spec types (stored
134 Check that the given magic_kind is one of the accepted spec types (stored
138 in the global `magic_spec`), raise ValueError otherwise.
135 in the global `magic_spec`), raise ValueError otherwise.
139 """
136 """
140 if magic_kind not in magic_spec:
137 if magic_kind not in magic_spec:
141 raise ValueError('magic_kind must be one of %s, %s given' %
138 raise ValueError('magic_kind must be one of %s, %s given' %
142 magic_kinds, magic_kind)
139 magic_kinds, magic_kind)
143
140
144
141
145 # The docstrings for the decorator below will be fairly similar for the two
142 # The docstrings for the decorator below will be fairly similar for the two
146 # types (method and function), so we generate them here once and reuse the
143 # types (method and function), so we generate them here once and reuse the
147 # templates below.
144 # templates below.
148 _docstring_template = \
145 _docstring_template = \
149 """Decorate the given {0} as {1} magic.
146 """Decorate the given {0} as {1} magic.
150
147
151 The decorator can be used with or without arguments, as follows.
148 The decorator can be used with or without arguments, as follows.
152
149
153 i) without arguments: it will create a {1} magic named as the {0} being
150 i) without arguments: it will create a {1} magic named as the {0} being
154 decorated::
151 decorated::
155
152
156 @deco
153 @deco
157 def foo(...)
154 def foo(...)
158
155
159 will create a {1} magic named `foo`.
156 will create a {1} magic named `foo`.
160
157
161 ii) with one string argument: which will be used as the actual name of the
158 ii) with one string argument: which will be used as the actual name of the
162 resulting magic::
159 resulting magic::
163
160
164 @deco('bar')
161 @deco('bar')
165 def foo(...)
162 def foo(...)
166
163
167 will create a {1} magic named `bar`.
164 will create a {1} magic named `bar`.
168
165
169 To register a class magic use ``Interactiveshell.register_magic(class or instance)``.
166 To register a class magic use ``Interactiveshell.register_magic(class or instance)``.
170 """
167 """
171
168
172 # These two are decorator factories. While they are conceptually very similar,
169 # These two are decorator factories. While they are conceptually very similar,
173 # there are enough differences in the details that it's simpler to have them
170 # there are enough differences in the details that it's simpler to have them
174 # written as completely standalone functions rather than trying to share code
171 # written as completely standalone functions rather than trying to share code
175 # and make a single one with convoluted logic.
172 # and make a single one with convoluted logic.
176
173
177 def _method_magic_marker(magic_kind):
174 def _method_magic_marker(magic_kind):
178 """Decorator factory for methods in Magics subclasses.
175 """Decorator factory for methods in Magics subclasses.
179 """
176 """
180
177
181 validate_type(magic_kind)
178 validate_type(magic_kind)
182
179
183 # This is a closure to capture the magic_kind. We could also use a class,
180 # This is a closure to capture the magic_kind. We could also use a class,
184 # but it's overkill for just that one bit of state.
181 # but it's overkill for just that one bit of state.
185 def magic_deco(arg):
182 def magic_deco(arg):
186 if callable(arg):
183 if callable(arg):
187 # "Naked" decorator call (just @foo, no args)
184 # "Naked" decorator call (just @foo, no args)
188 func = arg
185 func = arg
189 name = func.__name__
186 name = func.__name__
190 retval = arg
187 retval = arg
191 record_magic(magics, magic_kind, name, name)
188 record_magic(magics, magic_kind, name, name)
192 elif isinstance(arg, str):
189 elif isinstance(arg, str):
193 # Decorator called with arguments (@foo('bar'))
190 # Decorator called with arguments (@foo('bar'))
194 name = arg
191 name = arg
195 def mark(func, *a, **kw):
192 def mark(func, *a, **kw):
196 record_magic(magics, magic_kind, name, func.__name__)
193 record_magic(magics, magic_kind, name, func.__name__)
197 return func
194 return func
198 retval = mark
195 retval = mark
199 else:
196 else:
200 raise TypeError("Decorator can only be called with "
197 raise TypeError("Decorator can only be called with "
201 "string or function")
198 "string or function")
202 return retval
199 return retval
203
200
204 # Ensure the resulting decorator has a usable docstring
201 # Ensure the resulting decorator has a usable docstring
205 magic_deco.__doc__ = _docstring_template.format('method', magic_kind)
202 magic_deco.__doc__ = _docstring_template.format('method', magic_kind)
206 return magic_deco
203 return magic_deco
207
204
208
205
209 def _function_magic_marker(magic_kind):
206 def _function_magic_marker(magic_kind):
210 """Decorator factory for standalone functions.
207 """Decorator factory for standalone functions.
211 """
208 """
212 validate_type(magic_kind)
209 validate_type(magic_kind)
213
210
214 # This is a closure to capture the magic_kind. We could also use a class,
211 # This is a closure to capture the magic_kind. We could also use a class,
215 # but it's overkill for just that one bit of state.
212 # but it's overkill for just that one bit of state.
216 def magic_deco(arg):
213 def magic_deco(arg):
217 # Find get_ipython() in the caller's namespace
214 # Find get_ipython() in the caller's namespace
218 caller = sys._getframe(1)
215 caller = sys._getframe(1)
219 for ns in ['f_locals', 'f_globals', 'f_builtins']:
216 for ns in ['f_locals', 'f_globals', 'f_builtins']:
220 get_ipython = getattr(caller, ns).get('get_ipython')
217 get_ipython = getattr(caller, ns).get('get_ipython')
221 if get_ipython is not None:
218 if get_ipython is not None:
222 break
219 break
223 else:
220 else:
224 raise NameError('Decorator can only run in context where '
221 raise NameError('Decorator can only run in context where '
225 '`get_ipython` exists')
222 '`get_ipython` exists')
226
223
227 ip = get_ipython()
224 ip = get_ipython()
228
225
229 if callable(arg):
226 if callable(arg):
230 # "Naked" decorator call (just @foo, no args)
227 # "Naked" decorator call (just @foo, no args)
231 func = arg
228 func = arg
232 name = func.__name__
229 name = func.__name__
233 ip.register_magic_function(func, magic_kind, name)
230 ip.register_magic_function(func, magic_kind, name)
234 retval = arg
231 retval = arg
235 elif isinstance(arg, str):
232 elif isinstance(arg, str):
236 # Decorator called with arguments (@foo('bar'))
233 # Decorator called with arguments (@foo('bar'))
237 name = arg
234 name = arg
238 def mark(func, *a, **kw):
235 def mark(func, *a, **kw):
239 ip.register_magic_function(func, magic_kind, name)
236 ip.register_magic_function(func, magic_kind, name)
240 return func
237 return func
241 retval = mark
238 retval = mark
242 else:
239 else:
243 raise TypeError("Decorator can only be called with "
240 raise TypeError("Decorator can only be called with "
244 "string or function")
241 "string or function")
245 return retval
242 return retval
246
243
247 # Ensure the resulting decorator has a usable docstring
244 # Ensure the resulting decorator has a usable docstring
248 ds = _docstring_template.format('function', magic_kind)
245 ds = _docstring_template.format('function', magic_kind)
249
246
250 ds += dedent("""
247 ds += dedent("""
251 Note: this decorator can only be used in a context where IPython is already
248 Note: this decorator can only be used in a context where IPython is already
252 active, so that the `get_ipython()` call succeeds. You can therefore use
249 active, so that the `get_ipython()` call succeeds. You can therefore use
253 it in your startup files loaded after IPython initializes, but *not* in the
250 it in your startup files loaded after IPython initializes, but *not* in the
254 IPython configuration file itself, which is executed before IPython is
251 IPython configuration file itself, which is executed before IPython is
255 fully up and running. Any file located in the `startup` subdirectory of
252 fully up and running. Any file located in the `startup` subdirectory of
256 your configuration profile will be OK in this sense.
253 your configuration profile will be OK in this sense.
257 """)
254 """)
258
255
259 magic_deco.__doc__ = ds
256 magic_deco.__doc__ = ds
260 return magic_deco
257 return magic_deco
261
258
262
259
263 MAGIC_NO_VAR_EXPAND_ATTR = '_ipython_magic_no_var_expand'
260 MAGIC_NO_VAR_EXPAND_ATTR = '_ipython_magic_no_var_expand'
264
261
265
262
266 def no_var_expand(magic_func):
263 def no_var_expand(magic_func):
267 """Mark a magic function as not needing variable expansion
264 """Mark a magic function as not needing variable expansion
268
265
269 By default, IPython interprets `{a}` or `$a` in the line passed to magics
266 By default, IPython interprets `{a}` or `$a` in the line passed to magics
270 as variables that should be interpolated from the interactive namespace
267 as variables that should be interpolated from the interactive namespace
271 before passing the line to the magic function.
268 before passing the line to the magic function.
272 This is not always desirable, e.g. when the magic executes Python code
269 This is not always desirable, e.g. when the magic executes Python code
273 (%timeit, %time, etc.).
270 (%timeit, %time, etc.).
274 Decorate magics with `@no_var_expand` to opt-out of variable expansion.
271 Decorate magics with `@no_var_expand` to opt-out of variable expansion.
275
272
276 .. versionadded:: 7.3
273 .. versionadded:: 7.3
277 """
274 """
278 setattr(magic_func, MAGIC_NO_VAR_EXPAND_ATTR, True)
275 setattr(magic_func, MAGIC_NO_VAR_EXPAND_ATTR, True)
279 return magic_func
276 return magic_func
280
277
281
278
282 # Create the actual decorators for public use
279 # Create the actual decorators for public use
283
280
284 # These three are used to decorate methods in class definitions
281 # These three are used to decorate methods in class definitions
285 line_magic = _method_magic_marker('line')
282 line_magic = _method_magic_marker('line')
286 cell_magic = _method_magic_marker('cell')
283 cell_magic = _method_magic_marker('cell')
287 line_cell_magic = _method_magic_marker('line_cell')
284 line_cell_magic = _method_magic_marker('line_cell')
288
285
289 # These three decorate standalone functions and perform the decoration
286 # These three decorate standalone functions and perform the decoration
290 # immediately. They can only run where get_ipython() works
287 # immediately. They can only run where get_ipython() works
291 register_line_magic = _function_magic_marker('line')
288 register_line_magic = _function_magic_marker('line')
292 register_cell_magic = _function_magic_marker('cell')
289 register_cell_magic = _function_magic_marker('cell')
293 register_line_cell_magic = _function_magic_marker('line_cell')
290 register_line_cell_magic = _function_magic_marker('line_cell')
294
291
295 #-----------------------------------------------------------------------------
292 #-----------------------------------------------------------------------------
296 # Core Magic classes
293 # Core Magic classes
297 #-----------------------------------------------------------------------------
294 #-----------------------------------------------------------------------------
298
295
299 class MagicsManager(Configurable):
296 class MagicsManager(Configurable):
300 """Object that handles all magic-related functionality for IPython.
297 """Object that handles all magic-related functionality for IPython.
301 """
298 """
302 # Non-configurable class attributes
299 # Non-configurable class attributes
303
300
304 # A two-level dict, first keyed by magic type, then by magic function, and
301 # A two-level dict, first keyed by magic type, then by magic function, and
305 # holding the actual callable object as value. This is the dict used for
302 # holding the actual callable object as value. This is the dict used for
306 # magic function dispatch
303 # magic function dispatch
307 magics = Dict()
304 magics = Dict()
308
305
309 # A registry of the original objects that we've been given holding magics.
306 # A registry of the original objects that we've been given holding magics.
310 registry = Dict()
307 registry = Dict()
311
308
312 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True)
309 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True)
313
310
314 auto_magic = Bool(True, help=
311 auto_magic = Bool(True, help=
315 "Automatically call line magics without requiring explicit % prefix"
312 "Automatically call line magics without requiring explicit % prefix"
316 ).tag(config=True)
313 ).tag(config=True)
317 @observe('auto_magic')
314 @observe('auto_magic')
318 def _auto_magic_changed(self, change):
315 def _auto_magic_changed(self, change):
319 self.shell.automagic = change['new']
316 self.shell.automagic = change['new']
320
317
321 _auto_status = [
318 _auto_status = [
322 'Automagic is OFF, % prefix IS needed for line magics.',
319 'Automagic is OFF, % prefix IS needed for line magics.',
323 'Automagic is ON, % prefix IS NOT needed for line magics.']
320 'Automagic is ON, % prefix IS NOT needed for line magics.']
324
321
325 user_magics = Instance('IPython.core.magics.UserMagics', allow_none=True)
322 user_magics = Instance('IPython.core.magics.UserMagics', allow_none=True)
326
323
327 def __init__(self, shell=None, config=None, user_magics=None, **traits):
324 def __init__(self, shell=None, config=None, user_magics=None, **traits):
328
325
329 super(MagicsManager, self).__init__(shell=shell, config=config,
326 super(MagicsManager, self).__init__(shell=shell, config=config,
330 user_magics=user_magics, **traits)
327 user_magics=user_magics, **traits)
331 self.magics = dict(line={}, cell={})
328 self.magics = dict(line={}, cell={})
332 # Let's add the user_magics to the registry for uniformity, so *all*
329 # Let's add the user_magics to the registry for uniformity, so *all*
333 # registered magic containers can be found there.
330 # registered magic containers can be found there.
334 self.registry[user_magics.__class__.__name__] = user_magics
331 self.registry[user_magics.__class__.__name__] = user_magics
335
332
336 def auto_status(self):
333 def auto_status(self):
337 """Return descriptive string with automagic status."""
334 """Return descriptive string with automagic status."""
338 return self._auto_status[self.auto_magic]
335 return self._auto_status[self.auto_magic]
339
336
340 def lsmagic(self):
337 def lsmagic(self):
341 """Return a dict of currently available magic functions.
338 """Return a dict of currently available magic functions.
342
339
343 The return dict has the keys 'line' and 'cell', corresponding to the
340 The return dict has the keys 'line' and 'cell', corresponding to the
344 two types of magics we support. Each value is a list of names.
341 two types of magics we support. Each value is a list of names.
345 """
342 """
346 return self.magics
343 return self.magics
347
344
348 def lsmagic_docs(self, brief=False, missing=''):
345 def lsmagic_docs(self, brief=False, missing=''):
349 """Return dict of documentation of magic functions.
346 """Return dict of documentation of magic functions.
350
347
351 The return dict has the keys 'line' and 'cell', corresponding to the
348 The return dict has the keys 'line' and 'cell', corresponding to the
352 two types of magics we support. Each value is a dict keyed by magic
349 two types of magics we support. Each value is a dict keyed by magic
353 name whose value is the function docstring. If a docstring is
350 name whose value is the function docstring. If a docstring is
354 unavailable, the value of `missing` is used instead.
351 unavailable, the value of `missing` is used instead.
355
352
356 If brief is True, only the first line of each docstring will be returned.
353 If brief is True, only the first line of each docstring will be returned.
357 """
354 """
358 docs = {}
355 docs = {}
359 for m_type in self.magics:
356 for m_type in self.magics:
360 m_docs = {}
357 m_docs = {}
361 for m_name, m_func in self.magics[m_type].items():
358 for m_name, m_func in self.magics[m_type].items():
362 if m_func.__doc__:
359 if m_func.__doc__:
363 if brief:
360 if brief:
364 m_docs[m_name] = m_func.__doc__.split('\n', 1)[0]
361 m_docs[m_name] = m_func.__doc__.split('\n', 1)[0]
365 else:
362 else:
366 m_docs[m_name] = m_func.__doc__.rstrip()
363 m_docs[m_name] = m_func.__doc__.rstrip()
367 else:
364 else:
368 m_docs[m_name] = missing
365 m_docs[m_name] = missing
369 docs[m_type] = m_docs
366 docs[m_type] = m_docs
370 return docs
367 return docs
371
368
372 def register(self, *magic_objects):
369 def register(self, *magic_objects):
373 """Register one or more instances of Magics.
370 """Register one or more instances of Magics.
374
371
375 Take one or more classes or instances of classes that subclass the main
372 Take one or more classes or instances of classes that subclass the main
376 `core.Magic` class, and register them with IPython to use the magic
373 `core.Magic` class, and register them with IPython to use the magic
377 functions they provide. The registration process will then ensure that
374 functions they provide. The registration process will then ensure that
378 any methods that have decorated to provide line and/or cell magics will
375 any methods that have decorated to provide line and/or cell magics will
379 be recognized with the `%x`/`%%x` syntax as a line/cell magic
376 be recognized with the `%x`/`%%x` syntax as a line/cell magic
380 respectively.
377 respectively.
381
378
382 If classes are given, they will be instantiated with the default
379 If classes are given, they will be instantiated with the default
383 constructor. If your classes need a custom constructor, you should
380 constructor. If your classes need a custom constructor, you should
384 instanitate them first and pass the instance.
381 instanitate them first and pass the instance.
385
382
386 The provided arguments can be an arbitrary mix of classes and instances.
383 The provided arguments can be an arbitrary mix of classes and instances.
387
384
388 Parameters
385 Parameters
389 ----------
386 ----------
390 magic_objects : one or more classes or instances
387 *magic_objects : one or more classes or instances
391 """
388 """
392 # Start by validating them to ensure they have all had their magic
389 # Start by validating them to ensure they have all had their magic
393 # methods registered at the instance level
390 # methods registered at the instance level
394 for m in magic_objects:
391 for m in magic_objects:
395 if not m.registered:
392 if not m.registered:
396 raise ValueError("Class of magics %r was constructed without "
393 raise ValueError("Class of magics %r was constructed without "
397 "the @register_magics class decorator")
394 "the @register_magics class decorator")
398 if isinstance(m, type):
395 if isinstance(m, type):
399 # If we're given an uninstantiated class
396 # If we're given an uninstantiated class
400 m = m(shell=self.shell)
397 m = m(shell=self.shell)
401
398
402 # Now that we have an instance, we can register it and update the
399 # Now that we have an instance, we can register it and update the
403 # table of callables
400 # table of callables
404 self.registry[m.__class__.__name__] = m
401 self.registry[m.__class__.__name__] = m
405 for mtype in magic_kinds:
402 for mtype in magic_kinds:
406 self.magics[mtype].update(m.magics[mtype])
403 self.magics[mtype].update(m.magics[mtype])
407
404
408 def register_function(self, func, magic_kind='line', magic_name=None):
405 def register_function(self, func, magic_kind='line', magic_name=None):
409 """Expose a standalone function as magic function for IPython.
406 """Expose a standalone function as magic function for IPython.
410
407
411 This will create an IPython magic (line, cell or both) from a
408 This will create an IPython magic (line, cell or both) from a
412 standalone function. The functions should have the following
409 standalone function. The functions should have the following
413 signatures:
410 signatures:
414
411
415 * For line magics: `def f(line)`
412 * For line magics: `def f(line)`
416 * For cell magics: `def f(line, cell)`
413 * For cell magics: `def f(line, cell)`
417 * For a function that does both: `def f(line, cell=None)`
414 * For a function that does both: `def f(line, cell=None)`
418
415
419 In the latter case, the function will be called with `cell==None` when
416 In the latter case, the function will be called with `cell==None` when
420 invoked as `%f`, and with cell as a string when invoked as `%%f`.
417 invoked as `%f`, and with cell as a string when invoked as `%%f`.
421
418
422 Parameters
419 Parameters
423 ----------
420 ----------
424 func : callable
421 func : callable
425 Function to be registered as a magic.
422 Function to be registered as a magic.
426
427 magic_kind : str
423 magic_kind : str
428 Kind of magic, one of 'line', 'cell' or 'line_cell'
424 Kind of magic, one of 'line', 'cell' or 'line_cell'
429
430 magic_name : optional str
425 magic_name : optional str
431 If given, the name the magic will have in the IPython namespace. By
426 If given, the name the magic will have in the IPython namespace. By
432 default, the name of the function itself is used.
427 default, the name of the function itself is used.
433 """
428 """
434
429
435 # Create the new method in the user_magics and register it in the
430 # Create the new method in the user_magics and register it in the
436 # global table
431 # global table
437 validate_type(magic_kind)
432 validate_type(magic_kind)
438 magic_name = func.__name__ if magic_name is None else magic_name
433 magic_name = func.__name__ if magic_name is None else magic_name
439 setattr(self.user_magics, magic_name, func)
434 setattr(self.user_magics, magic_name, func)
440 record_magic(self.magics, magic_kind, magic_name, func)
435 record_magic(self.magics, magic_kind, magic_name, func)
441
436
442 def register_alias(self, alias_name, magic_name, magic_kind='line', magic_params=None):
437 def register_alias(self, alias_name, magic_name, magic_kind='line', magic_params=None):
443 """Register an alias to a magic function.
438 """Register an alias to a magic function.
444
439
445 The alias is an instance of :class:`MagicAlias`, which holds the
440 The alias is an instance of :class:`MagicAlias`, which holds the
446 name and kind of the magic it should call. Binding is done at
441 name and kind of the magic it should call. Binding is done at
447 call time, so if the underlying magic function is changed the alias
442 call time, so if the underlying magic function is changed the alias
448 will call the new function.
443 will call the new function.
449
444
450 Parameters
445 Parameters
451 ----------
446 ----------
452 alias_name : str
447 alias_name : str
453 The name of the magic to be registered.
448 The name of the magic to be registered.
454
455 magic_name : str
449 magic_name : str
456 The name of an existing magic.
450 The name of an existing magic.
457
458 magic_kind : str
451 magic_kind : str
459 Kind of magic, one of 'line' or 'cell'
452 Kind of magic, one of 'line' or 'cell'
460 """
453 """
461
454
462 # `validate_type` is too permissive, as it allows 'line_cell'
455 # `validate_type` is too permissive, as it allows 'line_cell'
463 # which we do not handle.
456 # which we do not handle.
464 if magic_kind not in magic_kinds:
457 if magic_kind not in magic_kinds:
465 raise ValueError('magic_kind must be one of %s, %s given' %
458 raise ValueError('magic_kind must be one of %s, %s given' %
466 magic_kinds, magic_kind)
459 magic_kinds, magic_kind)
467
460
468 alias = MagicAlias(self.shell, magic_name, magic_kind, magic_params)
461 alias = MagicAlias(self.shell, magic_name, magic_kind, magic_params)
469 setattr(self.user_magics, alias_name, alias)
462 setattr(self.user_magics, alias_name, alias)
470 record_magic(self.magics, magic_kind, alias_name, alias)
463 record_magic(self.magics, magic_kind, alias_name, alias)
471
464
472 # Key base class that provides the central functionality for magics.
465 # Key base class that provides the central functionality for magics.
473
466
474
467
475 class Magics(Configurable):
468 class Magics(Configurable):
476 """Base class for implementing magic functions.
469 """Base class for implementing magic functions.
477
470
478 Shell functions which can be reached as %function_name. All magic
471 Shell functions which can be reached as %function_name. All magic
479 functions should accept a string, which they can parse for their own
472 functions should accept a string, which they can parse for their own
480 needs. This can make some functions easier to type, eg `%cd ../`
473 needs. This can make some functions easier to type, eg `%cd ../`
481 vs. `%cd("../")`
474 vs. `%cd("../")`
482
475
483 Classes providing magic functions need to subclass this class, and they
476 Classes providing magic functions need to subclass this class, and they
484 MUST:
477 MUST:
485
478
486 - Use the method decorators `@line_magic` and `@cell_magic` to decorate
479 - Use the method decorators `@line_magic` and `@cell_magic` to decorate
487 individual methods as magic functions, AND
480 individual methods as magic functions, AND
488
481
489 - Use the class decorator `@magics_class` to ensure that the magic
482 - Use the class decorator `@magics_class` to ensure that the magic
490 methods are properly registered at the instance level upon instance
483 methods are properly registered at the instance level upon instance
491 initialization.
484 initialization.
492
485
493 See :mod:`magic_functions` for examples of actual implementation classes.
486 See :mod:`magic_functions` for examples of actual implementation classes.
494 """
487 """
495 # Dict holding all command-line options for each magic.
488 # Dict holding all command-line options for each magic.
496 options_table = None
489 options_table = None
497 # Dict for the mapping of magic names to methods, set by class decorator
490 # Dict for the mapping of magic names to methods, set by class decorator
498 magics = None
491 magics = None
499 # Flag to check that the class decorator was properly applied
492 # Flag to check that the class decorator was properly applied
500 registered = False
493 registered = False
501 # Instance of IPython shell
494 # Instance of IPython shell
502 shell = None
495 shell = None
503
496
504 def __init__(self, shell=None, **kwargs):
497 def __init__(self, shell=None, **kwargs):
505 if not(self.__class__.registered):
498 if not(self.__class__.registered):
506 raise ValueError('Magics subclass without registration - '
499 raise ValueError('Magics subclass without registration - '
507 'did you forget to apply @magics_class?')
500 'did you forget to apply @magics_class?')
508 if shell is not None:
501 if shell is not None:
509 if hasattr(shell, 'configurables'):
502 if hasattr(shell, 'configurables'):
510 shell.configurables.append(self)
503 shell.configurables.append(self)
511 if hasattr(shell, 'config'):
504 if hasattr(shell, 'config'):
512 kwargs.setdefault('parent', shell)
505 kwargs.setdefault('parent', shell)
513
506
514 self.shell = shell
507 self.shell = shell
515 self.options_table = {}
508 self.options_table = {}
516 # The method decorators are run when the instance doesn't exist yet, so
509 # The method decorators are run when the instance doesn't exist yet, so
517 # they can only record the names of the methods they are supposed to
510 # they can only record the names of the methods they are supposed to
518 # grab. Only now, that the instance exists, can we create the proper
511 # grab. Only now, that the instance exists, can we create the proper
519 # mapping to bound methods. So we read the info off the original names
512 # mapping to bound methods. So we read the info off the original names
520 # table and replace each method name by the actual bound method.
513 # table and replace each method name by the actual bound method.
521 # But we mustn't clobber the *class* mapping, in case of multiple instances.
514 # But we mustn't clobber the *class* mapping, in case of multiple instances.
522 class_magics = self.magics
515 class_magics = self.magics
523 self.magics = {}
516 self.magics = {}
524 for mtype in magic_kinds:
517 for mtype in magic_kinds:
525 tab = self.magics[mtype] = {}
518 tab = self.magics[mtype] = {}
526 cls_tab = class_magics[mtype]
519 cls_tab = class_magics[mtype]
527 for magic_name, meth_name in cls_tab.items():
520 for magic_name, meth_name in cls_tab.items():
528 if isinstance(meth_name, str):
521 if isinstance(meth_name, str):
529 # it's a method name, grab it
522 # it's a method name, grab it
530 tab[magic_name] = getattr(self, meth_name)
523 tab[magic_name] = getattr(self, meth_name)
531 else:
524 else:
532 # it's the real thing
525 # it's the real thing
533 tab[magic_name] = meth_name
526 tab[magic_name] = meth_name
534 # Configurable **needs** to be initiated at the end or the config
527 # Configurable **needs** to be initiated at the end or the config
535 # magics get screwed up.
528 # magics get screwed up.
536 super(Magics, self).__init__(**kwargs)
529 super(Magics, self).__init__(**kwargs)
537
530
538 def arg_err(self,func):
531 def arg_err(self,func):
539 """Print docstring if incorrect arguments were passed"""
532 """Print docstring if incorrect arguments were passed"""
540 print('Error in arguments:')
533 print('Error in arguments:')
541 print(oinspect.getdoc(func))
534 print(oinspect.getdoc(func))
542
535
543 def format_latex(self, strng):
536 def format_latex(self, strng):
544 """Format a string for latex inclusion."""
537 """Format a string for latex inclusion."""
545
538
546 # Characters that need to be escaped for latex:
539 # Characters that need to be escaped for latex:
547 escape_re = re.compile(r'(%|_|\$|#|&)',re.MULTILINE)
540 escape_re = re.compile(r'(%|_|\$|#|&)',re.MULTILINE)
548 # Magic command names as headers:
541 # Magic command names as headers:
549 cmd_name_re = re.compile(r'^(%s.*?):' % ESC_MAGIC,
542 cmd_name_re = re.compile(r'^(%s.*?):' % ESC_MAGIC,
550 re.MULTILINE)
543 re.MULTILINE)
551 # Magic commands
544 # Magic commands
552 cmd_re = re.compile(r'(?P<cmd>%s.+?\b)(?!\}\}:)' % ESC_MAGIC,
545 cmd_re = re.compile(r'(?P<cmd>%s.+?\b)(?!\}\}:)' % ESC_MAGIC,
553 re.MULTILINE)
546 re.MULTILINE)
554 # Paragraph continue
547 # Paragraph continue
555 par_re = re.compile(r'\\$',re.MULTILINE)
548 par_re = re.compile(r'\\$',re.MULTILINE)
556
549
557 # The "\n" symbol
550 # The "\n" symbol
558 newline_re = re.compile(r'\\n')
551 newline_re = re.compile(r'\\n')
559
552
560 # Now build the string for output:
553 # Now build the string for output:
561 #strng = cmd_name_re.sub(r'\n\\texttt{\\textsl{\\large \1}}:',strng)
554 #strng = cmd_name_re.sub(r'\n\\texttt{\\textsl{\\large \1}}:',strng)
562 strng = cmd_name_re.sub(r'\n\\bigskip\n\\texttt{\\textbf{ \1}}:',
555 strng = cmd_name_re.sub(r'\n\\bigskip\n\\texttt{\\textbf{ \1}}:',
563 strng)
556 strng)
564 strng = cmd_re.sub(r'\\texttt{\g<cmd>}',strng)
557 strng = cmd_re.sub(r'\\texttt{\g<cmd>}',strng)
565 strng = par_re.sub(r'\\\\',strng)
558 strng = par_re.sub(r'\\\\',strng)
566 strng = escape_re.sub(r'\\\1',strng)
559 strng = escape_re.sub(r'\\\1',strng)
567 strng = newline_re.sub(r'\\textbackslash{}n',strng)
560 strng = newline_re.sub(r'\\textbackslash{}n',strng)
568 return strng
561 return strng
569
562
570 def parse_options(self, arg_str, opt_str, *long_opts, **kw):
563 def parse_options(self, arg_str, opt_str, *long_opts, **kw):
571 """Parse options passed to an argument string.
564 """Parse options passed to an argument string.
572
565
573 The interface is similar to that of :func:`getopt.getopt`, but it
566 The interface is similar to that of :func:`getopt.getopt`, but it
574 returns a :class:`~IPython.utils.struct.Struct` with the options as keys
567 returns a :class:`~IPython.utils.struct.Struct` with the options as keys
575 and the stripped argument string still as a string.
568 and the stripped argument string still as a string.
576
569
577 arg_str is quoted as a true sys.argv vector by using shlex.split.
570 arg_str is quoted as a true sys.argv vector by using shlex.split.
578 This allows us to easily expand variables, glob files, quote
571 This allows us to easily expand variables, glob files, quote
579 arguments, etc.
572 arguments, etc.
580
573
581 Parameters
574 Parameters
582 ----------
575 ----------
583
584 arg_str : str
576 arg_str : str
585 The arguments to parse.
577 The arguments to parse.
586
587 opt_str : str
578 opt_str : str
588 The options specification.
579 The options specification.
589
590 mode : str, default 'string'
580 mode : str, default 'string'
591 If given as 'list', the argument string is returned as a list (split
581 If given as 'list', the argument string is returned as a list (split
592 on whitespace) instead of a string.
582 on whitespace) instead of a string.
593
594 list_all : bool, default False
583 list_all : bool, default False
595 Put all option values in lists. Normally only options
584 Put all option values in lists. Normally only options
596 appearing more than once are put in a list.
585 appearing more than once are put in a list.
597
598 posix : bool, default True
586 posix : bool, default True
599 Whether to split the input line in POSIX mode or not, as per the
587 Whether to split the input line in POSIX mode or not, as per the
600 conventions outlined in the :mod:`shlex` module from the standard
588 conventions outlined in the :mod:`shlex` module from the standard
601 library.
589 library.
602 """
590 """
603
591
604 # inject default options at the beginning of the input line
592 # inject default options at the beginning of the input line
605 caller = sys._getframe(1).f_code.co_name
593 caller = sys._getframe(1).f_code.co_name
606 arg_str = '%s %s' % (self.options_table.get(caller,''),arg_str)
594 arg_str = '%s %s' % (self.options_table.get(caller,''),arg_str)
607
595
608 mode = kw.get('mode','string')
596 mode = kw.get('mode','string')
609 if mode not in ['string','list']:
597 if mode not in ['string','list']:
610 raise ValueError('incorrect mode given: %s' % mode)
598 raise ValueError('incorrect mode given: %s' % mode)
611 # Get options
599 # Get options
612 list_all = kw.get('list_all',0)
600 list_all = kw.get('list_all',0)
613 posix = kw.get('posix', os.name == 'posix')
601 posix = kw.get('posix', os.name == 'posix')
614 strict = kw.get('strict', True)
602 strict = kw.get('strict', True)
615
603
616 preserve_non_opts = kw.get("preserve_non_opts", False)
604 preserve_non_opts = kw.get("preserve_non_opts", False)
617 remainder_arg_str = arg_str
605 remainder_arg_str = arg_str
618
606
619 # Check if we have more than one argument to warrant extra processing:
607 # Check if we have more than one argument to warrant extra processing:
620 odict = {} # Dictionary with options
608 odict = {} # Dictionary with options
621 args = arg_str.split()
609 args = arg_str.split()
622 if len(args) >= 1:
610 if len(args) >= 1:
623 # If the list of inputs only has 0 or 1 thing in it, there's no
611 # If the list of inputs only has 0 or 1 thing in it, there's no
624 # need to look for options
612 # need to look for options
625 argv = arg_split(arg_str, posix, strict)
613 argv = arg_split(arg_str, posix, strict)
626 # Do regular option processing
614 # Do regular option processing
627 try:
615 try:
628 opts,args = getopt(argv, opt_str, long_opts)
616 opts,args = getopt(argv, opt_str, long_opts)
629 except GetoptError as e:
617 except GetoptError as e:
630 raise UsageError(
618 raise UsageError(
631 '%s ( allowed: "%s" %s)' % (e.msg, opt_str, " ".join(long_opts))
619 '%s ( allowed: "%s" %s)' % (e.msg, opt_str, " ".join(long_opts))
632 ) from e
620 ) from e
633 for o, a in opts:
621 for o, a in opts:
634 if mode == "string" and preserve_non_opts:
622 if mode == "string" and preserve_non_opts:
635 # remove option-parts from the original args-string and preserve remaining-part.
623 # remove option-parts from the original args-string and preserve remaining-part.
636 # This relies on the arg_split(...) and getopt(...)'s impl spec, that the parsed options are
624 # This relies on the arg_split(...) and getopt(...)'s impl spec, that the parsed options are
637 # returned in the original order.
625 # returned in the original order.
638 remainder_arg_str = remainder_arg_str.replace(o, "", 1).replace(
626 remainder_arg_str = remainder_arg_str.replace(o, "", 1).replace(
639 a, "", 1
627 a, "", 1
640 )
628 )
641 if o.startswith("--"):
629 if o.startswith("--"):
642 o = o[2:]
630 o = o[2:]
643 else:
631 else:
644 o = o[1:]
632 o = o[1:]
645 try:
633 try:
646 odict[o].append(a)
634 odict[o].append(a)
647 except AttributeError:
635 except AttributeError:
648 odict[o] = [odict[o],a]
636 odict[o] = [odict[o],a]
649 except KeyError:
637 except KeyError:
650 if list_all:
638 if list_all:
651 odict[o] = [a]
639 odict[o] = [a]
652 else:
640 else:
653 odict[o] = a
641 odict[o] = a
654
642
655 # Prepare opts,args for return
643 # Prepare opts,args for return
656 opts = Struct(odict)
644 opts = Struct(odict)
657 if mode == 'string':
645 if mode == 'string':
658 if preserve_non_opts:
646 if preserve_non_opts:
659 args = remainder_arg_str.lstrip()
647 args = remainder_arg_str.lstrip()
660 else:
648 else:
661 args = " ".join(args)
649 args = " ".join(args)
662
650
663 return opts,args
651 return opts,args
664
652
665 def default_option(self, fn, optstr):
653 def default_option(self, fn, optstr):
666 """Make an entry in the options_table for fn, with value optstr"""
654 """Make an entry in the options_table for fn, with value optstr"""
667
655
668 if fn not in self.lsmagic():
656 if fn not in self.lsmagic():
669 error("%s is not a magic function" % fn)
657 error("%s is not a magic function" % fn)
670 self.options_table[fn] = optstr
658 self.options_table[fn] = optstr
671
659
672
660
673 class MagicAlias(object):
661 class MagicAlias(object):
674 """An alias to another magic function.
662 """An alias to another magic function.
675
663
676 An alias is determined by its magic name and magic kind. Lookup
664 An alias is determined by its magic name and magic kind. Lookup
677 is done at call time, so if the underlying magic changes the alias
665 is done at call time, so if the underlying magic changes the alias
678 will call the new function.
666 will call the new function.
679
667
680 Use the :meth:`MagicsManager.register_alias` method or the
668 Use the :meth:`MagicsManager.register_alias` method or the
681 `%alias_magic` magic function to create and register a new alias.
669 `%alias_magic` magic function to create and register a new alias.
682 """
670 """
683 def __init__(self, shell, magic_name, magic_kind, magic_params=None):
671 def __init__(self, shell, magic_name, magic_kind, magic_params=None):
684 self.shell = shell
672 self.shell = shell
685 self.magic_name = magic_name
673 self.magic_name = magic_name
686 self.magic_params = magic_params
674 self.magic_params = magic_params
687 self.magic_kind = magic_kind
675 self.magic_kind = magic_kind
688
676
689 self.pretty_target = '%s%s' % (magic_escapes[self.magic_kind], self.magic_name)
677 self.pretty_target = '%s%s' % (magic_escapes[self.magic_kind], self.magic_name)
690 self.__doc__ = "Alias for `%s`." % self.pretty_target
678 self.__doc__ = "Alias for `%s`." % self.pretty_target
691
679
692 self._in_call = False
680 self._in_call = False
693
681
694 def __call__(self, *args, **kwargs):
682 def __call__(self, *args, **kwargs):
695 """Call the magic alias."""
683 """Call the magic alias."""
696 fn = self.shell.find_magic(self.magic_name, self.magic_kind)
684 fn = self.shell.find_magic(self.magic_name, self.magic_kind)
697 if fn is None:
685 if fn is None:
698 raise UsageError("Magic `%s` not found." % self.pretty_target)
686 raise UsageError("Magic `%s` not found." % self.pretty_target)
699
687
700 # Protect against infinite recursion.
688 # Protect against infinite recursion.
701 if self._in_call:
689 if self._in_call:
702 raise UsageError("Infinite recursion detected; "
690 raise UsageError("Infinite recursion detected; "
703 "magic aliases cannot call themselves.")
691 "magic aliases cannot call themselves.")
704 self._in_call = True
692 self._in_call = True
705 try:
693 try:
706 if self.magic_params:
694 if self.magic_params:
707 args_list = list(args)
695 args_list = list(args)
708 args_list[0] = self.magic_params + " " + args[0]
696 args_list[0] = self.magic_params + " " + args[0]
709 args = tuple(args_list)
697 args = tuple(args_list)
710 return fn(*args, **kwargs)
698 return fn(*args, **kwargs)
711 finally:
699 finally:
712 self._in_call = False
700 self._in_call = False
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
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
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
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