##// END OF EJS Templates
fix some other syntax warnings
Matthias Bussonnier -
Show More
@@ -1,354 +1,354 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Implementations for various useful completers.
2 """Implementations for various useful completers.
3
3
4 These are all loaded by default by IPython.
4 These are all loaded by default by IPython.
5 """
5 """
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2010-2011 The IPython Development Team.
7 # Copyright (C) 2010-2011 The IPython Development Team.
8 #
8 #
9 # Distributed under the terms of the BSD License.
9 # Distributed under the terms of the BSD License.
10 #
10 #
11 # The full license is in the file COPYING.txt, distributed with this software.
11 # The full license is in the file COPYING.txt, distributed with this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 # Stdlib imports
18 # Stdlib imports
19 import glob
19 import glob
20 import inspect
20 import inspect
21 import os
21 import os
22 import re
22 import re
23 import sys
23 import sys
24 from importlib import import_module
24 from importlib import import_module
25 from importlib.machinery import all_suffixes
25 from importlib.machinery import all_suffixes
26
26
27
27
28 # Third-party imports
28 # Third-party imports
29 from time import time
29 from time import time
30 from zipimport import zipimporter
30 from zipimport import zipimporter
31
31
32 # Our own imports
32 # Our own imports
33 from IPython.core.completer import expand_user, compress_user
33 from IPython.core.completer import expand_user, compress_user
34 from IPython.core.error import TryNext
34 from IPython.core.error import TryNext
35 from IPython.utils._process_common import arg_split
35 from IPython.utils._process_common import arg_split
36
36
37 # FIXME: this should be pulled in with the right call via the component system
37 # FIXME: this should be pulled in with the right call via the component system
38 from IPython import get_ipython
38 from IPython import get_ipython
39
39
40 from typing import List
40 from typing import List
41
41
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43 # Globals and constants
43 # Globals and constants
44 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
45 _suffixes = all_suffixes()
45 _suffixes = all_suffixes()
46
46
47 # Time in seconds after which the rootmodules will be stored permanently in the
47 # Time in seconds after which the rootmodules will be stored permanently in the
48 # ipython ip.db database (kept in the user's .ipython dir).
48 # ipython ip.db database (kept in the user's .ipython dir).
49 TIMEOUT_STORAGE = 2
49 TIMEOUT_STORAGE = 2
50
50
51 # Time in seconds after which we give up
51 # Time in seconds after which we give up
52 TIMEOUT_GIVEUP = 20
52 TIMEOUT_GIVEUP = 20
53
53
54 # Regular expression for the python import statement
54 # Regular expression for the python import statement
55 import_re = re.compile(r'(?P<name>[a-zA-Z_][a-zA-Z0-9_]*?)'
55 import_re = re.compile(r'(?P<name>[a-zA-Z_][a-zA-Z0-9_]*?)'
56 r'(?P<package>[/\\]__init__)?'
56 r'(?P<package>[/\\]__init__)?'
57 r'(?P<suffix>%s)$' %
57 r'(?P<suffix>%s)$' %
58 r'|'.join(re.escape(s) for s in _suffixes))
58 r'|'.join(re.escape(s) for s in _suffixes))
59
59
60 # RE for the ipython %run command (python + ipython scripts)
60 # RE for the ipython %run command (python + ipython scripts)
61 magic_run_re = re.compile(r'.*(\.ipy|\.ipynb|\.py[w]?)$')
61 magic_run_re = re.compile(r'.*(\.ipy|\.ipynb|\.py[w]?)$')
62
62
63 #-----------------------------------------------------------------------------
63 #-----------------------------------------------------------------------------
64 # Local utilities
64 # Local utilities
65 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
66
66
67 def module_list(path):
67 def module_list(path):
68 """
68 """
69 Return the list containing the names of the modules available in the given
69 Return the list containing the names of the modules available in the given
70 folder.
70 folder.
71 """
71 """
72 # sys.path has the cwd as an empty string, but isdir/listdir need it as '.'
72 # sys.path has the cwd as an empty string, but isdir/listdir need it as '.'
73 if path == '':
73 if path == '':
74 path = '.'
74 path = '.'
75
75
76 # A few local constants to be used in loops below
76 # A few local constants to be used in loops below
77 pjoin = os.path.join
77 pjoin = os.path.join
78
78
79 if os.path.isdir(path):
79 if os.path.isdir(path):
80 # Build a list of all files in the directory and all files
80 # Build a list of all files in the directory and all files
81 # in its subdirectories. For performance reasons, do not
81 # in its subdirectories. For performance reasons, do not
82 # recurse more than one level into subdirectories.
82 # recurse more than one level into subdirectories.
83 files = []
83 files = []
84 for root, dirs, nondirs in os.walk(path, followlinks=True):
84 for root, dirs, nondirs in os.walk(path, followlinks=True):
85 subdir = root[len(path)+1:]
85 subdir = root[len(path)+1:]
86 if subdir:
86 if subdir:
87 files.extend(pjoin(subdir, f) for f in nondirs)
87 files.extend(pjoin(subdir, f) for f in nondirs)
88 dirs[:] = [] # Do not recurse into additional subdirectories.
88 dirs[:] = [] # Do not recurse into additional subdirectories.
89 else:
89 else:
90 files.extend(nondirs)
90 files.extend(nondirs)
91
91
92 else:
92 else:
93 try:
93 try:
94 files = list(zipimporter(path)._files.keys())
94 files = list(zipimporter(path)._files.keys())
95 except:
95 except:
96 files = []
96 files = []
97
97
98 # Build a list of modules which match the import_re regex.
98 # Build a list of modules which match the import_re regex.
99 modules = []
99 modules = []
100 for f in files:
100 for f in files:
101 m = import_re.match(f)
101 m = import_re.match(f)
102 if m:
102 if m:
103 modules.append(m.group('name'))
103 modules.append(m.group('name'))
104 return list(set(modules))
104 return list(set(modules))
105
105
106
106
107 def get_root_modules():
107 def get_root_modules():
108 """
108 """
109 Returns a list containing the names of all the modules available in the
109 Returns a list containing the names of all the modules available in the
110 folders of the pythonpath.
110 folders of the pythonpath.
111
111
112 ip.db['rootmodules_cache'] maps sys.path entries to list of modules.
112 ip.db['rootmodules_cache'] maps sys.path entries to list of modules.
113 """
113 """
114 ip = get_ipython()
114 ip = get_ipython()
115 if ip is None:
115 if ip is None:
116 # No global shell instance to store cached list of modules.
116 # No global shell instance to store cached list of modules.
117 # Don't try to scan for modules every time.
117 # Don't try to scan for modules every time.
118 return list(sys.builtin_module_names)
118 return list(sys.builtin_module_names)
119
119
120 rootmodules_cache = ip.db.get('rootmodules_cache', {})
120 rootmodules_cache = ip.db.get('rootmodules_cache', {})
121 rootmodules = list(sys.builtin_module_names)
121 rootmodules = list(sys.builtin_module_names)
122 start_time = time()
122 start_time = time()
123 store = False
123 store = False
124 for path in sys.path:
124 for path in sys.path:
125 try:
125 try:
126 modules = rootmodules_cache[path]
126 modules = rootmodules_cache[path]
127 except KeyError:
127 except KeyError:
128 modules = module_list(path)
128 modules = module_list(path)
129 try:
129 try:
130 modules.remove('__init__')
130 modules.remove('__init__')
131 except ValueError:
131 except ValueError:
132 pass
132 pass
133 if path not in ('', '.'): # cwd modules should not be cached
133 if path not in ('', '.'): # cwd modules should not be cached
134 rootmodules_cache[path] = modules
134 rootmodules_cache[path] = modules
135 if time() - start_time > TIMEOUT_STORAGE and not store:
135 if time() - start_time > TIMEOUT_STORAGE and not store:
136 store = True
136 store = True
137 print("\nCaching the list of root modules, please wait!")
137 print("\nCaching the list of root modules, please wait!")
138 print("(This will only be done once - type '%rehashx' to "
138 print("(This will only be done once - type '%rehashx' to "
139 "reset cache!)\n")
139 "reset cache!)\n")
140 sys.stdout.flush()
140 sys.stdout.flush()
141 if time() - start_time > TIMEOUT_GIVEUP:
141 if time() - start_time > TIMEOUT_GIVEUP:
142 print("This is taking too long, we give up.\n")
142 print("This is taking too long, we give up.\n")
143 return []
143 return []
144 rootmodules.extend(modules)
144 rootmodules.extend(modules)
145 if store:
145 if store:
146 ip.db['rootmodules_cache'] = rootmodules_cache
146 ip.db['rootmodules_cache'] = rootmodules_cache
147 rootmodules = list(set(rootmodules))
147 rootmodules = list(set(rootmodules))
148 return rootmodules
148 return rootmodules
149
149
150
150
151 def is_importable(module, attr, only_modules):
151 def is_importable(module, attr, only_modules):
152 if only_modules:
152 if only_modules:
153 return inspect.ismodule(getattr(module, attr))
153 return inspect.ismodule(getattr(module, attr))
154 else:
154 else:
155 return not(attr[:2] == '__' and attr[-2:] == '__')
155 return not(attr[:2] == '__' and attr[-2:] == '__')
156
156
157
157
158 def try_import(mod: str, only_modules=False) -> List[str]:
158 def try_import(mod: str, only_modules=False) -> List[str]:
159 """
159 """
160 Try to import given module and return list of potential completions.
160 Try to import given module and return list of potential completions.
161 """
161 """
162 mod = mod.rstrip('.')
162 mod = mod.rstrip('.')
163 try:
163 try:
164 m = import_module(mod)
164 m = import_module(mod)
165 except:
165 except:
166 return []
166 return []
167
167
168 m_is_init = '__init__' in (getattr(m, '__file__', '') or '')
168 m_is_init = '__init__' in (getattr(m, '__file__', '') or '')
169
169
170 completions = []
170 completions = []
171 if (not hasattr(m, '__file__')) or (not only_modules) or m_is_init:
171 if (not hasattr(m, '__file__')) or (not only_modules) or m_is_init:
172 completions.extend( [attr for attr in dir(m) if
172 completions.extend( [attr for attr in dir(m) if
173 is_importable(m, attr, only_modules)])
173 is_importable(m, attr, only_modules)])
174
174
175 completions.extend(getattr(m, '__all__', []))
175 completions.extend(getattr(m, '__all__', []))
176 if m_is_init:
176 if m_is_init:
177 completions.extend(module_list(os.path.dirname(m.__file__)))
177 completions.extend(module_list(os.path.dirname(m.__file__)))
178 completions_set = {c for c in completions if isinstance(c, str)}
178 completions_set = {c for c in completions if isinstance(c, str)}
179 completions_set.discard('__init__')
179 completions_set.discard('__init__')
180 return list(completions_set)
180 return list(completions_set)
181
181
182
182
183 #-----------------------------------------------------------------------------
183 #-----------------------------------------------------------------------------
184 # Completion-related functions.
184 # Completion-related functions.
185 #-----------------------------------------------------------------------------
185 #-----------------------------------------------------------------------------
186
186
187 def quick_completer(cmd, completions):
187 def quick_completer(cmd, completions):
188 """ Easily create a trivial completer for a command.
188 r""" Easily create a trivial completer for a command.
189
189
190 Takes either a list of completions, or all completions in string (that will
190 Takes either a list of completions, or all completions in string (that will
191 be split on whitespace).
191 be split on whitespace).
192
192
193 Example::
193 Example::
194
194
195 [d:\ipython]|1> import ipy_completers
195 [d:\ipython]|1> import ipy_completers
196 [d:\ipython]|2> ipy_completers.quick_completer('foo', ['bar','baz'])
196 [d:\ipython]|2> ipy_completers.quick_completer('foo', ['bar','baz'])
197 [d:\ipython]|3> foo b<TAB>
197 [d:\ipython]|3> foo b<TAB>
198 bar baz
198 bar baz
199 [d:\ipython]|3> foo ba
199 [d:\ipython]|3> foo ba
200 """
200 """
201
201
202 if isinstance(completions, str):
202 if isinstance(completions, str):
203 completions = completions.split()
203 completions = completions.split()
204
204
205 def do_complete(self, event):
205 def do_complete(self, event):
206 return completions
206 return completions
207
207
208 get_ipython().set_hook('complete_command',do_complete, str_key = cmd)
208 get_ipython().set_hook('complete_command',do_complete, str_key = cmd)
209
209
210 def module_completion(line):
210 def module_completion(line):
211 """
211 """
212 Returns a list containing the completion possibilities for an import line.
212 Returns a list containing the completion possibilities for an import line.
213
213
214 The line looks like this :
214 The line looks like this :
215 'import xml.d'
215 'import xml.d'
216 'from xml.dom import'
216 'from xml.dom import'
217 """
217 """
218
218
219 words = line.split(' ')
219 words = line.split(' ')
220 nwords = len(words)
220 nwords = len(words)
221
221
222 # from whatever <tab> -> 'import '
222 # from whatever <tab> -> 'import '
223 if nwords == 3 and words[0] == 'from':
223 if nwords == 3 and words[0] == 'from':
224 return ['import ']
224 return ['import ']
225
225
226 # 'from xy<tab>' or 'import xy<tab>'
226 # 'from xy<tab>' or 'import xy<tab>'
227 if nwords < 3 and (words[0] in {'%aimport', 'import', 'from'}) :
227 if nwords < 3 and (words[0] in {'%aimport', 'import', 'from'}) :
228 if nwords == 1:
228 if nwords == 1:
229 return get_root_modules()
229 return get_root_modules()
230 mod = words[1].split('.')
230 mod = words[1].split('.')
231 if len(mod) < 2:
231 if len(mod) < 2:
232 return get_root_modules()
232 return get_root_modules()
233 completion_list = try_import('.'.join(mod[:-1]), True)
233 completion_list = try_import('.'.join(mod[:-1]), True)
234 return ['.'.join(mod[:-1] + [el]) for el in completion_list]
234 return ['.'.join(mod[:-1] + [el]) for el in completion_list]
235
235
236 # 'from xyz import abc<tab>'
236 # 'from xyz import abc<tab>'
237 if nwords >= 3 and words[0] == 'from':
237 if nwords >= 3 and words[0] == 'from':
238 mod = words[1]
238 mod = words[1]
239 return try_import(mod)
239 return try_import(mod)
240
240
241 #-----------------------------------------------------------------------------
241 #-----------------------------------------------------------------------------
242 # Completers
242 # Completers
243 #-----------------------------------------------------------------------------
243 #-----------------------------------------------------------------------------
244 # These all have the func(self, event) signature to be used as custom
244 # These all have the func(self, event) signature to be used as custom
245 # completers
245 # completers
246
246
247 def module_completer(self,event):
247 def module_completer(self,event):
248 """Give completions after user has typed 'import ...' or 'from ...'"""
248 """Give completions after user has typed 'import ...' or 'from ...'"""
249
249
250 # This works in all versions of python. While 2.5 has
250 # This works in all versions of python. While 2.5 has
251 # pkgutil.walk_packages(), that particular routine is fairly dangerous,
251 # pkgutil.walk_packages(), that particular routine is fairly dangerous,
252 # since it imports *EVERYTHING* on sys.path. That is: a) very slow b) full
252 # since it imports *EVERYTHING* on sys.path. That is: a) very slow b) full
253 # of possibly problematic side effects.
253 # of possibly problematic side effects.
254 # This search the folders in the sys.path for available modules.
254 # This search the folders in the sys.path for available modules.
255
255
256 return module_completion(event.line)
256 return module_completion(event.line)
257
257
258 # FIXME: there's a lot of logic common to the run, cd and builtin file
258 # FIXME: there's a lot of logic common to the run, cd and builtin file
259 # completers, that is currently reimplemented in each.
259 # completers, that is currently reimplemented in each.
260
260
261 def magic_run_completer(self, event):
261 def magic_run_completer(self, event):
262 """Complete files that end in .py or .ipy or .ipynb for the %run command.
262 """Complete files that end in .py or .ipy or .ipynb for the %run command.
263 """
263 """
264 comps = arg_split(event.line, strict=False)
264 comps = arg_split(event.line, strict=False)
265 # relpath should be the current token that we need to complete.
265 # relpath should be the current token that we need to complete.
266 if (len(comps) > 1) and (not event.line.endswith(' ')):
266 if (len(comps) > 1) and (not event.line.endswith(' ')):
267 relpath = comps[-1].strip("'\"")
267 relpath = comps[-1].strip("'\"")
268 else:
268 else:
269 relpath = ''
269 relpath = ''
270
270
271 #print("\nev=", event) # dbg
271 #print("\nev=", event) # dbg
272 #print("rp=", relpath) # dbg
272 #print("rp=", relpath) # dbg
273 #print('comps=', comps) # dbg
273 #print('comps=', comps) # dbg
274
274
275 lglob = glob.glob
275 lglob = glob.glob
276 isdir = os.path.isdir
276 isdir = os.path.isdir
277 relpath, tilde_expand, tilde_val = expand_user(relpath)
277 relpath, tilde_expand, tilde_val = expand_user(relpath)
278
278
279 # Find if the user has already typed the first filename, after which we
279 # Find if the user has already typed the first filename, after which we
280 # should complete on all files, since after the first one other files may
280 # should complete on all files, since after the first one other files may
281 # be arguments to the input script.
281 # be arguments to the input script.
282
282
283 if any(magic_run_re.match(c) for c in comps):
283 if any(magic_run_re.match(c) for c in comps):
284 matches = [f.replace('\\','/') + ('/' if isdir(f) else '')
284 matches = [f.replace('\\','/') + ('/' if isdir(f) else '')
285 for f in lglob(relpath+'*')]
285 for f in lglob(relpath+'*')]
286 else:
286 else:
287 dirs = [f.replace('\\','/') + "/" for f in lglob(relpath+'*') if isdir(f)]
287 dirs = [f.replace('\\','/') + "/" for f in lglob(relpath+'*') if isdir(f)]
288 pys = [f.replace('\\','/')
288 pys = [f.replace('\\','/')
289 for f in lglob(relpath+'*.py') + lglob(relpath+'*.ipy') +
289 for f in lglob(relpath+'*.py') + lglob(relpath+'*.ipy') +
290 lglob(relpath+'*.ipynb') + lglob(relpath + '*.pyw')]
290 lglob(relpath+'*.ipynb') + lglob(relpath + '*.pyw')]
291
291
292 matches = dirs + pys
292 matches = dirs + pys
293
293
294 #print('run comp:', dirs+pys) # dbg
294 #print('run comp:', dirs+pys) # dbg
295 return [compress_user(p, tilde_expand, tilde_val) for p in matches]
295 return [compress_user(p, tilde_expand, tilde_val) for p in matches]
296
296
297
297
298 def cd_completer(self, event):
298 def cd_completer(self, event):
299 """Completer function for cd, which only returns directories."""
299 """Completer function for cd, which only returns directories."""
300 ip = get_ipython()
300 ip = get_ipython()
301 relpath = event.symbol
301 relpath = event.symbol
302
302
303 #print(event) # dbg
303 #print(event) # dbg
304 if event.line.endswith('-b') or ' -b ' in event.line:
304 if event.line.endswith('-b') or ' -b ' in event.line:
305 # return only bookmark completions
305 # return only bookmark completions
306 bkms = self.db.get('bookmarks', None)
306 bkms = self.db.get('bookmarks', None)
307 if bkms:
307 if bkms:
308 return bkms.keys()
308 return bkms.keys()
309 else:
309 else:
310 return []
310 return []
311
311
312 if event.symbol == '-':
312 if event.symbol == '-':
313 width_dh = str(len(str(len(ip.user_ns['_dh']) + 1)))
313 width_dh = str(len(str(len(ip.user_ns['_dh']) + 1)))
314 # jump in directory history by number
314 # jump in directory history by number
315 fmt = '-%0' + width_dh +'d [%s]'
315 fmt = '-%0' + width_dh +'d [%s]'
316 ents = [ fmt % (i,s) for i,s in enumerate(ip.user_ns['_dh'])]
316 ents = [ fmt % (i,s) for i,s in enumerate(ip.user_ns['_dh'])]
317 if len(ents) > 1:
317 if len(ents) > 1:
318 return ents
318 return ents
319 return []
319 return []
320
320
321 if event.symbol.startswith('--'):
321 if event.symbol.startswith('--'):
322 return ["--" + os.path.basename(d) for d in ip.user_ns['_dh']]
322 return ["--" + os.path.basename(d) for d in ip.user_ns['_dh']]
323
323
324 # Expand ~ in path and normalize directory separators.
324 # Expand ~ in path and normalize directory separators.
325 relpath, tilde_expand, tilde_val = expand_user(relpath)
325 relpath, tilde_expand, tilde_val = expand_user(relpath)
326 relpath = relpath.replace('\\','/')
326 relpath = relpath.replace('\\','/')
327
327
328 found = []
328 found = []
329 for d in [f.replace('\\','/') + '/' for f in glob.glob(relpath+'*')
329 for d in [f.replace('\\','/') + '/' for f in glob.glob(relpath+'*')
330 if os.path.isdir(f)]:
330 if os.path.isdir(f)]:
331 if ' ' in d:
331 if ' ' in d:
332 # we don't want to deal with any of that, complex code
332 # we don't want to deal with any of that, complex code
333 # for this is elsewhere
333 # for this is elsewhere
334 raise TryNext
334 raise TryNext
335
335
336 found.append(d)
336 found.append(d)
337
337
338 if not found:
338 if not found:
339 if os.path.isdir(relpath):
339 if os.path.isdir(relpath):
340 return [compress_user(relpath, tilde_expand, tilde_val)]
340 return [compress_user(relpath, tilde_expand, tilde_val)]
341
341
342 # if no completions so far, try bookmarks
342 # if no completions so far, try bookmarks
343 bks = self.db.get('bookmarks',{})
343 bks = self.db.get('bookmarks',{})
344 bkmatches = [s for s in bks if s.startswith(event.symbol)]
344 bkmatches = [s for s in bks if s.startswith(event.symbol)]
345 if bkmatches:
345 if bkmatches:
346 return bkmatches
346 return bkmatches
347
347
348 raise TryNext
348 raise TryNext
349
349
350 return [compress_user(p, tilde_expand, tilde_val) for p in found]
350 return [compress_user(p, tilde_expand, tilde_val) for p in found]
351
351
352 def reset_completer(self, event):
352 def reset_completer(self, event):
353 "A completer for %reset magic"
353 "A completer for %reset magic"
354 return '-f -s in out array dhist'.split()
354 return '-f -s in out array dhist'.split()
@@ -1,1466 +1,1466 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_hex, b2a_base64, hexlify
8 from binascii import b2a_hex, b2a_base64, hexlify
9 import json
9 import json
10 import mimetypes
10 import mimetypes
11 import os
11 import os
12 import struct
12 import struct
13 import sys
13 import sys
14 import warnings
14 import warnings
15 from copy import deepcopy
15 from copy import deepcopy
16
16
17 from IPython.utils.py3compat import cast_unicode
17 from IPython.utils.py3compat import cast_unicode
18 from IPython.testing.skipdoctest import skip_doctest
18 from IPython.testing.skipdoctest import skip_doctest
19
19
20 __all__ = ['display', 'display_pretty', 'display_html', 'display_markdown',
20 __all__ = ['display', 'display_pretty', 'display_html', 'display_markdown',
21 'display_svg', 'display_png', 'display_jpeg', 'display_latex', 'display_json',
21 'display_svg', 'display_png', 'display_jpeg', 'display_latex', 'display_json',
22 'display_javascript', 'display_pdf', 'DisplayObject', 'TextDisplayObject',
22 'display_javascript', 'display_pdf', 'DisplayObject', 'TextDisplayObject',
23 'Pretty', 'HTML', 'Markdown', 'Math', 'Latex', 'SVG', 'ProgressBar', 'JSON',
23 'Pretty', 'HTML', 'Markdown', 'Math', 'Latex', 'SVG', 'ProgressBar', 'JSON',
24 'GeoJSON', 'Javascript', 'Image', 'clear_output', 'set_matplotlib_formats',
24 'GeoJSON', 'Javascript', 'Image', 'clear_output', 'set_matplotlib_formats',
25 'set_matplotlib_close', 'publish_display_data', 'update_display', 'DisplayHandle',
25 'set_matplotlib_close', 'publish_display_data', 'update_display', 'DisplayHandle',
26 'Video']
26 'Video']
27
27
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29 # utility functions
29 # utility functions
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31
31
32 def _safe_exists(path):
32 def _safe_exists(path):
33 """Check path, but don't let exceptions raise"""
33 """Check path, but don't let exceptions raise"""
34 try:
34 try:
35 return os.path.exists(path)
35 return os.path.exists(path)
36 except Exception:
36 except Exception:
37 return False
37 return False
38
38
39 def _merge(d1, d2):
39 def _merge(d1, d2):
40 """Like update, but merges sub-dicts instead of clobbering at the top level.
40 """Like update, but merges sub-dicts instead of clobbering at the top level.
41
41
42 Updates d1 in-place
42 Updates d1 in-place
43 """
43 """
44
44
45 if not isinstance(d2, dict) or not isinstance(d1, dict):
45 if not isinstance(d2, dict) or not isinstance(d1, dict):
46 return d2
46 return d2
47 for key, value in d2.items():
47 for key, value in d2.items():
48 d1[key] = _merge(d1.get(key), value)
48 d1[key] = _merge(d1.get(key), value)
49 return d1
49 return d1
50
50
51 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
51 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
52 """internal implementation of all display_foo methods
52 """internal implementation of all display_foo methods
53
53
54 Parameters
54 Parameters
55 ----------
55 ----------
56 mimetype : str
56 mimetype : str
57 The mimetype to be published (e.g. 'image/png')
57 The mimetype to be published (e.g. 'image/png')
58 objs : tuple of objects
58 objs : tuple of objects
59 The Python objects to display, or if raw=True raw text data to
59 The Python objects to display, or if raw=True raw text data to
60 display.
60 display.
61 raw : bool
61 raw : bool
62 Are the data objects raw data or Python objects that need to be
62 Are the data objects raw data or Python objects that need to be
63 formatted before display? [default: False]
63 formatted before display? [default: False]
64 metadata : dict (optional)
64 metadata : dict (optional)
65 Metadata to be associated with the specific mimetype output.
65 Metadata to be associated with the specific mimetype output.
66 """
66 """
67 if metadata:
67 if metadata:
68 metadata = {mimetype: metadata}
68 metadata = {mimetype: metadata}
69 if raw:
69 if raw:
70 # turn list of pngdata into list of { 'image/png': pngdata }
70 # turn list of pngdata into list of { 'image/png': pngdata }
71 objs = [ {mimetype: obj} for obj in objs ]
71 objs = [ {mimetype: obj} for obj in objs ]
72 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
72 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
73
73
74 #-----------------------------------------------------------------------------
74 #-----------------------------------------------------------------------------
75 # Main functions
75 # Main functions
76 #-----------------------------------------------------------------------------
76 #-----------------------------------------------------------------------------
77
77
78 # use * to indicate transient is keyword-only
78 # use * to indicate transient is keyword-only
79 def publish_display_data(data, metadata=None, source=None, *, transient=None, **kwargs):
79 def publish_display_data(data, metadata=None, source=None, *, transient=None, **kwargs):
80 """Publish data and metadata to all frontends.
80 """Publish data and metadata to all frontends.
81
81
82 See the ``display_data`` message in the messaging documentation for
82 See the ``display_data`` message in the messaging documentation for
83 more details about this message type.
83 more details about this message type.
84
84
85 Keys of data and metadata can be any mime-type.
85 Keys of data and metadata can be any mime-type.
86
86
87 Parameters
87 Parameters
88 ----------
88 ----------
89 data : dict
89 data : dict
90 A dictionary having keys that are valid MIME types (like
90 A dictionary having keys that are valid MIME types (like
91 'text/plain' or 'image/svg+xml') and values that are the data for
91 'text/plain' or 'image/svg+xml') and values that are the data for
92 that MIME type. The data itself must be a JSON'able data
92 that MIME type. The data itself must be a JSON'able data
93 structure. Minimally all data should have the 'text/plain' data,
93 structure. Minimally all data should have the 'text/plain' data,
94 which can be displayed by all frontends. If more than the plain
94 which can be displayed by all frontends. If more than the plain
95 text is given, it is up to the frontend to decide which
95 text is given, it is up to the frontend to decide which
96 representation to use.
96 representation to use.
97 metadata : dict
97 metadata : dict
98 A dictionary for metadata related to the data. This can contain
98 A dictionary for metadata related to the data. This can contain
99 arbitrary key, value pairs that frontends can use to interpret
99 arbitrary key, value pairs that frontends can use to interpret
100 the data. mime-type keys matching those in data can be used
100 the data. mime-type keys matching those in data can be used
101 to specify metadata about particular representations.
101 to specify metadata about particular representations.
102 source : str, deprecated
102 source : str, deprecated
103 Unused.
103 Unused.
104 transient : dict, keyword-only
104 transient : dict, keyword-only
105 A dictionary of transient data, such as display_id.
105 A dictionary of transient data, such as display_id.
106 """
106 """
107 from IPython.core.interactiveshell import InteractiveShell
107 from IPython.core.interactiveshell import InteractiveShell
108
108
109 display_pub = InteractiveShell.instance().display_pub
109 display_pub = InteractiveShell.instance().display_pub
110
110
111 # only pass transient if supplied,
111 # only pass transient if supplied,
112 # to avoid errors with older ipykernel.
112 # to avoid errors with older ipykernel.
113 # TODO: We could check for ipykernel version and provide a detailed upgrade message.
113 # TODO: We could check for ipykernel version and provide a detailed upgrade message.
114 if transient:
114 if transient:
115 kwargs['transient'] = transient
115 kwargs['transient'] = transient
116
116
117 display_pub.publish(
117 display_pub.publish(
118 data=data,
118 data=data,
119 metadata=metadata,
119 metadata=metadata,
120 **kwargs
120 **kwargs
121 )
121 )
122
122
123
123
124 def _new_id():
124 def _new_id():
125 """Generate a new random text id with urandom"""
125 """Generate a new random text id with urandom"""
126 return b2a_hex(os.urandom(16)).decode('ascii')
126 return b2a_hex(os.urandom(16)).decode('ascii')
127
127
128
128
129 def display(*objs, include=None, exclude=None, metadata=None, transient=None, display_id=None, **kwargs):
129 def display(*objs, include=None, exclude=None, metadata=None, transient=None, display_id=None, **kwargs):
130 """Display a Python object in all frontends.
130 """Display a Python object in all frontends.
131
131
132 By default all representations will be computed and sent to the frontends.
132 By default all representations will be computed and sent to the frontends.
133 Frontends can decide which representation is used and how.
133 Frontends can decide which representation is used and how.
134
134
135 In terminal IPython this will be similar to using :func:`print`, for use in richer
135 In terminal IPython this will be similar to using :func:`print`, for use in richer
136 frontends see Jupyter notebook examples with rich display logic.
136 frontends see Jupyter notebook examples with rich display logic.
137
137
138 Parameters
138 Parameters
139 ----------
139 ----------
140 objs : tuple of objects
140 objs : tuple of objects
141 The Python objects to display.
141 The Python objects to display.
142 raw : bool, optional
142 raw : bool, optional
143 Are the objects to be displayed already mimetype-keyed dicts of raw display data,
143 Are the objects to be displayed already mimetype-keyed dicts of raw display data,
144 or Python objects that need to be formatted before display? [default: False]
144 or Python objects that need to be formatted before display? [default: False]
145 include : list, tuple or set, optional
145 include : list, tuple or set, optional
146 A list of format type strings (MIME types) to include in the
146 A list of format type strings (MIME types) to include in the
147 format data dict. If this is set *only* the format types included
147 format data dict. If this is set *only* the format types included
148 in this list will be computed.
148 in this list will be computed.
149 exclude : list, tuple or set, optional
149 exclude : list, tuple or set, optional
150 A list of format type strings (MIME types) to exclude in the format
150 A list of format type strings (MIME types) to exclude in the format
151 data dict. If this is set all format types will be computed,
151 data dict. If this is set all format types will be computed,
152 except for those included in this argument.
152 except for those included in this argument.
153 metadata : dict, optional
153 metadata : dict, optional
154 A dictionary of metadata to associate with the output.
154 A dictionary of metadata to associate with the output.
155 mime-type keys in this dictionary will be associated with the individual
155 mime-type keys in this dictionary will be associated with the individual
156 representation formats, if they exist.
156 representation formats, if they exist.
157 transient : dict, optional
157 transient : dict, optional
158 A dictionary of transient data to associate with the output.
158 A dictionary of transient data to associate with the output.
159 Data in this dict should not be persisted to files (e.g. notebooks).
159 Data in this dict should not be persisted to files (e.g. notebooks).
160 display_id : str, bool optional
160 display_id : str, bool optional
161 Set an id for the display.
161 Set an id for the display.
162 This id can be used for updating this display area later via update_display.
162 This id can be used for updating this display area later via update_display.
163 If given as `True`, generate a new `display_id`
163 If given as `True`, generate a new `display_id`
164 kwargs: additional keyword-args, optional
164 kwargs: additional keyword-args, optional
165 Additional keyword-arguments are passed through to the display publisher.
165 Additional keyword-arguments are passed through to the display publisher.
166
166
167 Returns
167 Returns
168 -------
168 -------
169
169
170 handle: DisplayHandle
170 handle: DisplayHandle
171 Returns a handle on updatable displays for use with :func:`update_display`,
171 Returns a handle on updatable displays for use with :func:`update_display`,
172 if `display_id` is given. Returns :any:`None` if no `display_id` is given
172 if `display_id` is given. Returns :any:`None` if no `display_id` is given
173 (default).
173 (default).
174
174
175 Examples
175 Examples
176 --------
176 --------
177
177
178 >>> class Json(object):
178 >>> class Json(object):
179 ... def __init__(self, json):
179 ... def __init__(self, json):
180 ... self.json = json
180 ... self.json = json
181 ... def _repr_pretty_(self, pp, cycle):
181 ... def _repr_pretty_(self, pp, cycle):
182 ... import json
182 ... import json
183 ... pp.text(json.dumps(self.json, indent=2))
183 ... pp.text(json.dumps(self.json, indent=2))
184 ... def __repr__(self):
184 ... def __repr__(self):
185 ... return str(self.json)
185 ... return str(self.json)
186 ...
186 ...
187
187
188 >>> d = Json({1:2, 3: {4:5}})
188 >>> d = Json({1:2, 3: {4:5}})
189
189
190 >>> print(d)
190 >>> print(d)
191 {1: 2, 3: {4: 5}}
191 {1: 2, 3: {4: 5}}
192
192
193 >>> display(d)
193 >>> display(d)
194 {
194 {
195 "1": 2,
195 "1": 2,
196 "3": {
196 "3": {
197 "4": 5
197 "4": 5
198 }
198 }
199 }
199 }
200
200
201 >>> def int_formatter(integer, pp, cycle):
201 >>> def int_formatter(integer, pp, cycle):
202 ... pp.text('I'*integer)
202 ... pp.text('I'*integer)
203
203
204 >>> plain = get_ipython().display_formatter.formatters['text/plain']
204 >>> plain = get_ipython().display_formatter.formatters['text/plain']
205 >>> plain.for_type(int, int_formatter)
205 >>> plain.for_type(int, int_formatter)
206 <function _repr_pprint at 0x...>
206 <function _repr_pprint at 0x...>
207 >>> display(7-5)
207 >>> display(7-5)
208 II
208 II
209
209
210 >>> del plain.type_printers[int]
210 >>> del plain.type_printers[int]
211 >>> display(7-5)
211 >>> display(7-5)
212 2
212 2
213
213
214 See Also
214 See Also
215 --------
215 --------
216
216
217 :func:`update_display`
217 :func:`update_display`
218
218
219 Notes
219 Notes
220 -----
220 -----
221
221
222 In Python, objects can declare their textual representation using the
222 In Python, objects can declare their textual representation using the
223 `__repr__` method. IPython expands on this idea and allows objects to declare
223 `__repr__` method. IPython expands on this idea and allows objects to declare
224 other, rich representations including:
224 other, rich representations including:
225
225
226 - HTML
226 - HTML
227 - JSON
227 - JSON
228 - PNG
228 - PNG
229 - JPEG
229 - JPEG
230 - SVG
230 - SVG
231 - LaTeX
231 - LaTeX
232
232
233 A single object can declare some or all of these representations; all are
233 A single object can declare some or all of these representations; all are
234 handled by IPython's display system.
234 handled by IPython's display system.
235
235
236 The main idea of the first approach is that you have to implement special
236 The main idea of the first approach is that you have to implement special
237 display methods when you define your class, one for each representation you
237 display methods when you define your class, one for each representation you
238 want to use. Here is a list of the names of the special methods and the
238 want to use. Here is a list of the names of the special methods and the
239 values they must return:
239 values they must return:
240
240
241 - `_repr_html_`: return raw HTML as a string, or a tuple (see below).
241 - `_repr_html_`: return raw HTML as a string, or a tuple (see below).
242 - `_repr_json_`: return a JSONable dict, or a tuple (see below).
242 - `_repr_json_`: return a JSONable dict, or a tuple (see below).
243 - `_repr_jpeg_`: return raw JPEG data, or a tuple (see below).
243 - `_repr_jpeg_`: return raw JPEG data, or a tuple (see below).
244 - `_repr_png_`: return raw PNG data, or a tuple (see below).
244 - `_repr_png_`: return raw PNG data, or a tuple (see below).
245 - `_repr_svg_`: return raw SVG data as a string, or a tuple (see below).
245 - `_repr_svg_`: return raw SVG data as a string, or a tuple (see below).
246 - `_repr_latex_`: return LaTeX commands in a string surrounded by "$",
246 - `_repr_latex_`: return LaTeX commands in a string surrounded by "$",
247 or a tuple (see below).
247 or a tuple (see below).
248 - `_repr_mimebundle_`: return a full mimebundle containing the mapping
248 - `_repr_mimebundle_`: return a full mimebundle containing the mapping
249 from all mimetypes to data.
249 from all mimetypes to data.
250 Use this for any mime-type not listed above.
250 Use this for any mime-type not listed above.
251
251
252 The above functions may also return the object's metadata alonside the
252 The above functions may also return the object's metadata alonside the
253 data. If the metadata is available, the functions will return a tuple
253 data. If the metadata is available, the functions will return a tuple
254 containing the data and metadata, in that order. If there is no metadata
254 containing the data and metadata, in that order. If there is no metadata
255 available, then the functions will return the data only.
255 available, then the functions will return the data only.
256
256
257 When you are directly writing your own classes, you can adapt them for
257 When you are directly writing your own classes, you can adapt them for
258 display in IPython by following the above approach. But in practice, you
258 display in IPython by following the above approach. But in practice, you
259 often need to work with existing classes that you can't easily modify.
259 often need to work with existing classes that you can't easily modify.
260
260
261 You can refer to the documentation on integrating with the display system in
261 You can refer to the documentation on integrating with the display system in
262 order to register custom formatters for already existing types
262 order to register custom formatters for already existing types
263 (:ref:`integrating_rich_display`).
263 (:ref:`integrating_rich_display`).
264
264
265 .. versionadded:: 5.4 display available without import
265 .. versionadded:: 5.4 display available without import
266 .. versionadded:: 6.1 display available without import
266 .. versionadded:: 6.1 display available without import
267
267
268 Since IPython 5.4 and 6.1 :func:`display` is automatically made available to
268 Since IPython 5.4 and 6.1 :func:`display` is automatically made available to
269 the user without import. If you are using display in a document that might
269 the user without import. If you are using display in a document that might
270 be used in a pure python context or with older version of IPython, use the
270 be used in a pure python context or with older version of IPython, use the
271 following import at the top of your file::
271 following import at the top of your file::
272
272
273 from IPython.display import display
273 from IPython.display import display
274
274
275 """
275 """
276 from IPython.core.interactiveshell import InteractiveShell
276 from IPython.core.interactiveshell import InteractiveShell
277
277
278 if not InteractiveShell.initialized():
278 if not InteractiveShell.initialized():
279 # Directly print objects.
279 # Directly print objects.
280 print(*objs)
280 print(*objs)
281 return
281 return
282
282
283 raw = kwargs.pop('raw', False)
283 raw = kwargs.pop('raw', False)
284 if transient is None:
284 if transient is None:
285 transient = {}
285 transient = {}
286 if metadata is None:
286 if metadata is None:
287 metadata={}
287 metadata={}
288 if display_id:
288 if display_id:
289 if display_id is True:
289 if display_id is True:
290 display_id = _new_id()
290 display_id = _new_id()
291 transient['display_id'] = display_id
291 transient['display_id'] = display_id
292 if kwargs.get('update') and 'display_id' not in transient:
292 if kwargs.get('update') and 'display_id' not in transient:
293 raise TypeError('display_id required for update_display')
293 raise TypeError('display_id required for update_display')
294 if transient:
294 if transient:
295 kwargs['transient'] = transient
295 kwargs['transient'] = transient
296
296
297 if not raw:
297 if not raw:
298 format = InteractiveShell.instance().display_formatter.format
298 format = InteractiveShell.instance().display_formatter.format
299
299
300 for obj in objs:
300 for obj in objs:
301 if raw:
301 if raw:
302 publish_display_data(data=obj, metadata=metadata, **kwargs)
302 publish_display_data(data=obj, metadata=metadata, **kwargs)
303 else:
303 else:
304 format_dict, md_dict = format(obj, include=include, exclude=exclude)
304 format_dict, md_dict = format(obj, include=include, exclude=exclude)
305 if not format_dict:
305 if not format_dict:
306 # nothing to display (e.g. _ipython_display_ took over)
306 # nothing to display (e.g. _ipython_display_ took over)
307 continue
307 continue
308 if metadata:
308 if metadata:
309 # kwarg-specified metadata gets precedence
309 # kwarg-specified metadata gets precedence
310 _merge(md_dict, metadata)
310 _merge(md_dict, metadata)
311 publish_display_data(data=format_dict, metadata=md_dict, **kwargs)
311 publish_display_data(data=format_dict, metadata=md_dict, **kwargs)
312 if display_id:
312 if display_id:
313 return DisplayHandle(display_id)
313 return DisplayHandle(display_id)
314
314
315
315
316 # use * for keyword-only display_id arg
316 # use * for keyword-only display_id arg
317 def update_display(obj, *, display_id, **kwargs):
317 def update_display(obj, *, display_id, **kwargs):
318 """Update an existing display by id
318 """Update an existing display by id
319
319
320 Parameters
320 Parameters
321 ----------
321 ----------
322
322
323 obj:
323 obj:
324 The object with which to update the display
324 The object with which to update the display
325 display_id: keyword-only
325 display_id: keyword-only
326 The id of the display to update
326 The id of the display to update
327
327
328 See Also
328 See Also
329 --------
329 --------
330
330
331 :func:`display`
331 :func:`display`
332 """
332 """
333 kwargs['update'] = True
333 kwargs['update'] = True
334 display(obj, display_id=display_id, **kwargs)
334 display(obj, display_id=display_id, **kwargs)
335
335
336
336
337 class DisplayHandle(object):
337 class DisplayHandle(object):
338 """A handle on an updatable display
338 """A handle on an updatable display
339
339
340 Call `.update(obj)` to display a new object.
340 Call `.update(obj)` to display a new object.
341
341
342 Call `.display(obj`) to add a new instance of this display,
342 Call `.display(obj`) to add a new instance of this display,
343 and update existing instances.
343 and update existing instances.
344
344
345 See Also
345 See Also
346 --------
346 --------
347
347
348 :func:`display`, :func:`update_display`
348 :func:`display`, :func:`update_display`
349
349
350 """
350 """
351
351
352 def __init__(self, display_id=None):
352 def __init__(self, display_id=None):
353 if display_id is None:
353 if display_id is None:
354 display_id = _new_id()
354 display_id = _new_id()
355 self.display_id = display_id
355 self.display_id = display_id
356
356
357 def __repr__(self):
357 def __repr__(self):
358 return "<%s display_id=%s>" % (self.__class__.__name__, self.display_id)
358 return "<%s display_id=%s>" % (self.__class__.__name__, self.display_id)
359
359
360 def display(self, obj, **kwargs):
360 def display(self, obj, **kwargs):
361 """Make a new display with my id, updating existing instances.
361 """Make a new display with my id, updating existing instances.
362
362
363 Parameters
363 Parameters
364 ----------
364 ----------
365
365
366 obj:
366 obj:
367 object to display
367 object to display
368 **kwargs:
368 **kwargs:
369 additional keyword arguments passed to display
369 additional keyword arguments passed to display
370 """
370 """
371 display(obj, display_id=self.display_id, **kwargs)
371 display(obj, display_id=self.display_id, **kwargs)
372
372
373 def update(self, obj, **kwargs):
373 def update(self, obj, **kwargs):
374 """Update existing displays with my id
374 """Update existing displays with my id
375
375
376 Parameters
376 Parameters
377 ----------
377 ----------
378
378
379 obj:
379 obj:
380 object to display
380 object to display
381 **kwargs:
381 **kwargs:
382 additional keyword arguments passed to update_display
382 additional keyword arguments passed to update_display
383 """
383 """
384 update_display(obj, display_id=self.display_id, **kwargs)
384 update_display(obj, display_id=self.display_id, **kwargs)
385
385
386
386
387 def display_pretty(*objs, **kwargs):
387 def display_pretty(*objs, **kwargs):
388 """Display the pretty (default) representation of an object.
388 """Display the pretty (default) representation of an object.
389
389
390 Parameters
390 Parameters
391 ----------
391 ----------
392 objs : tuple of objects
392 objs : tuple of objects
393 The Python objects to display, or if raw=True raw text data to
393 The Python objects to display, or if raw=True raw text data to
394 display.
394 display.
395 raw : bool
395 raw : bool
396 Are the data objects raw data or Python objects that need to be
396 Are the data objects raw data or Python objects that need to be
397 formatted before display? [default: False]
397 formatted before display? [default: False]
398 metadata : dict (optional)
398 metadata : dict (optional)
399 Metadata to be associated with the specific mimetype output.
399 Metadata to be associated with the specific mimetype output.
400 """
400 """
401 _display_mimetype('text/plain', objs, **kwargs)
401 _display_mimetype('text/plain', objs, **kwargs)
402
402
403
403
404 def display_html(*objs, **kwargs):
404 def display_html(*objs, **kwargs):
405 """Display the HTML representation of an object.
405 """Display the HTML representation of an object.
406
406
407 Note: If raw=False and the object does not have a HTML
407 Note: If raw=False and the object does not have a HTML
408 representation, no HTML will be shown.
408 representation, no HTML will be shown.
409
409
410 Parameters
410 Parameters
411 ----------
411 ----------
412 objs : tuple of objects
412 objs : tuple of objects
413 The Python objects to display, or if raw=True raw HTML data to
413 The Python objects to display, or if raw=True raw HTML data to
414 display.
414 display.
415 raw : bool
415 raw : bool
416 Are the data objects raw data or Python objects that need to be
416 Are the data objects raw data or Python objects that need to be
417 formatted before display? [default: False]
417 formatted before display? [default: False]
418 metadata : dict (optional)
418 metadata : dict (optional)
419 Metadata to be associated with the specific mimetype output.
419 Metadata to be associated with the specific mimetype output.
420 """
420 """
421 _display_mimetype('text/html', objs, **kwargs)
421 _display_mimetype('text/html', objs, **kwargs)
422
422
423
423
424 def display_markdown(*objs, **kwargs):
424 def display_markdown(*objs, **kwargs):
425 """Displays the Markdown representation of an object.
425 """Displays the Markdown representation of an object.
426
426
427 Parameters
427 Parameters
428 ----------
428 ----------
429 objs : tuple of objects
429 objs : tuple of objects
430 The Python objects to display, or if raw=True raw markdown data to
430 The Python objects to display, or if raw=True raw markdown data to
431 display.
431 display.
432 raw : bool
432 raw : bool
433 Are the data objects raw data or Python objects that need to be
433 Are the data objects raw data or Python objects that need to be
434 formatted before display? [default: False]
434 formatted before display? [default: False]
435 metadata : dict (optional)
435 metadata : dict (optional)
436 Metadata to be associated with the specific mimetype output.
436 Metadata to be associated with the specific mimetype output.
437 """
437 """
438
438
439 _display_mimetype('text/markdown', objs, **kwargs)
439 _display_mimetype('text/markdown', objs, **kwargs)
440
440
441
441
442 def display_svg(*objs, **kwargs):
442 def display_svg(*objs, **kwargs):
443 """Display the SVG representation of an object.
443 """Display the SVG representation of an object.
444
444
445 Parameters
445 Parameters
446 ----------
446 ----------
447 objs : tuple of objects
447 objs : tuple of objects
448 The Python objects to display, or if raw=True raw svg data to
448 The Python objects to display, or if raw=True raw svg data to
449 display.
449 display.
450 raw : bool
450 raw : bool
451 Are the data objects raw data or Python objects that need to be
451 Are the data objects raw data or Python objects that need to be
452 formatted before display? [default: False]
452 formatted before display? [default: False]
453 metadata : dict (optional)
453 metadata : dict (optional)
454 Metadata to be associated with the specific mimetype output.
454 Metadata to be associated with the specific mimetype output.
455 """
455 """
456 _display_mimetype('image/svg+xml', objs, **kwargs)
456 _display_mimetype('image/svg+xml', objs, **kwargs)
457
457
458
458
459 def display_png(*objs, **kwargs):
459 def display_png(*objs, **kwargs):
460 """Display the PNG representation of an object.
460 """Display the PNG representation of an object.
461
461
462 Parameters
462 Parameters
463 ----------
463 ----------
464 objs : tuple of objects
464 objs : tuple of objects
465 The Python objects to display, or if raw=True raw png data to
465 The Python objects to display, or if raw=True raw png data to
466 display.
466 display.
467 raw : bool
467 raw : bool
468 Are the data objects raw data or Python objects that need to be
468 Are the data objects raw data or Python objects that need to be
469 formatted before display? [default: False]
469 formatted before display? [default: False]
470 metadata : dict (optional)
470 metadata : dict (optional)
471 Metadata to be associated with the specific mimetype output.
471 Metadata to be associated with the specific mimetype output.
472 """
472 """
473 _display_mimetype('image/png', objs, **kwargs)
473 _display_mimetype('image/png', objs, **kwargs)
474
474
475
475
476 def display_jpeg(*objs, **kwargs):
476 def display_jpeg(*objs, **kwargs):
477 """Display the JPEG representation of an object.
477 """Display the JPEG representation of an object.
478
478
479 Parameters
479 Parameters
480 ----------
480 ----------
481 objs : tuple of objects
481 objs : tuple of objects
482 The Python objects to display, or if raw=True raw JPEG data to
482 The Python objects to display, or if raw=True raw JPEG data to
483 display.
483 display.
484 raw : bool
484 raw : bool
485 Are the data objects raw data or Python objects that need to be
485 Are the data objects raw data or Python objects that need to be
486 formatted before display? [default: False]
486 formatted before display? [default: False]
487 metadata : dict (optional)
487 metadata : dict (optional)
488 Metadata to be associated with the specific mimetype output.
488 Metadata to be associated with the specific mimetype output.
489 """
489 """
490 _display_mimetype('image/jpeg', objs, **kwargs)
490 _display_mimetype('image/jpeg', objs, **kwargs)
491
491
492
492
493 def display_latex(*objs, **kwargs):
493 def display_latex(*objs, **kwargs):
494 """Display the LaTeX representation of an object.
494 """Display the LaTeX representation of an object.
495
495
496 Parameters
496 Parameters
497 ----------
497 ----------
498 objs : tuple of objects
498 objs : tuple of objects
499 The Python objects to display, or if raw=True raw latex data to
499 The Python objects to display, or if raw=True raw latex data to
500 display.
500 display.
501 raw : bool
501 raw : bool
502 Are the data objects raw data or Python objects that need to be
502 Are the data objects raw data or Python objects that need to be
503 formatted before display? [default: False]
503 formatted before display? [default: False]
504 metadata : dict (optional)
504 metadata : dict (optional)
505 Metadata to be associated with the specific mimetype output.
505 Metadata to be associated with the specific mimetype output.
506 """
506 """
507 _display_mimetype('text/latex', objs, **kwargs)
507 _display_mimetype('text/latex', objs, **kwargs)
508
508
509
509
510 def display_json(*objs, **kwargs):
510 def display_json(*objs, **kwargs):
511 """Display the JSON representation of an object.
511 """Display the JSON representation of an object.
512
512
513 Note that not many frontends support displaying JSON.
513 Note that not many frontends support displaying JSON.
514
514
515 Parameters
515 Parameters
516 ----------
516 ----------
517 objs : tuple of objects
517 objs : tuple of objects
518 The Python objects to display, or if raw=True raw json data to
518 The Python objects to display, or if raw=True raw json data to
519 display.
519 display.
520 raw : bool
520 raw : bool
521 Are the data objects raw data or Python objects that need to be
521 Are the data objects raw data or Python objects that need to be
522 formatted before display? [default: False]
522 formatted before display? [default: False]
523 metadata : dict (optional)
523 metadata : dict (optional)
524 Metadata to be associated with the specific mimetype output.
524 Metadata to be associated with the specific mimetype output.
525 """
525 """
526 _display_mimetype('application/json', objs, **kwargs)
526 _display_mimetype('application/json', objs, **kwargs)
527
527
528
528
529 def display_javascript(*objs, **kwargs):
529 def display_javascript(*objs, **kwargs):
530 """Display the Javascript representation of an object.
530 """Display the Javascript representation of an object.
531
531
532 Parameters
532 Parameters
533 ----------
533 ----------
534 objs : tuple of objects
534 objs : tuple of objects
535 The Python objects to display, or if raw=True raw javascript data to
535 The Python objects to display, or if raw=True raw javascript data to
536 display.
536 display.
537 raw : bool
537 raw : bool
538 Are the data objects raw data or Python objects that need to be
538 Are the data objects raw data or Python objects that need to be
539 formatted before display? [default: False]
539 formatted before display? [default: False]
540 metadata : dict (optional)
540 metadata : dict (optional)
541 Metadata to be associated with the specific mimetype output.
541 Metadata to be associated with the specific mimetype output.
542 """
542 """
543 _display_mimetype('application/javascript', objs, **kwargs)
543 _display_mimetype('application/javascript', objs, **kwargs)
544
544
545
545
546 def display_pdf(*objs, **kwargs):
546 def display_pdf(*objs, **kwargs):
547 """Display the PDF representation of an object.
547 """Display the PDF representation of an object.
548
548
549 Parameters
549 Parameters
550 ----------
550 ----------
551 objs : tuple of objects
551 objs : tuple of objects
552 The Python objects to display, or if raw=True raw javascript data to
552 The Python objects to display, or if raw=True raw javascript data to
553 display.
553 display.
554 raw : bool
554 raw : bool
555 Are the data objects raw data or Python objects that need to be
555 Are the data objects raw data or Python objects that need to be
556 formatted before display? [default: False]
556 formatted before display? [default: False]
557 metadata : dict (optional)
557 metadata : dict (optional)
558 Metadata to be associated with the specific mimetype output.
558 Metadata to be associated with the specific mimetype output.
559 """
559 """
560 _display_mimetype('application/pdf', objs, **kwargs)
560 _display_mimetype('application/pdf', objs, **kwargs)
561
561
562
562
563 #-----------------------------------------------------------------------------
563 #-----------------------------------------------------------------------------
564 # Smart classes
564 # Smart classes
565 #-----------------------------------------------------------------------------
565 #-----------------------------------------------------------------------------
566
566
567
567
568 class DisplayObject(object):
568 class DisplayObject(object):
569 """An object that wraps data to be displayed."""
569 """An object that wraps data to be displayed."""
570
570
571 _read_flags = 'r'
571 _read_flags = 'r'
572 _show_mem_addr = False
572 _show_mem_addr = False
573 metadata = None
573 metadata = None
574
574
575 def __init__(self, data=None, url=None, filename=None, metadata=None):
575 def __init__(self, data=None, url=None, filename=None, metadata=None):
576 """Create a display object given raw data.
576 """Create a display object given raw data.
577
577
578 When this object is returned by an expression or passed to the
578 When this object is returned by an expression or passed to the
579 display function, it will result in the data being displayed
579 display function, it will result in the data being displayed
580 in the frontend. The MIME type of the data should match the
580 in the frontend. The MIME type of the data should match the
581 subclasses used, so the Png subclass should be used for 'image/png'
581 subclasses used, so the Png subclass should be used for 'image/png'
582 data. If the data is a URL, the data will first be downloaded
582 data. If the data is a URL, the data will first be downloaded
583 and then displayed. If
583 and then displayed. If
584
584
585 Parameters
585 Parameters
586 ----------
586 ----------
587 data : unicode, str or bytes
587 data : unicode, str or bytes
588 The raw data or a URL or file to load the data from
588 The raw data or a URL or file to load the data from
589 url : unicode
589 url : unicode
590 A URL to download the data from.
590 A URL to download the data from.
591 filename : unicode
591 filename : unicode
592 Path to a local file to load the data from.
592 Path to a local file to load the data from.
593 metadata : dict
593 metadata : dict
594 Dict of metadata associated to be the object when displayed
594 Dict of metadata associated to be the object when displayed
595 """
595 """
596 if data is not None and isinstance(data, str):
596 if data is not None and isinstance(data, str):
597 if data.startswith('http') and url is None:
597 if data.startswith('http') and url is None:
598 url = data
598 url = data
599 filename = None
599 filename = None
600 data = None
600 data = None
601 elif _safe_exists(data) and filename is None:
601 elif _safe_exists(data) and filename is None:
602 url = None
602 url = None
603 filename = data
603 filename = data
604 data = None
604 data = None
605
605
606 self.data = data
606 self.data = data
607 self.url = url
607 self.url = url
608 self.filename = filename
608 self.filename = filename
609
609
610 if metadata is not None:
610 if metadata is not None:
611 self.metadata = metadata
611 self.metadata = metadata
612 elif self.metadata is None:
612 elif self.metadata is None:
613 self.metadata = {}
613 self.metadata = {}
614
614
615 self.reload()
615 self.reload()
616 self._check_data()
616 self._check_data()
617
617
618 def __repr__(self):
618 def __repr__(self):
619 if not self._show_mem_addr:
619 if not self._show_mem_addr:
620 cls = self.__class__
620 cls = self.__class__
621 r = "<%s.%s object>" % (cls.__module__, cls.__name__)
621 r = "<%s.%s object>" % (cls.__module__, cls.__name__)
622 else:
622 else:
623 r = super(DisplayObject, self).__repr__()
623 r = super(DisplayObject, self).__repr__()
624 return r
624 return r
625
625
626 def _check_data(self):
626 def _check_data(self):
627 """Override in subclasses if there's something to check."""
627 """Override in subclasses if there's something to check."""
628 pass
628 pass
629
629
630 def _data_and_metadata(self):
630 def _data_and_metadata(self):
631 """shortcut for returning metadata with shape information, if defined"""
631 """shortcut for returning metadata with shape information, if defined"""
632 if self.metadata:
632 if self.metadata:
633 return self.data, deepcopy(self.metadata)
633 return self.data, deepcopy(self.metadata)
634 else:
634 else:
635 return self.data
635 return self.data
636
636
637 def reload(self):
637 def reload(self):
638 """Reload the raw data from file or URL."""
638 """Reload the raw data from file or URL."""
639 if self.filename is not None:
639 if self.filename is not None:
640 with open(self.filename, self._read_flags) as f:
640 with open(self.filename, self._read_flags) as f:
641 self.data = f.read()
641 self.data = f.read()
642 elif self.url is not None:
642 elif self.url is not None:
643 try:
643 try:
644 # Deferred import
644 # Deferred import
645 from urllib.request import urlopen
645 from urllib.request import urlopen
646 response = urlopen(self.url)
646 response = urlopen(self.url)
647 self.data = response.read()
647 self.data = response.read()
648 # extract encoding from header, if there is one:
648 # extract encoding from header, if there is one:
649 encoding = None
649 encoding = None
650 for sub in response.headers['content-type'].split(';'):
650 for sub in response.headers['content-type'].split(';'):
651 sub = sub.strip()
651 sub = sub.strip()
652 if sub.startswith('charset'):
652 if sub.startswith('charset'):
653 encoding = sub.split('=')[-1].strip()
653 encoding = sub.split('=')[-1].strip()
654 break
654 break
655 # decode data, if an encoding was specified
655 # decode data, if an encoding was specified
656 if encoding:
656 if encoding:
657 self.data = self.data.decode(encoding, 'replace')
657 self.data = self.data.decode(encoding, 'replace')
658 except:
658 except:
659 self.data = None
659 self.data = None
660
660
661 class TextDisplayObject(DisplayObject):
661 class TextDisplayObject(DisplayObject):
662 """Validate that display data is text"""
662 """Validate that display data is text"""
663 def _check_data(self):
663 def _check_data(self):
664 if self.data is not None and not isinstance(self.data, str):
664 if self.data is not None and not isinstance(self.data, str):
665 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
665 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
666
666
667 class Pretty(TextDisplayObject):
667 class Pretty(TextDisplayObject):
668
668
669 def _repr_pretty_(self, pp, cycle):
669 def _repr_pretty_(self, pp, cycle):
670 return pp.text(self.data)
670 return pp.text(self.data)
671
671
672
672
673 class HTML(TextDisplayObject):
673 class HTML(TextDisplayObject):
674
674
675 def __init__(self, data=None, url=None, filename=None, metadata=None):
675 def __init__(self, data=None, url=None, filename=None, metadata=None):
676 def warn():
676 def warn():
677 if not data:
677 if not data:
678 return False
678 return False
679
679
680 #
680 #
681 # Avoid calling lower() on the entire data, because it could be a
681 # Avoid calling lower() on the entire data, because it could be a
682 # long string and we're only interested in its beginning and end.
682 # long string and we're only interested in its beginning and end.
683 #
683 #
684 prefix = data[:10].lower()
684 prefix = data[:10].lower()
685 suffix = data[-10:].lower()
685 suffix = data[-10:].lower()
686 return prefix.startswith("<iframe ") and suffix.endswith("</iframe>")
686 return prefix.startswith("<iframe ") and suffix.endswith("</iframe>")
687
687
688 if warn():
688 if warn():
689 warnings.warn("Consider using IPython.display.IFrame instead")
689 warnings.warn("Consider using IPython.display.IFrame instead")
690 super(HTML, self).__init__(data=data, url=url, filename=filename, metadata=metadata)
690 super(HTML, self).__init__(data=data, url=url, filename=filename, metadata=metadata)
691
691
692 def _repr_html_(self):
692 def _repr_html_(self):
693 return self._data_and_metadata()
693 return self._data_and_metadata()
694
694
695 def __html__(self):
695 def __html__(self):
696 """
696 """
697 This method exists to inform other HTML-using modules (e.g. Markupsafe,
697 This method exists to inform other HTML-using modules (e.g. Markupsafe,
698 htmltag, etc) that this object is HTML and does not need things like
698 htmltag, etc) that this object is HTML and does not need things like
699 special characters (<>&) escaped.
699 special characters (<>&) escaped.
700 """
700 """
701 return self._repr_html_()
701 return self._repr_html_()
702
702
703
703
704 class Markdown(TextDisplayObject):
704 class Markdown(TextDisplayObject):
705
705
706 def _repr_markdown_(self):
706 def _repr_markdown_(self):
707 return self._data_and_metadata()
707 return self._data_and_metadata()
708
708
709
709
710 class Math(TextDisplayObject):
710 class Math(TextDisplayObject):
711
711
712 def _repr_latex_(self):
712 def _repr_latex_(self):
713 s = "$\displaystyle %s$" % self.data.strip('$')
713 s = r"$\displaystyle %s$" % self.data.strip('$')
714 if self.metadata:
714 if self.metadata:
715 return s, deepcopy(self.metadata)
715 return s, deepcopy(self.metadata)
716 else:
716 else:
717 return s
717 return s
718
718
719
719
720 class Latex(TextDisplayObject):
720 class Latex(TextDisplayObject):
721
721
722 def _repr_latex_(self):
722 def _repr_latex_(self):
723 return self._data_and_metadata()
723 return self._data_and_metadata()
724
724
725
725
726 class SVG(DisplayObject):
726 class SVG(DisplayObject):
727
727
728 _read_flags = 'rb'
728 _read_flags = 'rb'
729 # wrap data in a property, which extracts the <svg> tag, discarding
729 # wrap data in a property, which extracts the <svg> tag, discarding
730 # document headers
730 # document headers
731 _data = None
731 _data = None
732
732
733 @property
733 @property
734 def data(self):
734 def data(self):
735 return self._data
735 return self._data
736
736
737 @data.setter
737 @data.setter
738 def data(self, svg):
738 def data(self, svg):
739 if svg is None:
739 if svg is None:
740 self._data = None
740 self._data = None
741 return
741 return
742 # parse into dom object
742 # parse into dom object
743 from xml.dom import minidom
743 from xml.dom import minidom
744 x = minidom.parseString(svg)
744 x = minidom.parseString(svg)
745 # get svg tag (should be 1)
745 # get svg tag (should be 1)
746 found_svg = x.getElementsByTagName('svg')
746 found_svg = x.getElementsByTagName('svg')
747 if found_svg:
747 if found_svg:
748 svg = found_svg[0].toxml()
748 svg = found_svg[0].toxml()
749 else:
749 else:
750 # fallback on the input, trust the user
750 # fallback on the input, trust the user
751 # but this is probably an error.
751 # but this is probably an error.
752 pass
752 pass
753 svg = cast_unicode(svg)
753 svg = cast_unicode(svg)
754 self._data = svg
754 self._data = svg
755
755
756 def _repr_svg_(self):
756 def _repr_svg_(self):
757 return self._data_and_metadata()
757 return self._data_and_metadata()
758
758
759 class ProgressBar(DisplayObject):
759 class ProgressBar(DisplayObject):
760 """Progressbar supports displaying a progressbar like element
760 """Progressbar supports displaying a progressbar like element
761 """
761 """
762 def __init__(self, total):
762 def __init__(self, total):
763 """Creates a new progressbar
763 """Creates a new progressbar
764
764
765 Parameters
765 Parameters
766 ----------
766 ----------
767 total : int
767 total : int
768 maximum size of the progressbar
768 maximum size of the progressbar
769 """
769 """
770 self.total = total
770 self.total = total
771 self._progress = 0
771 self._progress = 0
772 self.html_width = '60ex'
772 self.html_width = '60ex'
773 self.text_width = 60
773 self.text_width = 60
774 self._display_id = hexlify(os.urandom(8)).decode('ascii')
774 self._display_id = hexlify(os.urandom(8)).decode('ascii')
775
775
776 def __repr__(self):
776 def __repr__(self):
777 fraction = self.progress / self.total
777 fraction = self.progress / self.total
778 filled = '=' * int(fraction * self.text_width)
778 filled = '=' * int(fraction * self.text_width)
779 rest = ' ' * (self.text_width - len(filled))
779 rest = ' ' * (self.text_width - len(filled))
780 return '[{}{}] {}/{}'.format(
780 return '[{}{}] {}/{}'.format(
781 filled, rest,
781 filled, rest,
782 self.progress, self.total,
782 self.progress, self.total,
783 )
783 )
784
784
785 def _repr_html_(self):
785 def _repr_html_(self):
786 return "<progress style='width:{}' max='{}' value='{}'></progress>".format(
786 return "<progress style='width:{}' max='{}' value='{}'></progress>".format(
787 self.html_width, self.total, self.progress)
787 self.html_width, self.total, self.progress)
788
788
789 def display(self):
789 def display(self):
790 display(self, display_id=self._display_id)
790 display(self, display_id=self._display_id)
791
791
792 def update(self):
792 def update(self):
793 display(self, display_id=self._display_id, update=True)
793 display(self, display_id=self._display_id, update=True)
794
794
795 @property
795 @property
796 def progress(self):
796 def progress(self):
797 return self._progress
797 return self._progress
798
798
799 @progress.setter
799 @progress.setter
800 def progress(self, value):
800 def progress(self, value):
801 self._progress = value
801 self._progress = value
802 self.update()
802 self.update()
803
803
804 def __iter__(self):
804 def __iter__(self):
805 self.display()
805 self.display()
806 self._progress = -1 # First iteration is 0
806 self._progress = -1 # First iteration is 0
807 return self
807 return self
808
808
809 def __next__(self):
809 def __next__(self):
810 """Returns current value and increments display by one."""
810 """Returns current value and increments display by one."""
811 self.progress += 1
811 self.progress += 1
812 if self.progress < self.total:
812 if self.progress < self.total:
813 return self.progress
813 return self.progress
814 else:
814 else:
815 raise StopIteration()
815 raise StopIteration()
816
816
817 class JSON(DisplayObject):
817 class JSON(DisplayObject):
818 """JSON expects a JSON-able dict or list
818 """JSON expects a JSON-able dict or list
819
819
820 not an already-serialized JSON string.
820 not an already-serialized JSON string.
821
821
822 Scalar types (None, number, string) are not allowed, only dict or list containers.
822 Scalar types (None, number, string) are not allowed, only dict or list containers.
823 """
823 """
824 # wrap data in a property, which warns about passing already-serialized JSON
824 # wrap data in a property, which warns about passing already-serialized JSON
825 _data = None
825 _data = None
826 def __init__(self, data=None, url=None, filename=None, expanded=False, metadata=None, root='root', **kwargs):
826 def __init__(self, data=None, url=None, filename=None, expanded=False, metadata=None, root='root', **kwargs):
827 """Create a JSON display object given raw data.
827 """Create a JSON display object given raw data.
828
828
829 Parameters
829 Parameters
830 ----------
830 ----------
831 data : dict or list
831 data : dict or list
832 JSON data to display. Not an already-serialized JSON string.
832 JSON data to display. Not an already-serialized JSON string.
833 Scalar types (None, number, string) are not allowed, only dict
833 Scalar types (None, number, string) are not allowed, only dict
834 or list containers.
834 or list containers.
835 url : unicode
835 url : unicode
836 A URL to download the data from.
836 A URL to download the data from.
837 filename : unicode
837 filename : unicode
838 Path to a local file to load the data from.
838 Path to a local file to load the data from.
839 expanded : boolean
839 expanded : boolean
840 Metadata to control whether a JSON display component is expanded.
840 Metadata to control whether a JSON display component is expanded.
841 metadata: dict
841 metadata: dict
842 Specify extra metadata to attach to the json display object.
842 Specify extra metadata to attach to the json display object.
843 root : str
843 root : str
844 The name of the root element of the JSON tree
844 The name of the root element of the JSON tree
845 """
845 """
846 self.metadata = {
846 self.metadata = {
847 'expanded': expanded,
847 'expanded': expanded,
848 'root': root,
848 'root': root,
849 }
849 }
850 if metadata:
850 if metadata:
851 self.metadata.update(metadata)
851 self.metadata.update(metadata)
852 if kwargs:
852 if kwargs:
853 self.metadata.update(kwargs)
853 self.metadata.update(kwargs)
854 super(JSON, self).__init__(data=data, url=url, filename=filename)
854 super(JSON, self).__init__(data=data, url=url, filename=filename)
855
855
856 def _check_data(self):
856 def _check_data(self):
857 if self.data is not None and not isinstance(self.data, (dict, list)):
857 if self.data is not None and not isinstance(self.data, (dict, list)):
858 raise TypeError("%s expects JSONable dict or list, not %r" % (self.__class__.__name__, self.data))
858 raise TypeError("%s expects JSONable dict or list, not %r" % (self.__class__.__name__, self.data))
859
859
860 @property
860 @property
861 def data(self):
861 def data(self):
862 return self._data
862 return self._data
863
863
864 @data.setter
864 @data.setter
865 def data(self, data):
865 def data(self, data):
866 if isinstance(data, str):
866 if isinstance(data, str):
867 if getattr(self, 'filename', None) is None:
867 if getattr(self, 'filename', None) is None:
868 warnings.warn("JSON expects JSONable dict or list, not JSON strings")
868 warnings.warn("JSON expects JSONable dict or list, not JSON strings")
869 data = json.loads(data)
869 data = json.loads(data)
870 self._data = data
870 self._data = data
871
871
872 def _data_and_metadata(self):
872 def _data_and_metadata(self):
873 return self.data, self.metadata
873 return self.data, self.metadata
874
874
875 def _repr_json_(self):
875 def _repr_json_(self):
876 return self._data_and_metadata()
876 return self._data_and_metadata()
877
877
878 _css_t = """var link = document.createElement("link");
878 _css_t = """var link = document.createElement("link");
879 link.ref = "stylesheet";
879 link.ref = "stylesheet";
880 link.type = "text/css";
880 link.type = "text/css";
881 link.href = "%s";
881 link.href = "%s";
882 document.head.appendChild(link);
882 document.head.appendChild(link);
883 """
883 """
884
884
885 _lib_t1 = """new Promise(function(resolve, reject) {
885 _lib_t1 = """new Promise(function(resolve, reject) {
886 var script = document.createElement("script");
886 var script = document.createElement("script");
887 script.onload = resolve;
887 script.onload = resolve;
888 script.onerror = reject;
888 script.onerror = reject;
889 script.src = "%s";
889 script.src = "%s";
890 document.head.appendChild(script);
890 document.head.appendChild(script);
891 }).then(() => {
891 }).then(() => {
892 """
892 """
893
893
894 _lib_t2 = """
894 _lib_t2 = """
895 });"""
895 });"""
896
896
897 class GeoJSON(JSON):
897 class GeoJSON(JSON):
898 """GeoJSON expects JSON-able dict
898 """GeoJSON expects JSON-able dict
899
899
900 not an already-serialized JSON string.
900 not an already-serialized JSON string.
901
901
902 Scalar types (None, number, string) are not allowed, only dict containers.
902 Scalar types (None, number, string) are not allowed, only dict containers.
903 """
903 """
904
904
905 def __init__(self, *args, **kwargs):
905 def __init__(self, *args, **kwargs):
906 """Create a GeoJSON display object given raw data.
906 """Create a GeoJSON display object given raw data.
907
907
908 Parameters
908 Parameters
909 ----------
909 ----------
910 data : dict or list
910 data : dict or list
911 VegaLite data. Not an already-serialized JSON string.
911 VegaLite data. Not an already-serialized JSON string.
912 Scalar types (None, number, string) are not allowed, only dict
912 Scalar types (None, number, string) are not allowed, only dict
913 or list containers.
913 or list containers.
914 url_template : string
914 url_template : string
915 Leaflet TileLayer URL template: http://leafletjs.com/reference.html#url-template
915 Leaflet TileLayer URL template: http://leafletjs.com/reference.html#url-template
916 layer_options : dict
916 layer_options : dict
917 Leaflet TileLayer options: http://leafletjs.com/reference.html#tilelayer-options
917 Leaflet TileLayer options: http://leafletjs.com/reference.html#tilelayer-options
918 url : unicode
918 url : unicode
919 A URL to download the data from.
919 A URL to download the data from.
920 filename : unicode
920 filename : unicode
921 Path to a local file to load the data from.
921 Path to a local file to load the data from.
922 metadata: dict
922 metadata: dict
923 Specify extra metadata to attach to the json display object.
923 Specify extra metadata to attach to the json display object.
924
924
925 Examples
925 Examples
926 --------
926 --------
927
927
928 The following will display an interactive map of Mars with a point of
928 The following will display an interactive map of Mars with a point of
929 interest on frontend that do support GeoJSON display.
929 interest on frontend that do support GeoJSON display.
930
930
931 >>> from IPython.display import GeoJSON
931 >>> from IPython.display import GeoJSON
932
932
933 >>> GeoJSON(data={
933 >>> GeoJSON(data={
934 ... "type": "Feature",
934 ... "type": "Feature",
935 ... "geometry": {
935 ... "geometry": {
936 ... "type": "Point",
936 ... "type": "Point",
937 ... "coordinates": [-81.327, 296.038]
937 ... "coordinates": [-81.327, 296.038]
938 ... }
938 ... }
939 ... },
939 ... },
940 ... url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
940 ... url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
941 ... layer_options={
941 ... layer_options={
942 ... "basemap_id": "celestia_mars-shaded-16k_global",
942 ... "basemap_id": "celestia_mars-shaded-16k_global",
943 ... "attribution" : "Celestia/praesepe",
943 ... "attribution" : "Celestia/praesepe",
944 ... "minZoom" : 0,
944 ... "minZoom" : 0,
945 ... "maxZoom" : 18,
945 ... "maxZoom" : 18,
946 ... })
946 ... })
947 <IPython.core.display.GeoJSON object>
947 <IPython.core.display.GeoJSON object>
948
948
949 In the terminal IPython, you will only see the text representation of
949 In the terminal IPython, you will only see the text representation of
950 the GeoJSON object.
950 the GeoJSON object.
951
951
952 """
952 """
953
953
954 super(GeoJSON, self).__init__(*args, **kwargs)
954 super(GeoJSON, self).__init__(*args, **kwargs)
955
955
956
956
957 def _ipython_display_(self):
957 def _ipython_display_(self):
958 bundle = {
958 bundle = {
959 'application/geo+json': self.data,
959 'application/geo+json': self.data,
960 'text/plain': '<IPython.display.GeoJSON object>'
960 'text/plain': '<IPython.display.GeoJSON object>'
961 }
961 }
962 metadata = {
962 metadata = {
963 'application/geo+json': self.metadata
963 'application/geo+json': self.metadata
964 }
964 }
965 display(bundle, metadata=metadata, raw=True)
965 display(bundle, metadata=metadata, raw=True)
966
966
967 class Javascript(TextDisplayObject):
967 class Javascript(TextDisplayObject):
968
968
969 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
969 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
970 """Create a Javascript display object given raw data.
970 """Create a Javascript display object given raw data.
971
971
972 When this object is returned by an expression or passed to the
972 When this object is returned by an expression or passed to the
973 display function, it will result in the data being displayed
973 display function, it will result in the data being displayed
974 in the frontend. If the data is a URL, the data will first be
974 in the frontend. If the data is a URL, the data will first be
975 downloaded and then displayed.
975 downloaded and then displayed.
976
976
977 In the Notebook, the containing element will be available as `element`,
977 In the Notebook, the containing element will be available as `element`,
978 and jQuery will be available. Content appended to `element` will be
978 and jQuery will be available. Content appended to `element` will be
979 visible in the output area.
979 visible in the output area.
980
980
981 Parameters
981 Parameters
982 ----------
982 ----------
983 data : unicode, str or bytes
983 data : unicode, str or bytes
984 The Javascript source code or a URL to download it from.
984 The Javascript source code or a URL to download it from.
985 url : unicode
985 url : unicode
986 A URL to download the data from.
986 A URL to download the data from.
987 filename : unicode
987 filename : unicode
988 Path to a local file to load the data from.
988 Path to a local file to load the data from.
989 lib : list or str
989 lib : list or str
990 A sequence of Javascript library URLs to load asynchronously before
990 A sequence of Javascript library URLs to load asynchronously before
991 running the source code. The full URLs of the libraries should
991 running the source code. The full URLs of the libraries should
992 be given. A single Javascript library URL can also be given as a
992 be given. A single Javascript library URL can also be given as a
993 string.
993 string.
994 css: : list or str
994 css: : list or str
995 A sequence of css files to load before running the source code.
995 A sequence of css files to load before running the source code.
996 The full URLs of the css files should be given. A single css URL
996 The full URLs of the css files should be given. A single css URL
997 can also be given as a string.
997 can also be given as a string.
998 """
998 """
999 if isinstance(lib, str):
999 if isinstance(lib, str):
1000 lib = [lib]
1000 lib = [lib]
1001 elif lib is None:
1001 elif lib is None:
1002 lib = []
1002 lib = []
1003 if isinstance(css, str):
1003 if isinstance(css, str):
1004 css = [css]
1004 css = [css]
1005 elif css is None:
1005 elif css is None:
1006 css = []
1006 css = []
1007 if not isinstance(lib, (list,tuple)):
1007 if not isinstance(lib, (list,tuple)):
1008 raise TypeError('expected sequence, got: %r' % lib)
1008 raise TypeError('expected sequence, got: %r' % lib)
1009 if not isinstance(css, (list,tuple)):
1009 if not isinstance(css, (list,tuple)):
1010 raise TypeError('expected sequence, got: %r' % css)
1010 raise TypeError('expected sequence, got: %r' % css)
1011 self.lib = lib
1011 self.lib = lib
1012 self.css = css
1012 self.css = css
1013 super(Javascript, self).__init__(data=data, url=url, filename=filename)
1013 super(Javascript, self).__init__(data=data, url=url, filename=filename)
1014
1014
1015 def _repr_javascript_(self):
1015 def _repr_javascript_(self):
1016 r = ''
1016 r = ''
1017 for c in self.css:
1017 for c in self.css:
1018 r += _css_t % c
1018 r += _css_t % c
1019 for l in self.lib:
1019 for l in self.lib:
1020 r += _lib_t1 % l
1020 r += _lib_t1 % l
1021 r += self.data
1021 r += self.data
1022 r += _lib_t2*len(self.lib)
1022 r += _lib_t2*len(self.lib)
1023 return r
1023 return r
1024
1024
1025 # constants for identifying png/jpeg data
1025 # constants for identifying png/jpeg data
1026 _PNG = b'\x89PNG\r\n\x1a\n'
1026 _PNG = b'\x89PNG\r\n\x1a\n'
1027 _JPEG = b'\xff\xd8'
1027 _JPEG = b'\xff\xd8'
1028
1028
1029 def _pngxy(data):
1029 def _pngxy(data):
1030 """read the (width, height) from a PNG header"""
1030 """read the (width, height) from a PNG header"""
1031 ihdr = data.index(b'IHDR')
1031 ihdr = data.index(b'IHDR')
1032 # next 8 bytes are width/height
1032 # next 8 bytes are width/height
1033 return struct.unpack('>ii', data[ihdr+4:ihdr+12])
1033 return struct.unpack('>ii', data[ihdr+4:ihdr+12])
1034
1034
1035 def _jpegxy(data):
1035 def _jpegxy(data):
1036 """read the (width, height) from a JPEG header"""
1036 """read the (width, height) from a JPEG header"""
1037 # adapted from http://www.64lines.com/jpeg-width-height
1037 # adapted from http://www.64lines.com/jpeg-width-height
1038
1038
1039 idx = 4
1039 idx = 4
1040 while True:
1040 while True:
1041 block_size = struct.unpack('>H', data[idx:idx+2])[0]
1041 block_size = struct.unpack('>H', data[idx:idx+2])[0]
1042 idx = idx + block_size
1042 idx = idx + block_size
1043 if data[idx:idx+2] == b'\xFF\xC0':
1043 if data[idx:idx+2] == b'\xFF\xC0':
1044 # found Start of Frame
1044 # found Start of Frame
1045 iSOF = idx
1045 iSOF = idx
1046 break
1046 break
1047 else:
1047 else:
1048 # read another block
1048 # read another block
1049 idx += 2
1049 idx += 2
1050
1050
1051 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
1051 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
1052 return w, h
1052 return w, h
1053
1053
1054 def _gifxy(data):
1054 def _gifxy(data):
1055 """read the (width, height) from a GIF header"""
1055 """read the (width, height) from a GIF header"""
1056 return struct.unpack('<HH', data[6:10])
1056 return struct.unpack('<HH', data[6:10])
1057
1057
1058
1058
1059 class Image(DisplayObject):
1059 class Image(DisplayObject):
1060
1060
1061 _read_flags = 'rb'
1061 _read_flags = 'rb'
1062 _FMT_JPEG = u'jpeg'
1062 _FMT_JPEG = u'jpeg'
1063 _FMT_PNG = u'png'
1063 _FMT_PNG = u'png'
1064 _FMT_GIF = u'gif'
1064 _FMT_GIF = u'gif'
1065 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG, _FMT_GIF]
1065 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG, _FMT_GIF]
1066 _MIMETYPES = {
1066 _MIMETYPES = {
1067 _FMT_PNG: 'image/png',
1067 _FMT_PNG: 'image/png',
1068 _FMT_JPEG: 'image/jpeg',
1068 _FMT_JPEG: 'image/jpeg',
1069 _FMT_GIF: 'image/gif',
1069 _FMT_GIF: 'image/gif',
1070 }
1070 }
1071
1071
1072 def __init__(self, data=None, url=None, filename=None, format=None,
1072 def __init__(self, data=None, url=None, filename=None, format=None,
1073 embed=None, width=None, height=None, retina=False,
1073 embed=None, width=None, height=None, retina=False,
1074 unconfined=False, metadata=None):
1074 unconfined=False, metadata=None):
1075 """Create a PNG/JPEG/GIF image object given raw data.
1075 """Create a PNG/JPEG/GIF image object given raw data.
1076
1076
1077 When this object is returned by an input cell or passed to the
1077 When this object is returned by an input cell or passed to the
1078 display function, it will result in the image being displayed
1078 display function, it will result in the image being displayed
1079 in the frontend.
1079 in the frontend.
1080
1080
1081 Parameters
1081 Parameters
1082 ----------
1082 ----------
1083 data : unicode, str or bytes
1083 data : unicode, str or bytes
1084 The raw image data or a URL or filename to load the data from.
1084 The raw image data or a URL or filename to load the data from.
1085 This always results in embedded image data.
1085 This always results in embedded image data.
1086 url : unicode
1086 url : unicode
1087 A URL to download the data from. If you specify `url=`,
1087 A URL to download the data from. If you specify `url=`,
1088 the image data will not be embedded unless you also specify `embed=True`.
1088 the image data will not be embedded unless you also specify `embed=True`.
1089 filename : unicode
1089 filename : unicode
1090 Path to a local file to load the data from.
1090 Path to a local file to load the data from.
1091 Images from a file are always embedded.
1091 Images from a file are always embedded.
1092 format : unicode
1092 format : unicode
1093 The format of the image data (png/jpeg/jpg/gif). If a filename or URL is given
1093 The format of the image data (png/jpeg/jpg/gif). If a filename or URL is given
1094 for format will be inferred from the filename extension.
1094 for format will be inferred from the filename extension.
1095 embed : bool
1095 embed : bool
1096 Should the image data be embedded using a data URI (True) or be
1096 Should the image data be embedded using a data URI (True) or be
1097 loaded using an <img> tag. Set this to True if you want the image
1097 loaded using an <img> tag. Set this to True if you want the image
1098 to be viewable later with no internet connection in the notebook.
1098 to be viewable later with no internet connection in the notebook.
1099
1099
1100 Default is `True`, unless the keyword argument `url` is set, then
1100 Default is `True`, unless the keyword argument `url` is set, then
1101 default value is `False`.
1101 default value is `False`.
1102
1102
1103 Note that QtConsole is not able to display images if `embed` is set to `False`
1103 Note that QtConsole is not able to display images if `embed` is set to `False`
1104 width : int
1104 width : int
1105 Width in pixels to which to constrain the image in html
1105 Width in pixels to which to constrain the image in html
1106 height : int
1106 height : int
1107 Height in pixels to which to constrain the image in html
1107 Height in pixels to which to constrain the image in html
1108 retina : bool
1108 retina : bool
1109 Automatically set the width and height to half of the measured
1109 Automatically set the width and height to half of the measured
1110 width and height.
1110 width and height.
1111 This only works for embedded images because it reads the width/height
1111 This only works for embedded images because it reads the width/height
1112 from image data.
1112 from image data.
1113 For non-embedded images, you can just set the desired display width
1113 For non-embedded images, you can just set the desired display width
1114 and height directly.
1114 and height directly.
1115 unconfined: bool
1115 unconfined: bool
1116 Set unconfined=True to disable max-width confinement of the image.
1116 Set unconfined=True to disable max-width confinement of the image.
1117 metadata: dict
1117 metadata: dict
1118 Specify extra metadata to attach to the image.
1118 Specify extra metadata to attach to the image.
1119
1119
1120 Examples
1120 Examples
1121 --------
1121 --------
1122 # embedded image data, works in qtconsole and notebook
1122 # embedded image data, works in qtconsole and notebook
1123 # when passed positionally, the first arg can be any of raw image data,
1123 # when passed positionally, the first arg can be any of raw image data,
1124 # a URL, or a filename from which to load image data.
1124 # a URL, or a filename from which to load image data.
1125 # The result is always embedding image data for inline images.
1125 # The result is always embedding image data for inline images.
1126 Image('http://www.google.fr/images/srpr/logo3w.png')
1126 Image('http://www.google.fr/images/srpr/logo3w.png')
1127 Image('/path/to/image.jpg')
1127 Image('/path/to/image.jpg')
1128 Image(b'RAW_PNG_DATA...')
1128 Image(b'RAW_PNG_DATA...')
1129
1129
1130 # Specifying Image(url=...) does not embed the image data,
1130 # Specifying Image(url=...) does not embed the image data,
1131 # it only generates `<img>` tag with a link to the source.
1131 # it only generates `<img>` tag with a link to the source.
1132 # This will not work in the qtconsole or offline.
1132 # This will not work in the qtconsole or offline.
1133 Image(url='http://www.google.fr/images/srpr/logo3w.png')
1133 Image(url='http://www.google.fr/images/srpr/logo3w.png')
1134
1134
1135 """
1135 """
1136 if filename is not None:
1136 if filename is not None:
1137 ext = self._find_ext(filename)
1137 ext = self._find_ext(filename)
1138 elif url is not None:
1138 elif url is not None:
1139 ext = self._find_ext(url)
1139 ext = self._find_ext(url)
1140 elif data is None:
1140 elif data is None:
1141 raise ValueError("No image data found. Expecting filename, url, or data.")
1141 raise ValueError("No image data found. Expecting filename, url, or data.")
1142 elif isinstance(data, str) and (
1142 elif isinstance(data, str) and (
1143 data.startswith('http') or _safe_exists(data)
1143 data.startswith('http') or _safe_exists(data)
1144 ):
1144 ):
1145 ext = self._find_ext(data)
1145 ext = self._find_ext(data)
1146 else:
1146 else:
1147 ext = None
1147 ext = None
1148
1148
1149 if format is None:
1149 if format is None:
1150 if ext is not None:
1150 if ext is not None:
1151 if ext == u'jpg' or ext == u'jpeg':
1151 if ext == u'jpg' or ext == u'jpeg':
1152 format = self._FMT_JPEG
1152 format = self._FMT_JPEG
1153 elif ext == u'png':
1153 elif ext == u'png':
1154 format = self._FMT_PNG
1154 format = self._FMT_PNG
1155 elif ext == u'gif':
1155 elif ext == u'gif':
1156 format = self._FMT_GIF
1156 format = self._FMT_GIF
1157 else:
1157 else:
1158 format = ext.lower()
1158 format = ext.lower()
1159 elif isinstance(data, bytes):
1159 elif isinstance(data, bytes):
1160 # infer image type from image data header,
1160 # infer image type from image data header,
1161 # only if format has not been specified.
1161 # only if format has not been specified.
1162 if data[:2] == _JPEG:
1162 if data[:2] == _JPEG:
1163 format = self._FMT_JPEG
1163 format = self._FMT_JPEG
1164
1164
1165 # failed to detect format, default png
1165 # failed to detect format, default png
1166 if format is None:
1166 if format is None:
1167 format = self._FMT_PNG
1167 format = self._FMT_PNG
1168
1168
1169 if format.lower() == 'jpg':
1169 if format.lower() == 'jpg':
1170 # jpg->jpeg
1170 # jpg->jpeg
1171 format = self._FMT_JPEG
1171 format = self._FMT_JPEG
1172
1172
1173 self.format = format.lower()
1173 self.format = format.lower()
1174 self.embed = embed if embed is not None else (url is None)
1174 self.embed = embed if embed is not None else (url is None)
1175
1175
1176 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
1176 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
1177 raise ValueError("Cannot embed the '%s' image format" % (self.format))
1177 raise ValueError("Cannot embed the '%s' image format" % (self.format))
1178 if self.embed:
1178 if self.embed:
1179 self._mimetype = self._MIMETYPES.get(self.format)
1179 self._mimetype = self._MIMETYPES.get(self.format)
1180
1180
1181 self.width = width
1181 self.width = width
1182 self.height = height
1182 self.height = height
1183 self.retina = retina
1183 self.retina = retina
1184 self.unconfined = unconfined
1184 self.unconfined = unconfined
1185 super(Image, self).__init__(data=data, url=url, filename=filename,
1185 super(Image, self).__init__(data=data, url=url, filename=filename,
1186 metadata=metadata)
1186 metadata=metadata)
1187
1187
1188 if self.width is None and self.metadata.get('width', {}):
1188 if self.width is None and self.metadata.get('width', {}):
1189 self.width = metadata['width']
1189 self.width = metadata['width']
1190
1190
1191 if self.height is None and self.metadata.get('height', {}):
1191 if self.height is None and self.metadata.get('height', {}):
1192 self.height = metadata['height']
1192 self.height = metadata['height']
1193
1193
1194 if retina:
1194 if retina:
1195 self._retina_shape()
1195 self._retina_shape()
1196
1196
1197
1197
1198 def _retina_shape(self):
1198 def _retina_shape(self):
1199 """load pixel-doubled width and height from image data"""
1199 """load pixel-doubled width and height from image data"""
1200 if not self.embed:
1200 if not self.embed:
1201 return
1201 return
1202 if self.format == self._FMT_PNG:
1202 if self.format == self._FMT_PNG:
1203 w, h = _pngxy(self.data)
1203 w, h = _pngxy(self.data)
1204 elif self.format == self._FMT_JPEG:
1204 elif self.format == self._FMT_JPEG:
1205 w, h = _jpegxy(self.data)
1205 w, h = _jpegxy(self.data)
1206 elif self.format == self._FMT_GIF:
1206 elif self.format == self._FMT_GIF:
1207 w, h = _gifxy(self.data)
1207 w, h = _gifxy(self.data)
1208 else:
1208 else:
1209 # retina only supports png
1209 # retina only supports png
1210 return
1210 return
1211 self.width = w // 2
1211 self.width = w // 2
1212 self.height = h // 2
1212 self.height = h // 2
1213
1213
1214 def reload(self):
1214 def reload(self):
1215 """Reload the raw data from file or URL."""
1215 """Reload the raw data from file or URL."""
1216 if self.embed:
1216 if self.embed:
1217 super(Image,self).reload()
1217 super(Image,self).reload()
1218 if self.retina:
1218 if self.retina:
1219 self._retina_shape()
1219 self._retina_shape()
1220
1220
1221 def _repr_html_(self):
1221 def _repr_html_(self):
1222 if not self.embed:
1222 if not self.embed:
1223 width = height = klass = ''
1223 width = height = klass = ''
1224 if self.width:
1224 if self.width:
1225 width = ' width="%d"' % self.width
1225 width = ' width="%d"' % self.width
1226 if self.height:
1226 if self.height:
1227 height = ' height="%d"' % self.height
1227 height = ' height="%d"' % self.height
1228 if self.unconfined:
1228 if self.unconfined:
1229 klass = ' class="unconfined"'
1229 klass = ' class="unconfined"'
1230 return u'<img src="{url}"{width}{height}{klass}/>'.format(
1230 return u'<img src="{url}"{width}{height}{klass}/>'.format(
1231 url=self.url,
1231 url=self.url,
1232 width=width,
1232 width=width,
1233 height=height,
1233 height=height,
1234 klass=klass,
1234 klass=klass,
1235 )
1235 )
1236
1236
1237 def _repr_mimebundle_(self, include=None, exclude=None):
1237 def _repr_mimebundle_(self, include=None, exclude=None):
1238 """Return the image as a mimebundle
1238 """Return the image as a mimebundle
1239
1239
1240 Any new mimetype support should be implemented here.
1240 Any new mimetype support should be implemented here.
1241 """
1241 """
1242 if self.embed:
1242 if self.embed:
1243 mimetype = self._mimetype
1243 mimetype = self._mimetype
1244 data, metadata = self._data_and_metadata(always_both=True)
1244 data, metadata = self._data_and_metadata(always_both=True)
1245 if metadata:
1245 if metadata:
1246 metadata = {mimetype: metadata}
1246 metadata = {mimetype: metadata}
1247 return {mimetype: data}, metadata
1247 return {mimetype: data}, metadata
1248 else:
1248 else:
1249 return {'text/html': self._repr_html_()}
1249 return {'text/html': self._repr_html_()}
1250
1250
1251 def _data_and_metadata(self, always_both=False):
1251 def _data_and_metadata(self, always_both=False):
1252 """shortcut for returning metadata with shape information, if defined"""
1252 """shortcut for returning metadata with shape information, if defined"""
1253 b64_data = b2a_base64(self.data).decode('ascii')
1253 b64_data = b2a_base64(self.data).decode('ascii')
1254 md = {}
1254 md = {}
1255 if self.metadata:
1255 if self.metadata:
1256 md.update(self.metadata)
1256 md.update(self.metadata)
1257 if self.width:
1257 if self.width:
1258 md['width'] = self.width
1258 md['width'] = self.width
1259 if self.height:
1259 if self.height:
1260 md['height'] = self.height
1260 md['height'] = self.height
1261 if self.unconfined:
1261 if self.unconfined:
1262 md['unconfined'] = self.unconfined
1262 md['unconfined'] = self.unconfined
1263 if md or always_both:
1263 if md or always_both:
1264 return b64_data, md
1264 return b64_data, md
1265 else:
1265 else:
1266 return b64_data
1266 return b64_data
1267
1267
1268 def _repr_png_(self):
1268 def _repr_png_(self):
1269 if self.embed and self.format == self._FMT_PNG:
1269 if self.embed and self.format == self._FMT_PNG:
1270 return self._data_and_metadata()
1270 return self._data_and_metadata()
1271
1271
1272 def _repr_jpeg_(self):
1272 def _repr_jpeg_(self):
1273 if self.embed and self.format == self._FMT_JPEG:
1273 if self.embed and self.format == self._FMT_JPEG:
1274 return self._data_and_metadata()
1274 return self._data_and_metadata()
1275
1275
1276 def _find_ext(self, s):
1276 def _find_ext(self, s):
1277 return s.split('.')[-1].lower()
1277 return s.split('.')[-1].lower()
1278
1278
1279
1279
1280 class Video(DisplayObject):
1280 class Video(DisplayObject):
1281
1281
1282 def __init__(self, data=None, url=None, filename=None, embed=False,
1282 def __init__(self, data=None, url=None, filename=None, embed=False,
1283 mimetype=None, width=None, height=None):
1283 mimetype=None, width=None, height=None):
1284 """Create a video object given raw data or an URL.
1284 """Create a video object given raw data or an URL.
1285
1285
1286 When this object is returned by an input cell or passed to the
1286 When this object is returned by an input cell or passed to the
1287 display function, it will result in the video being displayed
1287 display function, it will result in the video being displayed
1288 in the frontend.
1288 in the frontend.
1289
1289
1290 Parameters
1290 Parameters
1291 ----------
1291 ----------
1292 data : unicode, str or bytes
1292 data : unicode, str or bytes
1293 The raw video data or a URL or filename to load the data from.
1293 The raw video data or a URL or filename to load the data from.
1294 Raw data will require passing `embed=True`.
1294 Raw data will require passing `embed=True`.
1295 url : unicode
1295 url : unicode
1296 A URL for the video. If you specify `url=`,
1296 A URL for the video. If you specify `url=`,
1297 the image data will not be embedded.
1297 the image data will not be embedded.
1298 filename : unicode
1298 filename : unicode
1299 Path to a local file containing the video.
1299 Path to a local file containing the video.
1300 Will be interpreted as a local URL unless `embed=True`.
1300 Will be interpreted as a local URL unless `embed=True`.
1301 embed : bool
1301 embed : bool
1302 Should the video be embedded using a data URI (True) or be
1302 Should the video be embedded using a data URI (True) or be
1303 loaded using a <video> tag (False).
1303 loaded using a <video> tag (False).
1304
1304
1305 Since videos are large, embedding them should be avoided, if possible.
1305 Since videos are large, embedding them should be avoided, if possible.
1306 You must confirm embedding as your intention by passing `embed=True`.
1306 You must confirm embedding as your intention by passing `embed=True`.
1307
1307
1308 Local files can be displayed with URLs without embedding the content, via::
1308 Local files can be displayed with URLs without embedding the content, via::
1309
1309
1310 Video('./video.mp4')
1310 Video('./video.mp4')
1311
1311
1312 mimetype: unicode
1312 mimetype: unicode
1313 Specify the mimetype for embedded videos.
1313 Specify the mimetype for embedded videos.
1314 Default will be guessed from file extension, if available.
1314 Default will be guessed from file extension, if available.
1315 width : int
1315 width : int
1316 Width in pixels to which to constrain the video in HTML.
1316 Width in pixels to which to constrain the video in HTML.
1317 If not supplied, defaults to the width of the video.
1317 If not supplied, defaults to the width of the video.
1318 height : int
1318 height : int
1319 Height in pixels to which to constrain the video in html.
1319 Height in pixels to which to constrain the video in html.
1320 If not supplied, defaults to the height of the video.
1320 If not supplied, defaults to the height of the video.
1321
1321
1322 Examples
1322 Examples
1323 --------
1323 --------
1324
1324
1325 Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4')
1325 Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4')
1326 Video('path/to/video.mp4')
1326 Video('path/to/video.mp4')
1327 Video('path/to/video.mp4', embed=True)
1327 Video('path/to/video.mp4', embed=True)
1328 Video(b'raw-videodata', embed=True)
1328 Video(b'raw-videodata', embed=True)
1329 """
1329 """
1330 if url is None and isinstance(data, str) and data.startswith(('http:', 'https:')):
1330 if url is None and isinstance(data, str) and data.startswith(('http:', 'https:')):
1331 url = data
1331 url = data
1332 data = None
1332 data = None
1333 elif os.path.exists(data):
1333 elif os.path.exists(data):
1334 filename = data
1334 filename = data
1335 data = None
1335 data = None
1336
1336
1337 if data and not embed:
1337 if data and not embed:
1338 msg = ''.join([
1338 msg = ''.join([
1339 "To embed videos, you must pass embed=True ",
1339 "To embed videos, you must pass embed=True ",
1340 "(this may make your notebook files huge)\n",
1340 "(this may make your notebook files huge)\n",
1341 "Consider passing Video(url='...')",
1341 "Consider passing Video(url='...')",
1342 ])
1342 ])
1343 raise ValueError(msg)
1343 raise ValueError(msg)
1344
1344
1345 self.mimetype = mimetype
1345 self.mimetype = mimetype
1346 self.embed = embed
1346 self.embed = embed
1347 self.width = width
1347 self.width = width
1348 self.height = height
1348 self.height = height
1349 super(Video, self).__init__(data=data, url=url, filename=filename)
1349 super(Video, self).__init__(data=data, url=url, filename=filename)
1350
1350
1351 def _repr_html_(self):
1351 def _repr_html_(self):
1352 width = height = ''
1352 width = height = ''
1353 if self.width:
1353 if self.width:
1354 width = ' width="%d"' % self.width
1354 width = ' width="%d"' % self.width
1355 if self.height:
1355 if self.height:
1356 height = ' height="%d"' % self.height
1356 height = ' height="%d"' % self.height
1357
1357
1358 # External URLs and potentially local files are not embedded into the
1358 # External URLs and potentially local files are not embedded into the
1359 # notebook output.
1359 # notebook output.
1360 if not self.embed:
1360 if not self.embed:
1361 url = self.url if self.url is not None else self.filename
1361 url = self.url if self.url is not None else self.filename
1362 output = """<video src="{0}" controls {1} {2}>
1362 output = """<video src="{0}" controls {1} {2}>
1363 Your browser does not support the <code>video</code> element.
1363 Your browser does not support the <code>video</code> element.
1364 </video>""".format(url, width, height)
1364 </video>""".format(url, width, height)
1365 return output
1365 return output
1366
1366
1367 # Embedded videos are base64-encoded.
1367 # Embedded videos are base64-encoded.
1368 mimetype = self.mimetype
1368 mimetype = self.mimetype
1369 if self.filename is not None:
1369 if self.filename is not None:
1370 if not mimetype:
1370 if not mimetype:
1371 mimetype, _ = mimetypes.guess_type(self.filename)
1371 mimetype, _ = mimetypes.guess_type(self.filename)
1372
1372
1373 with open(self.filename, 'rb') as f:
1373 with open(self.filename, 'rb') as f:
1374 video = f.read()
1374 video = f.read()
1375 else:
1375 else:
1376 video = self.data
1376 video = self.data
1377 if isinstance(video, str):
1377 if isinstance(video, str):
1378 # unicode input is already b64-encoded
1378 # unicode input is already b64-encoded
1379 b64_video = video
1379 b64_video = video
1380 else:
1380 else:
1381 b64_video = b2a_base64(video).decode('ascii').rstrip()
1381 b64_video = b2a_base64(video).decode('ascii').rstrip()
1382
1382
1383 output = """<video controls {0} {1}>
1383 output = """<video controls {0} {1}>
1384 <source src="data:{2};base64,{3}" type="{2}">
1384 <source src="data:{2};base64,{3}" type="{2}">
1385 Your browser does not support the video tag.
1385 Your browser does not support the video tag.
1386 </video>""".format(width, height, mimetype, b64_video)
1386 </video>""".format(width, height, mimetype, b64_video)
1387 return output
1387 return output
1388
1388
1389 def reload(self):
1389 def reload(self):
1390 # TODO
1390 # TODO
1391 pass
1391 pass
1392
1392
1393
1393
1394 def clear_output(wait=False):
1394 def clear_output(wait=False):
1395 """Clear the output of the current cell receiving output.
1395 """Clear the output of the current cell receiving output.
1396
1396
1397 Parameters
1397 Parameters
1398 ----------
1398 ----------
1399 wait : bool [default: false]
1399 wait : bool [default: false]
1400 Wait to clear the output until new output is available to replace it."""
1400 Wait to clear the output until new output is available to replace it."""
1401 from IPython.core.interactiveshell import InteractiveShell
1401 from IPython.core.interactiveshell import InteractiveShell
1402 if InteractiveShell.initialized():
1402 if InteractiveShell.initialized():
1403 InteractiveShell.instance().display_pub.clear_output(wait)
1403 InteractiveShell.instance().display_pub.clear_output(wait)
1404 else:
1404 else:
1405 print('\033[2K\r', end='')
1405 print('\033[2K\r', end='')
1406 sys.stdout.flush()
1406 sys.stdout.flush()
1407 print('\033[2K\r', end='')
1407 print('\033[2K\r', end='')
1408 sys.stderr.flush()
1408 sys.stderr.flush()
1409
1409
1410
1410
1411 @skip_doctest
1411 @skip_doctest
1412 def set_matplotlib_formats(*formats, **kwargs):
1412 def set_matplotlib_formats(*formats, **kwargs):
1413 """Select figure formats for the inline backend. Optionally pass quality for JPEG.
1413 """Select figure formats for the inline backend. Optionally pass quality for JPEG.
1414
1414
1415 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
1415 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
1416
1416
1417 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
1417 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
1418
1418
1419 To set this in your config files use the following::
1419 To set this in your config files use the following::
1420
1420
1421 c.InlineBackend.figure_formats = {'png', 'jpeg'}
1421 c.InlineBackend.figure_formats = {'png', 'jpeg'}
1422 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
1422 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
1423
1423
1424 Parameters
1424 Parameters
1425 ----------
1425 ----------
1426 *formats : strs
1426 *formats : strs
1427 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
1427 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
1428 **kwargs :
1428 **kwargs :
1429 Keyword args will be relayed to ``figure.canvas.print_figure``.
1429 Keyword args will be relayed to ``figure.canvas.print_figure``.
1430 """
1430 """
1431 from IPython.core.interactiveshell import InteractiveShell
1431 from IPython.core.interactiveshell import InteractiveShell
1432 from IPython.core.pylabtools import select_figure_formats
1432 from IPython.core.pylabtools import select_figure_formats
1433 # build kwargs, starting with InlineBackend config
1433 # build kwargs, starting with InlineBackend config
1434 kw = {}
1434 kw = {}
1435 from ipykernel.pylab.config import InlineBackend
1435 from ipykernel.pylab.config import InlineBackend
1436 cfg = InlineBackend.instance()
1436 cfg = InlineBackend.instance()
1437 kw.update(cfg.print_figure_kwargs)
1437 kw.update(cfg.print_figure_kwargs)
1438 kw.update(**kwargs)
1438 kw.update(**kwargs)
1439 shell = InteractiveShell.instance()
1439 shell = InteractiveShell.instance()
1440 select_figure_formats(shell, formats, **kw)
1440 select_figure_formats(shell, formats, **kw)
1441
1441
1442 @skip_doctest
1442 @skip_doctest
1443 def set_matplotlib_close(close=True):
1443 def set_matplotlib_close(close=True):
1444 """Set whether the inline backend closes all figures automatically or not.
1444 """Set whether the inline backend closes all figures automatically or not.
1445
1445
1446 By default, the inline backend used in the IPython Notebook will close all
1446 By default, the inline backend used in the IPython Notebook will close all
1447 matplotlib figures automatically after each cell is run. This means that
1447 matplotlib figures automatically after each cell is run. This means that
1448 plots in different cells won't interfere. Sometimes, you may want to make
1448 plots in different cells won't interfere. Sometimes, you may want to make
1449 a plot in one cell and then refine it in later cells. This can be accomplished
1449 a plot in one cell and then refine it in later cells. This can be accomplished
1450 by::
1450 by::
1451
1451
1452 In [1]: set_matplotlib_close(False)
1452 In [1]: set_matplotlib_close(False)
1453
1453
1454 To set this in your config files use the following::
1454 To set this in your config files use the following::
1455
1455
1456 c.InlineBackend.close_figures = False
1456 c.InlineBackend.close_figures = False
1457
1457
1458 Parameters
1458 Parameters
1459 ----------
1459 ----------
1460 close : bool
1460 close : bool
1461 Should all matplotlib figures be automatically closed after each cell is
1461 Should all matplotlib figures be automatically closed after each cell is
1462 run?
1462 run?
1463 """
1463 """
1464 from ipykernel.pylab.config import InlineBackend
1464 from ipykernel.pylab.config import InlineBackend
1465 cfg = InlineBackend.instance()
1465 cfg = InlineBackend.instance()
1466 cfg.close_figures = close
1466 cfg.close_figures = close
@@ -1,707 +1,707 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 from codeop import compile_command
13 from codeop import compile_command
14 import re
14 import re
15 import tokenize
15 import tokenize
16 from typing import List, Tuple, Union
16 from typing import List, Tuple, Union
17 import warnings
17 import warnings
18
18
19 _indent_re = re.compile(r'^[ \t]+')
19 _indent_re = re.compile(r'^[ \t]+')
20
20
21 def leading_indent(lines):
21 def leading_indent(lines):
22 """Remove leading indentation.
22 """Remove leading indentation.
23
23
24 If the first line starts with a spaces or tabs, the same whitespace will be
24 If the first line starts with a spaces or tabs, the same whitespace will be
25 removed from each following line in the cell.
25 removed from each following line in the cell.
26 """
26 """
27 if not lines:
27 if not lines:
28 return lines
28 return lines
29 m = _indent_re.match(lines[0])
29 m = _indent_re.match(lines[0])
30 if not m:
30 if not m:
31 return lines
31 return lines
32 space = m.group(0)
32 space = m.group(0)
33 n = len(space)
33 n = len(space)
34 return [l[n:] if l.startswith(space) else l
34 return [l[n:] if l.startswith(space) else l
35 for l in lines]
35 for l in lines]
36
36
37 class PromptStripper:
37 class PromptStripper:
38 """Remove matching input prompts from a block of input.
38 """Remove matching input prompts from a block of input.
39
39
40 Parameters
40 Parameters
41 ----------
41 ----------
42 prompt_re : regular expression
42 prompt_re : regular expression
43 A regular expression matching any input prompt (including continuation,
43 A regular expression matching any input prompt (including continuation,
44 e.g. ``...``)
44 e.g. ``...``)
45 initial_re : regular expression, optional
45 initial_re : regular expression, optional
46 A regular expression matching only the initial prompt, but not continuation.
46 A regular expression matching only the initial prompt, but not continuation.
47 If no initial expression is given, prompt_re will be used everywhere.
47 If no initial expression is given, prompt_re will be used everywhere.
48 Used mainly for plain Python prompts (``>>>``), where the continuation prompt
48 Used mainly for plain Python prompts (``>>>``), where the continuation prompt
49 ``...`` is a valid Python expression in Python 3, so shouldn't be stripped.
49 ``...`` is a valid Python expression in Python 3, so shouldn't be stripped.
50
50
51 If initial_re and prompt_re differ,
51 If initial_re and prompt_re differ,
52 only initial_re will be tested against the first line.
52 only initial_re will be tested against the first line.
53 If any prompt is found on the first two lines,
53 If any prompt is found on the first two lines,
54 prompts will be stripped from the rest of the block.
54 prompts will be stripped from the rest of the block.
55 """
55 """
56 def __init__(self, prompt_re, initial_re=None):
56 def __init__(self, prompt_re, initial_re=None):
57 self.prompt_re = prompt_re
57 self.prompt_re = prompt_re
58 self.initial_re = initial_re or prompt_re
58 self.initial_re = initial_re or prompt_re
59
59
60 def _strip(self, lines):
60 def _strip(self, lines):
61 return [self.prompt_re.sub('', l, count=1) for l in lines]
61 return [self.prompt_re.sub('', l, count=1) for l in lines]
62
62
63 def __call__(self, lines):
63 def __call__(self, lines):
64 if not lines:
64 if not lines:
65 return lines
65 return lines
66 if self.initial_re.match(lines[0]) or \
66 if self.initial_re.match(lines[0]) or \
67 (len(lines) > 1 and self.prompt_re.match(lines[1])):
67 (len(lines) > 1 and self.prompt_re.match(lines[1])):
68 return self._strip(lines)
68 return self._strip(lines)
69 return lines
69 return lines
70
70
71 classic_prompt = PromptStripper(
71 classic_prompt = PromptStripper(
72 prompt_re=re.compile(r'^(>>>|\.\.\.)( |$)'),
72 prompt_re=re.compile(r'^(>>>|\.\.\.)( |$)'),
73 initial_re=re.compile(r'^>>>( |$)')
73 initial_re=re.compile(r'^>>>( |$)')
74 )
74 )
75
75
76 ipython_prompt = PromptStripper(re.compile(r'^(In \[\d+\]: |\s*\.{3,}: ?)'))
76 ipython_prompt = PromptStripper(re.compile(r'^(In \[\d+\]: |\s*\.{3,}: ?)'))
77
77
78 def cell_magic(lines):
78 def cell_magic(lines):
79 if not lines or not lines[0].startswith('%%'):
79 if not lines or not lines[0].startswith('%%'):
80 return lines
80 return lines
81 if re.match('%%\w+\?', lines[0]):
81 if re.match(r'%%\w+\?', lines[0]):
82 # This case will be handled by help_end
82 # This case will be handled by help_end
83 return lines
83 return lines
84 magic_name, _, first_line = lines[0][2:-1].partition(' ')
84 magic_name, _, first_line = lines[0][2:-1].partition(' ')
85 body = ''.join(lines[1:])
85 body = ''.join(lines[1:])
86 return ['get_ipython().run_cell_magic(%r, %r, %r)\n'
86 return ['get_ipython().run_cell_magic(%r, %r, %r)\n'
87 % (magic_name, first_line, body)]
87 % (magic_name, first_line, body)]
88
88
89
89
90 def _find_assign_op(token_line) -> Union[int, None]:
90 def _find_assign_op(token_line) -> Union[int, None]:
91 """Get the index of the first assignment in the line ('=' not inside brackets)
91 """Get the index of the first assignment in the line ('=' not inside brackets)
92
92
93 Note: We don't try to support multiple special assignment (a = b = %foo)
93 Note: We don't try to support multiple special assignment (a = b = %foo)
94 """
94 """
95 paren_level = 0
95 paren_level = 0
96 for i, ti in enumerate(token_line):
96 for i, ti in enumerate(token_line):
97 s = ti.string
97 s = ti.string
98 if s == '=' and paren_level == 0:
98 if s == '=' and paren_level == 0:
99 return i
99 return i
100 if s in {'(','[','{'}:
100 if s in {'(','[','{'}:
101 paren_level += 1
101 paren_level += 1
102 elif s in {')', ']', '}'}:
102 elif s in {')', ']', '}'}:
103 if paren_level > 0:
103 if paren_level > 0:
104 paren_level -= 1
104 paren_level -= 1
105
105
106 def find_end_of_continued_line(lines, start_line: int):
106 def find_end_of_continued_line(lines, start_line: int):
107 """Find the last line of a line explicitly extended using backslashes.
107 """Find the last line of a line explicitly extended using backslashes.
108
108
109 Uses 0-indexed line numbers.
109 Uses 0-indexed line numbers.
110 """
110 """
111 end_line = start_line
111 end_line = start_line
112 while lines[end_line].endswith('\\\n'):
112 while lines[end_line].endswith('\\\n'):
113 end_line += 1
113 end_line += 1
114 if end_line >= len(lines):
114 if end_line >= len(lines):
115 break
115 break
116 return end_line
116 return end_line
117
117
118 def assemble_continued_line(lines, start: Tuple[int, int], end_line: int):
118 def assemble_continued_line(lines, start: Tuple[int, int], end_line: int):
119 """Assemble a single line from multiple continued line pieces
119 r"""Assemble a single line from multiple continued line pieces
120
120
121 Continued lines are lines ending in ``\``, and the line following the last
121 Continued lines are lines ending in ``\``, and the line following the last
122 ``\`` in the block.
122 ``\`` in the block.
123
123
124 For example, this code continues over multiple lines::
124 For example, this code continues over multiple lines::
125
125
126 if (assign_ix is not None) \
126 if (assign_ix is not None) \
127 and (len(line) >= assign_ix + 2) \
127 and (len(line) >= assign_ix + 2) \
128 and (line[assign_ix+1].string == '%') \
128 and (line[assign_ix+1].string == '%') \
129 and (line[assign_ix+2].type == tokenize.NAME):
129 and (line[assign_ix+2].type == tokenize.NAME):
130
130
131 This statement contains four continued line pieces.
131 This statement contains four continued line pieces.
132 Assembling these pieces into a single line would give::
132 Assembling these pieces into a single line would give::
133
133
134 if (assign_ix is not None) and (len(line) >= assign_ix + 2) and (line[...
134 if (assign_ix is not None) and (len(line) >= assign_ix + 2) and (line[...
135
135
136 This uses 0-indexed line numbers. *start* is (lineno, colno).
136 This uses 0-indexed line numbers. *start* is (lineno, colno).
137
137
138 Used to allow ``%magic`` and ``!system`` commands to be continued over
138 Used to allow ``%magic`` and ``!system`` commands to be continued over
139 multiple lines.
139 multiple lines.
140 """
140 """
141 parts = [lines[start[0]][start[1]:]] + lines[start[0]+1:end_line+1]
141 parts = [lines[start[0]][start[1]:]] + lines[start[0]+1:end_line+1]
142 return ' '.join([p[:-2] for p in parts[:-1]] # Strip backslash+newline
142 return ' '.join([p[:-2] for p in parts[:-1]] # Strip backslash+newline
143 + [parts[-1][:-1]]) # Strip newline from last line
143 + [parts[-1][:-1]]) # Strip newline from last line
144
144
145 class TokenTransformBase:
145 class TokenTransformBase:
146 """Base class for transformations which examine tokens.
146 """Base class for transformations which examine tokens.
147
147
148 Special syntax should not be transformed when it occurs inside strings or
148 Special syntax should not be transformed when it occurs inside strings or
149 comments. This is hard to reliably avoid with regexes. The solution is to
149 comments. This is hard to reliably avoid with regexes. The solution is to
150 tokenise the code as Python, and recognise the special syntax in the tokens.
150 tokenise the code as Python, and recognise the special syntax in the tokens.
151
151
152 IPython's special syntax is not valid Python syntax, so tokenising may go
152 IPython's special syntax is not valid Python syntax, so tokenising may go
153 wrong after the special syntax starts. These classes therefore find and
153 wrong after the special syntax starts. These classes therefore find and
154 transform *one* instance of special syntax at a time into regular Python
154 transform *one* instance of special syntax at a time into regular Python
155 syntax. After each transformation, tokens are regenerated to find the next
155 syntax. After each transformation, tokens are regenerated to find the next
156 piece of special syntax.
156 piece of special syntax.
157
157
158 Subclasses need to implement one class method (find)
158 Subclasses need to implement one class method (find)
159 and one regular method (transform).
159 and one regular method (transform).
160
160
161 The priority attribute can select which transformation to apply if multiple
161 The priority attribute can select which transformation to apply if multiple
162 transformers match in the same place. Lower numbers have higher priority.
162 transformers match in the same place. Lower numbers have higher priority.
163 This allows "%magic?" to be turned into a help call rather than a magic call.
163 This allows "%magic?" to be turned into a help call rather than a magic call.
164 """
164 """
165 # Lower numbers -> higher priority (for matches in the same location)
165 # Lower numbers -> higher priority (for matches in the same location)
166 priority = 10
166 priority = 10
167
167
168 def sortby(self):
168 def sortby(self):
169 return self.start_line, self.start_col, self.priority
169 return self.start_line, self.start_col, self.priority
170
170
171 def __init__(self, start):
171 def __init__(self, start):
172 self.start_line = start[0] - 1 # Shift from 1-index to 0-index
172 self.start_line = start[0] - 1 # Shift from 1-index to 0-index
173 self.start_col = start[1]
173 self.start_col = start[1]
174
174
175 @classmethod
175 @classmethod
176 def find(cls, tokens_by_line):
176 def find(cls, tokens_by_line):
177 """Find one instance of special syntax in the provided tokens.
177 """Find one instance of special syntax in the provided tokens.
178
178
179 Tokens are grouped into logical lines for convenience,
179 Tokens are grouped into logical lines for convenience,
180 so it is easy to e.g. look at the first token of each line.
180 so it is easy to e.g. look at the first token of each line.
181 *tokens_by_line* is a list of lists of tokenize.TokenInfo objects.
181 *tokens_by_line* is a list of lists of tokenize.TokenInfo objects.
182
182
183 This should return an instance of its class, pointing to the start
183 This should return an instance of its class, pointing to the start
184 position it has found, or None if it found no match.
184 position it has found, or None if it found no match.
185 """
185 """
186 raise NotImplementedError
186 raise NotImplementedError
187
187
188 def transform(self, lines: List[str]):
188 def transform(self, lines: List[str]):
189 """Transform one instance of special syntax found by ``find()``
189 """Transform one instance of special syntax found by ``find()``
190
190
191 Takes a list of strings representing physical lines,
191 Takes a list of strings representing physical lines,
192 returns a similar list of transformed lines.
192 returns a similar list of transformed lines.
193 """
193 """
194 raise NotImplementedError
194 raise NotImplementedError
195
195
196 class MagicAssign(TokenTransformBase):
196 class MagicAssign(TokenTransformBase):
197 """Transformer for assignments from magics (a = %foo)"""
197 """Transformer for assignments from magics (a = %foo)"""
198 @classmethod
198 @classmethod
199 def find(cls, tokens_by_line):
199 def find(cls, tokens_by_line):
200 """Find the first magic assignment (a = %foo) in the cell.
200 """Find the first magic assignment (a = %foo) in the cell.
201 """
201 """
202 for line in tokens_by_line:
202 for line in tokens_by_line:
203 assign_ix = _find_assign_op(line)
203 assign_ix = _find_assign_op(line)
204 if (assign_ix is not None) \
204 if (assign_ix is not None) \
205 and (len(line) >= assign_ix + 2) \
205 and (len(line) >= assign_ix + 2) \
206 and (line[assign_ix+1].string == '%') \
206 and (line[assign_ix+1].string == '%') \
207 and (line[assign_ix+2].type == tokenize.NAME):
207 and (line[assign_ix+2].type == tokenize.NAME):
208 return cls(line[assign_ix+1].start)
208 return cls(line[assign_ix+1].start)
209
209
210 def transform(self, lines: List[str]):
210 def transform(self, lines: List[str]):
211 """Transform a magic assignment found by the ``find()`` classmethod.
211 """Transform a magic assignment found by the ``find()`` classmethod.
212 """
212 """
213 start_line, start_col = self.start_line, self.start_col
213 start_line, start_col = self.start_line, self.start_col
214 lhs = lines[start_line][:start_col]
214 lhs = lines[start_line][:start_col]
215 end_line = find_end_of_continued_line(lines, start_line)
215 end_line = find_end_of_continued_line(lines, start_line)
216 rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
216 rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
217 assert rhs.startswith('%'), rhs
217 assert rhs.startswith('%'), rhs
218 magic_name, _, args = rhs[1:].partition(' ')
218 magic_name, _, args = rhs[1:].partition(' ')
219
219
220 lines_before = lines[:start_line]
220 lines_before = lines[:start_line]
221 call = "get_ipython().run_line_magic({!r}, {!r})".format(magic_name, args)
221 call = "get_ipython().run_line_magic({!r}, {!r})".format(magic_name, args)
222 new_line = lhs + call + '\n'
222 new_line = lhs + call + '\n'
223 lines_after = lines[end_line+1:]
223 lines_after = lines[end_line+1:]
224
224
225 return lines_before + [new_line] + lines_after
225 return lines_before + [new_line] + lines_after
226
226
227
227
228 class SystemAssign(TokenTransformBase):
228 class SystemAssign(TokenTransformBase):
229 """Transformer for assignments from system commands (a = !foo)"""
229 """Transformer for assignments from system commands (a = !foo)"""
230 @classmethod
230 @classmethod
231 def find(cls, tokens_by_line):
231 def find(cls, tokens_by_line):
232 """Find the first system assignment (a = !foo) in the cell.
232 """Find the first system assignment (a = !foo) in the cell.
233 """
233 """
234 for line in tokens_by_line:
234 for line in tokens_by_line:
235 assign_ix = _find_assign_op(line)
235 assign_ix = _find_assign_op(line)
236 if (assign_ix is not None) \
236 if (assign_ix is not None) \
237 and not line[assign_ix].line.strip().startswith('=') \
237 and not line[assign_ix].line.strip().startswith('=') \
238 and (len(line) >= assign_ix + 2) \
238 and (len(line) >= assign_ix + 2) \
239 and (line[assign_ix + 1].type == tokenize.ERRORTOKEN):
239 and (line[assign_ix + 1].type == tokenize.ERRORTOKEN):
240 ix = assign_ix + 1
240 ix = assign_ix + 1
241
241
242 while ix < len(line) and line[ix].type == tokenize.ERRORTOKEN:
242 while ix < len(line) and line[ix].type == tokenize.ERRORTOKEN:
243 if line[ix].string == '!':
243 if line[ix].string == '!':
244 return cls(line[ix].start)
244 return cls(line[ix].start)
245 elif not line[ix].string.isspace():
245 elif not line[ix].string.isspace():
246 break
246 break
247 ix += 1
247 ix += 1
248
248
249 def transform(self, lines: List[str]):
249 def transform(self, lines: List[str]):
250 """Transform a system assignment found by the ``find()`` classmethod.
250 """Transform a system assignment found by the ``find()`` classmethod.
251 """
251 """
252 start_line, start_col = self.start_line, self.start_col
252 start_line, start_col = self.start_line, self.start_col
253
253
254 lhs = lines[start_line][:start_col]
254 lhs = lines[start_line][:start_col]
255 end_line = find_end_of_continued_line(lines, start_line)
255 end_line = find_end_of_continued_line(lines, start_line)
256 rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
256 rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
257 assert rhs.startswith('!'), rhs
257 assert rhs.startswith('!'), rhs
258 cmd = rhs[1:]
258 cmd = rhs[1:]
259
259
260 lines_before = lines[:start_line]
260 lines_before = lines[:start_line]
261 call = "get_ipython().getoutput({!r})".format(cmd)
261 call = "get_ipython().getoutput({!r})".format(cmd)
262 new_line = lhs + call + '\n'
262 new_line = lhs + call + '\n'
263 lines_after = lines[end_line + 1:]
263 lines_after = lines[end_line + 1:]
264
264
265 return lines_before + [new_line] + lines_after
265 return lines_before + [new_line] + lines_after
266
266
267 # The escape sequences that define the syntax transformations IPython will
267 # The escape sequences that define the syntax transformations IPython will
268 # apply to user input. These can NOT be just changed here: many regular
268 # apply to user input. These can NOT be just changed here: many regular
269 # expressions and other parts of the code may use their hardcoded values, and
269 # expressions and other parts of the code may use their hardcoded values, and
270 # for all intents and purposes they constitute the 'IPython syntax', so they
270 # for all intents and purposes they constitute the 'IPython syntax', so they
271 # should be considered fixed.
271 # should be considered fixed.
272
272
273 ESC_SHELL = '!' # Send line to underlying system shell
273 ESC_SHELL = '!' # Send line to underlying system shell
274 ESC_SH_CAP = '!!' # Send line to system shell and capture output
274 ESC_SH_CAP = '!!' # Send line to system shell and capture output
275 ESC_HELP = '?' # Find information about object
275 ESC_HELP = '?' # Find information about object
276 ESC_HELP2 = '??' # Find extra-detailed information about object
276 ESC_HELP2 = '??' # Find extra-detailed information about object
277 ESC_MAGIC = '%' # Call magic function
277 ESC_MAGIC = '%' # Call magic function
278 ESC_MAGIC2 = '%%' # Call cell-magic function
278 ESC_MAGIC2 = '%%' # Call cell-magic function
279 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
279 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
280 ESC_QUOTE2 = ';' # Quote all args as a single string, call
280 ESC_QUOTE2 = ';' # Quote all args as a single string, call
281 ESC_PAREN = '/' # Call first argument with rest of line as arguments
281 ESC_PAREN = '/' # Call first argument with rest of line as arguments
282
282
283 ESCAPE_SINGLES = {'!', '?', '%', ',', ';', '/'}
283 ESCAPE_SINGLES = {'!', '?', '%', ',', ';', '/'}
284 ESCAPE_DOUBLES = {'!!', '??'} # %% (cell magic) is handled separately
284 ESCAPE_DOUBLES = {'!!', '??'} # %% (cell magic) is handled separately
285
285
286 def _make_help_call(target, esc, next_input=None):
286 def _make_help_call(target, esc, next_input=None):
287 """Prepares a pinfo(2)/psearch call from a target name and the escape
287 """Prepares a pinfo(2)/psearch call from a target name and the escape
288 (i.e. ? or ??)"""
288 (i.e. ? or ??)"""
289 method = 'pinfo2' if esc == '??' \
289 method = 'pinfo2' if esc == '??' \
290 else 'psearch' if '*' in target \
290 else 'psearch' if '*' in target \
291 else 'pinfo'
291 else 'pinfo'
292 arg = " ".join([method, target])
292 arg = " ".join([method, target])
293 #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args)
293 #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args)
294 t_magic_name, _, t_magic_arg_s = arg.partition(' ')
294 t_magic_name, _, t_magic_arg_s = arg.partition(' ')
295 t_magic_name = t_magic_name.lstrip(ESC_MAGIC)
295 t_magic_name = t_magic_name.lstrip(ESC_MAGIC)
296 if next_input is None:
296 if next_input is None:
297 return 'get_ipython().run_line_magic(%r, %r)' % (t_magic_name, t_magic_arg_s)
297 return 'get_ipython().run_line_magic(%r, %r)' % (t_magic_name, t_magic_arg_s)
298 else:
298 else:
299 return 'get_ipython().set_next_input(%r);get_ipython().run_line_magic(%r, %r)' % \
299 return 'get_ipython().set_next_input(%r);get_ipython().run_line_magic(%r, %r)' % \
300 (next_input, t_magic_name, t_magic_arg_s)
300 (next_input, t_magic_name, t_magic_arg_s)
301
301
302 def _tr_help(content):
302 def _tr_help(content):
303 """Translate lines escaped with: ?
303 """Translate lines escaped with: ?
304
304
305 A naked help line should fire the intro help screen (shell.show_usage())
305 A naked help line should fire the intro help screen (shell.show_usage())
306 """
306 """
307 if not content:
307 if not content:
308 return 'get_ipython().show_usage()'
308 return 'get_ipython().show_usage()'
309
309
310 return _make_help_call(content, '?')
310 return _make_help_call(content, '?')
311
311
312 def _tr_help2(content):
312 def _tr_help2(content):
313 """Translate lines escaped with: ??
313 """Translate lines escaped with: ??
314
314
315 A naked help line should fire the intro help screen (shell.show_usage())
315 A naked help line should fire the intro help screen (shell.show_usage())
316 """
316 """
317 if not content:
317 if not content:
318 return 'get_ipython().show_usage()'
318 return 'get_ipython().show_usage()'
319
319
320 return _make_help_call(content, '??')
320 return _make_help_call(content, '??')
321
321
322 def _tr_magic(content):
322 def _tr_magic(content):
323 "Translate lines escaped with a percent sign: %"
323 "Translate lines escaped with a percent sign: %"
324 name, _, args = content.partition(' ')
324 name, _, args = content.partition(' ')
325 return 'get_ipython().run_line_magic(%r, %r)' % (name, args)
325 return 'get_ipython().run_line_magic(%r, %r)' % (name, args)
326
326
327 def _tr_quote(content):
327 def _tr_quote(content):
328 "Translate lines escaped with a comma: ,"
328 "Translate lines escaped with a comma: ,"
329 name, _, args = content.partition(' ')
329 name, _, args = content.partition(' ')
330 return '%s("%s")' % (name, '", "'.join(args.split()) )
330 return '%s("%s")' % (name, '", "'.join(args.split()) )
331
331
332 def _tr_quote2(content):
332 def _tr_quote2(content):
333 "Translate lines escaped with a semicolon: ;"
333 "Translate lines escaped with a semicolon: ;"
334 name, _, args = content.partition(' ')
334 name, _, args = content.partition(' ')
335 return '%s("%s")' % (name, args)
335 return '%s("%s")' % (name, args)
336
336
337 def _tr_paren(content):
337 def _tr_paren(content):
338 "Translate lines escaped with a slash: /"
338 "Translate lines escaped with a slash: /"
339 name, _, args = content.partition(' ')
339 name, _, args = content.partition(' ')
340 return '%s(%s)' % (name, ", ".join(args.split()))
340 return '%s(%s)' % (name, ", ".join(args.split()))
341
341
342 tr = { ESC_SHELL : 'get_ipython().system({!r})'.format,
342 tr = { ESC_SHELL : 'get_ipython().system({!r})'.format,
343 ESC_SH_CAP : 'get_ipython().getoutput({!r})'.format,
343 ESC_SH_CAP : 'get_ipython().getoutput({!r})'.format,
344 ESC_HELP : _tr_help,
344 ESC_HELP : _tr_help,
345 ESC_HELP2 : _tr_help2,
345 ESC_HELP2 : _tr_help2,
346 ESC_MAGIC : _tr_magic,
346 ESC_MAGIC : _tr_magic,
347 ESC_QUOTE : _tr_quote,
347 ESC_QUOTE : _tr_quote,
348 ESC_QUOTE2 : _tr_quote2,
348 ESC_QUOTE2 : _tr_quote2,
349 ESC_PAREN : _tr_paren }
349 ESC_PAREN : _tr_paren }
350
350
351 class EscapedCommand(TokenTransformBase):
351 class EscapedCommand(TokenTransformBase):
352 """Transformer for escaped commands like %foo, !foo, or /foo"""
352 """Transformer for escaped commands like %foo, !foo, or /foo"""
353 @classmethod
353 @classmethod
354 def find(cls, tokens_by_line):
354 def find(cls, tokens_by_line):
355 """Find the first escaped command (%foo, !foo, etc.) in the cell.
355 """Find the first escaped command (%foo, !foo, etc.) in the cell.
356 """
356 """
357 for line in tokens_by_line:
357 for line in tokens_by_line:
358 if not line:
358 if not line:
359 continue
359 continue
360 ix = 0
360 ix = 0
361 ll = len(line)
361 ll = len(line)
362 while ll > ix and line[ix].type in {tokenize.INDENT, tokenize.DEDENT}:
362 while ll > ix and line[ix].type in {tokenize.INDENT, tokenize.DEDENT}:
363 ix += 1
363 ix += 1
364 if ix >= ll:
364 if ix >= ll:
365 continue
365 continue
366 if line[ix].string in ESCAPE_SINGLES:
366 if line[ix].string in ESCAPE_SINGLES:
367 return cls(line[ix].start)
367 return cls(line[ix].start)
368
368
369 def transform(self, lines):
369 def transform(self, lines):
370 """Transform an escaped line found by the ``find()`` classmethod.
370 """Transform an escaped line found by the ``find()`` classmethod.
371 """
371 """
372 start_line, start_col = self.start_line, self.start_col
372 start_line, start_col = self.start_line, self.start_col
373
373
374 indent = lines[start_line][:start_col]
374 indent = lines[start_line][:start_col]
375 end_line = find_end_of_continued_line(lines, start_line)
375 end_line = find_end_of_continued_line(lines, start_line)
376 line = assemble_continued_line(lines, (start_line, start_col), end_line)
376 line = assemble_continued_line(lines, (start_line, start_col), end_line)
377
377
378 if len(line) > 1 and line[:2] in ESCAPE_DOUBLES:
378 if len(line) > 1 and line[:2] in ESCAPE_DOUBLES:
379 escape, content = line[:2], line[2:]
379 escape, content = line[:2], line[2:]
380 else:
380 else:
381 escape, content = line[:1], line[1:]
381 escape, content = line[:1], line[1:]
382
382
383 if escape in tr:
383 if escape in tr:
384 call = tr[escape](content)
384 call = tr[escape](content)
385 else:
385 else:
386 call = ''
386 call = ''
387
387
388 lines_before = lines[:start_line]
388 lines_before = lines[:start_line]
389 new_line = indent + call + '\n'
389 new_line = indent + call + '\n'
390 lines_after = lines[end_line + 1:]
390 lines_after = lines[end_line + 1:]
391
391
392 return lines_before + [new_line] + lines_after
392 return lines_before + [new_line] + lines_after
393
393
394 _help_end_re = re.compile(r"""(%{0,2}
394 _help_end_re = re.compile(r"""(%{0,2}
395 [a-zA-Z_*][\w*]* # Variable name
395 [a-zA-Z_*][\w*]* # Variable name
396 (\.[a-zA-Z_*][\w*]*)* # .etc.etc
396 (\.[a-zA-Z_*][\w*]*)* # .etc.etc
397 )
397 )
398 (\?\??)$ # ? or ??
398 (\?\??)$ # ? or ??
399 """,
399 """,
400 re.VERBOSE)
400 re.VERBOSE)
401
401
402 class HelpEnd(TokenTransformBase):
402 class HelpEnd(TokenTransformBase):
403 """Transformer for help syntax: obj? and obj??"""
403 """Transformer for help syntax: obj? and obj??"""
404 # This needs to be higher priority (lower number) than EscapedCommand so
404 # This needs to be higher priority (lower number) than EscapedCommand so
405 # that inspecting magics (%foo?) works.
405 # that inspecting magics (%foo?) works.
406 priority = 5
406 priority = 5
407
407
408 def __init__(self, start, q_locn):
408 def __init__(self, start, q_locn):
409 super().__init__(start)
409 super().__init__(start)
410 self.q_line = q_locn[0] - 1 # Shift from 1-indexed to 0-indexed
410 self.q_line = q_locn[0] - 1 # Shift from 1-indexed to 0-indexed
411 self.q_col = q_locn[1]
411 self.q_col = q_locn[1]
412
412
413 @classmethod
413 @classmethod
414 def find(cls, tokens_by_line):
414 def find(cls, tokens_by_line):
415 """Find the first help command (foo?) in the cell.
415 """Find the first help command (foo?) in the cell.
416 """
416 """
417 for line in tokens_by_line:
417 for line in tokens_by_line:
418 # Last token is NEWLINE; look at last but one
418 # Last token is NEWLINE; look at last but one
419 if len(line) > 2 and line[-2].string == '?':
419 if len(line) > 2 and line[-2].string == '?':
420 # Find the first token that's not INDENT/DEDENT
420 # Find the first token that's not INDENT/DEDENT
421 ix = 0
421 ix = 0
422 while line[ix].type in {tokenize.INDENT, tokenize.DEDENT}:
422 while line[ix].type in {tokenize.INDENT, tokenize.DEDENT}:
423 ix += 1
423 ix += 1
424 return cls(line[ix].start, line[-2].start)
424 return cls(line[ix].start, line[-2].start)
425
425
426 def transform(self, lines):
426 def transform(self, lines):
427 """Transform a help command found by the ``find()`` classmethod.
427 """Transform a help command found by the ``find()`` classmethod.
428 """
428 """
429 piece = ''.join(lines[self.start_line:self.q_line+1])
429 piece = ''.join(lines[self.start_line:self.q_line+1])
430 indent, content = piece[:self.start_col], piece[self.start_col:]
430 indent, content = piece[:self.start_col], piece[self.start_col:]
431 lines_before = lines[:self.start_line]
431 lines_before = lines[:self.start_line]
432 lines_after = lines[self.q_line + 1:]
432 lines_after = lines[self.q_line + 1:]
433
433
434 m = _help_end_re.search(content)
434 m = _help_end_re.search(content)
435 if not m:
435 if not m:
436 raise SyntaxError(content)
436 raise SyntaxError(content)
437 assert m is not None, content
437 assert m is not None, content
438 target = m.group(1)
438 target = m.group(1)
439 esc = m.group(3)
439 esc = m.group(3)
440
440
441 # If we're mid-command, put it back on the next prompt for the user.
441 # If we're mid-command, put it back on the next prompt for the user.
442 next_input = None
442 next_input = None
443 if (not lines_before) and (not lines_after) \
443 if (not lines_before) and (not lines_after) \
444 and content.strip() != m.group(0):
444 and content.strip() != m.group(0):
445 next_input = content.rstrip('?\n')
445 next_input = content.rstrip('?\n')
446
446
447 call = _make_help_call(target, esc, next_input=next_input)
447 call = _make_help_call(target, esc, next_input=next_input)
448 new_line = indent + call + '\n'
448 new_line = indent + call + '\n'
449
449
450 return lines_before + [new_line] + lines_after
450 return lines_before + [new_line] + lines_after
451
451
452 def make_tokens_by_line(lines:List[str]):
452 def make_tokens_by_line(lines:List[str]):
453 """Tokenize a series of lines and group tokens by line.
453 """Tokenize a series of lines and group tokens by line.
454
454
455 The tokens for a multiline Python string or expression are grouped as one
455 The tokens for a multiline Python string or expression are grouped as one
456 line. All lines except the last lines should keep their line ending ('\\n',
456 line. All lines except the last lines should keep their line ending ('\\n',
457 '\\r\\n') for this to properly work. Use `.splitlines(keeplineending=True)`
457 '\\r\\n') for this to properly work. Use `.splitlines(keeplineending=True)`
458 for example when passing block of text to this function.
458 for example when passing block of text to this function.
459
459
460 """
460 """
461 # NL tokens are used inside multiline expressions, but also after blank
461 # NL tokens are used inside multiline expressions, but also after blank
462 # lines or comments. This is intentional - see https://bugs.python.org/issue17061
462 # lines or comments. This is intentional - see https://bugs.python.org/issue17061
463 # We want to group the former case together but split the latter, so we
463 # We want to group the former case together but split the latter, so we
464 # track parentheses level, similar to the internals of tokenize.
464 # track parentheses level, similar to the internals of tokenize.
465 NEWLINE, NL = tokenize.NEWLINE, tokenize.NL
465 NEWLINE, NL = tokenize.NEWLINE, tokenize.NL
466 tokens_by_line = [[]]
466 tokens_by_line = [[]]
467 if len(lines) > 1 and not lines[0].endswith(('\n', '\r', '\r\n', '\x0b', '\x0c')):
467 if len(lines) > 1 and not lines[0].endswith(('\n', '\r', '\r\n', '\x0b', '\x0c')):
468 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")
468 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")
469 parenlev = 0
469 parenlev = 0
470 try:
470 try:
471 for token in tokenize.generate_tokens(iter(lines).__next__):
471 for token in tokenize.generate_tokens(iter(lines).__next__):
472 tokens_by_line[-1].append(token)
472 tokens_by_line[-1].append(token)
473 if (token.type == NEWLINE) \
473 if (token.type == NEWLINE) \
474 or ((token.type == NL) and (parenlev <= 0)):
474 or ((token.type == NL) and (parenlev <= 0)):
475 tokens_by_line.append([])
475 tokens_by_line.append([])
476 elif token.string in {'(', '[', '{'}:
476 elif token.string in {'(', '[', '{'}:
477 parenlev += 1
477 parenlev += 1
478 elif token.string in {')', ']', '}'}:
478 elif token.string in {')', ']', '}'}:
479 if parenlev > 0:
479 if parenlev > 0:
480 parenlev -= 1
480 parenlev -= 1
481 except tokenize.TokenError:
481 except tokenize.TokenError:
482 # Input ended in a multiline string or expression. That's OK for us.
482 # Input ended in a multiline string or expression. That's OK for us.
483 pass
483 pass
484
484
485
485
486 if not tokens_by_line[-1]:
486 if not tokens_by_line[-1]:
487 tokens_by_line.pop()
487 tokens_by_line.pop()
488
488
489
489
490 return tokens_by_line
490 return tokens_by_line
491
491
492 def show_linewise_tokens(s: str):
492 def show_linewise_tokens(s: str):
493 """For investigation and debugging"""
493 """For investigation and debugging"""
494 if not s.endswith('\n'):
494 if not s.endswith('\n'):
495 s += '\n'
495 s += '\n'
496 lines = s.splitlines(keepends=True)
496 lines = s.splitlines(keepends=True)
497 for line in make_tokens_by_line(lines):
497 for line in make_tokens_by_line(lines):
498 print("Line -------")
498 print("Line -------")
499 for tokinfo in line:
499 for tokinfo in line:
500 print(" ", tokinfo)
500 print(" ", tokinfo)
501
501
502 # Arbitrary limit to prevent getting stuck in infinite loops
502 # Arbitrary limit to prevent getting stuck in infinite loops
503 TRANSFORM_LOOP_LIMIT = 500
503 TRANSFORM_LOOP_LIMIT = 500
504
504
505 class TransformerManager:
505 class TransformerManager:
506 """Applies various transformations to a cell or code block.
506 """Applies various transformations to a cell or code block.
507
507
508 The key methods for external use are ``transform_cell()``
508 The key methods for external use are ``transform_cell()``
509 and ``check_complete()``.
509 and ``check_complete()``.
510 """
510 """
511 def __init__(self):
511 def __init__(self):
512 self.cleanup_transforms = [
512 self.cleanup_transforms = [
513 leading_indent,
513 leading_indent,
514 classic_prompt,
514 classic_prompt,
515 ipython_prompt,
515 ipython_prompt,
516 ]
516 ]
517 self.line_transforms = [
517 self.line_transforms = [
518 cell_magic,
518 cell_magic,
519 ]
519 ]
520 self.token_transformers = [
520 self.token_transformers = [
521 MagicAssign,
521 MagicAssign,
522 SystemAssign,
522 SystemAssign,
523 EscapedCommand,
523 EscapedCommand,
524 HelpEnd,
524 HelpEnd,
525 ]
525 ]
526
526
527 def do_one_token_transform(self, lines):
527 def do_one_token_transform(self, lines):
528 """Find and run the transform earliest in the code.
528 """Find and run the transform earliest in the code.
529
529
530 Returns (changed, lines).
530 Returns (changed, lines).
531
531
532 This method is called repeatedly until changed is False, indicating
532 This method is called repeatedly until changed is False, indicating
533 that all available transformations are complete.
533 that all available transformations are complete.
534
534
535 The tokens following IPython special syntax might not be valid, so
535 The tokens following IPython special syntax might not be valid, so
536 the transformed code is retokenised every time to identify the next
536 the transformed code is retokenised every time to identify the next
537 piece of special syntax. Hopefully long code cells are mostly valid
537 piece of special syntax. Hopefully long code cells are mostly valid
538 Python, not using lots of IPython special syntax, so this shouldn't be
538 Python, not using lots of IPython special syntax, so this shouldn't be
539 a performance issue.
539 a performance issue.
540 """
540 """
541 tokens_by_line = make_tokens_by_line(lines)
541 tokens_by_line = make_tokens_by_line(lines)
542 candidates = []
542 candidates = []
543 for transformer_cls in self.token_transformers:
543 for transformer_cls in self.token_transformers:
544 transformer = transformer_cls.find(tokens_by_line)
544 transformer = transformer_cls.find(tokens_by_line)
545 if transformer:
545 if transformer:
546 candidates.append(transformer)
546 candidates.append(transformer)
547
547
548 if not candidates:
548 if not candidates:
549 # Nothing to transform
549 # Nothing to transform
550 return False, lines
550 return False, lines
551 ordered_transformers = sorted(candidates, key=TokenTransformBase.sortby)
551 ordered_transformers = sorted(candidates, key=TokenTransformBase.sortby)
552 for transformer in ordered_transformers:
552 for transformer in ordered_transformers:
553 try:
553 try:
554 return True, transformer.transform(lines)
554 return True, transformer.transform(lines)
555 except SyntaxError:
555 except SyntaxError:
556 pass
556 pass
557 return False, lines
557 return False, lines
558
558
559 def do_token_transforms(self, lines):
559 def do_token_transforms(self, lines):
560 for _ in range(TRANSFORM_LOOP_LIMIT):
560 for _ in range(TRANSFORM_LOOP_LIMIT):
561 changed, lines = self.do_one_token_transform(lines)
561 changed, lines = self.do_one_token_transform(lines)
562 if not changed:
562 if not changed:
563 return lines
563 return lines
564
564
565 raise RuntimeError("Input transformation still changing after "
565 raise RuntimeError("Input transformation still changing after "
566 "%d iterations. Aborting." % TRANSFORM_LOOP_LIMIT)
566 "%d iterations. Aborting." % TRANSFORM_LOOP_LIMIT)
567
567
568 def transform_cell(self, cell: str) -> str:
568 def transform_cell(self, cell: str) -> str:
569 """Transforms a cell of input code"""
569 """Transforms a cell of input code"""
570 if not cell.endswith('\n'):
570 if not cell.endswith('\n'):
571 cell += '\n' # Ensure the cell has a trailing newline
571 cell += '\n' # Ensure the cell has a trailing newline
572 lines = cell.splitlines(keepends=True)
572 lines = cell.splitlines(keepends=True)
573 for transform in self.cleanup_transforms + self.line_transforms:
573 for transform in self.cleanup_transforms + self.line_transforms:
574 lines = transform(lines)
574 lines = transform(lines)
575
575
576 lines = self.do_token_transforms(lines)
576 lines = self.do_token_transforms(lines)
577 return ''.join(lines)
577 return ''.join(lines)
578
578
579 def check_complete(self, cell: str):
579 def check_complete(self, cell: str):
580 """Return whether a block of code is ready to execute, or should be continued
580 """Return whether a block of code is ready to execute, or should be continued
581
581
582 Parameters
582 Parameters
583 ----------
583 ----------
584 source : string
584 source : string
585 Python input code, which can be multiline.
585 Python input code, which can be multiline.
586
586
587 Returns
587 Returns
588 -------
588 -------
589 status : str
589 status : str
590 One of 'complete', 'incomplete', or 'invalid' if source is not a
590 One of 'complete', 'incomplete', or 'invalid' if source is not a
591 prefix of valid code.
591 prefix of valid code.
592 indent_spaces : int or None
592 indent_spaces : int or None
593 The number of spaces by which to indent the next line of code. If
593 The number of spaces by which to indent the next line of code. If
594 status is not 'incomplete', this is None.
594 status is not 'incomplete', this is None.
595 """
595 """
596 # Remember if the lines ends in a new line.
596 # Remember if the lines ends in a new line.
597 ends_with_newline = False
597 ends_with_newline = False
598 for character in reversed(cell):
598 for character in reversed(cell):
599 if character == '\n':
599 if character == '\n':
600 ends_with_newline = True
600 ends_with_newline = True
601 break
601 break
602 elif character.strip():
602 elif character.strip():
603 break
603 break
604 else:
604 else:
605 continue
605 continue
606
606
607 if not ends_with_newline:
607 if not ends_with_newline:
608 # Append an newline for consistent tokenization
608 # Append an newline for consistent tokenization
609 # See https://bugs.python.org/issue33899
609 # See https://bugs.python.org/issue33899
610 cell += '\n'
610 cell += '\n'
611
611
612 lines = cell.splitlines(keepends=True)
612 lines = cell.splitlines(keepends=True)
613
613
614 if not lines:
614 if not lines:
615 return 'complete', None
615 return 'complete', None
616
616
617 if lines[-1].endswith('\\'):
617 if lines[-1].endswith('\\'):
618 # Explicit backslash continuation
618 # Explicit backslash continuation
619 return 'incomplete', find_last_indent(lines)
619 return 'incomplete', find_last_indent(lines)
620
620
621 try:
621 try:
622 for transform in self.cleanup_transforms:
622 for transform in self.cleanup_transforms:
623 lines = transform(lines)
623 lines = transform(lines)
624 except SyntaxError:
624 except SyntaxError:
625 return 'invalid', None
625 return 'invalid', None
626
626
627 if lines[0].startswith('%%'):
627 if lines[0].startswith('%%'):
628 # Special case for cell magics - completion marked by blank line
628 # Special case for cell magics - completion marked by blank line
629 if lines[-1].strip():
629 if lines[-1].strip():
630 return 'incomplete', find_last_indent(lines)
630 return 'incomplete', find_last_indent(lines)
631 else:
631 else:
632 return 'complete', None
632 return 'complete', None
633
633
634 try:
634 try:
635 for transform in self.line_transforms:
635 for transform in self.line_transforms:
636 lines = transform(lines)
636 lines = transform(lines)
637 lines = self.do_token_transforms(lines)
637 lines = self.do_token_transforms(lines)
638 except SyntaxError:
638 except SyntaxError:
639 return 'invalid', None
639 return 'invalid', None
640
640
641 tokens_by_line = make_tokens_by_line(lines)
641 tokens_by_line = make_tokens_by_line(lines)
642
642
643 if not tokens_by_line:
643 if not tokens_by_line:
644 return 'incomplete', find_last_indent(lines)
644 return 'incomplete', find_last_indent(lines)
645
645
646 if tokens_by_line[-1][-1].type != tokenize.ENDMARKER:
646 if tokens_by_line[-1][-1].type != tokenize.ENDMARKER:
647 # We're in a multiline string or expression
647 # We're in a multiline string or expression
648 return 'incomplete', find_last_indent(lines)
648 return 'incomplete', find_last_indent(lines)
649
649
650 newline_types = {tokenize.NEWLINE, tokenize.COMMENT, tokenize.ENDMARKER}
650 newline_types = {tokenize.NEWLINE, tokenize.COMMENT, tokenize.ENDMARKER}
651
651
652 # Pop the last line which only contains DEDENTs and ENDMARKER
652 # Pop the last line which only contains DEDENTs and ENDMARKER
653 last_token_line = None
653 last_token_line = None
654 if {t.type for t in tokens_by_line[-1]} in [
654 if {t.type for t in tokens_by_line[-1]} in [
655 {tokenize.DEDENT, tokenize.ENDMARKER},
655 {tokenize.DEDENT, tokenize.ENDMARKER},
656 {tokenize.ENDMARKER}
656 {tokenize.ENDMARKER}
657 ] and len(tokens_by_line) > 1:
657 ] and len(tokens_by_line) > 1:
658 last_token_line = tokens_by_line.pop()
658 last_token_line = tokens_by_line.pop()
659
659
660 while tokens_by_line[-1] and tokens_by_line[-1][-1].type in newline_types:
660 while tokens_by_line[-1] and tokens_by_line[-1][-1].type in newline_types:
661 tokens_by_line[-1].pop()
661 tokens_by_line[-1].pop()
662
662
663 if len(tokens_by_line) == 1 and not tokens_by_line[-1]:
663 if len(tokens_by_line) == 1 and not tokens_by_line[-1]:
664 return 'incomplete', 0
664 return 'incomplete', 0
665
665
666 if tokens_by_line[-1][-1].string == ':':
666 if tokens_by_line[-1][-1].string == ':':
667 # The last line starts a block (e.g. 'if foo:')
667 # The last line starts a block (e.g. 'if foo:')
668 ix = 0
668 ix = 0
669 while tokens_by_line[-1][ix].type in {tokenize.INDENT, tokenize.DEDENT}:
669 while tokens_by_line[-1][ix].type in {tokenize.INDENT, tokenize.DEDENT}:
670 ix += 1
670 ix += 1
671
671
672 indent = tokens_by_line[-1][ix].start[1]
672 indent = tokens_by_line[-1][ix].start[1]
673 return 'incomplete', indent + 4
673 return 'incomplete', indent + 4
674
674
675 if tokens_by_line[-1][0].line.endswith('\\'):
675 if tokens_by_line[-1][0].line.endswith('\\'):
676 return 'incomplete', None
676 return 'incomplete', None
677
677
678 # At this point, our checks think the code is complete (or invalid).
678 # At this point, our checks think the code is complete (or invalid).
679 # We'll use codeop.compile_command to check this with the real parser
679 # We'll use codeop.compile_command to check this with the real parser
680 try:
680 try:
681 with warnings.catch_warnings():
681 with warnings.catch_warnings():
682 warnings.simplefilter('error', SyntaxWarning)
682 warnings.simplefilter('error', SyntaxWarning)
683 res = compile_command(''.join(lines), symbol='exec')
683 res = compile_command(''.join(lines), symbol='exec')
684 except (SyntaxError, OverflowError, ValueError, TypeError,
684 except (SyntaxError, OverflowError, ValueError, TypeError,
685 MemoryError, SyntaxWarning):
685 MemoryError, SyntaxWarning):
686 return 'invalid', None
686 return 'invalid', None
687 else:
687 else:
688 if res is None:
688 if res is None:
689 return 'incomplete', find_last_indent(lines)
689 return 'incomplete', find_last_indent(lines)
690
690
691 if last_token_line and last_token_line[0].type == tokenize.DEDENT:
691 if last_token_line and last_token_line[0].type == tokenize.DEDENT:
692 if ends_with_newline:
692 if ends_with_newline:
693 return 'complete', None
693 return 'complete', None
694 return 'incomplete', find_last_indent(lines)
694 return 'incomplete', find_last_indent(lines)
695
695
696 # If there's a blank line at the end, assume we're ready to execute
696 # If there's a blank line at the end, assume we're ready to execute
697 if not lines[-1].strip():
697 if not lines[-1].strip():
698 return 'complete', None
698 return 'complete', None
699
699
700 return 'complete', None
700 return 'complete', None
701
701
702
702
703 def find_last_indent(lines):
703 def find_last_indent(lines):
704 m = _indent_re.match(lines[-1])
704 m = _indent_re.match(lines[-1])
705 if not m:
705 if not m:
706 return 0
706 return 0
707 return len(m.group(0).replace('\t', ' '*4))
707 return len(m.group(0).replace('\t', ' '*4))
@@ -1,702 +1,702 b''
1 """Implementation of namespace-related magic functions.
1 """Implementation of namespace-related magic functions.
2 """
2 """
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (c) 2012 The IPython Development Team.
4 # Copyright (c) 2012 The IPython Development Team.
5 #
5 #
6 # Distributed under the terms of the Modified BSD License.
6 # Distributed under the terms of the Modified BSD License.
7 #
7 #
8 # The full license is in the file COPYING.txt, distributed with this software.
8 # The full license is in the file COPYING.txt, distributed with this software.
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 # Stdlib
15 # Stdlib
16 import gc
16 import gc
17 import re
17 import re
18 import sys
18 import sys
19
19
20 # Our own packages
20 # Our own packages
21 from IPython.core import page
21 from IPython.core import page
22 from IPython.core.error import StdinNotImplementedError, UsageError
22 from IPython.core.error import StdinNotImplementedError, UsageError
23 from IPython.core.magic import Magics, magics_class, line_magic
23 from IPython.core.magic import Magics, magics_class, line_magic
24 from IPython.testing.skipdoctest import skip_doctest
24 from IPython.testing.skipdoctest import skip_doctest
25 from IPython.utils.encoding import DEFAULT_ENCODING
25 from IPython.utils.encoding import DEFAULT_ENCODING
26 from IPython.utils.openpy import read_py_file
26 from IPython.utils.openpy import read_py_file
27 from IPython.utils.path import get_py_filename
27 from IPython.utils.path import get_py_filename
28
28
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30 # Magic implementation classes
30 # Magic implementation classes
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32
32
33 @magics_class
33 @magics_class
34 class NamespaceMagics(Magics):
34 class NamespaceMagics(Magics):
35 """Magics to manage various aspects of the user's namespace.
35 """Magics to manage various aspects of the user's namespace.
36
36
37 These include listing variables, introspecting into them, etc.
37 These include listing variables, introspecting into them, etc.
38 """
38 """
39
39
40 @line_magic
40 @line_magic
41 def pinfo(self, parameter_s='', namespaces=None):
41 def pinfo(self, parameter_s='', namespaces=None):
42 """Provide detailed information about an object.
42 """Provide detailed information about an object.
43
43
44 '%pinfo object' is just a synonym for object? or ?object."""
44 '%pinfo object' is just a synonym for object? or ?object."""
45
45
46 #print 'pinfo par: <%s>' % parameter_s # dbg
46 #print 'pinfo par: <%s>' % parameter_s # dbg
47 # detail_level: 0 -> obj? , 1 -> obj??
47 # detail_level: 0 -> obj? , 1 -> obj??
48 detail_level = 0
48 detail_level = 0
49 # We need to detect if we got called as 'pinfo pinfo foo', which can
49 # We need to detect if we got called as 'pinfo pinfo foo', which can
50 # happen if the user types 'pinfo foo?' at the cmd line.
50 # happen if the user types 'pinfo foo?' at the cmd line.
51 pinfo,qmark1,oname,qmark2 = \
51 pinfo,qmark1,oname,qmark2 = \
52 re.match('(pinfo )?(\?*)(.*?)(\??$)',parameter_s).groups()
52 re.match(r'(pinfo )?(\?*)(.*?)(\??$)',parameter_s).groups()
53 if pinfo or qmark1 or qmark2:
53 if pinfo or qmark1 or qmark2:
54 detail_level = 1
54 detail_level = 1
55 if "*" in oname:
55 if "*" in oname:
56 self.psearch(oname)
56 self.psearch(oname)
57 else:
57 else:
58 self.shell._inspect('pinfo', oname, detail_level=detail_level,
58 self.shell._inspect('pinfo', oname, detail_level=detail_level,
59 namespaces=namespaces)
59 namespaces=namespaces)
60
60
61 @line_magic
61 @line_magic
62 def pinfo2(self, parameter_s='', namespaces=None):
62 def pinfo2(self, parameter_s='', namespaces=None):
63 """Provide extra detailed information about an object.
63 """Provide extra detailed information about an object.
64
64
65 '%pinfo2 object' is just a synonym for object?? or ??object."""
65 '%pinfo2 object' is just a synonym for object?? or ??object."""
66 self.shell._inspect('pinfo', parameter_s, detail_level=1,
66 self.shell._inspect('pinfo', parameter_s, detail_level=1,
67 namespaces=namespaces)
67 namespaces=namespaces)
68
68
69 @skip_doctest
69 @skip_doctest
70 @line_magic
70 @line_magic
71 def pdef(self, parameter_s='', namespaces=None):
71 def pdef(self, parameter_s='', namespaces=None):
72 """Print the call signature for any callable object.
72 """Print the call signature for any callable object.
73
73
74 If the object is a class, print the constructor information.
74 If the object is a class, print the constructor information.
75
75
76 Examples
76 Examples
77 --------
77 --------
78 ::
78 ::
79
79
80 In [3]: %pdef urllib.urlopen
80 In [3]: %pdef urllib.urlopen
81 urllib.urlopen(url, data=None, proxies=None)
81 urllib.urlopen(url, data=None, proxies=None)
82 """
82 """
83 self.shell._inspect('pdef',parameter_s, namespaces)
83 self.shell._inspect('pdef',parameter_s, namespaces)
84
84
85 @line_magic
85 @line_magic
86 def pdoc(self, parameter_s='', namespaces=None):
86 def pdoc(self, parameter_s='', namespaces=None):
87 """Print the docstring for an object.
87 """Print the docstring for an object.
88
88
89 If the given object is a class, it will print both the class and the
89 If the given object is a class, it will print both the class and the
90 constructor docstrings."""
90 constructor docstrings."""
91 self.shell._inspect('pdoc',parameter_s, namespaces)
91 self.shell._inspect('pdoc',parameter_s, namespaces)
92
92
93 @line_magic
93 @line_magic
94 def psource(self, parameter_s='', namespaces=None):
94 def psource(self, parameter_s='', namespaces=None):
95 """Print (or run through pager) the source code for an object."""
95 """Print (or run through pager) the source code for an object."""
96 if not parameter_s:
96 if not parameter_s:
97 raise UsageError('Missing object name.')
97 raise UsageError('Missing object name.')
98 self.shell._inspect('psource',parameter_s, namespaces)
98 self.shell._inspect('psource',parameter_s, namespaces)
99
99
100 @line_magic
100 @line_magic
101 def pfile(self, parameter_s='', namespaces=None):
101 def pfile(self, parameter_s='', namespaces=None):
102 """Print (or run through pager) the file where an object is defined.
102 """Print (or run through pager) the file where an object is defined.
103
103
104 The file opens at the line where the object definition begins. IPython
104 The file opens at the line where the object definition begins. IPython
105 will honor the environment variable PAGER if set, and otherwise will
105 will honor the environment variable PAGER if set, and otherwise will
106 do its best to print the file in a convenient form.
106 do its best to print the file in a convenient form.
107
107
108 If the given argument is not an object currently defined, IPython will
108 If the given argument is not an object currently defined, IPython will
109 try to interpret it as a filename (automatically adding a .py extension
109 try to interpret it as a filename (automatically adding a .py extension
110 if needed). You can thus use %pfile as a syntax highlighting code
110 if needed). You can thus use %pfile as a syntax highlighting code
111 viewer."""
111 viewer."""
112
112
113 # first interpret argument as an object name
113 # first interpret argument as an object name
114 out = self.shell._inspect('pfile',parameter_s, namespaces)
114 out = self.shell._inspect('pfile',parameter_s, namespaces)
115 # if not, try the input as a filename
115 # if not, try the input as a filename
116 if out == 'not found':
116 if out == 'not found':
117 try:
117 try:
118 filename = get_py_filename(parameter_s)
118 filename = get_py_filename(parameter_s)
119 except IOError as msg:
119 except IOError as msg:
120 print(msg)
120 print(msg)
121 return
121 return
122 page.page(self.shell.pycolorize(read_py_file(filename, skip_encoding_cookie=False)))
122 page.page(self.shell.pycolorize(read_py_file(filename, skip_encoding_cookie=False)))
123
123
124 @line_magic
124 @line_magic
125 def psearch(self, parameter_s=''):
125 def psearch(self, parameter_s=''):
126 """Search for object in namespaces by wildcard.
126 """Search for object in namespaces by wildcard.
127
127
128 %psearch [options] PATTERN [OBJECT TYPE]
128 %psearch [options] PATTERN [OBJECT TYPE]
129
129
130 Note: ? can be used as a synonym for %psearch, at the beginning or at
130 Note: ? can be used as a synonym for %psearch, at the beginning or at
131 the end: both a*? and ?a* are equivalent to '%psearch a*'. Still, the
131 the end: both a*? and ?a* are equivalent to '%psearch a*'. Still, the
132 rest of the command line must be unchanged (options come first), so
132 rest of the command line must be unchanged (options come first), so
133 for example the following forms are equivalent
133 for example the following forms are equivalent
134
134
135 %psearch -i a* function
135 %psearch -i a* function
136 -i a* function?
136 -i a* function?
137 ?-i a* function
137 ?-i a* function
138
138
139 Arguments:
139 Arguments:
140
140
141 PATTERN
141 PATTERN
142
142
143 where PATTERN is a string containing * as a wildcard similar to its
143 where PATTERN is a string containing * as a wildcard similar to its
144 use in a shell. The pattern is matched in all namespaces on the
144 use in a shell. The pattern is matched in all namespaces on the
145 search path. By default objects starting with a single _ are not
145 search path. By default objects starting with a single _ are not
146 matched, many IPython generated objects have a single
146 matched, many IPython generated objects have a single
147 underscore. The default is case insensitive matching. Matching is
147 underscore. The default is case insensitive matching. Matching is
148 also done on the attributes of objects and not only on the objects
148 also done on the attributes of objects and not only on the objects
149 in a module.
149 in a module.
150
150
151 [OBJECT TYPE]
151 [OBJECT TYPE]
152
152
153 Is the name of a python type from the types module. The name is
153 Is the name of a python type from the types module. The name is
154 given in lowercase without the ending type, ex. StringType is
154 given in lowercase without the ending type, ex. StringType is
155 written string. By adding a type here only objects matching the
155 written string. By adding a type here only objects matching the
156 given type are matched. Using all here makes the pattern match all
156 given type are matched. Using all here makes the pattern match all
157 types (this is the default).
157 types (this is the default).
158
158
159 Options:
159 Options:
160
160
161 -a: makes the pattern match even objects whose names start with a
161 -a: makes the pattern match even objects whose names start with a
162 single underscore. These names are normally omitted from the
162 single underscore. These names are normally omitted from the
163 search.
163 search.
164
164
165 -i/-c: make the pattern case insensitive/sensitive. If neither of
165 -i/-c: make the pattern case insensitive/sensitive. If neither of
166 these options are given, the default is read from your configuration
166 these options are given, the default is read from your configuration
167 file, with the option ``InteractiveShell.wildcards_case_sensitive``.
167 file, with the option ``InteractiveShell.wildcards_case_sensitive``.
168 If this option is not specified in your configuration file, IPython's
168 If this option is not specified in your configuration file, IPython's
169 internal default is to do a case sensitive search.
169 internal default is to do a case sensitive search.
170
170
171 -e/-s NAMESPACE: exclude/search a given namespace. The pattern you
171 -e/-s NAMESPACE: exclude/search a given namespace. The pattern you
172 specify can be searched in any of the following namespaces:
172 specify can be searched in any of the following namespaces:
173 'builtin', 'user', 'user_global','internal', 'alias', where
173 'builtin', 'user', 'user_global','internal', 'alias', where
174 'builtin' and 'user' are the search defaults. Note that you should
174 'builtin' and 'user' are the search defaults. Note that you should
175 not use quotes when specifying namespaces.
175 not use quotes when specifying namespaces.
176
176
177 'Builtin' contains the python module builtin, 'user' contains all
177 'Builtin' contains the python module builtin, 'user' contains all
178 user data, 'alias' only contain the shell aliases and no python
178 user data, 'alias' only contain the shell aliases and no python
179 objects, 'internal' contains objects used by IPython. The
179 objects, 'internal' contains objects used by IPython. The
180 'user_global' namespace is only used by embedded IPython instances,
180 'user_global' namespace is only used by embedded IPython instances,
181 and it contains module-level globals. You can add namespaces to the
181 and it contains module-level globals. You can add namespaces to the
182 search with -s or exclude them with -e (these options can be given
182 search with -s or exclude them with -e (these options can be given
183 more than once).
183 more than once).
184
184
185 Examples
185 Examples
186 --------
186 --------
187 ::
187 ::
188
188
189 %psearch a* -> objects beginning with an a
189 %psearch a* -> objects beginning with an a
190 %psearch -e builtin a* -> objects NOT in the builtin space starting in a
190 %psearch -e builtin a* -> objects NOT in the builtin space starting in a
191 %psearch a* function -> all functions beginning with an a
191 %psearch a* function -> all functions beginning with an a
192 %psearch re.e* -> objects beginning with an e in module re
192 %psearch re.e* -> objects beginning with an e in module re
193 %psearch r*.e* -> objects that start with e in modules starting in r
193 %psearch r*.e* -> objects that start with e in modules starting in r
194 %psearch r*.* string -> all strings in modules beginning with r
194 %psearch r*.* string -> all strings in modules beginning with r
195
195
196 Case sensitive search::
196 Case sensitive search::
197
197
198 %psearch -c a* list all object beginning with lower case a
198 %psearch -c a* list all object beginning with lower case a
199
199
200 Show objects beginning with a single _::
200 Show objects beginning with a single _::
201
201
202 %psearch -a _* list objects beginning with a single underscore
202 %psearch -a _* list objects beginning with a single underscore
203 """
203 """
204 try:
204 try:
205 parameter_s.encode('ascii')
205 parameter_s.encode('ascii')
206 except UnicodeEncodeError:
206 except UnicodeEncodeError:
207 print('Python identifiers can only contain ascii characters.')
207 print('Python identifiers can only contain ascii characters.')
208 return
208 return
209
209
210 # default namespaces to be searched
210 # default namespaces to be searched
211 def_search = ['user_local', 'user_global', 'builtin']
211 def_search = ['user_local', 'user_global', 'builtin']
212
212
213 # Process options/args
213 # Process options/args
214 opts,args = self.parse_options(parameter_s,'cias:e:',list_all=True)
214 opts,args = self.parse_options(parameter_s,'cias:e:',list_all=True)
215 opt = opts.get
215 opt = opts.get
216 shell = self.shell
216 shell = self.shell
217 psearch = shell.inspector.psearch
217 psearch = shell.inspector.psearch
218
218
219 # select case options
219 # select case options
220 if 'i' in opts:
220 if 'i' in opts:
221 ignore_case = True
221 ignore_case = True
222 elif 'c' in opts:
222 elif 'c' in opts:
223 ignore_case = False
223 ignore_case = False
224 else:
224 else:
225 ignore_case = not shell.wildcards_case_sensitive
225 ignore_case = not shell.wildcards_case_sensitive
226
226
227 # Build list of namespaces to search from user options
227 # Build list of namespaces to search from user options
228 def_search.extend(opt('s',[]))
228 def_search.extend(opt('s',[]))
229 ns_exclude = ns_exclude=opt('e',[])
229 ns_exclude = ns_exclude=opt('e',[])
230 ns_search = [nm for nm in def_search if nm not in ns_exclude]
230 ns_search = [nm for nm in def_search if nm not in ns_exclude]
231
231
232 # Call the actual search
232 # Call the actual search
233 try:
233 try:
234 psearch(args,shell.ns_table,ns_search,
234 psearch(args,shell.ns_table,ns_search,
235 show_all=opt('a'),ignore_case=ignore_case)
235 show_all=opt('a'),ignore_case=ignore_case)
236 except:
236 except:
237 shell.showtraceback()
237 shell.showtraceback()
238
238
239 @skip_doctest
239 @skip_doctest
240 @line_magic
240 @line_magic
241 def who_ls(self, parameter_s=''):
241 def who_ls(self, parameter_s=''):
242 """Return a sorted list of all interactive variables.
242 """Return a sorted list of all interactive variables.
243
243
244 If arguments are given, only variables of types matching these
244 If arguments are given, only variables of types matching these
245 arguments are returned.
245 arguments are returned.
246
246
247 Examples
247 Examples
248 --------
248 --------
249
249
250 Define two variables and list them with who_ls::
250 Define two variables and list them with who_ls::
251
251
252 In [1]: alpha = 123
252 In [1]: alpha = 123
253
253
254 In [2]: beta = 'test'
254 In [2]: beta = 'test'
255
255
256 In [3]: %who_ls
256 In [3]: %who_ls
257 Out[3]: ['alpha', 'beta']
257 Out[3]: ['alpha', 'beta']
258
258
259 In [4]: %who_ls int
259 In [4]: %who_ls int
260 Out[4]: ['alpha']
260 Out[4]: ['alpha']
261
261
262 In [5]: %who_ls str
262 In [5]: %who_ls str
263 Out[5]: ['beta']
263 Out[5]: ['beta']
264 """
264 """
265
265
266 user_ns = self.shell.user_ns
266 user_ns = self.shell.user_ns
267 user_ns_hidden = self.shell.user_ns_hidden
267 user_ns_hidden = self.shell.user_ns_hidden
268 nonmatching = object() # This can never be in user_ns
268 nonmatching = object() # This can never be in user_ns
269 out = [ i for i in user_ns
269 out = [ i for i in user_ns
270 if not i.startswith('_') \
270 if not i.startswith('_') \
271 and (user_ns[i] is not user_ns_hidden.get(i, nonmatching)) ]
271 and (user_ns[i] is not user_ns_hidden.get(i, nonmatching)) ]
272
272
273 typelist = parameter_s.split()
273 typelist = parameter_s.split()
274 if typelist:
274 if typelist:
275 typeset = set(typelist)
275 typeset = set(typelist)
276 out = [i for i in out if type(user_ns[i]).__name__ in typeset]
276 out = [i for i in out if type(user_ns[i]).__name__ in typeset]
277
277
278 out.sort()
278 out.sort()
279 return out
279 return out
280
280
281 @skip_doctest
281 @skip_doctest
282 @line_magic
282 @line_magic
283 def who(self, parameter_s=''):
283 def who(self, parameter_s=''):
284 """Print all interactive variables, with some minimal formatting.
284 """Print all interactive variables, with some minimal formatting.
285
285
286 If any arguments are given, only variables whose type matches one of
286 If any arguments are given, only variables whose type matches one of
287 these are printed. For example::
287 these are printed. For example::
288
288
289 %who function str
289 %who function str
290
290
291 will only list functions and strings, excluding all other types of
291 will only list functions and strings, excluding all other types of
292 variables. To find the proper type names, simply use type(var) at a
292 variables. To find the proper type names, simply use type(var) at a
293 command line to see how python prints type names. For example:
293 command line to see how python prints type names. For example:
294
294
295 ::
295 ::
296
296
297 In [1]: type('hello')\\
297 In [1]: type('hello')\\
298 Out[1]: <type 'str'>
298 Out[1]: <type 'str'>
299
299
300 indicates that the type name for strings is 'str'.
300 indicates that the type name for strings is 'str'.
301
301
302 ``%who`` always excludes executed names loaded through your configuration
302 ``%who`` always excludes executed names loaded through your configuration
303 file and things which are internal to IPython.
303 file and things which are internal to IPython.
304
304
305 This is deliberate, as typically you may load many modules and the
305 This is deliberate, as typically you may load many modules and the
306 purpose of %who is to show you only what you've manually defined.
306 purpose of %who is to show you only what you've manually defined.
307
307
308 Examples
308 Examples
309 --------
309 --------
310
310
311 Define two variables and list them with who::
311 Define two variables and list them with who::
312
312
313 In [1]: alpha = 123
313 In [1]: alpha = 123
314
314
315 In [2]: beta = 'test'
315 In [2]: beta = 'test'
316
316
317 In [3]: %who
317 In [3]: %who
318 alpha beta
318 alpha beta
319
319
320 In [4]: %who int
320 In [4]: %who int
321 alpha
321 alpha
322
322
323 In [5]: %who str
323 In [5]: %who str
324 beta
324 beta
325 """
325 """
326
326
327 varlist = self.who_ls(parameter_s)
327 varlist = self.who_ls(parameter_s)
328 if not varlist:
328 if not varlist:
329 if parameter_s:
329 if parameter_s:
330 print('No variables match your requested type.')
330 print('No variables match your requested type.')
331 else:
331 else:
332 print('Interactive namespace is empty.')
332 print('Interactive namespace is empty.')
333 return
333 return
334
334
335 # if we have variables, move on...
335 # if we have variables, move on...
336 count = 0
336 count = 0
337 for i in varlist:
337 for i in varlist:
338 print(i+'\t', end=' ')
338 print(i+'\t', end=' ')
339 count += 1
339 count += 1
340 if count > 8:
340 if count > 8:
341 count = 0
341 count = 0
342 print()
342 print()
343 print()
343 print()
344
344
345 @skip_doctest
345 @skip_doctest
346 @line_magic
346 @line_magic
347 def whos(self, parameter_s=''):
347 def whos(self, parameter_s=''):
348 """Like %who, but gives some extra information about each variable.
348 """Like %who, but gives some extra information about each variable.
349
349
350 The same type filtering of %who can be applied here.
350 The same type filtering of %who can be applied here.
351
351
352 For all variables, the type is printed. Additionally it prints:
352 For all variables, the type is printed. Additionally it prints:
353
353
354 - For {},[],(): their length.
354 - For {},[],(): their length.
355
355
356 - For numpy arrays, a summary with shape, number of
356 - For numpy arrays, a summary with shape, number of
357 elements, typecode and size in memory.
357 elements, typecode and size in memory.
358
358
359 - Everything else: a string representation, snipping their middle if
359 - Everything else: a string representation, snipping their middle if
360 too long.
360 too long.
361
361
362 Examples
362 Examples
363 --------
363 --------
364
364
365 Define two variables and list them with whos::
365 Define two variables and list them with whos::
366
366
367 In [1]: alpha = 123
367 In [1]: alpha = 123
368
368
369 In [2]: beta = 'test'
369 In [2]: beta = 'test'
370
370
371 In [3]: %whos
371 In [3]: %whos
372 Variable Type Data/Info
372 Variable Type Data/Info
373 --------------------------------
373 --------------------------------
374 alpha int 123
374 alpha int 123
375 beta str test
375 beta str test
376 """
376 """
377
377
378 varnames = self.who_ls(parameter_s)
378 varnames = self.who_ls(parameter_s)
379 if not varnames:
379 if not varnames:
380 if parameter_s:
380 if parameter_s:
381 print('No variables match your requested type.')
381 print('No variables match your requested type.')
382 else:
382 else:
383 print('Interactive namespace is empty.')
383 print('Interactive namespace is empty.')
384 return
384 return
385
385
386 # if we have variables, move on...
386 # if we have variables, move on...
387
387
388 # for these types, show len() instead of data:
388 # for these types, show len() instead of data:
389 seq_types = ['dict', 'list', 'tuple']
389 seq_types = ['dict', 'list', 'tuple']
390
390
391 # for numpy arrays, display summary info
391 # for numpy arrays, display summary info
392 ndarray_type = None
392 ndarray_type = None
393 if 'numpy' in sys.modules:
393 if 'numpy' in sys.modules:
394 try:
394 try:
395 from numpy import ndarray
395 from numpy import ndarray
396 except ImportError:
396 except ImportError:
397 pass
397 pass
398 else:
398 else:
399 ndarray_type = ndarray.__name__
399 ndarray_type = ndarray.__name__
400
400
401 # Find all variable names and types so we can figure out column sizes
401 # Find all variable names and types so we can figure out column sizes
402
402
403 # some types are well known and can be shorter
403 # some types are well known and can be shorter
404 abbrevs = {'IPython.core.macro.Macro' : 'Macro'}
404 abbrevs = {'IPython.core.macro.Macro' : 'Macro'}
405 def type_name(v):
405 def type_name(v):
406 tn = type(v).__name__
406 tn = type(v).__name__
407 return abbrevs.get(tn,tn)
407 return abbrevs.get(tn,tn)
408
408
409 varlist = [self.shell.user_ns[n] for n in varnames]
409 varlist = [self.shell.user_ns[n] for n in varnames]
410
410
411 typelist = []
411 typelist = []
412 for vv in varlist:
412 for vv in varlist:
413 tt = type_name(vv)
413 tt = type_name(vv)
414
414
415 if tt=='instance':
415 if tt=='instance':
416 typelist.append( abbrevs.get(str(vv.__class__),
416 typelist.append( abbrevs.get(str(vv.__class__),
417 str(vv.__class__)))
417 str(vv.__class__)))
418 else:
418 else:
419 typelist.append(tt)
419 typelist.append(tt)
420
420
421 # column labels and # of spaces as separator
421 # column labels and # of spaces as separator
422 varlabel = 'Variable'
422 varlabel = 'Variable'
423 typelabel = 'Type'
423 typelabel = 'Type'
424 datalabel = 'Data/Info'
424 datalabel = 'Data/Info'
425 colsep = 3
425 colsep = 3
426 # variable format strings
426 # variable format strings
427 vformat = "{0:<{varwidth}}{1:<{typewidth}}"
427 vformat = "{0:<{varwidth}}{1:<{typewidth}}"
428 aformat = "%s: %s elems, type `%s`, %s bytes"
428 aformat = "%s: %s elems, type `%s`, %s bytes"
429 # find the size of the columns to format the output nicely
429 # find the size of the columns to format the output nicely
430 varwidth = max(max(map(len,varnames)), len(varlabel)) + colsep
430 varwidth = max(max(map(len,varnames)), len(varlabel)) + colsep
431 typewidth = max(max(map(len,typelist)), len(typelabel)) + colsep
431 typewidth = max(max(map(len,typelist)), len(typelabel)) + colsep
432 # table header
432 # table header
433 print(varlabel.ljust(varwidth) + typelabel.ljust(typewidth) + \
433 print(varlabel.ljust(varwidth) + typelabel.ljust(typewidth) + \
434 ' '+datalabel+'\n' + '-'*(varwidth+typewidth+len(datalabel)+1))
434 ' '+datalabel+'\n' + '-'*(varwidth+typewidth+len(datalabel)+1))
435 # and the table itself
435 # and the table itself
436 kb = 1024
436 kb = 1024
437 Mb = 1048576 # kb**2
437 Mb = 1048576 # kb**2
438 for vname,var,vtype in zip(varnames,varlist,typelist):
438 for vname,var,vtype in zip(varnames,varlist,typelist):
439 print(vformat.format(vname, vtype, varwidth=varwidth, typewidth=typewidth), end=' ')
439 print(vformat.format(vname, vtype, varwidth=varwidth, typewidth=typewidth), end=' ')
440 if vtype in seq_types:
440 if vtype in seq_types:
441 print("n="+str(len(var)))
441 print("n="+str(len(var)))
442 elif vtype == ndarray_type:
442 elif vtype == ndarray_type:
443 vshape = str(var.shape).replace(',','').replace(' ','x')[1:-1]
443 vshape = str(var.shape).replace(',','').replace(' ','x')[1:-1]
444 if vtype==ndarray_type:
444 if vtype==ndarray_type:
445 # numpy
445 # numpy
446 vsize = var.size
446 vsize = var.size
447 vbytes = vsize*var.itemsize
447 vbytes = vsize*var.itemsize
448 vdtype = var.dtype
448 vdtype = var.dtype
449
449
450 if vbytes < 100000:
450 if vbytes < 100000:
451 print(aformat % (vshape, vsize, vdtype, vbytes))
451 print(aformat % (vshape, vsize, vdtype, vbytes))
452 else:
452 else:
453 print(aformat % (vshape, vsize, vdtype, vbytes), end=' ')
453 print(aformat % (vshape, vsize, vdtype, vbytes), end=' ')
454 if vbytes < Mb:
454 if vbytes < Mb:
455 print('(%s kb)' % (vbytes/kb,))
455 print('(%s kb)' % (vbytes/kb,))
456 else:
456 else:
457 print('(%s Mb)' % (vbytes/Mb,))
457 print('(%s Mb)' % (vbytes/Mb,))
458 else:
458 else:
459 try:
459 try:
460 vstr = str(var)
460 vstr = str(var)
461 except UnicodeEncodeError:
461 except UnicodeEncodeError:
462 vstr = var.encode(DEFAULT_ENCODING,
462 vstr = var.encode(DEFAULT_ENCODING,
463 'backslashreplace')
463 'backslashreplace')
464 except:
464 except:
465 vstr = "<object with id %d (str() failed)>" % id(var)
465 vstr = "<object with id %d (str() failed)>" % id(var)
466 vstr = vstr.replace('\n', '\\n')
466 vstr = vstr.replace('\n', '\\n')
467 if len(vstr) < 50:
467 if len(vstr) < 50:
468 print(vstr)
468 print(vstr)
469 else:
469 else:
470 print(vstr[:25] + "<...>" + vstr[-25:])
470 print(vstr[:25] + "<...>" + vstr[-25:])
471
471
472 @line_magic
472 @line_magic
473 def reset(self, parameter_s=''):
473 def reset(self, parameter_s=''):
474 """Resets the namespace by removing all names defined by the user, if
474 """Resets the namespace by removing all names defined by the user, if
475 called without arguments, or by removing some types of objects, such
475 called without arguments, or by removing some types of objects, such
476 as everything currently in IPython's In[] and Out[] containers (see
476 as everything currently in IPython's In[] and Out[] containers (see
477 the parameters for details).
477 the parameters for details).
478
478
479 Parameters
479 Parameters
480 ----------
480 ----------
481 -f : force reset without asking for confirmation.
481 -f : force reset without asking for confirmation.
482
482
483 -s : 'Soft' reset: Only clears your namespace, leaving history intact.
483 -s : 'Soft' reset: Only clears your namespace, leaving history intact.
484 References to objects may be kept. By default (without this option),
484 References to objects may be kept. By default (without this option),
485 we do a 'hard' reset, giving you a new session and removing all
485 we do a 'hard' reset, giving you a new session and removing all
486 references to objects from the current session.
486 references to objects from the current session.
487
487
488 in : reset input history
488 in : reset input history
489
489
490 out : reset output history
490 out : reset output history
491
491
492 dhist : reset directory history
492 dhist : reset directory history
493
493
494 array : reset only variables that are NumPy arrays
494 array : reset only variables that are NumPy arrays
495
495
496 See Also
496 See Also
497 --------
497 --------
498 reset_selective : invoked as ``%reset_selective``
498 reset_selective : invoked as ``%reset_selective``
499
499
500 Examples
500 Examples
501 --------
501 --------
502 ::
502 ::
503
503
504 In [6]: a = 1
504 In [6]: a = 1
505
505
506 In [7]: a
506 In [7]: a
507 Out[7]: 1
507 Out[7]: 1
508
508
509 In [8]: 'a' in _ip.user_ns
509 In [8]: 'a' in _ip.user_ns
510 Out[8]: True
510 Out[8]: True
511
511
512 In [9]: %reset -f
512 In [9]: %reset -f
513
513
514 In [1]: 'a' in _ip.user_ns
514 In [1]: 'a' in _ip.user_ns
515 Out[1]: False
515 Out[1]: False
516
516
517 In [2]: %reset -f in
517 In [2]: %reset -f in
518 Flushing input history
518 Flushing input history
519
519
520 In [3]: %reset -f dhist in
520 In [3]: %reset -f dhist in
521 Flushing directory history
521 Flushing directory history
522 Flushing input history
522 Flushing input history
523
523
524 Notes
524 Notes
525 -----
525 -----
526 Calling this magic from clients that do not implement standard input,
526 Calling this magic from clients that do not implement standard input,
527 such as the ipython notebook interface, will reset the namespace
527 such as the ipython notebook interface, will reset the namespace
528 without confirmation.
528 without confirmation.
529 """
529 """
530 opts, args = self.parse_options(parameter_s,'sf', mode='list')
530 opts, args = self.parse_options(parameter_s,'sf', mode='list')
531 if 'f' in opts:
531 if 'f' in opts:
532 ans = True
532 ans = True
533 else:
533 else:
534 try:
534 try:
535 ans = self.shell.ask_yes_no(
535 ans = self.shell.ask_yes_no(
536 "Once deleted, variables cannot be recovered. Proceed (y/[n])?",
536 "Once deleted, variables cannot be recovered. Proceed (y/[n])?",
537 default='n')
537 default='n')
538 except StdinNotImplementedError:
538 except StdinNotImplementedError:
539 ans = True
539 ans = True
540 if not ans:
540 if not ans:
541 print('Nothing done.')
541 print('Nothing done.')
542 return
542 return
543
543
544 if 's' in opts: # Soft reset
544 if 's' in opts: # Soft reset
545 user_ns = self.shell.user_ns
545 user_ns = self.shell.user_ns
546 for i in self.who_ls():
546 for i in self.who_ls():
547 del(user_ns[i])
547 del(user_ns[i])
548 elif len(args) == 0: # Hard reset
548 elif len(args) == 0: # Hard reset
549 self.shell.reset(new_session = False)
549 self.shell.reset(new_session = False)
550
550
551 # reset in/out/dhist/array: previously extensinions/clearcmd.py
551 # reset in/out/dhist/array: previously extensinions/clearcmd.py
552 ip = self.shell
552 ip = self.shell
553 user_ns = self.shell.user_ns # local lookup, heavily used
553 user_ns = self.shell.user_ns # local lookup, heavily used
554
554
555 for target in args:
555 for target in args:
556 target = target.lower() # make matches case insensitive
556 target = target.lower() # make matches case insensitive
557 if target == 'out':
557 if target == 'out':
558 print("Flushing output cache (%d entries)" % len(user_ns['_oh']))
558 print("Flushing output cache (%d entries)" % len(user_ns['_oh']))
559 self.shell.displayhook.flush()
559 self.shell.displayhook.flush()
560
560
561 elif target == 'in':
561 elif target == 'in':
562 print("Flushing input history")
562 print("Flushing input history")
563 pc = self.shell.displayhook.prompt_count + 1
563 pc = self.shell.displayhook.prompt_count + 1
564 for n in range(1, pc):
564 for n in range(1, pc):
565 key = '_i'+repr(n)
565 key = '_i'+repr(n)
566 user_ns.pop(key,None)
566 user_ns.pop(key,None)
567 user_ns.update(dict(_i=u'',_ii=u'',_iii=u''))
567 user_ns.update(dict(_i=u'',_ii=u'',_iii=u''))
568 hm = ip.history_manager
568 hm = ip.history_manager
569 # don't delete these, as %save and %macro depending on the
569 # don't delete these, as %save and %macro depending on the
570 # length of these lists to be preserved
570 # length of these lists to be preserved
571 hm.input_hist_parsed[:] = [''] * pc
571 hm.input_hist_parsed[:] = [''] * pc
572 hm.input_hist_raw[:] = [''] * pc
572 hm.input_hist_raw[:] = [''] * pc
573 # hm has internal machinery for _i,_ii,_iii, clear it out
573 # hm has internal machinery for _i,_ii,_iii, clear it out
574 hm._i = hm._ii = hm._iii = hm._i00 = u''
574 hm._i = hm._ii = hm._iii = hm._i00 = u''
575
575
576 elif target == 'array':
576 elif target == 'array':
577 # Support cleaning up numpy arrays
577 # Support cleaning up numpy arrays
578 try:
578 try:
579 from numpy import ndarray
579 from numpy import ndarray
580 # This must be done with items and not iteritems because
580 # This must be done with items and not iteritems because
581 # we're going to modify the dict in-place.
581 # we're going to modify the dict in-place.
582 for x,val in list(user_ns.items()):
582 for x,val in list(user_ns.items()):
583 if isinstance(val,ndarray):
583 if isinstance(val,ndarray):
584 del user_ns[x]
584 del user_ns[x]
585 except ImportError:
585 except ImportError:
586 print("reset array only works if Numpy is available.")
586 print("reset array only works if Numpy is available.")
587
587
588 elif target == 'dhist':
588 elif target == 'dhist':
589 print("Flushing directory history")
589 print("Flushing directory history")
590 del user_ns['_dh'][:]
590 del user_ns['_dh'][:]
591
591
592 else:
592 else:
593 print("Don't know how to reset ", end=' ')
593 print("Don't know how to reset ", end=' ')
594 print(target + ", please run `%reset?` for details")
594 print(target + ", please run `%reset?` for details")
595
595
596 gc.collect()
596 gc.collect()
597
597
598 @line_magic
598 @line_magic
599 def reset_selective(self, parameter_s=''):
599 def reset_selective(self, parameter_s=''):
600 """Resets the namespace by removing names defined by the user.
600 """Resets the namespace by removing names defined by the user.
601
601
602 Input/Output history are left around in case you need them.
602 Input/Output history are left around in case you need them.
603
603
604 %reset_selective [-f] regex
604 %reset_selective [-f] regex
605
605
606 No action is taken if regex is not included
606 No action is taken if regex is not included
607
607
608 Options
608 Options
609 -f : force reset without asking for confirmation.
609 -f : force reset without asking for confirmation.
610
610
611 See Also
611 See Also
612 --------
612 --------
613 reset : invoked as ``%reset``
613 reset : invoked as ``%reset``
614
614
615 Examples
615 Examples
616 --------
616 --------
617
617
618 We first fully reset the namespace so your output looks identical to
618 We first fully reset the namespace so your output looks identical to
619 this example for pedagogical reasons; in practice you do not need a
619 this example for pedagogical reasons; in practice you do not need a
620 full reset::
620 full reset::
621
621
622 In [1]: %reset -f
622 In [1]: %reset -f
623
623
624 Now, with a clean namespace we can make a few variables and use
624 Now, with a clean namespace we can make a few variables and use
625 ``%reset_selective`` to only delete names that match our regexp::
625 ``%reset_selective`` to only delete names that match our regexp::
626
626
627 In [2]: a=1; b=2; c=3; b1m=4; b2m=5; b3m=6; b4m=7; b2s=8
627 In [2]: a=1; b=2; c=3; b1m=4; b2m=5; b3m=6; b4m=7; b2s=8
628
628
629 In [3]: who_ls
629 In [3]: who_ls
630 Out[3]: ['a', 'b', 'b1m', 'b2m', 'b2s', 'b3m', 'b4m', 'c']
630 Out[3]: ['a', 'b', 'b1m', 'b2m', 'b2s', 'b3m', 'b4m', 'c']
631
631
632 In [4]: %reset_selective -f b[2-3]m
632 In [4]: %reset_selective -f b[2-3]m
633
633
634 In [5]: who_ls
634 In [5]: who_ls
635 Out[5]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c']
635 Out[5]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c']
636
636
637 In [6]: %reset_selective -f d
637 In [6]: %reset_selective -f d
638
638
639 In [7]: who_ls
639 In [7]: who_ls
640 Out[7]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c']
640 Out[7]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c']
641
641
642 In [8]: %reset_selective -f c
642 In [8]: %reset_selective -f c
643
643
644 In [9]: who_ls
644 In [9]: who_ls
645 Out[9]: ['a', 'b', 'b1m', 'b2s', 'b4m']
645 Out[9]: ['a', 'b', 'b1m', 'b2s', 'b4m']
646
646
647 In [10]: %reset_selective -f b
647 In [10]: %reset_selective -f b
648
648
649 In [11]: who_ls
649 In [11]: who_ls
650 Out[11]: ['a']
650 Out[11]: ['a']
651
651
652 Notes
652 Notes
653 -----
653 -----
654 Calling this magic from clients that do not implement standard input,
654 Calling this magic from clients that do not implement standard input,
655 such as the ipython notebook interface, will reset the namespace
655 such as the ipython notebook interface, will reset the namespace
656 without confirmation.
656 without confirmation.
657 """
657 """
658
658
659 opts, regex = self.parse_options(parameter_s,'f')
659 opts, regex = self.parse_options(parameter_s,'f')
660
660
661 if 'f' in opts:
661 if 'f' in opts:
662 ans = True
662 ans = True
663 else:
663 else:
664 try:
664 try:
665 ans = self.shell.ask_yes_no(
665 ans = self.shell.ask_yes_no(
666 "Once deleted, variables cannot be recovered. Proceed (y/[n])? ",
666 "Once deleted, variables cannot be recovered. Proceed (y/[n])? ",
667 default='n')
667 default='n')
668 except StdinNotImplementedError:
668 except StdinNotImplementedError:
669 ans = True
669 ans = True
670 if not ans:
670 if not ans:
671 print('Nothing done.')
671 print('Nothing done.')
672 return
672 return
673 user_ns = self.shell.user_ns
673 user_ns = self.shell.user_ns
674 if not regex:
674 if not regex:
675 print('No regex pattern specified. Nothing done.')
675 print('No regex pattern specified. Nothing done.')
676 return
676 return
677 else:
677 else:
678 try:
678 try:
679 m = re.compile(regex)
679 m = re.compile(regex)
680 except TypeError:
680 except TypeError:
681 raise TypeError('regex must be a string or compiled pattern')
681 raise TypeError('regex must be a string or compiled pattern')
682 for i in self.who_ls():
682 for i in self.who_ls():
683 if m.search(i):
683 if m.search(i):
684 del(user_ns[i])
684 del(user_ns[i])
685
685
686 @line_magic
686 @line_magic
687 def xdel(self, parameter_s=''):
687 def xdel(self, parameter_s=''):
688 """Delete a variable, trying to clear it from anywhere that
688 """Delete a variable, trying to clear it from anywhere that
689 IPython's machinery has references to it. By default, this uses
689 IPython's machinery has references to it. By default, this uses
690 the identity of the named object in the user namespace to remove
690 the identity of the named object in the user namespace to remove
691 references held under other names. The object is also removed
691 references held under other names. The object is also removed
692 from the output history.
692 from the output history.
693
693
694 Options
694 Options
695 -n : Delete the specified name from all namespaces, without
695 -n : Delete the specified name from all namespaces, without
696 checking their identity.
696 checking their identity.
697 """
697 """
698 opts, varname = self.parse_options(parameter_s,'n')
698 opts, varname = self.parse_options(parameter_s,'n')
699 try:
699 try:
700 self.shell.del_var(varname, ('n' in opts))
700 self.shell.del_var(varname, ('n' in opts))
701 except (NameError, ValueError) as e:
701 except (NameError, ValueError) as e:
702 print(type(e).__name__ +": "+ str(e))
702 print(type(e).__name__ +": "+ str(e))
@@ -1,667 +1,667 b''
1 """Module for interactive demos using IPython.
1 """Module for interactive demos using IPython.
2
2
3 This module implements a few classes for running Python scripts interactively
3 This module implements a few classes for running Python scripts interactively
4 in IPython for demonstrations. With very simple markup (a few tags in
4 in IPython for demonstrations. With very simple markup (a few tags in
5 comments), you can control points where the script stops executing and returns
5 comments), you can control points where the script stops executing and returns
6 control to IPython.
6 control to IPython.
7
7
8
8
9 Provided classes
9 Provided classes
10 ----------------
10 ----------------
11
11
12 The classes are (see their docstrings for further details):
12 The classes are (see their docstrings for further details):
13
13
14 - Demo: pure python demos
14 - Demo: pure python demos
15
15
16 - IPythonDemo: demos with input to be processed by IPython as if it had been
16 - IPythonDemo: demos with input to be processed by IPython as if it had been
17 typed interactively (so magics work, as well as any other special syntax you
17 typed interactively (so magics work, as well as any other special syntax you
18 may have added via input prefilters).
18 may have added via input prefilters).
19
19
20 - LineDemo: single-line version of the Demo class. These demos are executed
20 - LineDemo: single-line version of the Demo class. These demos are executed
21 one line at a time, and require no markup.
21 one line at a time, and require no markup.
22
22
23 - IPythonLineDemo: IPython version of the LineDemo class (the demo is
23 - IPythonLineDemo: IPython version of the LineDemo class (the demo is
24 executed a line at a time, but processed via IPython).
24 executed a line at a time, but processed via IPython).
25
25
26 - ClearMixin: mixin to make Demo classes with less visual clutter. It
26 - ClearMixin: mixin to make Demo classes with less visual clutter. It
27 declares an empty marquee and a pre_cmd that clears the screen before each
27 declares an empty marquee and a pre_cmd that clears the screen before each
28 block (see Subclassing below).
28 block (see Subclassing below).
29
29
30 - ClearDemo, ClearIPDemo: mixin-enabled versions of the Demo and IPythonDemo
30 - ClearDemo, ClearIPDemo: mixin-enabled versions of the Demo and IPythonDemo
31 classes.
31 classes.
32
32
33 Inheritance diagram:
33 Inheritance diagram:
34
34
35 .. inheritance-diagram:: IPython.lib.demo
35 .. inheritance-diagram:: IPython.lib.demo
36 :parts: 3
36 :parts: 3
37
37
38 Subclassing
38 Subclassing
39 -----------
39 -----------
40
40
41 The classes here all include a few methods meant to make customization by
41 The classes here all include a few methods meant to make customization by
42 subclassing more convenient. Their docstrings below have some more details:
42 subclassing more convenient. Their docstrings below have some more details:
43
43
44 - highlight(): format every block and optionally highlight comments and
44 - highlight(): format every block and optionally highlight comments and
45 docstring content.
45 docstring content.
46
46
47 - marquee(): generates a marquee to provide visible on-screen markers at each
47 - marquee(): generates a marquee to provide visible on-screen markers at each
48 block start and end.
48 block start and end.
49
49
50 - pre_cmd(): run right before the execution of each block.
50 - pre_cmd(): run right before the execution of each block.
51
51
52 - post_cmd(): run right after the execution of each block. If the block
52 - post_cmd(): run right after the execution of each block. If the block
53 raises an exception, this is NOT called.
53 raises an exception, this is NOT called.
54
54
55
55
56 Operation
56 Operation
57 ---------
57 ---------
58
58
59 The file is run in its own empty namespace (though you can pass it a string of
59 The file is run in its own empty namespace (though you can pass it a string of
60 arguments as if in a command line environment, and it will see those as
60 arguments as if in a command line environment, and it will see those as
61 sys.argv). But at each stop, the global IPython namespace is updated with the
61 sys.argv). But at each stop, the global IPython namespace is updated with the
62 current internal demo namespace, so you can work interactively with the data
62 current internal demo namespace, so you can work interactively with the data
63 accumulated so far.
63 accumulated so far.
64
64
65 By default, each block of code is printed (with syntax highlighting) before
65 By default, each block of code is printed (with syntax highlighting) before
66 executing it and you have to confirm execution. This is intended to show the
66 executing it and you have to confirm execution. This is intended to show the
67 code to an audience first so you can discuss it, and only proceed with
67 code to an audience first so you can discuss it, and only proceed with
68 execution once you agree. There are a few tags which allow you to modify this
68 execution once you agree. There are a few tags which allow you to modify this
69 behavior.
69 behavior.
70
70
71 The supported tags are:
71 The supported tags are:
72
72
73 # <demo> stop
73 # <demo> stop
74
74
75 Defines block boundaries, the points where IPython stops execution of the
75 Defines block boundaries, the points where IPython stops execution of the
76 file and returns to the interactive prompt.
76 file and returns to the interactive prompt.
77
77
78 You can optionally mark the stop tag with extra dashes before and after the
78 You can optionally mark the stop tag with extra dashes before and after the
79 word 'stop', to help visually distinguish the blocks in a text editor:
79 word 'stop', to help visually distinguish the blocks in a text editor:
80
80
81 # <demo> --- stop ---
81 # <demo> --- stop ---
82
82
83
83
84 # <demo> silent
84 # <demo> silent
85
85
86 Make a block execute silently (and hence automatically). Typically used in
86 Make a block execute silently (and hence automatically). Typically used in
87 cases where you have some boilerplate or initialization code which you need
87 cases where you have some boilerplate or initialization code which you need
88 executed but do not want to be seen in the demo.
88 executed but do not want to be seen in the demo.
89
89
90 # <demo> auto
90 # <demo> auto
91
91
92 Make a block execute automatically, but still being printed. Useful for
92 Make a block execute automatically, but still being printed. Useful for
93 simple code which does not warrant discussion, since it avoids the extra
93 simple code which does not warrant discussion, since it avoids the extra
94 manual confirmation.
94 manual confirmation.
95
95
96 # <demo> auto_all
96 # <demo> auto_all
97
97
98 This tag can _only_ be in the first block, and if given it overrides the
98 This tag can _only_ be in the first block, and if given it overrides the
99 individual auto tags to make the whole demo fully automatic (no block asks
99 individual auto tags to make the whole demo fully automatic (no block asks
100 for confirmation). It can also be given at creation time (or the attribute
100 for confirmation). It can also be given at creation time (or the attribute
101 set later) to override what's in the file.
101 set later) to override what's in the file.
102
102
103 While _any_ python file can be run as a Demo instance, if there are no stop
103 While _any_ python file can be run as a Demo instance, if there are no stop
104 tags the whole file will run in a single block (no different that calling
104 tags the whole file will run in a single block (no different that calling
105 first %pycat and then %run). The minimal markup to make this useful is to
105 first %pycat and then %run). The minimal markup to make this useful is to
106 place a set of stop tags; the other tags are only there to let you fine-tune
106 place a set of stop tags; the other tags are only there to let you fine-tune
107 the execution.
107 the execution.
108
108
109 This is probably best explained with the simple example file below. You can
109 This is probably best explained with the simple example file below. You can
110 copy this into a file named ex_demo.py, and try running it via::
110 copy this into a file named ex_demo.py, and try running it via::
111
111
112 from IPython.lib.demo import Demo
112 from IPython.lib.demo import Demo
113 d = Demo('ex_demo.py')
113 d = Demo('ex_demo.py')
114 d()
114 d()
115
115
116 Each time you call the demo object, it runs the next block. The demo object
116 Each time you call the demo object, it runs the next block. The demo object
117 has a few useful methods for navigation, like again(), edit(), jump(), seek()
117 has a few useful methods for navigation, like again(), edit(), jump(), seek()
118 and back(). It can be reset for a new run via reset() or reloaded from disk
118 and back(). It can be reset for a new run via reset() or reloaded from disk
119 (in case you've edited the source) via reload(). See their docstrings below.
119 (in case you've edited the source) via reload(). See their docstrings below.
120
120
121 Note: To make this simpler to explore, a file called "demo-exercizer.py" has
121 Note: To make this simpler to explore, a file called "demo-exercizer.py" has
122 been added to the "docs/examples/core" directory. Just cd to this directory in
122 been added to the "docs/examples/core" directory. Just cd to this directory in
123 an IPython session, and type::
123 an IPython session, and type::
124
124
125 %run demo-exercizer.py
125 %run demo-exercizer.py
126
126
127 and then follow the directions.
127 and then follow the directions.
128
128
129 Example
129 Example
130 -------
130 -------
131
131
132 The following is a very simple example of a valid demo file.
132 The following is a very simple example of a valid demo file.
133
133
134 ::
134 ::
135
135
136 #################### EXAMPLE DEMO <ex_demo.py> ###############################
136 #################### EXAMPLE DEMO <ex_demo.py> ###############################
137 '''A simple interactive demo to illustrate the use of IPython's Demo class.'''
137 '''A simple interactive demo to illustrate the use of IPython's Demo class.'''
138
138
139 print 'Hello, welcome to an interactive IPython demo.'
139 print 'Hello, welcome to an interactive IPython demo.'
140
140
141 # The mark below defines a block boundary, which is a point where IPython will
141 # The mark below defines a block boundary, which is a point where IPython will
142 # stop execution and return to the interactive prompt. The dashes are actually
142 # stop execution and return to the interactive prompt. The dashes are actually
143 # optional and used only as a visual aid to clearly separate blocks while
143 # optional and used only as a visual aid to clearly separate blocks while
144 # editing the demo code.
144 # editing the demo code.
145 # <demo> stop
145 # <demo> stop
146
146
147 x = 1
147 x = 1
148 y = 2
148 y = 2
149
149
150 # <demo> stop
150 # <demo> stop
151
151
152 # the mark below makes this block as silent
152 # the mark below makes this block as silent
153 # <demo> silent
153 # <demo> silent
154
154
155 print 'This is a silent block, which gets executed but not printed.'
155 print 'This is a silent block, which gets executed but not printed.'
156
156
157 # <demo> stop
157 # <demo> stop
158 # <demo> auto
158 # <demo> auto
159 print 'This is an automatic block.'
159 print 'This is an automatic block.'
160 print 'It is executed without asking for confirmation, but printed.'
160 print 'It is executed without asking for confirmation, but printed.'
161 z = x+y
161 z = x+y
162
162
163 print 'z=',x
163 print 'z=',x
164
164
165 # <demo> stop
165 # <demo> stop
166 # This is just another normal block.
166 # This is just another normal block.
167 print 'z is now:', z
167 print 'z is now:', z
168
168
169 print 'bye!'
169 print 'bye!'
170 ################### END EXAMPLE DEMO <ex_demo.py> ############################
170 ################### END EXAMPLE DEMO <ex_demo.py> ############################
171 """
171 """
172
172
173
173
174 #*****************************************************************************
174 #*****************************************************************************
175 # Copyright (C) 2005-2006 Fernando Perez. <Fernando.Perez@colorado.edu>
175 # Copyright (C) 2005-2006 Fernando Perez. <Fernando.Perez@colorado.edu>
176 #
176 #
177 # Distributed under the terms of the BSD License. The full license is in
177 # Distributed under the terms of the BSD License. The full license is in
178 # the file COPYING, distributed as part of this software.
178 # the file COPYING, distributed as part of this software.
179 #
179 #
180 #*****************************************************************************
180 #*****************************************************************************
181
181
182 import os
182 import os
183 import re
183 import re
184 import shlex
184 import shlex
185 import sys
185 import sys
186 import pygments
186 import pygments
187
187
188 from IPython.utils.text import marquee
188 from IPython.utils.text import marquee
189 from IPython.utils import openpy
189 from IPython.utils import openpy
190 from IPython.utils import py3compat
190 from IPython.utils import py3compat
191 __all__ = ['Demo','IPythonDemo','LineDemo','IPythonLineDemo','DemoError']
191 __all__ = ['Demo','IPythonDemo','LineDemo','IPythonLineDemo','DemoError']
192
192
193 class DemoError(Exception): pass
193 class DemoError(Exception): pass
194
194
195 def re_mark(mark):
195 def re_mark(mark):
196 return re.compile(r'^\s*#\s+<demo>\s+%s\s*$' % mark,re.MULTILINE)
196 return re.compile(r'^\s*#\s+<demo>\s+%s\s*$' % mark,re.MULTILINE)
197
197
198 class Demo(object):
198 class Demo(object):
199
199
200 re_stop = re_mark('-*\s?stop\s?-*')
200 re_stop = re_mark(r'-*\s?stop\s?-*')
201 re_silent = re_mark('silent')
201 re_silent = re_mark('silent')
202 re_auto = re_mark('auto')
202 re_auto = re_mark('auto')
203 re_auto_all = re_mark('auto_all')
203 re_auto_all = re_mark('auto_all')
204
204
205 def __init__(self,src,title='',arg_str='',auto_all=None, format_rst=False,
205 def __init__(self,src,title='',arg_str='',auto_all=None, format_rst=False,
206 formatter='terminal', style='default'):
206 formatter='terminal', style='default'):
207 """Make a new demo object. To run the demo, simply call the object.
207 """Make a new demo object. To run the demo, simply call the object.
208
208
209 See the module docstring for full details and an example (you can use
209 See the module docstring for full details and an example (you can use
210 IPython.Demo? in IPython to see it).
210 IPython.Demo? in IPython to see it).
211
211
212 Inputs:
212 Inputs:
213
213
214 - src is either a file, or file-like object, or a
214 - src is either a file, or file-like object, or a
215 string that can be resolved to a filename.
215 string that can be resolved to a filename.
216
216
217 Optional inputs:
217 Optional inputs:
218
218
219 - title: a string to use as the demo name. Of most use when the demo
219 - title: a string to use as the demo name. Of most use when the demo
220 you are making comes from an object that has no filename, or if you
220 you are making comes from an object that has no filename, or if you
221 want an alternate denotation distinct from the filename.
221 want an alternate denotation distinct from the filename.
222
222
223 - arg_str(''): a string of arguments, internally converted to a list
223 - arg_str(''): a string of arguments, internally converted to a list
224 just like sys.argv, so the demo script can see a similar
224 just like sys.argv, so the demo script can see a similar
225 environment.
225 environment.
226
226
227 - auto_all(None): global flag to run all blocks automatically without
227 - auto_all(None): global flag to run all blocks automatically without
228 confirmation. This attribute overrides the block-level tags and
228 confirmation. This attribute overrides the block-level tags and
229 applies to the whole demo. It is an attribute of the object, and
229 applies to the whole demo. It is an attribute of the object, and
230 can be changed at runtime simply by reassigning it to a boolean
230 can be changed at runtime simply by reassigning it to a boolean
231 value.
231 value.
232
232
233 - format_rst(False): a bool to enable comments and doc strings
233 - format_rst(False): a bool to enable comments and doc strings
234 formatting with pygments rst lexer
234 formatting with pygments rst lexer
235
235
236 - formatter('terminal'): a string of pygments formatter name to be
236 - formatter('terminal'): a string of pygments formatter name to be
237 used. Useful values for terminals: terminal, terminal256,
237 used. Useful values for terminals: terminal, terminal256,
238 terminal16m
238 terminal16m
239
239
240 - style('default'): a string of pygments style name to be used.
240 - style('default'): a string of pygments style name to be used.
241 """
241 """
242 if hasattr(src, "read"):
242 if hasattr(src, "read"):
243 # It seems to be a file or a file-like object
243 # It seems to be a file or a file-like object
244 self.fname = "from a file-like object"
244 self.fname = "from a file-like object"
245 if title == '':
245 if title == '':
246 self.title = "from a file-like object"
246 self.title = "from a file-like object"
247 else:
247 else:
248 self.title = title
248 self.title = title
249 else:
249 else:
250 # Assume it's a string or something that can be converted to one
250 # Assume it's a string or something that can be converted to one
251 self.fname = src
251 self.fname = src
252 if title == '':
252 if title == '':
253 (filepath, filename) = os.path.split(src)
253 (filepath, filename) = os.path.split(src)
254 self.title = filename
254 self.title = filename
255 else:
255 else:
256 self.title = title
256 self.title = title
257 self.sys_argv = [src] + shlex.split(arg_str)
257 self.sys_argv = [src] + shlex.split(arg_str)
258 self.auto_all = auto_all
258 self.auto_all = auto_all
259 self.src = src
259 self.src = src
260
260
261 self.inside_ipython = "get_ipython" in globals()
261 self.inside_ipython = "get_ipython" in globals()
262 if self.inside_ipython:
262 if self.inside_ipython:
263 # get a few things from ipython. While it's a bit ugly design-wise,
263 # get a few things from ipython. While it's a bit ugly design-wise,
264 # it ensures that things like color scheme and the like are always in
264 # it ensures that things like color scheme and the like are always in
265 # sync with the ipython mode being used. This class is only meant to
265 # sync with the ipython mode being used. This class is only meant to
266 # be used inside ipython anyways, so it's OK.
266 # be used inside ipython anyways, so it's OK.
267 ip = get_ipython() # this is in builtins whenever IPython is running
267 ip = get_ipython() # this is in builtins whenever IPython is running
268 self.ip_ns = ip.user_ns
268 self.ip_ns = ip.user_ns
269 self.ip_colorize = ip.pycolorize
269 self.ip_colorize = ip.pycolorize
270 self.ip_showtb = ip.showtraceback
270 self.ip_showtb = ip.showtraceback
271 self.ip_run_cell = ip.run_cell
271 self.ip_run_cell = ip.run_cell
272 self.shell = ip
272 self.shell = ip
273
273
274 self.formatter = pygments.formatters.get_formatter_by_name(formatter,
274 self.formatter = pygments.formatters.get_formatter_by_name(formatter,
275 style=style)
275 style=style)
276 self.python_lexer = pygments.lexers.get_lexer_by_name("py3")
276 self.python_lexer = pygments.lexers.get_lexer_by_name("py3")
277 self.format_rst = format_rst
277 self.format_rst = format_rst
278 if format_rst:
278 if format_rst:
279 self.rst_lexer = pygments.lexers.get_lexer_by_name("rst")
279 self.rst_lexer = pygments.lexers.get_lexer_by_name("rst")
280
280
281 # load user data and initialize data structures
281 # load user data and initialize data structures
282 self.reload()
282 self.reload()
283
283
284 def fload(self):
284 def fload(self):
285 """Load file object."""
285 """Load file object."""
286 # read data and parse into blocks
286 # read data and parse into blocks
287 if hasattr(self, 'fobj') and self.fobj is not None:
287 if hasattr(self, 'fobj') and self.fobj is not None:
288 self.fobj.close()
288 self.fobj.close()
289 if hasattr(self.src, "read"):
289 if hasattr(self.src, "read"):
290 # It seems to be a file or a file-like object
290 # It seems to be a file or a file-like object
291 self.fobj = self.src
291 self.fobj = self.src
292 else:
292 else:
293 # Assume it's a string or something that can be converted to one
293 # Assume it's a string or something that can be converted to one
294 self.fobj = openpy.open(self.fname)
294 self.fobj = openpy.open(self.fname)
295
295
296 def reload(self):
296 def reload(self):
297 """Reload source from disk and initialize state."""
297 """Reload source from disk and initialize state."""
298 self.fload()
298 self.fload()
299
299
300 self.src = "".join(openpy.strip_encoding_cookie(self.fobj))
300 self.src = "".join(openpy.strip_encoding_cookie(self.fobj))
301 src_b = [b.strip() for b in self.re_stop.split(self.src) if b]
301 src_b = [b.strip() for b in self.re_stop.split(self.src) if b]
302 self._silent = [bool(self.re_silent.findall(b)) for b in src_b]
302 self._silent = [bool(self.re_silent.findall(b)) for b in src_b]
303 self._auto = [bool(self.re_auto.findall(b)) for b in src_b]
303 self._auto = [bool(self.re_auto.findall(b)) for b in src_b]
304
304
305 # if auto_all is not given (def. None), we read it from the file
305 # if auto_all is not given (def. None), we read it from the file
306 if self.auto_all is None:
306 if self.auto_all is None:
307 self.auto_all = bool(self.re_auto_all.findall(src_b[0]))
307 self.auto_all = bool(self.re_auto_all.findall(src_b[0]))
308 else:
308 else:
309 self.auto_all = bool(self.auto_all)
309 self.auto_all = bool(self.auto_all)
310
310
311 # Clean the sources from all markup so it doesn't get displayed when
311 # Clean the sources from all markup so it doesn't get displayed when
312 # running the demo
312 # running the demo
313 src_blocks = []
313 src_blocks = []
314 auto_strip = lambda s: self.re_auto.sub('',s)
314 auto_strip = lambda s: self.re_auto.sub('',s)
315 for i,b in enumerate(src_b):
315 for i,b in enumerate(src_b):
316 if self._auto[i]:
316 if self._auto[i]:
317 src_blocks.append(auto_strip(b))
317 src_blocks.append(auto_strip(b))
318 else:
318 else:
319 src_blocks.append(b)
319 src_blocks.append(b)
320 # remove the auto_all marker
320 # remove the auto_all marker
321 src_blocks[0] = self.re_auto_all.sub('',src_blocks[0])
321 src_blocks[0] = self.re_auto_all.sub('',src_blocks[0])
322
322
323 self.nblocks = len(src_blocks)
323 self.nblocks = len(src_blocks)
324 self.src_blocks = src_blocks
324 self.src_blocks = src_blocks
325
325
326 # also build syntax-highlighted source
326 # also build syntax-highlighted source
327 self.src_blocks_colored = list(map(self.highlight,self.src_blocks))
327 self.src_blocks_colored = list(map(self.highlight,self.src_blocks))
328
328
329 # ensure clean namespace and seek offset
329 # ensure clean namespace and seek offset
330 self.reset()
330 self.reset()
331
331
332 def reset(self):
332 def reset(self):
333 """Reset the namespace and seek pointer to restart the demo"""
333 """Reset the namespace and seek pointer to restart the demo"""
334 self.user_ns = {}
334 self.user_ns = {}
335 self.finished = False
335 self.finished = False
336 self.block_index = 0
336 self.block_index = 0
337
337
338 def _validate_index(self,index):
338 def _validate_index(self,index):
339 if index<0 or index>=self.nblocks:
339 if index<0 or index>=self.nblocks:
340 raise ValueError('invalid block index %s' % index)
340 raise ValueError('invalid block index %s' % index)
341
341
342 def _get_index(self,index):
342 def _get_index(self,index):
343 """Get the current block index, validating and checking status.
343 """Get the current block index, validating and checking status.
344
344
345 Returns None if the demo is finished"""
345 Returns None if the demo is finished"""
346
346
347 if index is None:
347 if index is None:
348 if self.finished:
348 if self.finished:
349 print('Demo finished. Use <demo_name>.reset() if you want to rerun it.')
349 print('Demo finished. Use <demo_name>.reset() if you want to rerun it.')
350 return None
350 return None
351 index = self.block_index
351 index = self.block_index
352 else:
352 else:
353 self._validate_index(index)
353 self._validate_index(index)
354 return index
354 return index
355
355
356 def seek(self,index):
356 def seek(self,index):
357 """Move the current seek pointer to the given block.
357 """Move the current seek pointer to the given block.
358
358
359 You can use negative indices to seek from the end, with identical
359 You can use negative indices to seek from the end, with identical
360 semantics to those of Python lists."""
360 semantics to those of Python lists."""
361 if index<0:
361 if index<0:
362 index = self.nblocks + index
362 index = self.nblocks + index
363 self._validate_index(index)
363 self._validate_index(index)
364 self.block_index = index
364 self.block_index = index
365 self.finished = False
365 self.finished = False
366
366
367 def back(self,num=1):
367 def back(self,num=1):
368 """Move the seek pointer back num blocks (default is 1)."""
368 """Move the seek pointer back num blocks (default is 1)."""
369 self.seek(self.block_index-num)
369 self.seek(self.block_index-num)
370
370
371 def jump(self,num=1):
371 def jump(self,num=1):
372 """Jump a given number of blocks relative to the current one.
372 """Jump a given number of blocks relative to the current one.
373
373
374 The offset can be positive or negative, defaults to 1."""
374 The offset can be positive or negative, defaults to 1."""
375 self.seek(self.block_index+num)
375 self.seek(self.block_index+num)
376
376
377 def again(self):
377 def again(self):
378 """Move the seek pointer back one block and re-execute."""
378 """Move the seek pointer back one block and re-execute."""
379 self.back(1)
379 self.back(1)
380 self()
380 self()
381
381
382 def edit(self,index=None):
382 def edit(self,index=None):
383 """Edit a block.
383 """Edit a block.
384
384
385 If no number is given, use the last block executed.
385 If no number is given, use the last block executed.
386
386
387 This edits the in-memory copy of the demo, it does NOT modify the
387 This edits the in-memory copy of the demo, it does NOT modify the
388 original source file. If you want to do that, simply open the file in
388 original source file. If you want to do that, simply open the file in
389 an editor and use reload() when you make changes to the file. This
389 an editor and use reload() when you make changes to the file. This
390 method is meant to let you change a block during a demonstration for
390 method is meant to let you change a block during a demonstration for
391 explanatory purposes, without damaging your original script."""
391 explanatory purposes, without damaging your original script."""
392
392
393 index = self._get_index(index)
393 index = self._get_index(index)
394 if index is None:
394 if index is None:
395 return
395 return
396 # decrease the index by one (unless we're at the very beginning), so
396 # decrease the index by one (unless we're at the very beginning), so
397 # that the default demo.edit() call opens up the sblock we've last run
397 # that the default demo.edit() call opens up the sblock we've last run
398 if index>0:
398 if index>0:
399 index -= 1
399 index -= 1
400
400
401 filename = self.shell.mktempfile(self.src_blocks[index])
401 filename = self.shell.mktempfile(self.src_blocks[index])
402 self.shell.hooks.editor(filename,1)
402 self.shell.hooks.editor(filename,1)
403 with open(filename, 'r') as f:
403 with open(filename, 'r') as f:
404 new_block = f.read()
404 new_block = f.read()
405 # update the source and colored block
405 # update the source and colored block
406 self.src_blocks[index] = new_block
406 self.src_blocks[index] = new_block
407 self.src_blocks_colored[index] = self.highlight(new_block)
407 self.src_blocks_colored[index] = self.highlight(new_block)
408 self.block_index = index
408 self.block_index = index
409 # call to run with the newly edited index
409 # call to run with the newly edited index
410 self()
410 self()
411
411
412 def show(self,index=None):
412 def show(self,index=None):
413 """Show a single block on screen"""
413 """Show a single block on screen"""
414
414
415 index = self._get_index(index)
415 index = self._get_index(index)
416 if index is None:
416 if index is None:
417 return
417 return
418
418
419 print(self.marquee('<%s> block # %s (%s remaining)' %
419 print(self.marquee('<%s> block # %s (%s remaining)' %
420 (self.title,index,self.nblocks-index-1)))
420 (self.title,index,self.nblocks-index-1)))
421 print(self.src_blocks_colored[index])
421 print(self.src_blocks_colored[index])
422 sys.stdout.flush()
422 sys.stdout.flush()
423
423
424 def show_all(self):
424 def show_all(self):
425 """Show entire demo on screen, block by block"""
425 """Show entire demo on screen, block by block"""
426
426
427 fname = self.title
427 fname = self.title
428 title = self.title
428 title = self.title
429 nblocks = self.nblocks
429 nblocks = self.nblocks
430 silent = self._silent
430 silent = self._silent
431 marquee = self.marquee
431 marquee = self.marquee
432 for index,block in enumerate(self.src_blocks_colored):
432 for index,block in enumerate(self.src_blocks_colored):
433 if silent[index]:
433 if silent[index]:
434 print(marquee('<%s> SILENT block # %s (%s remaining)' %
434 print(marquee('<%s> SILENT block # %s (%s remaining)' %
435 (title,index,nblocks-index-1)))
435 (title,index,nblocks-index-1)))
436 else:
436 else:
437 print(marquee('<%s> block # %s (%s remaining)' %
437 print(marquee('<%s> block # %s (%s remaining)' %
438 (title,index,nblocks-index-1)))
438 (title,index,nblocks-index-1)))
439 print(block, end=' ')
439 print(block, end=' ')
440 sys.stdout.flush()
440 sys.stdout.flush()
441
441
442 def run_cell(self,source):
442 def run_cell(self,source):
443 """Execute a string with one or more lines of code"""
443 """Execute a string with one or more lines of code"""
444
444
445 exec(source, self.user_ns)
445 exec(source, self.user_ns)
446
446
447 def __call__(self,index=None):
447 def __call__(self,index=None):
448 """run a block of the demo.
448 """run a block of the demo.
449
449
450 If index is given, it should be an integer >=1 and <= nblocks. This
450 If index is given, it should be an integer >=1 and <= nblocks. This
451 means that the calling convention is one off from typical Python
451 means that the calling convention is one off from typical Python
452 lists. The reason for the inconsistency is that the demo always
452 lists. The reason for the inconsistency is that the demo always
453 prints 'Block n/N, and N is the total, so it would be very odd to use
453 prints 'Block n/N, and N is the total, so it would be very odd to use
454 zero-indexing here."""
454 zero-indexing here."""
455
455
456 index = self._get_index(index)
456 index = self._get_index(index)
457 if index is None:
457 if index is None:
458 return
458 return
459 try:
459 try:
460 marquee = self.marquee
460 marquee = self.marquee
461 next_block = self.src_blocks[index]
461 next_block = self.src_blocks[index]
462 self.block_index += 1
462 self.block_index += 1
463 if self._silent[index]:
463 if self._silent[index]:
464 print(marquee('Executing silent block # %s (%s remaining)' %
464 print(marquee('Executing silent block # %s (%s remaining)' %
465 (index,self.nblocks-index-1)))
465 (index,self.nblocks-index-1)))
466 else:
466 else:
467 self.pre_cmd()
467 self.pre_cmd()
468 self.show(index)
468 self.show(index)
469 if self.auto_all or self._auto[index]:
469 if self.auto_all or self._auto[index]:
470 print(marquee('output:'))
470 print(marquee('output:'))
471 else:
471 else:
472 print(marquee('Press <q> to quit, <Enter> to execute...'), end=' ')
472 print(marquee('Press <q> to quit, <Enter> to execute...'), end=' ')
473 ans = py3compat.input().strip()
473 ans = py3compat.input().strip()
474 if ans:
474 if ans:
475 print(marquee('Block NOT executed'))
475 print(marquee('Block NOT executed'))
476 return
476 return
477 try:
477 try:
478 save_argv = sys.argv
478 save_argv = sys.argv
479 sys.argv = self.sys_argv
479 sys.argv = self.sys_argv
480 self.run_cell(next_block)
480 self.run_cell(next_block)
481 self.post_cmd()
481 self.post_cmd()
482 finally:
482 finally:
483 sys.argv = save_argv
483 sys.argv = save_argv
484
484
485 except:
485 except:
486 if self.inside_ipython:
486 if self.inside_ipython:
487 self.ip_showtb(filename=self.fname)
487 self.ip_showtb(filename=self.fname)
488 else:
488 else:
489 if self.inside_ipython:
489 if self.inside_ipython:
490 self.ip_ns.update(self.user_ns)
490 self.ip_ns.update(self.user_ns)
491
491
492 if self.block_index == self.nblocks:
492 if self.block_index == self.nblocks:
493 mq1 = self.marquee('END OF DEMO')
493 mq1 = self.marquee('END OF DEMO')
494 if mq1:
494 if mq1:
495 # avoid spurious print if empty marquees are used
495 # avoid spurious print if empty marquees are used
496 print()
496 print()
497 print(mq1)
497 print(mq1)
498 print(self.marquee('Use <demo_name>.reset() if you want to rerun it.'))
498 print(self.marquee('Use <demo_name>.reset() if you want to rerun it.'))
499 self.finished = True
499 self.finished = True
500
500
501 # These methods are meant to be overridden by subclasses who may wish to
501 # These methods are meant to be overridden by subclasses who may wish to
502 # customize the behavior of of their demos.
502 # customize the behavior of of their demos.
503 def marquee(self,txt='',width=78,mark='*'):
503 def marquee(self,txt='',width=78,mark='*'):
504 """Return the input string centered in a 'marquee'."""
504 """Return the input string centered in a 'marquee'."""
505 return marquee(txt,width,mark)
505 return marquee(txt,width,mark)
506
506
507 def pre_cmd(self):
507 def pre_cmd(self):
508 """Method called before executing each block."""
508 """Method called before executing each block."""
509 pass
509 pass
510
510
511 def post_cmd(self):
511 def post_cmd(self):
512 """Method called after executing each block."""
512 """Method called after executing each block."""
513 pass
513 pass
514
514
515 def highlight(self, block):
515 def highlight(self, block):
516 """Method called on each block to highlight it content"""
516 """Method called on each block to highlight it content"""
517 tokens = pygments.lex(block, self.python_lexer)
517 tokens = pygments.lex(block, self.python_lexer)
518 if self.format_rst:
518 if self.format_rst:
519 from pygments.token import Token
519 from pygments.token import Token
520 toks = []
520 toks = []
521 for token in tokens:
521 for token in tokens:
522 if token[0] == Token.String.Doc and len(token[1]) > 6:
522 if token[0] == Token.String.Doc and len(token[1]) > 6:
523 toks += pygments.lex(token[1][:3], self.python_lexer)
523 toks += pygments.lex(token[1][:3], self.python_lexer)
524 # parse doc string content by rst lexer
524 # parse doc string content by rst lexer
525 toks += pygments.lex(token[1][3:-3], self.rst_lexer)
525 toks += pygments.lex(token[1][3:-3], self.rst_lexer)
526 toks += pygments.lex(token[1][-3:], self.python_lexer)
526 toks += pygments.lex(token[1][-3:], self.python_lexer)
527 elif token[0] == Token.Comment.Single:
527 elif token[0] == Token.Comment.Single:
528 toks.append((Token.Comment.Single, token[1][0]))
528 toks.append((Token.Comment.Single, token[1][0]))
529 # parse comment content by rst lexer
529 # parse comment content by rst lexer
530 # remove the extrat newline added by rst lexer
530 # remove the extrat newline added by rst lexer
531 toks += list(pygments.lex(token[1][1:], self.rst_lexer))[:-1]
531 toks += list(pygments.lex(token[1][1:], self.rst_lexer))[:-1]
532 else:
532 else:
533 toks.append(token)
533 toks.append(token)
534 tokens = toks
534 tokens = toks
535 return pygments.format(tokens, self.formatter)
535 return pygments.format(tokens, self.formatter)
536
536
537
537
538 class IPythonDemo(Demo):
538 class IPythonDemo(Demo):
539 """Class for interactive demos with IPython's input processing applied.
539 """Class for interactive demos with IPython's input processing applied.
540
540
541 This subclasses Demo, but instead of executing each block by the Python
541 This subclasses Demo, but instead of executing each block by the Python
542 interpreter (via exec), it actually calls IPython on it, so that any input
542 interpreter (via exec), it actually calls IPython on it, so that any input
543 filters which may be in place are applied to the input block.
543 filters which may be in place are applied to the input block.
544
544
545 If you have an interactive environment which exposes special input
545 If you have an interactive environment which exposes special input
546 processing, you can use this class instead to write demo scripts which
546 processing, you can use this class instead to write demo scripts which
547 operate exactly as if you had typed them interactively. The default Demo
547 operate exactly as if you had typed them interactively. The default Demo
548 class requires the input to be valid, pure Python code.
548 class requires the input to be valid, pure Python code.
549 """
549 """
550
550
551 def run_cell(self,source):
551 def run_cell(self,source):
552 """Execute a string with one or more lines of code"""
552 """Execute a string with one or more lines of code"""
553
553
554 self.shell.run_cell(source)
554 self.shell.run_cell(source)
555
555
556 class LineDemo(Demo):
556 class LineDemo(Demo):
557 """Demo where each line is executed as a separate block.
557 """Demo where each line is executed as a separate block.
558
558
559 The input script should be valid Python code.
559 The input script should be valid Python code.
560
560
561 This class doesn't require any markup at all, and it's meant for simple
561 This class doesn't require any markup at all, and it's meant for simple
562 scripts (with no nesting or any kind of indentation) which consist of
562 scripts (with no nesting or any kind of indentation) which consist of
563 multiple lines of input to be executed, one at a time, as if they had been
563 multiple lines of input to be executed, one at a time, as if they had been
564 typed in the interactive prompt.
564 typed in the interactive prompt.
565
565
566 Note: the input can not have *any* indentation, which means that only
566 Note: the input can not have *any* indentation, which means that only
567 single-lines of input are accepted, not even function definitions are
567 single-lines of input are accepted, not even function definitions are
568 valid."""
568 valid."""
569
569
570 def reload(self):
570 def reload(self):
571 """Reload source from disk and initialize state."""
571 """Reload source from disk and initialize state."""
572 # read data and parse into blocks
572 # read data and parse into blocks
573 self.fload()
573 self.fload()
574 lines = self.fobj.readlines()
574 lines = self.fobj.readlines()
575 src_b = [l for l in lines if l.strip()]
575 src_b = [l for l in lines if l.strip()]
576 nblocks = len(src_b)
576 nblocks = len(src_b)
577 self.src = ''.join(lines)
577 self.src = ''.join(lines)
578 self._silent = [False]*nblocks
578 self._silent = [False]*nblocks
579 self._auto = [True]*nblocks
579 self._auto = [True]*nblocks
580 self.auto_all = True
580 self.auto_all = True
581 self.nblocks = nblocks
581 self.nblocks = nblocks
582 self.src_blocks = src_b
582 self.src_blocks = src_b
583
583
584 # also build syntax-highlighted source
584 # also build syntax-highlighted source
585 self.src_blocks_colored = list(map(self.highlight,self.src_blocks))
585 self.src_blocks_colored = list(map(self.highlight,self.src_blocks))
586
586
587 # ensure clean namespace and seek offset
587 # ensure clean namespace and seek offset
588 self.reset()
588 self.reset()
589
589
590
590
591 class IPythonLineDemo(IPythonDemo,LineDemo):
591 class IPythonLineDemo(IPythonDemo,LineDemo):
592 """Variant of the LineDemo class whose input is processed by IPython."""
592 """Variant of the LineDemo class whose input is processed by IPython."""
593 pass
593 pass
594
594
595
595
596 class ClearMixin(object):
596 class ClearMixin(object):
597 """Use this mixin to make Demo classes with less visual clutter.
597 """Use this mixin to make Demo classes with less visual clutter.
598
598
599 Demos using this mixin will clear the screen before every block and use
599 Demos using this mixin will clear the screen before every block and use
600 blank marquees.
600 blank marquees.
601
601
602 Note that in order for the methods defined here to actually override those
602 Note that in order for the methods defined here to actually override those
603 of the classes it's mixed with, it must go /first/ in the inheritance
603 of the classes it's mixed with, it must go /first/ in the inheritance
604 tree. For example:
604 tree. For example:
605
605
606 class ClearIPDemo(ClearMixin,IPythonDemo): pass
606 class ClearIPDemo(ClearMixin,IPythonDemo): pass
607
607
608 will provide an IPythonDemo class with the mixin's features.
608 will provide an IPythonDemo class with the mixin's features.
609 """
609 """
610
610
611 def marquee(self,txt='',width=78,mark='*'):
611 def marquee(self,txt='',width=78,mark='*'):
612 """Blank marquee that returns '' no matter what the input."""
612 """Blank marquee that returns '' no matter what the input."""
613 return ''
613 return ''
614
614
615 def pre_cmd(self):
615 def pre_cmd(self):
616 """Method called before executing each block.
616 """Method called before executing each block.
617
617
618 This one simply clears the screen."""
618 This one simply clears the screen."""
619 from IPython.utils.terminal import _term_clear
619 from IPython.utils.terminal import _term_clear
620 _term_clear()
620 _term_clear()
621
621
622 class ClearDemo(ClearMixin,Demo):
622 class ClearDemo(ClearMixin,Demo):
623 pass
623 pass
624
624
625
625
626 class ClearIPDemo(ClearMixin,IPythonDemo):
626 class ClearIPDemo(ClearMixin,IPythonDemo):
627 pass
627 pass
628
628
629
629
630 def slide(file_path, noclear=False, format_rst=True, formatter="terminal",
630 def slide(file_path, noclear=False, format_rst=True, formatter="terminal",
631 style="native", auto_all=False, delimiter='...'):
631 style="native", auto_all=False, delimiter='...'):
632 if noclear:
632 if noclear:
633 demo_class = Demo
633 demo_class = Demo
634 else:
634 else:
635 demo_class = ClearDemo
635 demo_class = ClearDemo
636 demo = demo_class(file_path, format_rst=format_rst, formatter=formatter,
636 demo = demo_class(file_path, format_rst=format_rst, formatter=formatter,
637 style=style, auto_all=auto_all)
637 style=style, auto_all=auto_all)
638 while not demo.finished:
638 while not demo.finished:
639 demo()
639 demo()
640 try:
640 try:
641 py3compat.input('\n' + delimiter)
641 py3compat.input('\n' + delimiter)
642 except KeyboardInterrupt:
642 except KeyboardInterrupt:
643 exit(1)
643 exit(1)
644
644
645 if __name__ == '__main__':
645 if __name__ == '__main__':
646 import argparse
646 import argparse
647 parser = argparse.ArgumentParser(description='Run python demos')
647 parser = argparse.ArgumentParser(description='Run python demos')
648 parser.add_argument('--noclear', '-C', action='store_true',
648 parser.add_argument('--noclear', '-C', action='store_true',
649 help='Do not clear terminal on each slide')
649 help='Do not clear terminal on each slide')
650 parser.add_argument('--rst', '-r', action='store_true',
650 parser.add_argument('--rst', '-r', action='store_true',
651 help='Highlight comments and dostrings as rst')
651 help='Highlight comments and dostrings as rst')
652 parser.add_argument('--formatter', '-f', default='terminal',
652 parser.add_argument('--formatter', '-f', default='terminal',
653 help='pygments formatter name could be: terminal, '
653 help='pygments formatter name could be: terminal, '
654 'terminal256, terminal16m')
654 'terminal256, terminal16m')
655 parser.add_argument('--style', '-s', default='default',
655 parser.add_argument('--style', '-s', default='default',
656 help='pygments style name')
656 help='pygments style name')
657 parser.add_argument('--auto', '-a', action='store_true',
657 parser.add_argument('--auto', '-a', action='store_true',
658 help='Run all blocks automatically without'
658 help='Run all blocks automatically without'
659 'confirmation')
659 'confirmation')
660 parser.add_argument('--delimiter', '-d', default='...',
660 parser.add_argument('--delimiter', '-d', default='...',
661 help='slides delimiter added after each slide run')
661 help='slides delimiter added after each slide run')
662 parser.add_argument('file', nargs=1,
662 parser.add_argument('file', nargs=1,
663 help='python demo file')
663 help='python demo file')
664 args = parser.parse_args()
664 args = parser.parse_args()
665 slide(args.file[0], noclear=args.noclear, format_rst=args.rst,
665 slide(args.file[0], noclear=args.noclear, format_rst=args.rst,
666 formatter=args.formatter, style=args.style, auto_all=args.auto,
666 formatter=args.formatter, style=args.style, auto_all=args.auto,
667 delimiter=args.delimiter)
667 delimiter=args.delimiter)
@@ -1,201 +1,201 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tools for handling LaTeX."""
2 """Tools for handling LaTeX."""
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 from io import BytesIO, open
7 from io import BytesIO, open
8 import os
8 import os
9 import tempfile
9 import tempfile
10 import shutil
10 import shutil
11 import subprocess
11 import subprocess
12 from base64 import encodebytes
12 from base64 import encodebytes
13
13
14 from IPython.utils.process import find_cmd, FindCmdError
14 from IPython.utils.process import find_cmd, FindCmdError
15 from traitlets.config import get_config
15 from traitlets.config import get_config
16 from traitlets.config.configurable import SingletonConfigurable
16 from traitlets.config.configurable import SingletonConfigurable
17 from traitlets import List, Bool, Unicode
17 from traitlets import List, Bool, Unicode
18 from IPython.utils.py3compat import cast_unicode
18 from IPython.utils.py3compat import cast_unicode
19
19
20
20
21 class LaTeXTool(SingletonConfigurable):
21 class LaTeXTool(SingletonConfigurable):
22 """An object to store configuration of the LaTeX tool."""
22 """An object to store configuration of the LaTeX tool."""
23 def _config_default(self):
23 def _config_default(self):
24 return get_config()
24 return get_config()
25
25
26 backends = List(
26 backends = List(
27 Unicode(), ["matplotlib", "dvipng"],
27 Unicode(), ["matplotlib", "dvipng"],
28 help="Preferred backend to draw LaTeX math equations. "
28 help="Preferred backend to draw LaTeX math equations. "
29 "Backends in the list are checked one by one and the first "
29 "Backends in the list are checked one by one and the first "
30 "usable one is used. Note that `matplotlib` backend "
30 "usable one is used. Note that `matplotlib` backend "
31 "is usable only for inline style equations. To draw "
31 "is usable only for inline style equations. To draw "
32 "display style equations, `dvipng` backend must be specified. ",
32 "display style equations, `dvipng` backend must be specified. ",
33 # It is a List instead of Enum, to make configuration more
33 # It is a List instead of Enum, to make configuration more
34 # flexible. For example, to use matplotlib mainly but dvipng
34 # flexible. For example, to use matplotlib mainly but dvipng
35 # for display style, the default ["matplotlib", "dvipng"] can
35 # for display style, the default ["matplotlib", "dvipng"] can
36 # be used. To NOT use dvipng so that other repr such as
36 # be used. To NOT use dvipng so that other repr such as
37 # unicode pretty printing is used, you can use ["matplotlib"].
37 # unicode pretty printing is used, you can use ["matplotlib"].
38 ).tag(config=True)
38 ).tag(config=True)
39
39
40 use_breqn = Bool(
40 use_breqn = Bool(
41 True,
41 True,
42 help="Use breqn.sty to automatically break long equations. "
42 help="Use breqn.sty to automatically break long equations. "
43 "This configuration takes effect only for dvipng backend.",
43 "This configuration takes effect only for dvipng backend.",
44 ).tag(config=True)
44 ).tag(config=True)
45
45
46 packages = List(
46 packages = List(
47 ['amsmath', 'amsthm', 'amssymb', 'bm'],
47 ['amsmath', 'amsthm', 'amssymb', 'bm'],
48 help="A list of packages to use for dvipng backend. "
48 help="A list of packages to use for dvipng backend. "
49 "'breqn' will be automatically appended when use_breqn=True.",
49 "'breqn' will be automatically appended when use_breqn=True.",
50 ).tag(config=True)
50 ).tag(config=True)
51
51
52 preamble = Unicode(
52 preamble = Unicode(
53 help="Additional preamble to use when generating LaTeX source "
53 help="Additional preamble to use when generating LaTeX source "
54 "for dvipng backend.",
54 "for dvipng backend.",
55 ).tag(config=True)
55 ).tag(config=True)
56
56
57
57
58 def latex_to_png(s, encode=False, backend=None, wrap=False):
58 def latex_to_png(s, encode=False, backend=None, wrap=False):
59 """Render a LaTeX string to PNG.
59 """Render a LaTeX string to PNG.
60
60
61 Parameters
61 Parameters
62 ----------
62 ----------
63 s : str
63 s : str
64 The raw string containing valid inline LaTeX.
64 The raw string containing valid inline LaTeX.
65 encode : bool, optional
65 encode : bool, optional
66 Should the PNG data base64 encoded to make it JSON'able.
66 Should the PNG data base64 encoded to make it JSON'able.
67 backend : {matplotlib, dvipng}
67 backend : {matplotlib, dvipng}
68 Backend for producing PNG data.
68 Backend for producing PNG data.
69 wrap : bool
69 wrap : bool
70 If true, Automatically wrap `s` as a LaTeX equation.
70 If true, Automatically wrap `s` as a LaTeX equation.
71
71
72 None is returned when the backend cannot be used.
72 None is returned when the backend cannot be used.
73
73
74 """
74 """
75 s = cast_unicode(s)
75 s = cast_unicode(s)
76 allowed_backends = LaTeXTool.instance().backends
76 allowed_backends = LaTeXTool.instance().backends
77 if backend is None:
77 if backend is None:
78 backend = allowed_backends[0]
78 backend = allowed_backends[0]
79 if backend not in allowed_backends:
79 if backend not in allowed_backends:
80 return None
80 return None
81 if backend == 'matplotlib':
81 if backend == 'matplotlib':
82 f = latex_to_png_mpl
82 f = latex_to_png_mpl
83 elif backend == 'dvipng':
83 elif backend == 'dvipng':
84 f = latex_to_png_dvipng
84 f = latex_to_png_dvipng
85 else:
85 else:
86 raise ValueError('No such backend {0}'.format(backend))
86 raise ValueError('No such backend {0}'.format(backend))
87 bin_data = f(s, wrap)
87 bin_data = f(s, wrap)
88 if encode and bin_data:
88 if encode and bin_data:
89 bin_data = encodebytes(bin_data)
89 bin_data = encodebytes(bin_data)
90 return bin_data
90 return bin_data
91
91
92
92
93 def latex_to_png_mpl(s, wrap):
93 def latex_to_png_mpl(s, wrap):
94 try:
94 try:
95 from matplotlib import mathtext
95 from matplotlib import mathtext
96 from pyparsing import ParseFatalException
96 from pyparsing import ParseFatalException
97 except ImportError:
97 except ImportError:
98 return None
98 return None
99
99
100 # mpl mathtext doesn't support display math, force inline
100 # mpl mathtext doesn't support display math, force inline
101 s = s.replace('$$', '$')
101 s = s.replace('$$', '$')
102 if wrap:
102 if wrap:
103 s = u'${0}$'.format(s)
103 s = u'${0}$'.format(s)
104
104
105 try:
105 try:
106 mt = mathtext.MathTextParser('bitmap')
106 mt = mathtext.MathTextParser('bitmap')
107 f = BytesIO()
107 f = BytesIO()
108 mt.to_png(f, s, fontsize=12)
108 mt.to_png(f, s, fontsize=12)
109 return f.getvalue()
109 return f.getvalue()
110 except (ValueError, RuntimeError, ParseFatalException):
110 except (ValueError, RuntimeError, ParseFatalException):
111 return None
111 return None
112
112
113
113
114 def latex_to_png_dvipng(s, wrap):
114 def latex_to_png_dvipng(s, wrap):
115 try:
115 try:
116 find_cmd('latex')
116 find_cmd('latex')
117 find_cmd('dvipng')
117 find_cmd('dvipng')
118 except FindCmdError:
118 except FindCmdError:
119 return None
119 return None
120 try:
120 try:
121 workdir = tempfile.mkdtemp()
121 workdir = tempfile.mkdtemp()
122 tmpfile = os.path.join(workdir, "tmp.tex")
122 tmpfile = os.path.join(workdir, "tmp.tex")
123 dvifile = os.path.join(workdir, "tmp.dvi")
123 dvifile = os.path.join(workdir, "tmp.dvi")
124 outfile = os.path.join(workdir, "tmp.png")
124 outfile = os.path.join(workdir, "tmp.png")
125
125
126 with open(tmpfile, "w", encoding='utf8') as f:
126 with open(tmpfile, "w", encoding='utf8') as f:
127 f.writelines(genelatex(s, wrap))
127 f.writelines(genelatex(s, wrap))
128
128
129 with open(os.devnull, 'wb') as devnull:
129 with open(os.devnull, 'wb') as devnull:
130 subprocess.check_call(
130 subprocess.check_call(
131 ["latex", "-halt-on-error", "-interaction", "batchmode", tmpfile],
131 ["latex", "-halt-on-error", "-interaction", "batchmode", tmpfile],
132 cwd=workdir, stdout=devnull, stderr=devnull)
132 cwd=workdir, stdout=devnull, stderr=devnull)
133
133
134 subprocess.check_call(
134 subprocess.check_call(
135 ["dvipng", "-T", "tight", "-x", "1500", "-z", "9",
135 ["dvipng", "-T", "tight", "-x", "1500", "-z", "9",
136 "-bg", "transparent", "-o", outfile, dvifile], cwd=workdir,
136 "-bg", "transparent", "-o", outfile, dvifile], cwd=workdir,
137 stdout=devnull, stderr=devnull)
137 stdout=devnull, stderr=devnull)
138
138
139 with open(outfile, "rb") as f:
139 with open(outfile, "rb") as f:
140 return f.read()
140 return f.read()
141 except subprocess.CalledProcessError:
141 except subprocess.CalledProcessError:
142 return None
142 return None
143 finally:
143 finally:
144 shutil.rmtree(workdir)
144 shutil.rmtree(workdir)
145
145
146
146
147 def kpsewhich(filename):
147 def kpsewhich(filename):
148 """Invoke kpsewhich command with an argument `filename`."""
148 """Invoke kpsewhich command with an argument `filename`."""
149 try:
149 try:
150 find_cmd("kpsewhich")
150 find_cmd("kpsewhich")
151 proc = subprocess.Popen(
151 proc = subprocess.Popen(
152 ["kpsewhich", filename],
152 ["kpsewhich", filename],
153 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
153 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
154 (stdout, stderr) = proc.communicate()
154 (stdout, stderr) = proc.communicate()
155 return stdout.strip().decode('utf8', 'replace')
155 return stdout.strip().decode('utf8', 'replace')
156 except FindCmdError:
156 except FindCmdError:
157 pass
157 pass
158
158
159
159
160 def genelatex(body, wrap):
160 def genelatex(body, wrap):
161 """Generate LaTeX document for dvipng backend."""
161 """Generate LaTeX document for dvipng backend."""
162 lt = LaTeXTool.instance()
162 lt = LaTeXTool.instance()
163 breqn = wrap and lt.use_breqn and kpsewhich("breqn.sty")
163 breqn = wrap and lt.use_breqn and kpsewhich("breqn.sty")
164 yield r'\documentclass{article}'
164 yield r'\documentclass{article}'
165 packages = lt.packages
165 packages = lt.packages
166 if breqn:
166 if breqn:
167 packages = packages + ['breqn']
167 packages = packages + ['breqn']
168 for pack in packages:
168 for pack in packages:
169 yield r'\usepackage{{{0}}}'.format(pack)
169 yield r'\usepackage{{{0}}}'.format(pack)
170 yield r'\pagestyle{empty}'
170 yield r'\pagestyle{empty}'
171 if lt.preamble:
171 if lt.preamble:
172 yield lt.preamble
172 yield lt.preamble
173 yield r'\begin{document}'
173 yield r'\begin{document}'
174 if breqn:
174 if breqn:
175 yield r'\begin{dmath*}'
175 yield r'\begin{dmath*}'
176 yield body
176 yield body
177 yield r'\end{dmath*}'
177 yield r'\end{dmath*}'
178 elif wrap:
178 elif wrap:
179 yield u'$${0}$$'.format(body)
179 yield u'$${0}$$'.format(body)
180 else:
180 else:
181 yield body
181 yield body
182 yield u'\end{document}'
182 yield u'\\end{document}'
183
183
184
184
185 _data_uri_template_png = u"""<img src="data:image/png;base64,%s" alt=%s />"""
185 _data_uri_template_png = u"""<img src="data:image/png;base64,%s" alt=%s />"""
186
186
187 def latex_to_html(s, alt='image'):
187 def latex_to_html(s, alt='image'):
188 """Render LaTeX to HTML with embedded PNG data using data URIs.
188 """Render LaTeX to HTML with embedded PNG data using data URIs.
189
189
190 Parameters
190 Parameters
191 ----------
191 ----------
192 s : str
192 s : str
193 The raw string containing valid inline LateX.
193 The raw string containing valid inline LateX.
194 alt : str
194 alt : str
195 The alt text to use for the HTML.
195 The alt text to use for the HTML.
196 """
196 """
197 base64_data = latex_to_png(s, encode=True).decode('ascii')
197 base64_data = latex_to_png(s, encode=True).decode('ascii')
198 if base64_data:
198 if base64_data:
199 return _data_uri_template_png % (base64_data, alt)
199 return _data_uri_template_png % (base64_data, alt)
200
200
201
201
@@ -1,532 +1,532 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Defines a variety of Pygments lexers for highlighting IPython code.
3 Defines a variety of Pygments lexers for highlighting IPython code.
4
4
5 This includes:
5 This includes:
6
6
7 IPythonLexer, IPython3Lexer
7 IPythonLexer, IPython3Lexer
8 Lexers for pure IPython (python + magic/shell commands)
8 Lexers for pure IPython (python + magic/shell commands)
9
9
10 IPythonPartialTracebackLexer, IPythonTracebackLexer
10 IPythonPartialTracebackLexer, IPythonTracebackLexer
11 Supports 2.x and 3.x via keyword `python3`. The partial traceback
11 Supports 2.x and 3.x via keyword `python3`. The partial traceback
12 lexer reads everything but the Python code appearing in a traceback.
12 lexer reads everything but the Python code appearing in a traceback.
13 The full lexer combines the partial lexer with an IPython lexer.
13 The full lexer combines the partial lexer with an IPython lexer.
14
14
15 IPythonConsoleLexer
15 IPythonConsoleLexer
16 A lexer for IPython console sessions, with support for tracebacks.
16 A lexer for IPython console sessions, with support for tracebacks.
17
17
18 IPyLexer
18 IPyLexer
19 A friendly lexer which examines the first line of text and from it,
19 A friendly lexer which examines the first line of text and from it,
20 decides whether to use an IPython lexer or an IPython console lexer.
20 decides whether to use an IPython lexer or an IPython console lexer.
21 This is probably the only lexer that needs to be explicitly added
21 This is probably the only lexer that needs to be explicitly added
22 to Pygments.
22 to Pygments.
23
23
24 """
24 """
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26 # Copyright (c) 2013, the IPython Development Team.
26 # Copyright (c) 2013, the IPython Development Team.
27 #
27 #
28 # Distributed under the terms of the Modified BSD License.
28 # Distributed under the terms of the Modified BSD License.
29 #
29 #
30 # The full license is in the file COPYING.txt, distributed with this software.
30 # The full license is in the file COPYING.txt, distributed with this software.
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32
32
33 # Standard library
33 # Standard library
34 import re
34 import re
35
35
36 # Third party
36 # Third party
37 from pygments.lexers import (
37 from pygments.lexers import (
38 BashLexer, HtmlLexer, JavascriptLexer, RubyLexer, PerlLexer, PythonLexer,
38 BashLexer, HtmlLexer, JavascriptLexer, RubyLexer, PerlLexer, PythonLexer,
39 Python3Lexer, TexLexer)
39 Python3Lexer, TexLexer)
40 from pygments.lexer import (
40 from pygments.lexer import (
41 Lexer, DelegatingLexer, RegexLexer, do_insertions, bygroups, using,
41 Lexer, DelegatingLexer, RegexLexer, do_insertions, bygroups, using,
42 )
42 )
43 from pygments.token import (
43 from pygments.token import (
44 Generic, Keyword, Literal, Name, Operator, Other, Text, Error,
44 Generic, Keyword, Literal, Name, Operator, Other, Text, Error,
45 )
45 )
46 from pygments.util import get_bool_opt
46 from pygments.util import get_bool_opt
47
47
48 # Local
48 # Local
49
49
50 line_re = re.compile('.*?\n')
50 line_re = re.compile('.*?\n')
51
51
52 __all__ = ['build_ipy_lexer', 'IPython3Lexer', 'IPythonLexer',
52 __all__ = ['build_ipy_lexer', 'IPython3Lexer', 'IPythonLexer',
53 'IPythonPartialTracebackLexer', 'IPythonTracebackLexer',
53 'IPythonPartialTracebackLexer', 'IPythonTracebackLexer',
54 'IPythonConsoleLexer', 'IPyLexer']
54 'IPythonConsoleLexer', 'IPyLexer']
55
55
56
56
57 def build_ipy_lexer(python3):
57 def build_ipy_lexer(python3):
58 """Builds IPython lexers depending on the value of `python3`.
58 """Builds IPython lexers depending on the value of `python3`.
59
59
60 The lexer inherits from an appropriate Python lexer and then adds
60 The lexer inherits from an appropriate Python lexer and then adds
61 information about IPython specific keywords (i.e. magic commands,
61 information about IPython specific keywords (i.e. magic commands,
62 shell commands, etc.)
62 shell commands, etc.)
63
63
64 Parameters
64 Parameters
65 ----------
65 ----------
66 python3 : bool
66 python3 : bool
67 If `True`, then build an IPython lexer from a Python 3 lexer.
67 If `True`, then build an IPython lexer from a Python 3 lexer.
68
68
69 """
69 """
70 # It would be nice to have a single IPython lexer class which takes
70 # It would be nice to have a single IPython lexer class which takes
71 # a boolean `python3`. But since there are two Python lexer classes,
71 # a boolean `python3`. But since there are two Python lexer classes,
72 # we will also have two IPython lexer classes.
72 # we will also have two IPython lexer classes.
73 if python3:
73 if python3:
74 PyLexer = Python3Lexer
74 PyLexer = Python3Lexer
75 name = 'IPython3'
75 name = 'IPython3'
76 aliases = ['ipython3']
76 aliases = ['ipython3']
77 doc = """IPython3 Lexer"""
77 doc = """IPython3 Lexer"""
78 else:
78 else:
79 PyLexer = PythonLexer
79 PyLexer = PythonLexer
80 name = 'IPython'
80 name = 'IPython'
81 aliases = ['ipython2', 'ipython']
81 aliases = ['ipython2', 'ipython']
82 doc = """IPython Lexer"""
82 doc = """IPython Lexer"""
83
83
84 ipython_tokens = [
84 ipython_tokens = [
85 (r'(?s)(\s*)(%%capture)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PyLexer))),
85 (r'(?s)(\s*)(%%capture)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PyLexer))),
86 (r'(?s)(\s*)(%%debug)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PyLexer))),
86 (r'(?s)(\s*)(%%debug)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PyLexer))),
87 (r'(?is)(\s*)(%%html)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(HtmlLexer))),
87 (r'(?is)(\s*)(%%html)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(HtmlLexer))),
88 (r'(?s)(\s*)(%%javascript)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(JavascriptLexer))),
88 (r'(?s)(\s*)(%%javascript)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(JavascriptLexer))),
89 (r'(?s)(\s*)(%%js)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(JavascriptLexer))),
89 (r'(?s)(\s*)(%%js)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(JavascriptLexer))),
90 (r'(?s)(\s*)(%%latex)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(TexLexer))),
90 (r'(?s)(\s*)(%%latex)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(TexLexer))),
91 (r'(?s)(\s*)(%%perl)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PerlLexer))),
91 (r'(?s)(\s*)(%%perl)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PerlLexer))),
92 (r'(?s)(\s*)(%%prun)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PyLexer))),
92 (r'(?s)(\s*)(%%prun)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PyLexer))),
93 (r'(?s)(\s*)(%%pypy)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PyLexer))),
93 (r'(?s)(\s*)(%%pypy)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PyLexer))),
94 (r'(?s)(\s*)(%%python)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PyLexer))),
94 (r'(?s)(\s*)(%%python)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PyLexer))),
95 (r'(?s)(\s*)(%%python2)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PythonLexer))),
95 (r'(?s)(\s*)(%%python2)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PythonLexer))),
96 (r'(?s)(\s*)(%%python3)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(Python3Lexer))),
96 (r'(?s)(\s*)(%%python3)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(Python3Lexer))),
97 (r'(?s)(\s*)(%%ruby)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(RubyLexer))),
97 (r'(?s)(\s*)(%%ruby)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(RubyLexer))),
98 (r'(?s)(\s*)(%%time)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PyLexer))),
98 (r'(?s)(\s*)(%%time)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PyLexer))),
99 (r'(?s)(\s*)(%%timeit)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PyLexer))),
99 (r'(?s)(\s*)(%%timeit)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PyLexer))),
100 (r'(?s)(\s*)(%%writefile)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PyLexer))),
100 (r'(?s)(\s*)(%%writefile)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PyLexer))),
101 (r'(?s)(\s*)(%%file)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PyLexer))),
101 (r'(?s)(\s*)(%%file)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PyLexer))),
102 (r"(?s)(\s*)(%%)(\w+)(.*)", bygroups(Text, Operator, Keyword, Text)),
102 (r"(?s)(\s*)(%%)(\w+)(.*)", bygroups(Text, Operator, Keyword, Text)),
103 (r'(?s)(^\s*)(%%!)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(BashLexer))),
103 (r'(?s)(^\s*)(%%!)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(BashLexer))),
104 (r"(%%?)(\w+)(\?\??)$", bygroups(Operator, Keyword, Operator)),
104 (r"(%%?)(\w+)(\?\??)$", bygroups(Operator, Keyword, Operator)),
105 (r"\b(\?\??)(\s*)$", bygroups(Operator, Text)),
105 (r"\b(\?\??)(\s*)$", bygroups(Operator, Text)),
106 (r'(%)(sx|sc|system)(.*)(\n)', bygroups(Operator, Keyword,
106 (r'(%)(sx|sc|system)(.*)(\n)', bygroups(Operator, Keyword,
107 using(BashLexer), Text)),
107 using(BashLexer), Text)),
108 (r'(%)(\w+)(.*\n)', bygroups(Operator, Keyword, Text)),
108 (r'(%)(\w+)(.*\n)', bygroups(Operator, Keyword, Text)),
109 (r'^(!!)(.+)(\n)', bygroups(Operator, using(BashLexer), Text)),
109 (r'^(!!)(.+)(\n)', bygroups(Operator, using(BashLexer), Text)),
110 (r'(!)(?!=)(.+)(\n)', bygroups(Operator, using(BashLexer), Text)),
110 (r'(!)(?!=)(.+)(\n)', bygroups(Operator, using(BashLexer), Text)),
111 (r'^(\s*)(\?\??)(\s*%{0,2}[\w\.\*]*)', bygroups(Text, Operator, Text)),
111 (r'^(\s*)(\?\??)(\s*%{0,2}[\w\.\*]*)', bygroups(Text, Operator, Text)),
112 (r'(\s*%{0,2}[\w\.\*]*)(\?\??)(\s*)$', bygroups(Text, Operator, Text)),
112 (r'(\s*%{0,2}[\w\.\*]*)(\?\??)(\s*)$', bygroups(Text, Operator, Text)),
113 ]
113 ]
114
114
115 tokens = PyLexer.tokens.copy()
115 tokens = PyLexer.tokens.copy()
116 tokens['root'] = ipython_tokens + tokens['root']
116 tokens['root'] = ipython_tokens + tokens['root']
117
117
118 attrs = {'name': name, 'aliases': aliases, 'filenames': [],
118 attrs = {'name': name, 'aliases': aliases, 'filenames': [],
119 '__doc__': doc, 'tokens': tokens}
119 '__doc__': doc, 'tokens': tokens}
120
120
121 return type(name, (PyLexer,), attrs)
121 return type(name, (PyLexer,), attrs)
122
122
123
123
124 IPython3Lexer = build_ipy_lexer(python3=True)
124 IPython3Lexer = build_ipy_lexer(python3=True)
125 IPythonLexer = build_ipy_lexer(python3=False)
125 IPythonLexer = build_ipy_lexer(python3=False)
126
126
127
127
128 class IPythonPartialTracebackLexer(RegexLexer):
128 class IPythonPartialTracebackLexer(RegexLexer):
129 """
129 """
130 Partial lexer for IPython tracebacks.
130 Partial lexer for IPython tracebacks.
131
131
132 Handles all the non-python output. This works for both Python 2.x and 3.x.
132 Handles all the non-python output. This works for both Python 2.x and 3.x.
133
133
134 """
134 """
135 name = 'IPython Partial Traceback'
135 name = 'IPython Partial Traceback'
136
136
137 tokens = {
137 tokens = {
138 'root': [
138 'root': [
139 # Tracebacks for syntax errors have a different style.
139 # Tracebacks for syntax errors have a different style.
140 # For both types of tracebacks, we mark the first line with
140 # For both types of tracebacks, we mark the first line with
141 # Generic.Traceback. For syntax errors, we mark the filename
141 # Generic.Traceback. For syntax errors, we mark the filename
142 # as we mark the filenames for non-syntax tracebacks.
142 # as we mark the filenames for non-syntax tracebacks.
143 #
143 #
144 # These two regexps define how IPythonConsoleLexer finds a
144 # These two regexps define how IPythonConsoleLexer finds a
145 # traceback.
145 # traceback.
146 #
146 #
147 ## Non-syntax traceback
147 ## Non-syntax traceback
148 (r'^(\^C)?(-+\n)', bygroups(Error, Generic.Traceback)),
148 (r'^(\^C)?(-+\n)', bygroups(Error, Generic.Traceback)),
149 ## Syntax traceback
149 ## Syntax traceback
150 (r'^( File)(.*)(, line )(\d+\n)',
150 (r'^( File)(.*)(, line )(\d+\n)',
151 bygroups(Generic.Traceback, Name.Namespace,
151 bygroups(Generic.Traceback, Name.Namespace,
152 Generic.Traceback, Literal.Number.Integer)),
152 Generic.Traceback, Literal.Number.Integer)),
153
153
154 # (Exception Identifier)(Whitespace)(Traceback Message)
154 # (Exception Identifier)(Whitespace)(Traceback Message)
155 (r'(?u)(^[^\d\W]\w*)(\s*)(Traceback.*?\n)',
155 (r'(?u)(^[^\d\W]\w*)(\s*)(Traceback.*?\n)',
156 bygroups(Name.Exception, Generic.Whitespace, Text)),
156 bygroups(Name.Exception, Generic.Whitespace, Text)),
157 # (Module/Filename)(Text)(Callee)(Function Signature)
157 # (Module/Filename)(Text)(Callee)(Function Signature)
158 # Better options for callee and function signature?
158 # Better options for callee and function signature?
159 (r'(.*)( in )(.*)(\(.*\)\n)',
159 (r'(.*)( in )(.*)(\(.*\)\n)',
160 bygroups(Name.Namespace, Text, Name.Entity, Name.Tag)),
160 bygroups(Name.Namespace, Text, Name.Entity, Name.Tag)),
161 # Regular line: (Whitespace)(Line Number)(Python Code)
161 # Regular line: (Whitespace)(Line Number)(Python Code)
162 (r'(\s*?)(\d+)(.*?\n)',
162 (r'(\s*?)(\d+)(.*?\n)',
163 bygroups(Generic.Whitespace, Literal.Number.Integer, Other)),
163 bygroups(Generic.Whitespace, Literal.Number.Integer, Other)),
164 # Emphasized line: (Arrow)(Line Number)(Python Code)
164 # Emphasized line: (Arrow)(Line Number)(Python Code)
165 # Using Exception token so arrow color matches the Exception.
165 # Using Exception token so arrow color matches the Exception.
166 (r'(-*>?\s?)(\d+)(.*?\n)',
166 (r'(-*>?\s?)(\d+)(.*?\n)',
167 bygroups(Name.Exception, Literal.Number.Integer, Other)),
167 bygroups(Name.Exception, Literal.Number.Integer, Other)),
168 # (Exception Identifier)(Message)
168 # (Exception Identifier)(Message)
169 (r'(?u)(^[^\d\W]\w*)(:.*?\n)',
169 (r'(?u)(^[^\d\W]\w*)(:.*?\n)',
170 bygroups(Name.Exception, Text)),
170 bygroups(Name.Exception, Text)),
171 # Tag everything else as Other, will be handled later.
171 # Tag everything else as Other, will be handled later.
172 (r'.*\n', Other),
172 (r'.*\n', Other),
173 ],
173 ],
174 }
174 }
175
175
176
176
177 class IPythonTracebackLexer(DelegatingLexer):
177 class IPythonTracebackLexer(DelegatingLexer):
178 """
178 """
179 IPython traceback lexer.
179 IPython traceback lexer.
180
180
181 For doctests, the tracebacks can be snipped as much as desired with the
181 For doctests, the tracebacks can be snipped as much as desired with the
182 exception to the lines that designate a traceback. For non-syntax error
182 exception to the lines that designate a traceback. For non-syntax error
183 tracebacks, this is the line of hyphens. For syntax error tracebacks,
183 tracebacks, this is the line of hyphens. For syntax error tracebacks,
184 this is the line which lists the File and line number.
184 this is the line which lists the File and line number.
185
185
186 """
186 """
187 # The lexer inherits from DelegatingLexer. The "root" lexer is an
187 # The lexer inherits from DelegatingLexer. The "root" lexer is an
188 # appropriate IPython lexer, which depends on the value of the boolean
188 # appropriate IPython lexer, which depends on the value of the boolean
189 # `python3`. First, we parse with the partial IPython traceback lexer.
189 # `python3`. First, we parse with the partial IPython traceback lexer.
190 # Then, any code marked with the "Other" token is delegated to the root
190 # Then, any code marked with the "Other" token is delegated to the root
191 # lexer.
191 # lexer.
192 #
192 #
193 name = 'IPython Traceback'
193 name = 'IPython Traceback'
194 aliases = ['ipythontb']
194 aliases = ['ipythontb']
195
195
196 def __init__(self, **options):
196 def __init__(self, **options):
197 self.python3 = get_bool_opt(options, 'python3', False)
197 self.python3 = get_bool_opt(options, 'python3', False)
198 if self.python3:
198 if self.python3:
199 self.aliases = ['ipython3tb']
199 self.aliases = ['ipython3tb']
200 else:
200 else:
201 self.aliases = ['ipython2tb', 'ipythontb']
201 self.aliases = ['ipython2tb', 'ipythontb']
202
202
203 if self.python3:
203 if self.python3:
204 IPyLexer = IPython3Lexer
204 IPyLexer = IPython3Lexer
205 else:
205 else:
206 IPyLexer = IPythonLexer
206 IPyLexer = IPythonLexer
207
207
208 DelegatingLexer.__init__(self, IPyLexer,
208 DelegatingLexer.__init__(self, IPyLexer,
209 IPythonPartialTracebackLexer, **options)
209 IPythonPartialTracebackLexer, **options)
210
210
211 class IPythonConsoleLexer(Lexer):
211 class IPythonConsoleLexer(Lexer):
212 """
212 """
213 An IPython console lexer for IPython code-blocks and doctests, such as:
213 An IPython console lexer for IPython code-blocks and doctests, such as:
214
214
215 .. code-block:: rst
215 .. code-block:: rst
216
216
217 .. code-block:: ipythonconsole
217 .. code-block:: ipythonconsole
218
218
219 In [1]: a = 'foo'
219 In [1]: a = 'foo'
220
220
221 In [2]: a
221 In [2]: a
222 Out[2]: 'foo'
222 Out[2]: 'foo'
223
223
224 In [3]: print a
224 In [3]: print a
225 foo
225 foo
226
226
227 In [4]: 1 / 0
227 In [4]: 1 / 0
228
228
229
229
230 Support is also provided for IPython exceptions:
230 Support is also provided for IPython exceptions:
231
231
232 .. code-block:: rst
232 .. code-block:: rst
233
233
234 .. code-block:: ipythonconsole
234 .. code-block:: ipythonconsole
235
235
236 In [1]: raise Exception
236 In [1]: raise Exception
237
237
238 ---------------------------------------------------------------------------
238 ---------------------------------------------------------------------------
239 Exception Traceback (most recent call last)
239 Exception Traceback (most recent call last)
240 <ipython-input-1-fca2ab0ca76b> in <module>
240 <ipython-input-1-fca2ab0ca76b> in <module>
241 ----> 1 raise Exception
241 ----> 1 raise Exception
242
242
243 Exception:
243 Exception:
244
244
245 """
245 """
246 name = 'IPython console session'
246 name = 'IPython console session'
247 aliases = ['ipythonconsole']
247 aliases = ['ipythonconsole']
248 mimetypes = ['text/x-ipython-console']
248 mimetypes = ['text/x-ipython-console']
249
249
250 # The regexps used to determine what is input and what is output.
250 # The regexps used to determine what is input and what is output.
251 # The default prompts for IPython are:
251 # The default prompts for IPython are:
252 #
252 #
253 # in = 'In [#]: '
253 # in = 'In [#]: '
254 # continuation = ' .D.: '
254 # continuation = ' .D.: '
255 # template = 'Out[#]: '
255 # template = 'Out[#]: '
256 #
256 #
257 # Where '#' is the 'prompt number' or 'execution count' and 'D'
257 # Where '#' is the 'prompt number' or 'execution count' and 'D'
258 # D is a number of dots matching the width of the execution count
258 # D is a number of dots matching the width of the execution count
259 #
259 #
260 in1_regex = r'In \[[0-9]+\]: '
260 in1_regex = r'In \[[0-9]+\]: '
261 in2_regex = r' \.\.+\.: '
261 in2_regex = r' \.\.+\.: '
262 out_regex = r'Out\[[0-9]+\]: '
262 out_regex = r'Out\[[0-9]+\]: '
263
263
264 #: The regex to determine when a traceback starts.
264 #: The regex to determine when a traceback starts.
265 ipytb_start = re.compile(r'^(\^C)?(-+\n)|^( File)(.*)(, line )(\d+\n)')
265 ipytb_start = re.compile(r'^(\^C)?(-+\n)|^( File)(.*)(, line )(\d+\n)')
266
266
267 def __init__(self, **options):
267 def __init__(self, **options):
268 """Initialize the IPython console lexer.
268 """Initialize the IPython console lexer.
269
269
270 Parameters
270 Parameters
271 ----------
271 ----------
272 python3 : bool
272 python3 : bool
273 If `True`, then the console inputs are parsed using a Python 3
273 If `True`, then the console inputs are parsed using a Python 3
274 lexer. Otherwise, they are parsed using a Python 2 lexer.
274 lexer. Otherwise, they are parsed using a Python 2 lexer.
275 in1_regex : RegexObject
275 in1_regex : RegexObject
276 The compiled regular expression used to detect the start
276 The compiled regular expression used to detect the start
277 of inputs. Although the IPython configuration setting may have a
277 of inputs. Although the IPython configuration setting may have a
278 trailing whitespace, do not include it in the regex. If `None`,
278 trailing whitespace, do not include it in the regex. If `None`,
279 then the default input prompt is assumed.
279 then the default input prompt is assumed.
280 in2_regex : RegexObject
280 in2_regex : RegexObject
281 The compiled regular expression used to detect the continuation
281 The compiled regular expression used to detect the continuation
282 of inputs. Although the IPython configuration setting may have a
282 of inputs. Although the IPython configuration setting may have a
283 trailing whitespace, do not include it in the regex. If `None`,
283 trailing whitespace, do not include it in the regex. If `None`,
284 then the default input prompt is assumed.
284 then the default input prompt is assumed.
285 out_regex : RegexObject
285 out_regex : RegexObject
286 The compiled regular expression used to detect outputs. If `None`,
286 The compiled regular expression used to detect outputs. If `None`,
287 then the default output prompt is assumed.
287 then the default output prompt is assumed.
288
288
289 """
289 """
290 self.python3 = get_bool_opt(options, 'python3', False)
290 self.python3 = get_bool_opt(options, 'python3', False)
291 if self.python3:
291 if self.python3:
292 self.aliases = ['ipython3console']
292 self.aliases = ['ipython3console']
293 else:
293 else:
294 self.aliases = ['ipython2console', 'ipythonconsole']
294 self.aliases = ['ipython2console', 'ipythonconsole']
295
295
296 in1_regex = options.get('in1_regex', self.in1_regex)
296 in1_regex = options.get('in1_regex', self.in1_regex)
297 in2_regex = options.get('in2_regex', self.in2_regex)
297 in2_regex = options.get('in2_regex', self.in2_regex)
298 out_regex = options.get('out_regex', self.out_regex)
298 out_regex = options.get('out_regex', self.out_regex)
299
299
300 # So that we can work with input and output prompts which have been
300 # So that we can work with input and output prompts which have been
301 # rstrip'd (possibly by editors) we also need rstrip'd variants. If
301 # rstrip'd (possibly by editors) we also need rstrip'd variants. If
302 # we do not do this, then such prompts will be tagged as 'output'.
302 # we do not do this, then such prompts will be tagged as 'output'.
303 # The reason can't just use the rstrip'd variants instead is because
303 # The reason can't just use the rstrip'd variants instead is because
304 # we want any whitespace associated with the prompt to be inserted
304 # we want any whitespace associated with the prompt to be inserted
305 # with the token. This allows formatted code to be modified so as hide
305 # with the token. This allows formatted code to be modified so as hide
306 # the appearance of prompts, with the whitespace included. One example
306 # the appearance of prompts, with the whitespace included. One example
307 # use of this is in copybutton.js from the standard lib Python docs.
307 # use of this is in copybutton.js from the standard lib Python docs.
308 in1_regex_rstrip = in1_regex.rstrip() + '\n'
308 in1_regex_rstrip = in1_regex.rstrip() + '\n'
309 in2_regex_rstrip = in2_regex.rstrip() + '\n'
309 in2_regex_rstrip = in2_regex.rstrip() + '\n'
310 out_regex_rstrip = out_regex.rstrip() + '\n'
310 out_regex_rstrip = out_regex.rstrip() + '\n'
311
311
312 # Compile and save them all.
312 # Compile and save them all.
313 attrs = ['in1_regex', 'in2_regex', 'out_regex',
313 attrs = ['in1_regex', 'in2_regex', 'out_regex',
314 'in1_regex_rstrip', 'in2_regex_rstrip', 'out_regex_rstrip']
314 'in1_regex_rstrip', 'in2_regex_rstrip', 'out_regex_rstrip']
315 for attr in attrs:
315 for attr in attrs:
316 self.__setattr__(attr, re.compile(locals()[attr]))
316 self.__setattr__(attr, re.compile(locals()[attr]))
317
317
318 Lexer.__init__(self, **options)
318 Lexer.__init__(self, **options)
319
319
320 if self.python3:
320 if self.python3:
321 pylexer = IPython3Lexer
321 pylexer = IPython3Lexer
322 tblexer = IPythonTracebackLexer
322 tblexer = IPythonTracebackLexer
323 else:
323 else:
324 pylexer = IPythonLexer
324 pylexer = IPythonLexer
325 tblexer = IPythonTracebackLexer
325 tblexer = IPythonTracebackLexer
326
326
327 self.pylexer = pylexer(**options)
327 self.pylexer = pylexer(**options)
328 self.tblexer = tblexer(**options)
328 self.tblexer = tblexer(**options)
329
329
330 self.reset()
330 self.reset()
331
331
332 def reset(self):
332 def reset(self):
333 self.mode = 'output'
333 self.mode = 'output'
334 self.index = 0
334 self.index = 0
335 self.buffer = u''
335 self.buffer = u''
336 self.insertions = []
336 self.insertions = []
337
337
338 def buffered_tokens(self):
338 def buffered_tokens(self):
339 """
339 """
340 Generator of unprocessed tokens after doing insertions and before
340 Generator of unprocessed tokens after doing insertions and before
341 changing to a new state.
341 changing to a new state.
342
342
343 """
343 """
344 if self.mode == 'output':
344 if self.mode == 'output':
345 tokens = [(0, Generic.Output, self.buffer)]
345 tokens = [(0, Generic.Output, self.buffer)]
346 elif self.mode == 'input':
346 elif self.mode == 'input':
347 tokens = self.pylexer.get_tokens_unprocessed(self.buffer)
347 tokens = self.pylexer.get_tokens_unprocessed(self.buffer)
348 else: # traceback
348 else: # traceback
349 tokens = self.tblexer.get_tokens_unprocessed(self.buffer)
349 tokens = self.tblexer.get_tokens_unprocessed(self.buffer)
350
350
351 for i, t, v in do_insertions(self.insertions, tokens):
351 for i, t, v in do_insertions(self.insertions, tokens):
352 # All token indexes are relative to the buffer.
352 # All token indexes are relative to the buffer.
353 yield self.index + i, t, v
353 yield self.index + i, t, v
354
354
355 # Clear it all
355 # Clear it all
356 self.index += len(self.buffer)
356 self.index += len(self.buffer)
357 self.buffer = u''
357 self.buffer = u''
358 self.insertions = []
358 self.insertions = []
359
359
360 def get_mci(self, line):
360 def get_mci(self, line):
361 """
361 """
362 Parses the line and returns a 3-tuple: (mode, code, insertion).
362 Parses the line and returns a 3-tuple: (mode, code, insertion).
363
363
364 `mode` is the next mode (or state) of the lexer, and is always equal
364 `mode` is the next mode (or state) of the lexer, and is always equal
365 to 'input', 'output', or 'tb'.
365 to 'input', 'output', or 'tb'.
366
366
367 `code` is a portion of the line that should be added to the buffer
367 `code` is a portion of the line that should be added to the buffer
368 corresponding to the next mode and eventually lexed by another lexer.
368 corresponding to the next mode and eventually lexed by another lexer.
369 For example, `code` could be Python code if `mode` were 'input'.
369 For example, `code` could be Python code if `mode` were 'input'.
370
370
371 `insertion` is a 3-tuple (index, token, text) representing an
371 `insertion` is a 3-tuple (index, token, text) representing an
372 unprocessed "token" that will be inserted into the stream of tokens
372 unprocessed "token" that will be inserted into the stream of tokens
373 that are created from the buffer once we change modes. This is usually
373 that are created from the buffer once we change modes. This is usually
374 the input or output prompt.
374 the input or output prompt.
375
375
376 In general, the next mode depends on current mode and on the contents
376 In general, the next mode depends on current mode and on the contents
377 of `line`.
377 of `line`.
378
378
379 """
379 """
380 # To reduce the number of regex match checks, we have multiple
380 # To reduce the number of regex match checks, we have multiple
381 # 'if' blocks instead of 'if-elif' blocks.
381 # 'if' blocks instead of 'if-elif' blocks.
382
382
383 # Check for possible end of input
383 # Check for possible end of input
384 in2_match = self.in2_regex.match(line)
384 in2_match = self.in2_regex.match(line)
385 in2_match_rstrip = self.in2_regex_rstrip.match(line)
385 in2_match_rstrip = self.in2_regex_rstrip.match(line)
386 if (in2_match and in2_match.group().rstrip() == line.rstrip()) or \
386 if (in2_match and in2_match.group().rstrip() == line.rstrip()) or \
387 in2_match_rstrip:
387 in2_match_rstrip:
388 end_input = True
388 end_input = True
389 else:
389 else:
390 end_input = False
390 end_input = False
391 if end_input and self.mode != 'tb':
391 if end_input and self.mode != 'tb':
392 # Only look for an end of input when not in tb mode.
392 # Only look for an end of input when not in tb mode.
393 # An ellipsis could appear within the traceback.
393 # An ellipsis could appear within the traceback.
394 mode = 'output'
394 mode = 'output'
395 code = u''
395 code = u''
396 insertion = (0, Generic.Prompt, line)
396 insertion = (0, Generic.Prompt, line)
397 return mode, code, insertion
397 return mode, code, insertion
398
398
399 # Check for output prompt
399 # Check for output prompt
400 out_match = self.out_regex.match(line)
400 out_match = self.out_regex.match(line)
401 out_match_rstrip = self.out_regex_rstrip.match(line)
401 out_match_rstrip = self.out_regex_rstrip.match(line)
402 if out_match or out_match_rstrip:
402 if out_match or out_match_rstrip:
403 mode = 'output'
403 mode = 'output'
404 if out_match:
404 if out_match:
405 idx = out_match.end()
405 idx = out_match.end()
406 else:
406 else:
407 idx = out_match_rstrip.end()
407 idx = out_match_rstrip.end()
408 code = line[idx:]
408 code = line[idx:]
409 # Use the 'heading' token for output. We cannot use Generic.Error
409 # Use the 'heading' token for output. We cannot use Generic.Error
410 # since it would conflict with exceptions.
410 # since it would conflict with exceptions.
411 insertion = (0, Generic.Heading, line[:idx])
411 insertion = (0, Generic.Heading, line[:idx])
412 return mode, code, insertion
412 return mode, code, insertion
413
413
414
414
415 # Check for input or continuation prompt (non stripped version)
415 # Check for input or continuation prompt (non stripped version)
416 in1_match = self.in1_regex.match(line)
416 in1_match = self.in1_regex.match(line)
417 if in1_match or (in2_match and self.mode != 'tb'):
417 if in1_match or (in2_match and self.mode != 'tb'):
418 # New input or when not in tb, continued input.
418 # New input or when not in tb, continued input.
419 # We do not check for continued input when in tb since it is
419 # We do not check for continued input when in tb since it is
420 # allowable to replace a long stack with an ellipsis.
420 # allowable to replace a long stack with an ellipsis.
421 mode = 'input'
421 mode = 'input'
422 if in1_match:
422 if in1_match:
423 idx = in1_match.end()
423 idx = in1_match.end()
424 else: # in2_match
424 else: # in2_match
425 idx = in2_match.end()
425 idx = in2_match.end()
426 code = line[idx:]
426 code = line[idx:]
427 insertion = (0, Generic.Prompt, line[:idx])
427 insertion = (0, Generic.Prompt, line[:idx])
428 return mode, code, insertion
428 return mode, code, insertion
429
429
430 # Check for input or continuation prompt (stripped version)
430 # Check for input or continuation prompt (stripped version)
431 in1_match_rstrip = self.in1_regex_rstrip.match(line)
431 in1_match_rstrip = self.in1_regex_rstrip.match(line)
432 if in1_match_rstrip or (in2_match_rstrip and self.mode != 'tb'):
432 if in1_match_rstrip or (in2_match_rstrip and self.mode != 'tb'):
433 # New input or when not in tb, continued input.
433 # New input or when not in tb, continued input.
434 # We do not check for continued input when in tb since it is
434 # We do not check for continued input when in tb since it is
435 # allowable to replace a long stack with an ellipsis.
435 # allowable to replace a long stack with an ellipsis.
436 mode = 'input'
436 mode = 'input'
437 if in1_match_rstrip:
437 if in1_match_rstrip:
438 idx = in1_match_rstrip.end()
438 idx = in1_match_rstrip.end()
439 else: # in2_match
439 else: # in2_match
440 idx = in2_match_rstrip.end()
440 idx = in2_match_rstrip.end()
441 code = line[idx:]
441 code = line[idx:]
442 insertion = (0, Generic.Prompt, line[:idx])
442 insertion = (0, Generic.Prompt, line[:idx])
443 return mode, code, insertion
443 return mode, code, insertion
444
444
445 # Check for traceback
445 # Check for traceback
446 if self.ipytb_start.match(line):
446 if self.ipytb_start.match(line):
447 mode = 'tb'
447 mode = 'tb'
448 code = line
448 code = line
449 insertion = None
449 insertion = None
450 return mode, code, insertion
450 return mode, code, insertion
451
451
452 # All other stuff...
452 # All other stuff...
453 if self.mode in ('input', 'output'):
453 if self.mode in ('input', 'output'):
454 # We assume all other text is output. Multiline input that
454 # We assume all other text is output. Multiline input that
455 # does not use the continuation marker cannot be detected.
455 # does not use the continuation marker cannot be detected.
456 # For example, the 3 in the following is clearly output:
456 # For example, the 3 in the following is clearly output:
457 #
457 #
458 # In [1]: print 3
458 # In [1]: print 3
459 # 3
459 # 3
460 #
460 #
461 # But the following second line is part of the input:
461 # But the following second line is part of the input:
462 #
462 #
463 # In [2]: while True:
463 # In [2]: while True:
464 # print True
464 # print True
465 #
465 #
466 # In both cases, the 2nd line will be 'output'.
466 # In both cases, the 2nd line will be 'output'.
467 #
467 #
468 mode = 'output'
468 mode = 'output'
469 else:
469 else:
470 mode = 'tb'
470 mode = 'tb'
471
471
472 code = line
472 code = line
473 insertion = None
473 insertion = None
474
474
475 return mode, code, insertion
475 return mode, code, insertion
476
476
477 def get_tokens_unprocessed(self, text):
477 def get_tokens_unprocessed(self, text):
478 self.reset()
478 self.reset()
479 for match in line_re.finditer(text):
479 for match in line_re.finditer(text):
480 line = match.group()
480 line = match.group()
481 mode, code, insertion = self.get_mci(line)
481 mode, code, insertion = self.get_mci(line)
482
482
483 if mode != self.mode:
483 if mode != self.mode:
484 # Yield buffered tokens before transitioning to new mode.
484 # Yield buffered tokens before transitioning to new mode.
485 for token in self.buffered_tokens():
485 for token in self.buffered_tokens():
486 yield token
486 yield token
487 self.mode = mode
487 self.mode = mode
488
488
489 if insertion:
489 if insertion:
490 self.insertions.append((len(self.buffer), [insertion]))
490 self.insertions.append((len(self.buffer), [insertion]))
491 self.buffer += code
491 self.buffer += code
492
492
493 for token in self.buffered_tokens():
493 for token in self.buffered_tokens():
494 yield token
494 yield token
495
495
496 class IPyLexer(Lexer):
496 class IPyLexer(Lexer):
497 """
497 r"""
498 Primary lexer for all IPython-like code.
498 Primary lexer for all IPython-like code.
499
499
500 This is a simple helper lexer. If the first line of the text begins with
500 This is a simple helper lexer. If the first line of the text begins with
501 "In \[[0-9]+\]:", then the entire text is parsed with an IPython console
501 "In \[[0-9]+\]:", then the entire text is parsed with an IPython console
502 lexer. If not, then the entire text is parsed with an IPython lexer.
502 lexer. If not, then the entire text is parsed with an IPython lexer.
503
503
504 The goal is to reduce the number of lexers that are registered
504 The goal is to reduce the number of lexers that are registered
505 with Pygments.
505 with Pygments.
506
506
507 """
507 """
508 name = 'IPy session'
508 name = 'IPy session'
509 aliases = ['ipy']
509 aliases = ['ipy']
510
510
511 def __init__(self, **options):
511 def __init__(self, **options):
512 self.python3 = get_bool_opt(options, 'python3', False)
512 self.python3 = get_bool_opt(options, 'python3', False)
513 if self.python3:
513 if self.python3:
514 self.aliases = ['ipy3']
514 self.aliases = ['ipy3']
515 else:
515 else:
516 self.aliases = ['ipy2', 'ipy']
516 self.aliases = ['ipy2', 'ipy']
517
517
518 Lexer.__init__(self, **options)
518 Lexer.__init__(self, **options)
519
519
520 self.IPythonLexer = IPythonLexer(**options)
520 self.IPythonLexer = IPythonLexer(**options)
521 self.IPythonConsoleLexer = IPythonConsoleLexer(**options)
521 self.IPythonConsoleLexer = IPythonConsoleLexer(**options)
522
522
523 def get_tokens_unprocessed(self, text):
523 def get_tokens_unprocessed(self, text):
524 # Search for the input prompt anywhere...this allows code blocks to
524 # Search for the input prompt anywhere...this allows code blocks to
525 # begin with comments as well.
525 # begin with comments as well.
526 if re.match(r'.*(In \[[0-9]+\]:)', text.strip(), re.DOTALL):
526 if re.match(r'.*(In \[[0-9]+\]:)', text.strip(), re.DOTALL):
527 lex = self.IPythonConsoleLexer
527 lex = self.IPythonConsoleLexer
528 else:
528 else:
529 lex = self.IPythonLexer
529 lex = self.IPythonLexer
530 for token in lex.get_tokens_unprocessed(text):
530 for token in lex.get_tokens_unprocessed(text):
531 yield token
531 yield token
532
532
@@ -1,177 +1,177 b''
1 """Experimental code for cleaner support of IPython syntax with unittest.
1 """Experimental code for cleaner support of IPython syntax with unittest.
2
2
3 In IPython up until 0.10, we've used very hacked up nose machinery for running
3 In IPython up until 0.10, we've used very hacked up nose machinery for running
4 tests with IPython special syntax, and this has proved to be extremely slow.
4 tests with IPython special syntax, and this has proved to be extremely slow.
5 This module provides decorators to try a different approach, stemming from a
5 This module provides decorators to try a different approach, stemming from a
6 conversation Brian and I (FP) had about this problem Sept/09.
6 conversation Brian and I (FP) had about this problem Sept/09.
7
7
8 The goal is to be able to easily write simple functions that can be seen by
8 The goal is to be able to easily write simple functions that can be seen by
9 unittest as tests, and ultimately for these to support doctests with full
9 unittest as tests, and ultimately for these to support doctests with full
10 IPython syntax. Nose already offers this based on naming conventions and our
10 IPython syntax. Nose already offers this based on naming conventions and our
11 hackish plugins, but we are seeking to move away from nose dependencies if
11 hackish plugins, but we are seeking to move away from nose dependencies if
12 possible.
12 possible.
13
13
14 This module follows a different approach, based on decorators.
14 This module follows a different approach, based on decorators.
15
15
16 - A decorator called @ipdoctest can mark any function as having a docstring
16 - A decorator called @ipdoctest can mark any function as having a docstring
17 that should be viewed as a doctest, but after syntax conversion.
17 that should be viewed as a doctest, but after syntax conversion.
18
18
19 Authors
19 Authors
20 -------
20 -------
21
21
22 - Fernando Perez <Fernando.Perez@berkeley.edu>
22 - Fernando Perez <Fernando.Perez@berkeley.edu>
23 """
23 """
24
24
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Copyright (C) 2009-2011 The IPython Development Team
27 # Copyright (C) 2009-2011 The IPython Development Team
28 #
28 #
29 # Distributed under the terms of the BSD License. The full license is in
29 # Distributed under the terms of the BSD License. The full license is in
30 # the file COPYING, distributed as part of this software.
30 # the file COPYING, distributed as part of this software.
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32
32
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34 # Imports
34 # Imports
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36
36
37 # Stdlib
37 # Stdlib
38 import re
38 import re
39 import unittest
39 import unittest
40 from doctest import DocTestFinder, DocTestRunner, TestResults
40 from doctest import DocTestFinder, DocTestRunner, TestResults
41
41
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43 # Classes and functions
43 # Classes and functions
44 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
45
45
46 def count_failures(runner):
46 def count_failures(runner):
47 """Count number of failures in a doctest runner.
47 """Count number of failures in a doctest runner.
48
48
49 Code modeled after the summarize() method in doctest.
49 Code modeled after the summarize() method in doctest.
50 """
50 """
51 return [TestResults(f, t) for f, t in runner._name2ft.values() if f > 0 ]
51 return [TestResults(f, t) for f, t in runner._name2ft.values() if f > 0 ]
52
52
53
53
54 class IPython2PythonConverter(object):
54 class IPython2PythonConverter(object):
55 """Convert IPython 'syntax' to valid Python.
55 """Convert IPython 'syntax' to valid Python.
56
56
57 Eventually this code may grow to be the full IPython syntax conversion
57 Eventually this code may grow to be the full IPython syntax conversion
58 implementation, but for now it only does prompt conversion."""
58 implementation, but for now it only does prompt conversion."""
59
59
60 def __init__(self):
60 def __init__(self):
61 self.rps1 = re.compile(r'In\ \[\d+\]: ')
61 self.rps1 = re.compile(r'In\ \[\d+\]: ')
62 self.rps2 = re.compile(r'\ \ \ \.\.\.+: ')
62 self.rps2 = re.compile(r'\ \ \ \.\.\.+: ')
63 self.rout = re.compile(r'Out\[\d+\]: \s*?\n?')
63 self.rout = re.compile(r'Out\[\d+\]: \s*?\n?')
64 self.pyps1 = '>>> '
64 self.pyps1 = '>>> '
65 self.pyps2 = '... '
65 self.pyps2 = '... '
66 self.rpyps1 = re.compile ('(\s*%s)(.*)$' % self.pyps1)
66 self.rpyps1 = re.compile (r'(\s*%s)(.*)$' % self.pyps1)
67 self.rpyps2 = re.compile ('(\s*%s)(.*)$' % self.pyps2)
67 self.rpyps2 = re.compile (r'(\s*%s)(.*)$' % self.pyps2)
68
68
69 def __call__(self, ds):
69 def __call__(self, ds):
70 """Convert IPython prompts to python ones in a string."""
70 """Convert IPython prompts to python ones in a string."""
71 from . import globalipapp
71 from . import globalipapp
72
72
73 pyps1 = '>>> '
73 pyps1 = '>>> '
74 pyps2 = '... '
74 pyps2 = '... '
75 pyout = ''
75 pyout = ''
76
76
77 dnew = ds
77 dnew = ds
78 dnew = self.rps1.sub(pyps1, dnew)
78 dnew = self.rps1.sub(pyps1, dnew)
79 dnew = self.rps2.sub(pyps2, dnew)
79 dnew = self.rps2.sub(pyps2, dnew)
80 dnew = self.rout.sub(pyout, dnew)
80 dnew = self.rout.sub(pyout, dnew)
81 ip = globalipapp.get_ipython()
81 ip = globalipapp.get_ipython()
82
82
83 # Convert input IPython source into valid Python.
83 # Convert input IPython source into valid Python.
84 out = []
84 out = []
85 newline = out.append
85 newline = out.append
86 for line in dnew.splitlines():
86 for line in dnew.splitlines():
87
87
88 mps1 = self.rpyps1.match(line)
88 mps1 = self.rpyps1.match(line)
89 if mps1 is not None:
89 if mps1 is not None:
90 prompt, text = mps1.groups()
90 prompt, text = mps1.groups()
91 newline(prompt+ip.prefilter(text, False))
91 newline(prompt+ip.prefilter(text, False))
92 continue
92 continue
93
93
94 mps2 = self.rpyps2.match(line)
94 mps2 = self.rpyps2.match(line)
95 if mps2 is not None:
95 if mps2 is not None:
96 prompt, text = mps2.groups()
96 prompt, text = mps2.groups()
97 newline(prompt+ip.prefilter(text, True))
97 newline(prompt+ip.prefilter(text, True))
98 continue
98 continue
99
99
100 newline(line)
100 newline(line)
101 newline('') # ensure a closing newline, needed by doctest
101 newline('') # ensure a closing newline, needed by doctest
102 #print "PYSRC:", '\n'.join(out) # dbg
102 #print "PYSRC:", '\n'.join(out) # dbg
103 return '\n'.join(out)
103 return '\n'.join(out)
104
104
105 #return dnew
105 #return dnew
106
106
107
107
108 class Doc2UnitTester(object):
108 class Doc2UnitTester(object):
109 """Class whose instances act as a decorator for docstring testing.
109 """Class whose instances act as a decorator for docstring testing.
110
110
111 In practice we're only likely to need one instance ever, made below (though
111 In practice we're only likely to need one instance ever, made below (though
112 no attempt is made at turning it into a singleton, there is no need for
112 no attempt is made at turning it into a singleton, there is no need for
113 that).
113 that).
114 """
114 """
115 def __init__(self, verbose=False):
115 def __init__(self, verbose=False):
116 """New decorator.
116 """New decorator.
117
117
118 Parameters
118 Parameters
119 ----------
119 ----------
120
120
121 verbose : boolean, optional (False)
121 verbose : boolean, optional (False)
122 Passed to the doctest finder and runner to control verbosity.
122 Passed to the doctest finder and runner to control verbosity.
123 """
123 """
124 self.verbose = verbose
124 self.verbose = verbose
125 # We can reuse the same finder for all instances
125 # We can reuse the same finder for all instances
126 self.finder = DocTestFinder(verbose=verbose, recurse=False)
126 self.finder = DocTestFinder(verbose=verbose, recurse=False)
127
127
128 def __call__(self, func):
128 def __call__(self, func):
129 """Use as a decorator: doctest a function's docstring as a unittest.
129 """Use as a decorator: doctest a function's docstring as a unittest.
130
130
131 This version runs normal doctests, but the idea is to make it later run
131 This version runs normal doctests, but the idea is to make it later run
132 ipython syntax instead."""
132 ipython syntax instead."""
133
133
134 # Capture the enclosing instance with a different name, so the new
134 # Capture the enclosing instance with a different name, so the new
135 # class below can see it without confusion regarding its own 'self'
135 # class below can see it without confusion regarding its own 'self'
136 # that will point to the test instance at runtime
136 # that will point to the test instance at runtime
137 d2u = self
137 d2u = self
138
138
139 # Rewrite the function's docstring to have python syntax
139 # Rewrite the function's docstring to have python syntax
140 if func.__doc__ is not None:
140 if func.__doc__ is not None:
141 func.__doc__ = ip2py(func.__doc__)
141 func.__doc__ = ip2py(func.__doc__)
142
142
143 # Now, create a tester object that is a real unittest instance, so
143 # Now, create a tester object that is a real unittest instance, so
144 # normal unittest machinery (or Nose, or Trial) can find it.
144 # normal unittest machinery (or Nose, or Trial) can find it.
145 class Tester(unittest.TestCase):
145 class Tester(unittest.TestCase):
146 def test(self):
146 def test(self):
147 # Make a new runner per function to be tested
147 # Make a new runner per function to be tested
148 runner = DocTestRunner(verbose=d2u.verbose)
148 runner = DocTestRunner(verbose=d2u.verbose)
149 for the_test in d2u.finder.find(func, func.__name__):
149 for the_test in d2u.finder.find(func, func.__name__):
150 runner.run(the_test)
150 runner.run(the_test)
151 failed = count_failures(runner)
151 failed = count_failures(runner)
152 if failed:
152 if failed:
153 # Since we only looked at a single function's docstring,
153 # Since we only looked at a single function's docstring,
154 # failed should contain at most one item. More than that
154 # failed should contain at most one item. More than that
155 # is a case we can't handle and should error out on
155 # is a case we can't handle and should error out on
156 if len(failed) > 1:
156 if len(failed) > 1:
157 err = "Invalid number of test results:" % failed
157 err = "Invalid number of test results:" % failed
158 raise ValueError(err)
158 raise ValueError(err)
159 # Report a normal failure.
159 # Report a normal failure.
160 self.fail('failed doctests: %s' % str(failed[0]))
160 self.fail('failed doctests: %s' % str(failed[0]))
161
161
162 # Rename it so test reports have the original signature.
162 # Rename it so test reports have the original signature.
163 Tester.__name__ = func.__name__
163 Tester.__name__ = func.__name__
164 return Tester
164 return Tester
165
165
166
166
167 def ipdocstring(func):
167 def ipdocstring(func):
168 """Change the function docstring via ip2py.
168 """Change the function docstring via ip2py.
169 """
169 """
170 if func.__doc__ is not None:
170 if func.__doc__ is not None:
171 func.__doc__ = ip2py(func.__doc__)
171 func.__doc__ = ip2py(func.__doc__)
172 return func
172 return func
173
173
174
174
175 # Make an instance of the classes for public use
175 # Make an instance of the classes for public use
176 ipdoctest = Doc2UnitTester()
176 ipdoctest = Doc2UnitTester()
177 ip2py = IPython2PythonConverter()
177 ip2py = IPython2PythonConverter()
General Comments 0
You need to be logged in to leave comments. Login now