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