##// END OF EJS Templates
Merge pull request #13548 from Carreau/no-network...
Matthias Bussonnier -
r27567:9ed8ecd6 merge
parent child Browse files
Show More
@@ -1,1277 +1,1277 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Top-level display functions for displaying object in different formats."""
2 """Top-level display functions for displaying object in different formats."""
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7
7
8 from binascii import b2a_base64, hexlify
8 from binascii import b2a_base64, hexlify
9 import html
9 import html
10 import json
10 import json
11 import mimetypes
11 import mimetypes
12 import os
12 import os
13 import struct
13 import struct
14 import warnings
14 import warnings
15 from copy import deepcopy
15 from copy import deepcopy
16 from os.path import splitext
16 from os.path import splitext
17 from pathlib import Path, PurePath
17 from pathlib import Path, PurePath
18
18
19 from IPython.utils.py3compat import cast_unicode
19 from IPython.utils.py3compat import cast_unicode
20 from IPython.testing.skipdoctest import skip_doctest
20 from IPython.testing.skipdoctest import skip_doctest
21 from . import display_functions
21 from . import display_functions
22
22
23
23
24 __all__ = ['display_pretty', 'display_html', 'display_markdown',
24 __all__ = ['display_pretty', 'display_html', 'display_markdown',
25 'display_svg', 'display_png', 'display_jpeg', 'display_latex', 'display_json',
25 'display_svg', 'display_png', 'display_jpeg', 'display_latex', 'display_json',
26 'display_javascript', 'display_pdf', 'DisplayObject', 'TextDisplayObject',
26 'display_javascript', 'display_pdf', 'DisplayObject', 'TextDisplayObject',
27 'Pretty', 'HTML', 'Markdown', 'Math', 'Latex', 'SVG', 'ProgressBar', 'JSON',
27 'Pretty', 'HTML', 'Markdown', 'Math', 'Latex', 'SVG', 'ProgressBar', 'JSON',
28 'GeoJSON', 'Javascript', 'Image', 'set_matplotlib_formats',
28 'GeoJSON', 'Javascript', 'Image', 'set_matplotlib_formats',
29 'set_matplotlib_close',
29 'set_matplotlib_close',
30 'Video']
30 'Video']
31
31
32 _deprecated_names = ["display", "clear_output", "publish_display_data", "update_display", "DisplayHandle"]
32 _deprecated_names = ["display", "clear_output", "publish_display_data", "update_display", "DisplayHandle"]
33
33
34 __all__ = __all__ + _deprecated_names
34 __all__ = __all__ + _deprecated_names
35
35
36
36
37 # ----- warn to import from IPython.display -----
37 # ----- warn to import from IPython.display -----
38
38
39 from warnings import warn
39 from warnings import warn
40
40
41
41
42 def __getattr__(name):
42 def __getattr__(name):
43 if name in _deprecated_names:
43 if name in _deprecated_names:
44 warn(f"Importing {name} from IPython.core.display is deprecated since IPython 7.14, please import from IPython display", DeprecationWarning, stacklevel=2)
44 warn(f"Importing {name} from IPython.core.display is deprecated since IPython 7.14, please import from IPython display", DeprecationWarning, stacklevel=2)
45 return getattr(display_functions, name)
45 return getattr(display_functions, name)
46
46
47 if name in globals().keys():
47 if name in globals().keys():
48 return globals()[name]
48 return globals()[name]
49 else:
49 else:
50 raise AttributeError(f"module {__name__} has no attribute {name}")
50 raise AttributeError(f"module {__name__} has no attribute {name}")
51
51
52
52
53 #-----------------------------------------------------------------------------
53 #-----------------------------------------------------------------------------
54 # utility functions
54 # utility functions
55 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
56
56
57 def _safe_exists(path):
57 def _safe_exists(path):
58 """Check path, but don't let exceptions raise"""
58 """Check path, but don't let exceptions raise"""
59 try:
59 try:
60 return os.path.exists(path)
60 return os.path.exists(path)
61 except Exception:
61 except Exception:
62 return False
62 return False
63
63
64
64
65 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
65 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
66 """internal implementation of all display_foo methods
66 """internal implementation of all display_foo methods
67
67
68 Parameters
68 Parameters
69 ----------
69 ----------
70 mimetype : str
70 mimetype : str
71 The mimetype to be published (e.g. 'image/png')
71 The mimetype to be published (e.g. 'image/png')
72 *objs : object
72 *objs : object
73 The Python objects to display, or if raw=True raw text data to
73 The Python objects to display, or if raw=True raw text data to
74 display.
74 display.
75 raw : bool
75 raw : bool
76 Are the data objects raw data or Python objects that need to be
76 Are the data objects raw data or Python objects that need to be
77 formatted before display? [default: False]
77 formatted before display? [default: False]
78 metadata : dict (optional)
78 metadata : dict (optional)
79 Metadata to be associated with the specific mimetype output.
79 Metadata to be associated with the specific mimetype output.
80 """
80 """
81 if metadata:
81 if metadata:
82 metadata = {mimetype: metadata}
82 metadata = {mimetype: metadata}
83 if raw:
83 if raw:
84 # turn list of pngdata into list of { 'image/png': pngdata }
84 # turn list of pngdata into list of { 'image/png': pngdata }
85 objs = [ {mimetype: obj} for obj in objs ]
85 objs = [ {mimetype: obj} for obj in objs ]
86 display_functions.display(*objs, raw=raw, metadata=metadata, include=[mimetype])
86 display_functions.display(*objs, raw=raw, metadata=metadata, include=[mimetype])
87
87
88 #-----------------------------------------------------------------------------
88 #-----------------------------------------------------------------------------
89 # Main functions
89 # Main functions
90 #-----------------------------------------------------------------------------
90 #-----------------------------------------------------------------------------
91
91
92
92
93 def display_pretty(*objs, **kwargs):
93 def display_pretty(*objs, **kwargs):
94 """Display the pretty (default) representation of an object.
94 """Display the pretty (default) representation of an object.
95
95
96 Parameters
96 Parameters
97 ----------
97 ----------
98 *objs : object
98 *objs : object
99 The Python objects to display, or if raw=True raw text data to
99 The Python objects to display, or if raw=True raw text data to
100 display.
100 display.
101 raw : bool
101 raw : bool
102 Are the data objects raw data or Python objects that need to be
102 Are the data objects raw data or Python objects that need to be
103 formatted before display? [default: False]
103 formatted before display? [default: False]
104 metadata : dict (optional)
104 metadata : dict (optional)
105 Metadata to be associated with the specific mimetype output.
105 Metadata to be associated with the specific mimetype output.
106 """
106 """
107 _display_mimetype('text/plain', objs, **kwargs)
107 _display_mimetype('text/plain', objs, **kwargs)
108
108
109
109
110 def display_html(*objs, **kwargs):
110 def display_html(*objs, **kwargs):
111 """Display the HTML representation of an object.
111 """Display the HTML representation of an object.
112
112
113 Note: If raw=False and the object does not have a HTML
113 Note: If raw=False and the object does not have a HTML
114 representation, no HTML will be shown.
114 representation, no HTML will be shown.
115
115
116 Parameters
116 Parameters
117 ----------
117 ----------
118 *objs : object
118 *objs : object
119 The Python objects to display, or if raw=True raw HTML data to
119 The Python objects to display, or if raw=True raw HTML data to
120 display.
120 display.
121 raw : bool
121 raw : bool
122 Are the data objects raw data or Python objects that need to be
122 Are the data objects raw data or Python objects that need to be
123 formatted before display? [default: False]
123 formatted before display? [default: False]
124 metadata : dict (optional)
124 metadata : dict (optional)
125 Metadata to be associated with the specific mimetype output.
125 Metadata to be associated with the specific mimetype output.
126 """
126 """
127 _display_mimetype('text/html', objs, **kwargs)
127 _display_mimetype('text/html', objs, **kwargs)
128
128
129
129
130 def display_markdown(*objs, **kwargs):
130 def display_markdown(*objs, **kwargs):
131 """Displays the Markdown representation of an object.
131 """Displays the Markdown representation of an object.
132
132
133 Parameters
133 Parameters
134 ----------
134 ----------
135 *objs : object
135 *objs : object
136 The Python objects to display, or if raw=True raw markdown data to
136 The Python objects to display, or if raw=True raw markdown data to
137 display.
137 display.
138 raw : bool
138 raw : bool
139 Are the data objects raw data or Python objects that need to be
139 Are the data objects raw data or Python objects that need to be
140 formatted before display? [default: False]
140 formatted before display? [default: False]
141 metadata : dict (optional)
141 metadata : dict (optional)
142 Metadata to be associated with the specific mimetype output.
142 Metadata to be associated with the specific mimetype output.
143 """
143 """
144
144
145 _display_mimetype('text/markdown', objs, **kwargs)
145 _display_mimetype('text/markdown', objs, **kwargs)
146
146
147
147
148 def display_svg(*objs, **kwargs):
148 def display_svg(*objs, **kwargs):
149 """Display the SVG representation of an object.
149 """Display the SVG representation of an object.
150
150
151 Parameters
151 Parameters
152 ----------
152 ----------
153 *objs : object
153 *objs : object
154 The Python objects to display, or if raw=True raw svg data to
154 The Python objects to display, or if raw=True raw svg data to
155 display.
155 display.
156 raw : bool
156 raw : bool
157 Are the data objects raw data or Python objects that need to be
157 Are the data objects raw data or Python objects that need to be
158 formatted before display? [default: False]
158 formatted before display? [default: False]
159 metadata : dict (optional)
159 metadata : dict (optional)
160 Metadata to be associated with the specific mimetype output.
160 Metadata to be associated with the specific mimetype output.
161 """
161 """
162 _display_mimetype('image/svg+xml', objs, **kwargs)
162 _display_mimetype('image/svg+xml', objs, **kwargs)
163
163
164
164
165 def display_png(*objs, **kwargs):
165 def display_png(*objs, **kwargs):
166 """Display the PNG representation of an object.
166 """Display the PNG representation of an object.
167
167
168 Parameters
168 Parameters
169 ----------
169 ----------
170 *objs : object
170 *objs : object
171 The Python objects to display, or if raw=True raw png data to
171 The Python objects to display, or if raw=True raw png data to
172 display.
172 display.
173 raw : bool
173 raw : bool
174 Are the data objects raw data or Python objects that need to be
174 Are the data objects raw data or Python objects that need to be
175 formatted before display? [default: False]
175 formatted before display? [default: False]
176 metadata : dict (optional)
176 metadata : dict (optional)
177 Metadata to be associated with the specific mimetype output.
177 Metadata to be associated with the specific mimetype output.
178 """
178 """
179 _display_mimetype('image/png', objs, **kwargs)
179 _display_mimetype('image/png', objs, **kwargs)
180
180
181
181
182 def display_jpeg(*objs, **kwargs):
182 def display_jpeg(*objs, **kwargs):
183 """Display the JPEG representation of an object.
183 """Display the JPEG representation of an object.
184
184
185 Parameters
185 Parameters
186 ----------
186 ----------
187 *objs : object
187 *objs : object
188 The Python objects to display, or if raw=True raw JPEG data to
188 The Python objects to display, or if raw=True raw JPEG data to
189 display.
189 display.
190 raw : bool
190 raw : bool
191 Are the data objects raw data or Python objects that need to be
191 Are the data objects raw data or Python objects that need to be
192 formatted before display? [default: False]
192 formatted before display? [default: False]
193 metadata : dict (optional)
193 metadata : dict (optional)
194 Metadata to be associated with the specific mimetype output.
194 Metadata to be associated with the specific mimetype output.
195 """
195 """
196 _display_mimetype('image/jpeg', objs, **kwargs)
196 _display_mimetype('image/jpeg', objs, **kwargs)
197
197
198
198
199 def display_latex(*objs, **kwargs):
199 def display_latex(*objs, **kwargs):
200 """Display the LaTeX representation of an object.
200 """Display the LaTeX representation of an object.
201
201
202 Parameters
202 Parameters
203 ----------
203 ----------
204 *objs : object
204 *objs : object
205 The Python objects to display, or if raw=True raw latex data to
205 The Python objects to display, or if raw=True raw latex data to
206 display.
206 display.
207 raw : bool
207 raw : bool
208 Are the data objects raw data or Python objects that need to be
208 Are the data objects raw data or Python objects that need to be
209 formatted before display? [default: False]
209 formatted before display? [default: False]
210 metadata : dict (optional)
210 metadata : dict (optional)
211 Metadata to be associated with the specific mimetype output.
211 Metadata to be associated with the specific mimetype output.
212 """
212 """
213 _display_mimetype('text/latex', objs, **kwargs)
213 _display_mimetype('text/latex', objs, **kwargs)
214
214
215
215
216 def display_json(*objs, **kwargs):
216 def display_json(*objs, **kwargs):
217 """Display the JSON representation of an object.
217 """Display the JSON representation of an object.
218
218
219 Note that not many frontends support displaying JSON.
219 Note that not many frontends support displaying JSON.
220
220
221 Parameters
221 Parameters
222 ----------
222 ----------
223 *objs : object
223 *objs : object
224 The Python objects to display, or if raw=True raw json data to
224 The Python objects to display, or if raw=True raw json data to
225 display.
225 display.
226 raw : bool
226 raw : bool
227 Are the data objects raw data or Python objects that need to be
227 Are the data objects raw data or Python objects that need to be
228 formatted before display? [default: False]
228 formatted before display? [default: False]
229 metadata : dict (optional)
229 metadata : dict (optional)
230 Metadata to be associated with the specific mimetype output.
230 Metadata to be associated with the specific mimetype output.
231 """
231 """
232 _display_mimetype('application/json', objs, **kwargs)
232 _display_mimetype('application/json', objs, **kwargs)
233
233
234
234
235 def display_javascript(*objs, **kwargs):
235 def display_javascript(*objs, **kwargs):
236 """Display the Javascript representation of an object.
236 """Display the Javascript representation of an object.
237
237
238 Parameters
238 Parameters
239 ----------
239 ----------
240 *objs : object
240 *objs : object
241 The Python objects to display, or if raw=True raw javascript data to
241 The Python objects to display, or if raw=True raw javascript data to
242 display.
242 display.
243 raw : bool
243 raw : bool
244 Are the data objects raw data or Python objects that need to be
244 Are the data objects raw data or Python objects that need to be
245 formatted before display? [default: False]
245 formatted before display? [default: False]
246 metadata : dict (optional)
246 metadata : dict (optional)
247 Metadata to be associated with the specific mimetype output.
247 Metadata to be associated with the specific mimetype output.
248 """
248 """
249 _display_mimetype('application/javascript', objs, **kwargs)
249 _display_mimetype('application/javascript', objs, **kwargs)
250
250
251
251
252 def display_pdf(*objs, **kwargs):
252 def display_pdf(*objs, **kwargs):
253 """Display the PDF representation of an object.
253 """Display the PDF representation of an object.
254
254
255 Parameters
255 Parameters
256 ----------
256 ----------
257 *objs : object
257 *objs : object
258 The Python objects to display, or if raw=True raw javascript data to
258 The Python objects to display, or if raw=True raw javascript data to
259 display.
259 display.
260 raw : bool
260 raw : bool
261 Are the data objects raw data or Python objects that need to be
261 Are the data objects raw data or Python objects that need to be
262 formatted before display? [default: False]
262 formatted before display? [default: False]
263 metadata : dict (optional)
263 metadata : dict (optional)
264 Metadata to be associated with the specific mimetype output.
264 Metadata to be associated with the specific mimetype output.
265 """
265 """
266 _display_mimetype('application/pdf', objs, **kwargs)
266 _display_mimetype('application/pdf', objs, **kwargs)
267
267
268
268
269 #-----------------------------------------------------------------------------
269 #-----------------------------------------------------------------------------
270 # Smart classes
270 # Smart classes
271 #-----------------------------------------------------------------------------
271 #-----------------------------------------------------------------------------
272
272
273
273
274 class DisplayObject(object):
274 class DisplayObject(object):
275 """An object that wraps data to be displayed."""
275 """An object that wraps data to be displayed."""
276
276
277 _read_flags = 'r'
277 _read_flags = 'r'
278 _show_mem_addr = False
278 _show_mem_addr = False
279 metadata = None
279 metadata = None
280
280
281 def __init__(self, data=None, url=None, filename=None, metadata=None):
281 def __init__(self, data=None, url=None, filename=None, metadata=None):
282 """Create a display object given raw data.
282 """Create a display object given raw data.
283
283
284 When this object is returned by an expression or passed to the
284 When this object is returned by an expression or passed to the
285 display function, it will result in the data being displayed
285 display function, it will result in the data being displayed
286 in the frontend. The MIME type of the data should match the
286 in the frontend. The MIME type of the data should match the
287 subclasses used, so the Png subclass should be used for 'image/png'
287 subclasses used, so the Png subclass should be used for 'image/png'
288 data. If the data is a URL, the data will first be downloaded
288 data. If the data is a URL, the data will first be downloaded
289 and then displayed. If
289 and then displayed. If
290
290
291 Parameters
291 Parameters
292 ----------
292 ----------
293 data : unicode, str or bytes
293 data : unicode, str or bytes
294 The raw data or a URL or file to load the data from
294 The raw data or a URL or file to load the data from
295 url : unicode
295 url : unicode
296 A URL to download the data from.
296 A URL to download the data from.
297 filename : unicode
297 filename : unicode
298 Path to a local file to load the data from.
298 Path to a local file to load the data from.
299 metadata : dict
299 metadata : dict
300 Dict of metadata associated to be the object when displayed
300 Dict of metadata associated to be the object when displayed
301 """
301 """
302 if isinstance(data, (Path, PurePath)):
302 if isinstance(data, (Path, PurePath)):
303 data = str(data)
303 data = str(data)
304
304
305 if data is not None and isinstance(data, str):
305 if data is not None and isinstance(data, str):
306 if data.startswith('http') and url is None:
306 if data.startswith('http') and url is None:
307 url = data
307 url = data
308 filename = None
308 filename = None
309 data = None
309 data = None
310 elif _safe_exists(data) and filename is None:
310 elif _safe_exists(data) and filename is None:
311 url = None
311 url = None
312 filename = data
312 filename = data
313 data = None
313 data = None
314
314
315 self.url = url
315 self.url = url
316 self.filename = filename
316 self.filename = filename
317 # because of @data.setter methods in
317 # because of @data.setter methods in
318 # subclasses ensure url and filename are set
318 # subclasses ensure url and filename are set
319 # before assigning to self.data
319 # before assigning to self.data
320 self.data = data
320 self.data = data
321
321
322 if metadata is not None:
322 if metadata is not None:
323 self.metadata = metadata
323 self.metadata = metadata
324 elif self.metadata is None:
324 elif self.metadata is None:
325 self.metadata = {}
325 self.metadata = {}
326
326
327 self.reload()
327 self.reload()
328 self._check_data()
328 self._check_data()
329
329
330 def __repr__(self):
330 def __repr__(self):
331 if not self._show_mem_addr:
331 if not self._show_mem_addr:
332 cls = self.__class__
332 cls = self.__class__
333 r = "<%s.%s object>" % (cls.__module__, cls.__name__)
333 r = "<%s.%s object>" % (cls.__module__, cls.__name__)
334 else:
334 else:
335 r = super(DisplayObject, self).__repr__()
335 r = super(DisplayObject, self).__repr__()
336 return r
336 return r
337
337
338 def _check_data(self):
338 def _check_data(self):
339 """Override in subclasses if there's something to check."""
339 """Override in subclasses if there's something to check."""
340 pass
340 pass
341
341
342 def _data_and_metadata(self):
342 def _data_and_metadata(self):
343 """shortcut for returning metadata with shape information, if defined"""
343 """shortcut for returning metadata with shape information, if defined"""
344 if self.metadata:
344 if self.metadata:
345 return self.data, deepcopy(self.metadata)
345 return self.data, deepcopy(self.metadata)
346 else:
346 else:
347 return self.data
347 return self.data
348
348
349 def reload(self):
349 def reload(self):
350 """Reload the raw data from file or URL."""
350 """Reload the raw data from file or URL."""
351 if self.filename is not None:
351 if self.filename is not None:
352 encoding = None if "b" in self._read_flags else "utf-8"
352 encoding = None if "b" in self._read_flags else "utf-8"
353 with open(self.filename, self._read_flags, encoding=encoding) as f:
353 with open(self.filename, self._read_flags, encoding=encoding) as f:
354 self.data = f.read()
354 self.data = f.read()
355 elif self.url is not None:
355 elif self.url is not None:
356 # Deferred import
356 # Deferred import
357 from urllib.request import urlopen
357 from urllib.request import urlopen
358 response = urlopen(self.url)
358 response = urlopen(self.url)
359 data = response.read()
359 data = response.read()
360 # extract encoding from header, if there is one:
360 # extract encoding from header, if there is one:
361 encoding = None
361 encoding = None
362 if 'content-type' in response.headers:
362 if 'content-type' in response.headers:
363 for sub in response.headers['content-type'].split(';'):
363 for sub in response.headers['content-type'].split(';'):
364 sub = sub.strip()
364 sub = sub.strip()
365 if sub.startswith('charset'):
365 if sub.startswith('charset'):
366 encoding = sub.split('=')[-1].strip()
366 encoding = sub.split('=')[-1].strip()
367 break
367 break
368 if 'content-encoding' in response.headers:
368 if 'content-encoding' in response.headers:
369 # TODO: do deflate?
369 # TODO: do deflate?
370 if 'gzip' in response.headers['content-encoding']:
370 if 'gzip' in response.headers['content-encoding']:
371 import gzip
371 import gzip
372 from io import BytesIO
372 from io import BytesIO
373
373
374 # assume utf-8 if encoding is not specified
374 # assume utf-8 if encoding is not specified
375 with gzip.open(
375 with gzip.open(
376 BytesIO(data), "rt", encoding=encoding or "utf-8"
376 BytesIO(data), "rt", encoding=encoding or "utf-8"
377 ) as fp:
377 ) as fp:
378 encoding = None
378 encoding = None
379 data = fp.read()
379 data = fp.read()
380
380
381 # decode data, if an encoding was specified
381 # decode data, if an encoding was specified
382 # We only touch self.data once since
382 # We only touch self.data once since
383 # subclasses such as SVG have @data.setter methods
383 # subclasses such as SVG have @data.setter methods
384 # that transform self.data into ... well svg.
384 # that transform self.data into ... well svg.
385 if encoding:
385 if encoding:
386 self.data = data.decode(encoding, 'replace')
386 self.data = data.decode(encoding, 'replace')
387 else:
387 else:
388 self.data = data
388 self.data = data
389
389
390
390
391 class TextDisplayObject(DisplayObject):
391 class TextDisplayObject(DisplayObject):
392 """Validate that display data is text"""
392 """Validate that display data is text"""
393 def _check_data(self):
393 def _check_data(self):
394 if self.data is not None and not isinstance(self.data, str):
394 if self.data is not None and not isinstance(self.data, str):
395 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
395 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
396
396
397 class Pretty(TextDisplayObject):
397 class Pretty(TextDisplayObject):
398
398
399 def _repr_pretty_(self, pp, cycle):
399 def _repr_pretty_(self, pp, cycle):
400 return pp.text(self.data)
400 return pp.text(self.data)
401
401
402
402
403 class HTML(TextDisplayObject):
403 class HTML(TextDisplayObject):
404
404
405 def __init__(self, data=None, url=None, filename=None, metadata=None):
405 def __init__(self, data=None, url=None, filename=None, metadata=None):
406 def warn():
406 def warn():
407 if not data:
407 if not data:
408 return False
408 return False
409
409
410 #
410 #
411 # Avoid calling lower() on the entire data, because it could be a
411 # Avoid calling lower() on the entire data, because it could be a
412 # long string and we're only interested in its beginning and end.
412 # long string and we're only interested in its beginning and end.
413 #
413 #
414 prefix = data[:10].lower()
414 prefix = data[:10].lower()
415 suffix = data[-10:].lower()
415 suffix = data[-10:].lower()
416 return prefix.startswith("<iframe ") and suffix.endswith("</iframe>")
416 return prefix.startswith("<iframe ") and suffix.endswith("</iframe>")
417
417
418 if warn():
418 if warn():
419 warnings.warn("Consider using IPython.display.IFrame instead")
419 warnings.warn("Consider using IPython.display.IFrame instead")
420 super(HTML, self).__init__(data=data, url=url, filename=filename, metadata=metadata)
420 super(HTML, self).__init__(data=data, url=url, filename=filename, metadata=metadata)
421
421
422 def _repr_html_(self):
422 def _repr_html_(self):
423 return self._data_and_metadata()
423 return self._data_and_metadata()
424
424
425 def __html__(self):
425 def __html__(self):
426 """
426 """
427 This method exists to inform other HTML-using modules (e.g. Markupsafe,
427 This method exists to inform other HTML-using modules (e.g. Markupsafe,
428 htmltag, etc) that this object is HTML and does not need things like
428 htmltag, etc) that this object is HTML and does not need things like
429 special characters (<>&) escaped.
429 special characters (<>&) escaped.
430 """
430 """
431 return self._repr_html_()
431 return self._repr_html_()
432
432
433
433
434 class Markdown(TextDisplayObject):
434 class Markdown(TextDisplayObject):
435
435
436 def _repr_markdown_(self):
436 def _repr_markdown_(self):
437 return self._data_and_metadata()
437 return self._data_and_metadata()
438
438
439
439
440 class Math(TextDisplayObject):
440 class Math(TextDisplayObject):
441
441
442 def _repr_latex_(self):
442 def _repr_latex_(self):
443 s = r"$\displaystyle %s$" % self.data.strip('$')
443 s = r"$\displaystyle %s$" % self.data.strip('$')
444 if self.metadata:
444 if self.metadata:
445 return s, deepcopy(self.metadata)
445 return s, deepcopy(self.metadata)
446 else:
446 else:
447 return s
447 return s
448
448
449
449
450 class Latex(TextDisplayObject):
450 class Latex(TextDisplayObject):
451
451
452 def _repr_latex_(self):
452 def _repr_latex_(self):
453 return self._data_and_metadata()
453 return self._data_and_metadata()
454
454
455
455
456 class SVG(DisplayObject):
456 class SVG(DisplayObject):
457 """Embed an SVG into the display.
457 """Embed an SVG into the display.
458
458
459 Note if you just want to view a svg image via a URL use `:class:Image` with
459 Note if you just want to view a svg image via a URL use `:class:Image` with
460 a url=URL keyword argument.
460 a url=URL keyword argument.
461 """
461 """
462
462
463 _read_flags = 'rb'
463 _read_flags = 'rb'
464 # wrap data in a property, which extracts the <svg> tag, discarding
464 # wrap data in a property, which extracts the <svg> tag, discarding
465 # document headers
465 # document headers
466 _data = None
466 _data = None
467
467
468 @property
468 @property
469 def data(self):
469 def data(self):
470 return self._data
470 return self._data
471
471
472 @data.setter
472 @data.setter
473 def data(self, svg):
473 def data(self, svg):
474 if svg is None:
474 if svg is None:
475 self._data = None
475 self._data = None
476 return
476 return
477 # parse into dom object
477 # parse into dom object
478 from xml.dom import minidom
478 from xml.dom import minidom
479 x = minidom.parseString(svg)
479 x = minidom.parseString(svg)
480 # get svg tag (should be 1)
480 # get svg tag (should be 1)
481 found_svg = x.getElementsByTagName('svg')
481 found_svg = x.getElementsByTagName('svg')
482 if found_svg:
482 if found_svg:
483 svg = found_svg[0].toxml()
483 svg = found_svg[0].toxml()
484 else:
484 else:
485 # fallback on the input, trust the user
485 # fallback on the input, trust the user
486 # but this is probably an error.
486 # but this is probably an error.
487 pass
487 pass
488 svg = cast_unicode(svg)
488 svg = cast_unicode(svg)
489 self._data = svg
489 self._data = svg
490
490
491 def _repr_svg_(self):
491 def _repr_svg_(self):
492 return self._data_and_metadata()
492 return self._data_and_metadata()
493
493
494 class ProgressBar(DisplayObject):
494 class ProgressBar(DisplayObject):
495 """Progressbar supports displaying a progressbar like element
495 """Progressbar supports displaying a progressbar like element
496 """
496 """
497 def __init__(self, total):
497 def __init__(self, total):
498 """Creates a new progressbar
498 """Creates a new progressbar
499
499
500 Parameters
500 Parameters
501 ----------
501 ----------
502 total : int
502 total : int
503 maximum size of the progressbar
503 maximum size of the progressbar
504 """
504 """
505 self.total = total
505 self.total = total
506 self._progress = 0
506 self._progress = 0
507 self.html_width = '60ex'
507 self.html_width = '60ex'
508 self.text_width = 60
508 self.text_width = 60
509 self._display_id = hexlify(os.urandom(8)).decode('ascii')
509 self._display_id = hexlify(os.urandom(8)).decode('ascii')
510
510
511 def __repr__(self):
511 def __repr__(self):
512 fraction = self.progress / self.total
512 fraction = self.progress / self.total
513 filled = '=' * int(fraction * self.text_width)
513 filled = '=' * int(fraction * self.text_width)
514 rest = ' ' * (self.text_width - len(filled))
514 rest = ' ' * (self.text_width - len(filled))
515 return '[{}{}] {}/{}'.format(
515 return '[{}{}] {}/{}'.format(
516 filled, rest,
516 filled, rest,
517 self.progress, self.total,
517 self.progress, self.total,
518 )
518 )
519
519
520 def _repr_html_(self):
520 def _repr_html_(self):
521 return "<progress style='width:{}' max='{}' value='{}'></progress>".format(
521 return "<progress style='width:{}' max='{}' value='{}'></progress>".format(
522 self.html_width, self.total, self.progress)
522 self.html_width, self.total, self.progress)
523
523
524 def display(self):
524 def display(self):
525 display_functions.display(self, display_id=self._display_id)
525 display_functions.display(self, display_id=self._display_id)
526
526
527 def update(self):
527 def update(self):
528 display_functions.display(self, display_id=self._display_id, update=True)
528 display_functions.display(self, display_id=self._display_id, update=True)
529
529
530 @property
530 @property
531 def progress(self):
531 def progress(self):
532 return self._progress
532 return self._progress
533
533
534 @progress.setter
534 @progress.setter
535 def progress(self, value):
535 def progress(self, value):
536 self._progress = value
536 self._progress = value
537 self.update()
537 self.update()
538
538
539 def __iter__(self):
539 def __iter__(self):
540 self.display()
540 self.display()
541 self._progress = -1 # First iteration is 0
541 self._progress = -1 # First iteration is 0
542 return self
542 return self
543
543
544 def __next__(self):
544 def __next__(self):
545 """Returns current value and increments display by one."""
545 """Returns current value and increments display by one."""
546 self.progress += 1
546 self.progress += 1
547 if self.progress < self.total:
547 if self.progress < self.total:
548 return self.progress
548 return self.progress
549 else:
549 else:
550 raise StopIteration()
550 raise StopIteration()
551
551
552 class JSON(DisplayObject):
552 class JSON(DisplayObject):
553 """JSON expects a JSON-able dict or list
553 """JSON expects a JSON-able dict or list
554
554
555 not an already-serialized JSON string.
555 not an already-serialized JSON string.
556
556
557 Scalar types (None, number, string) are not allowed, only dict or list containers.
557 Scalar types (None, number, string) are not allowed, only dict or list containers.
558 """
558 """
559 # wrap data in a property, which warns about passing already-serialized JSON
559 # wrap data in a property, which warns about passing already-serialized JSON
560 _data = None
560 _data = None
561 def __init__(self, data=None, url=None, filename=None, expanded=False, metadata=None, root='root', **kwargs):
561 def __init__(self, data=None, url=None, filename=None, expanded=False, metadata=None, root='root', **kwargs):
562 """Create a JSON display object given raw data.
562 """Create a JSON display object given raw data.
563
563
564 Parameters
564 Parameters
565 ----------
565 ----------
566 data : dict or list
566 data : dict or list
567 JSON data to display. Not an already-serialized JSON string.
567 JSON data to display. Not an already-serialized JSON string.
568 Scalar types (None, number, string) are not allowed, only dict
568 Scalar types (None, number, string) are not allowed, only dict
569 or list containers.
569 or list containers.
570 url : unicode
570 url : unicode
571 A URL to download the data from.
571 A URL to download the data from.
572 filename : unicode
572 filename : unicode
573 Path to a local file to load the data from.
573 Path to a local file to load the data from.
574 expanded : boolean
574 expanded : boolean
575 Metadata to control whether a JSON display component is expanded.
575 Metadata to control whether a JSON display component is expanded.
576 metadata : dict
576 metadata : dict
577 Specify extra metadata to attach to the json display object.
577 Specify extra metadata to attach to the json display object.
578 root : str
578 root : str
579 The name of the root element of the JSON tree
579 The name of the root element of the JSON tree
580 """
580 """
581 self.metadata = {
581 self.metadata = {
582 'expanded': expanded,
582 'expanded': expanded,
583 'root': root,
583 'root': root,
584 }
584 }
585 if metadata:
585 if metadata:
586 self.metadata.update(metadata)
586 self.metadata.update(metadata)
587 if kwargs:
587 if kwargs:
588 self.metadata.update(kwargs)
588 self.metadata.update(kwargs)
589 super(JSON, self).__init__(data=data, url=url, filename=filename)
589 super(JSON, self).__init__(data=data, url=url, filename=filename)
590
590
591 def _check_data(self):
591 def _check_data(self):
592 if self.data is not None and not isinstance(self.data, (dict, list)):
592 if self.data is not None and not isinstance(self.data, (dict, list)):
593 raise TypeError("%s expects JSONable dict or list, not %r" % (self.__class__.__name__, self.data))
593 raise TypeError("%s expects JSONable dict or list, not %r" % (self.__class__.__name__, self.data))
594
594
595 @property
595 @property
596 def data(self):
596 def data(self):
597 return self._data
597 return self._data
598
598
599 @data.setter
599 @data.setter
600 def data(self, data):
600 def data(self, data):
601 if isinstance(data, (Path, PurePath)):
601 if isinstance(data, (Path, PurePath)):
602 data = str(data)
602 data = str(data)
603
603
604 if isinstance(data, str):
604 if isinstance(data, str):
605 if self.filename is None and self.url is None:
605 if self.filename is None and self.url is None:
606 warnings.warn("JSON expects JSONable dict or list, not JSON strings")
606 warnings.warn("JSON expects JSONable dict or list, not JSON strings")
607 data = json.loads(data)
607 data = json.loads(data)
608 self._data = data
608 self._data = data
609
609
610 def _data_and_metadata(self):
610 def _data_and_metadata(self):
611 return self.data, self.metadata
611 return self.data, self.metadata
612
612
613 def _repr_json_(self):
613 def _repr_json_(self):
614 return self._data_and_metadata()
614 return self._data_and_metadata()
615
615
616 _css_t = """var link = document.createElement("link");
616 _css_t = """var link = document.createElement("link");
617 link.ref = "stylesheet";
617 link.ref = "stylesheet";
618 link.type = "text/css";
618 link.type = "text/css";
619 link.href = "%s";
619 link.href = "%s";
620 document.head.appendChild(link);
620 document.head.appendChild(link);
621 """
621 """
622
622
623 _lib_t1 = """new Promise(function(resolve, reject) {
623 _lib_t1 = """new Promise(function(resolve, reject) {
624 var script = document.createElement("script");
624 var script = document.createElement("script");
625 script.onload = resolve;
625 script.onload = resolve;
626 script.onerror = reject;
626 script.onerror = reject;
627 script.src = "%s";
627 script.src = "%s";
628 document.head.appendChild(script);
628 document.head.appendChild(script);
629 }).then(() => {
629 }).then(() => {
630 """
630 """
631
631
632 _lib_t2 = """
632 _lib_t2 = """
633 });"""
633 });"""
634
634
635 class GeoJSON(JSON):
635 class GeoJSON(JSON):
636 """GeoJSON expects JSON-able dict
636 """GeoJSON expects JSON-able dict
637
637
638 not an already-serialized JSON string.
638 not an already-serialized JSON string.
639
639
640 Scalar types (None, number, string) are not allowed, only dict containers.
640 Scalar types (None, number, string) are not allowed, only dict containers.
641 """
641 """
642
642
643 def __init__(self, *args, **kwargs):
643 def __init__(self, *args, **kwargs):
644 """Create a GeoJSON display object given raw data.
644 """Create a GeoJSON display object given raw data.
645
645
646 Parameters
646 Parameters
647 ----------
647 ----------
648 data : dict or list
648 data : dict or list
649 VegaLite data. Not an already-serialized JSON string.
649 VegaLite data. Not an already-serialized JSON string.
650 Scalar types (None, number, string) are not allowed, only dict
650 Scalar types (None, number, string) are not allowed, only dict
651 or list containers.
651 or list containers.
652 url_template : string
652 url_template : string
653 Leaflet TileLayer URL template: http://leafletjs.com/reference.html#url-template
653 Leaflet TileLayer URL template: http://leafletjs.com/reference.html#url-template
654 layer_options : dict
654 layer_options : dict
655 Leaflet TileLayer options: http://leafletjs.com/reference.html#tilelayer-options
655 Leaflet TileLayer options: http://leafletjs.com/reference.html#tilelayer-options
656 url : unicode
656 url : unicode
657 A URL to download the data from.
657 A URL to download the data from.
658 filename : unicode
658 filename : unicode
659 Path to a local file to load the data from.
659 Path to a local file to load the data from.
660 metadata : dict
660 metadata : dict
661 Specify extra metadata to attach to the json display object.
661 Specify extra metadata to attach to the json display object.
662
662
663 Examples
663 Examples
664 --------
664 --------
665 The following will display an interactive map of Mars with a point of
665 The following will display an interactive map of Mars with a point of
666 interest on frontend that do support GeoJSON display.
666 interest on frontend that do support GeoJSON display.
667
667
668 >>> from IPython.display import GeoJSON
668 >>> from IPython.display import GeoJSON
669
669
670 >>> GeoJSON(data={
670 >>> GeoJSON(data={
671 ... "type": "Feature",
671 ... "type": "Feature",
672 ... "geometry": {
672 ... "geometry": {
673 ... "type": "Point",
673 ... "type": "Point",
674 ... "coordinates": [-81.327, 296.038]
674 ... "coordinates": [-81.327, 296.038]
675 ... }
675 ... }
676 ... },
676 ... },
677 ... url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
677 ... url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
678 ... layer_options={
678 ... layer_options={
679 ... "basemap_id": "celestia_mars-shaded-16k_global",
679 ... "basemap_id": "celestia_mars-shaded-16k_global",
680 ... "attribution" : "Celestia/praesepe",
680 ... "attribution" : "Celestia/praesepe",
681 ... "minZoom" : 0,
681 ... "minZoom" : 0,
682 ... "maxZoom" : 18,
682 ... "maxZoom" : 18,
683 ... })
683 ... })
684 <IPython.core.display.GeoJSON object>
684 <IPython.core.display.GeoJSON object>
685
685
686 In the terminal IPython, you will only see the text representation of
686 In the terminal IPython, you will only see the text representation of
687 the GeoJSON object.
687 the GeoJSON object.
688
688
689 """
689 """
690
690
691 super(GeoJSON, self).__init__(*args, **kwargs)
691 super(GeoJSON, self).__init__(*args, **kwargs)
692
692
693
693
694 def _ipython_display_(self):
694 def _ipython_display_(self):
695 bundle = {
695 bundle = {
696 'application/geo+json': self.data,
696 'application/geo+json': self.data,
697 'text/plain': '<IPython.display.GeoJSON object>'
697 'text/plain': '<IPython.display.GeoJSON object>'
698 }
698 }
699 metadata = {
699 metadata = {
700 'application/geo+json': self.metadata
700 'application/geo+json': self.metadata
701 }
701 }
702 display_functions.display(bundle, metadata=metadata, raw=True)
702 display_functions.display(bundle, metadata=metadata, raw=True)
703
703
704 class Javascript(TextDisplayObject):
704 class Javascript(TextDisplayObject):
705
705
706 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
706 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
707 """Create a Javascript display object given raw data.
707 """Create a Javascript display object given raw data.
708
708
709 When this object is returned by an expression or passed to the
709 When this object is returned by an expression or passed to the
710 display function, it will result in the data being displayed
710 display function, it will result in the data being displayed
711 in the frontend. If the data is a URL, the data will first be
711 in the frontend. If the data is a URL, the data will first be
712 downloaded and then displayed.
712 downloaded and then displayed.
713
713
714 In the Notebook, the containing element will be available as `element`,
714 In the Notebook, the containing element will be available as `element`,
715 and jQuery will be available. Content appended to `element` will be
715 and jQuery will be available. Content appended to `element` will be
716 visible in the output area.
716 visible in the output area.
717
717
718 Parameters
718 Parameters
719 ----------
719 ----------
720 data : unicode, str or bytes
720 data : unicode, str or bytes
721 The Javascript source code or a URL to download it from.
721 The Javascript source code or a URL to download it from.
722 url : unicode
722 url : unicode
723 A URL to download the data from.
723 A URL to download the data from.
724 filename : unicode
724 filename : unicode
725 Path to a local file to load the data from.
725 Path to a local file to load the data from.
726 lib : list or str
726 lib : list or str
727 A sequence of Javascript library URLs to load asynchronously before
727 A sequence of Javascript library URLs to load asynchronously before
728 running the source code. The full URLs of the libraries should
728 running the source code. The full URLs of the libraries should
729 be given. A single Javascript library URL can also be given as a
729 be given. A single Javascript library URL can also be given as a
730 string.
730 string.
731 css : list or str
731 css : list or str
732 A sequence of css files to load before running the source code.
732 A sequence of css files to load before running the source code.
733 The full URLs of the css files should be given. A single css URL
733 The full URLs of the css files should be given. A single css URL
734 can also be given as a string.
734 can also be given as a string.
735 """
735 """
736 if isinstance(lib, str):
736 if isinstance(lib, str):
737 lib = [lib]
737 lib = [lib]
738 elif lib is None:
738 elif lib is None:
739 lib = []
739 lib = []
740 if isinstance(css, str):
740 if isinstance(css, str):
741 css = [css]
741 css = [css]
742 elif css is None:
742 elif css is None:
743 css = []
743 css = []
744 if not isinstance(lib, (list,tuple)):
744 if not isinstance(lib, (list,tuple)):
745 raise TypeError('expected sequence, got: %r' % lib)
745 raise TypeError('expected sequence, got: %r' % lib)
746 if not isinstance(css, (list,tuple)):
746 if not isinstance(css, (list,tuple)):
747 raise TypeError('expected sequence, got: %r' % css)
747 raise TypeError('expected sequence, got: %r' % css)
748 self.lib = lib
748 self.lib = lib
749 self.css = css
749 self.css = css
750 super(Javascript, self).__init__(data=data, url=url, filename=filename)
750 super(Javascript, self).__init__(data=data, url=url, filename=filename)
751
751
752 def _repr_javascript_(self):
752 def _repr_javascript_(self):
753 r = ''
753 r = ''
754 for c in self.css:
754 for c in self.css:
755 r += _css_t % c
755 r += _css_t % c
756 for l in self.lib:
756 for l in self.lib:
757 r += _lib_t1 % l
757 r += _lib_t1 % l
758 r += self.data
758 r += self.data
759 r += _lib_t2*len(self.lib)
759 r += _lib_t2*len(self.lib)
760 return r
760 return r
761
761
762 # constants for identifying png/jpeg data
762 # constants for identifying png/jpeg data
763 _PNG = b'\x89PNG\r\n\x1a\n'
763 _PNG = b'\x89PNG\r\n\x1a\n'
764 _JPEG = b'\xff\xd8'
764 _JPEG = b'\xff\xd8'
765
765
766 def _pngxy(data):
766 def _pngxy(data):
767 """read the (width, height) from a PNG header"""
767 """read the (width, height) from a PNG header"""
768 ihdr = data.index(b'IHDR')
768 ihdr = data.index(b'IHDR')
769 # next 8 bytes are width/height
769 # next 8 bytes are width/height
770 return struct.unpack('>ii', data[ihdr+4:ihdr+12])
770 return struct.unpack('>ii', data[ihdr+4:ihdr+12])
771
771
772 def _jpegxy(data):
772 def _jpegxy(data):
773 """read the (width, height) from a JPEG header"""
773 """read the (width, height) from a JPEG header"""
774 # adapted from http://www.64lines.com/jpeg-width-height
774 # adapted from http://www.64lines.com/jpeg-width-height
775
775
776 idx = 4
776 idx = 4
777 while True:
777 while True:
778 block_size = struct.unpack('>H', data[idx:idx+2])[0]
778 block_size = struct.unpack('>H', data[idx:idx+2])[0]
779 idx = idx + block_size
779 idx = idx + block_size
780 if data[idx:idx+2] == b'\xFF\xC0':
780 if data[idx:idx+2] == b'\xFF\xC0':
781 # found Start of Frame
781 # found Start of Frame
782 iSOF = idx
782 iSOF = idx
783 break
783 break
784 else:
784 else:
785 # read another block
785 # read another block
786 idx += 2
786 idx += 2
787
787
788 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
788 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
789 return w, h
789 return w, h
790
790
791 def _gifxy(data):
791 def _gifxy(data):
792 """read the (width, height) from a GIF header"""
792 """read the (width, height) from a GIF header"""
793 return struct.unpack('<HH', data[6:10])
793 return struct.unpack('<HH', data[6:10])
794
794
795
795
796 class Image(DisplayObject):
796 class Image(DisplayObject):
797
797
798 _read_flags = 'rb'
798 _read_flags = 'rb'
799 _FMT_JPEG = u'jpeg'
799 _FMT_JPEG = u'jpeg'
800 _FMT_PNG = u'png'
800 _FMT_PNG = u'png'
801 _FMT_GIF = u'gif'
801 _FMT_GIF = u'gif'
802 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG, _FMT_GIF]
802 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG, _FMT_GIF]
803 _MIMETYPES = {
803 _MIMETYPES = {
804 _FMT_PNG: 'image/png',
804 _FMT_PNG: 'image/png',
805 _FMT_JPEG: 'image/jpeg',
805 _FMT_JPEG: 'image/jpeg',
806 _FMT_GIF: 'image/gif',
806 _FMT_GIF: 'image/gif',
807 }
807 }
808
808
809 def __init__(
809 def __init__(
810 self,
810 self,
811 data=None,
811 data=None,
812 url=None,
812 url=None,
813 filename=None,
813 filename=None,
814 format=None,
814 format=None,
815 embed=None,
815 embed=None,
816 width=None,
816 width=None,
817 height=None,
817 height=None,
818 retina=False,
818 retina=False,
819 unconfined=False,
819 unconfined=False,
820 metadata=None,
820 metadata=None,
821 alt=None,
821 alt=None,
822 ):
822 ):
823 """Create a PNG/JPEG/GIF image object given raw data.
823 """Create a PNG/JPEG/GIF image object given raw data.
824
824
825 When this object is returned by an input cell or passed to the
825 When this object is returned by an input cell or passed to the
826 display function, it will result in the image being displayed
826 display function, it will result in the image being displayed
827 in the frontend.
827 in the frontend.
828
828
829 Parameters
829 Parameters
830 ----------
830 ----------
831 data : unicode, str or bytes
831 data : unicode, str or bytes
832 The raw image data or a URL or filename to load the data from.
832 The raw image data or a URL or filename to load the data from.
833 This always results in embedded image data.
833 This always results in embedded image data.
834
834
835 url : unicode
835 url : unicode
836 A URL to download the data from. If you specify `url=`,
836 A URL to download the data from. If you specify `url=`,
837 the image data will not be embedded unless you also specify `embed=True`.
837 the image data will not be embedded unless you also specify `embed=True`.
838
838
839 filename : unicode
839 filename : unicode
840 Path to a local file to load the data from.
840 Path to a local file to load the data from.
841 Images from a file are always embedded.
841 Images from a file are always embedded.
842
842
843 format : unicode
843 format : unicode
844 The format of the image data (png/jpeg/jpg/gif). If a filename or URL is given
844 The format of the image data (png/jpeg/jpg/gif). If a filename or URL is given
845 for format will be inferred from the filename extension.
845 for format will be inferred from the filename extension.
846
846
847 embed : bool
847 embed : bool
848 Should the image data be embedded using a data URI (True) or be
848 Should the image data be embedded using a data URI (True) or be
849 loaded using an <img> tag. Set this to True if you want the image
849 loaded using an <img> tag. Set this to True if you want the image
850 to be viewable later with no internet connection in the notebook.
850 to be viewable later with no internet connection in the notebook.
851
851
852 Default is `True`, unless the keyword argument `url` is set, then
852 Default is `True`, unless the keyword argument `url` is set, then
853 default value is `False`.
853 default value is `False`.
854
854
855 Note that QtConsole is not able to display images if `embed` is set to `False`
855 Note that QtConsole is not able to display images if `embed` is set to `False`
856
856
857 width : int
857 width : int
858 Width in pixels to which to constrain the image in html
858 Width in pixels to which to constrain the image in html
859
859
860 height : int
860 height : int
861 Height in pixels to which to constrain the image in html
861 Height in pixels to which to constrain the image in html
862
862
863 retina : bool
863 retina : bool
864 Automatically set the width and height to half of the measured
864 Automatically set the width and height to half of the measured
865 width and height.
865 width and height.
866 This only works for embedded images because it reads the width/height
866 This only works for embedded images because it reads the width/height
867 from image data.
867 from image data.
868 For non-embedded images, you can just set the desired display width
868 For non-embedded images, you can just set the desired display width
869 and height directly.
869 and height directly.
870
870
871 unconfined : bool
871 unconfined : bool
872 Set unconfined=True to disable max-width confinement of the image.
872 Set unconfined=True to disable max-width confinement of the image.
873
873
874 metadata : dict
874 metadata : dict
875 Specify extra metadata to attach to the image.
875 Specify extra metadata to attach to the image.
876
876
877 alt : unicode
877 alt : unicode
878 Alternative text for the image, for use by screen readers.
878 Alternative text for the image, for use by screen readers.
879
879
880 Examples
880 Examples
881 --------
881 --------
882 embedded image data, works in qtconsole and notebook
882 embedded image data, works in qtconsole and notebook
883 when passed positionally, the first arg can be any of raw image data,
883 when passed positionally, the first arg can be any of raw image data,
884 a URL, or a filename from which to load image data.
884 a URL, or a filename from which to load image data.
885 The result is always embedding image data for inline images.
885 The result is always embedding image data for inline images.
886
886
887 >>> Image('http://www.google.fr/images/srpr/logo3w.png')
887 >>> Image('https://www.google.fr/images/srpr/logo3w.png') # doctest: +SKIP
888 <IPython.core.display.Image object>
888 <IPython.core.display.Image object>
889
889
890 >>> Image('/path/to/image.jpg')
890 >>> Image('/path/to/image.jpg')
891 <IPython.core.display.Image object>
891 <IPython.core.display.Image object>
892
892
893 >>> Image(b'RAW_PNG_DATA...')
893 >>> Image(b'RAW_PNG_DATA...')
894 <IPython.core.display.Image object>
894 <IPython.core.display.Image object>
895
895
896 Specifying Image(url=...) does not embed the image data,
896 Specifying Image(url=...) does not embed the image data,
897 it only generates ``<img>`` tag with a link to the source.
897 it only generates ``<img>`` tag with a link to the source.
898 This will not work in the qtconsole or offline.
898 This will not work in the qtconsole or offline.
899
899
900 >>> Image(url='http://www.google.fr/images/srpr/logo3w.png')
900 >>> Image(url='https://www.google.fr/images/srpr/logo3w.png')
901 <IPython.core.display.Image object>
901 <IPython.core.display.Image object>
902
902
903 """
903 """
904 if isinstance(data, (Path, PurePath)):
904 if isinstance(data, (Path, PurePath)):
905 data = str(data)
905 data = str(data)
906
906
907 if filename is not None:
907 if filename is not None:
908 ext = self._find_ext(filename)
908 ext = self._find_ext(filename)
909 elif url is not None:
909 elif url is not None:
910 ext = self._find_ext(url)
910 ext = self._find_ext(url)
911 elif data is None:
911 elif data is None:
912 raise ValueError("No image data found. Expecting filename, url, or data.")
912 raise ValueError("No image data found. Expecting filename, url, or data.")
913 elif isinstance(data, str) and (
913 elif isinstance(data, str) and (
914 data.startswith('http') or _safe_exists(data)
914 data.startswith('http') or _safe_exists(data)
915 ):
915 ):
916 ext = self._find_ext(data)
916 ext = self._find_ext(data)
917 else:
917 else:
918 ext = None
918 ext = None
919
919
920 if format is None:
920 if format is None:
921 if ext is not None:
921 if ext is not None:
922 if ext == u'jpg' or ext == u'jpeg':
922 if ext == u'jpg' or ext == u'jpeg':
923 format = self._FMT_JPEG
923 format = self._FMT_JPEG
924 elif ext == u'png':
924 elif ext == u'png':
925 format = self._FMT_PNG
925 format = self._FMT_PNG
926 elif ext == u'gif':
926 elif ext == u'gif':
927 format = self._FMT_GIF
927 format = self._FMT_GIF
928 else:
928 else:
929 format = ext.lower()
929 format = ext.lower()
930 elif isinstance(data, bytes):
930 elif isinstance(data, bytes):
931 # infer image type from image data header,
931 # infer image type from image data header,
932 # only if format has not been specified.
932 # only if format has not been specified.
933 if data[:2] == _JPEG:
933 if data[:2] == _JPEG:
934 format = self._FMT_JPEG
934 format = self._FMT_JPEG
935
935
936 # failed to detect format, default png
936 # failed to detect format, default png
937 if format is None:
937 if format is None:
938 format = self._FMT_PNG
938 format = self._FMT_PNG
939
939
940 if format.lower() == 'jpg':
940 if format.lower() == 'jpg':
941 # jpg->jpeg
941 # jpg->jpeg
942 format = self._FMT_JPEG
942 format = self._FMT_JPEG
943
943
944 self.format = format.lower()
944 self.format = format.lower()
945 self.embed = embed if embed is not None else (url is None)
945 self.embed = embed if embed is not None else (url is None)
946
946
947 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
947 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
948 raise ValueError("Cannot embed the '%s' image format" % (self.format))
948 raise ValueError("Cannot embed the '%s' image format" % (self.format))
949 if self.embed:
949 if self.embed:
950 self._mimetype = self._MIMETYPES.get(self.format)
950 self._mimetype = self._MIMETYPES.get(self.format)
951
951
952 self.width = width
952 self.width = width
953 self.height = height
953 self.height = height
954 self.retina = retina
954 self.retina = retina
955 self.unconfined = unconfined
955 self.unconfined = unconfined
956 self.alt = alt
956 self.alt = alt
957 super(Image, self).__init__(data=data, url=url, filename=filename,
957 super(Image, self).__init__(data=data, url=url, filename=filename,
958 metadata=metadata)
958 metadata=metadata)
959
959
960 if self.width is None and self.metadata.get('width', {}):
960 if self.width is None and self.metadata.get('width', {}):
961 self.width = metadata['width']
961 self.width = metadata['width']
962
962
963 if self.height is None and self.metadata.get('height', {}):
963 if self.height is None and self.metadata.get('height', {}):
964 self.height = metadata['height']
964 self.height = metadata['height']
965
965
966 if self.alt is None and self.metadata.get("alt", {}):
966 if self.alt is None and self.metadata.get("alt", {}):
967 self.alt = metadata["alt"]
967 self.alt = metadata["alt"]
968
968
969 if retina:
969 if retina:
970 self._retina_shape()
970 self._retina_shape()
971
971
972
972
973 def _retina_shape(self):
973 def _retina_shape(self):
974 """load pixel-doubled width and height from image data"""
974 """load pixel-doubled width and height from image data"""
975 if not self.embed:
975 if not self.embed:
976 return
976 return
977 if self.format == self._FMT_PNG:
977 if self.format == self._FMT_PNG:
978 w, h = _pngxy(self.data)
978 w, h = _pngxy(self.data)
979 elif self.format == self._FMT_JPEG:
979 elif self.format == self._FMT_JPEG:
980 w, h = _jpegxy(self.data)
980 w, h = _jpegxy(self.data)
981 elif self.format == self._FMT_GIF:
981 elif self.format == self._FMT_GIF:
982 w, h = _gifxy(self.data)
982 w, h = _gifxy(self.data)
983 else:
983 else:
984 # retina only supports png
984 # retina only supports png
985 return
985 return
986 self.width = w // 2
986 self.width = w // 2
987 self.height = h // 2
987 self.height = h // 2
988
988
989 def reload(self):
989 def reload(self):
990 """Reload the raw data from file or URL."""
990 """Reload the raw data from file or URL."""
991 if self.embed:
991 if self.embed:
992 super(Image,self).reload()
992 super(Image,self).reload()
993 if self.retina:
993 if self.retina:
994 self._retina_shape()
994 self._retina_shape()
995
995
996 def _repr_html_(self):
996 def _repr_html_(self):
997 if not self.embed:
997 if not self.embed:
998 width = height = klass = alt = ""
998 width = height = klass = alt = ""
999 if self.width:
999 if self.width:
1000 width = ' width="%d"' % self.width
1000 width = ' width="%d"' % self.width
1001 if self.height:
1001 if self.height:
1002 height = ' height="%d"' % self.height
1002 height = ' height="%d"' % self.height
1003 if self.unconfined:
1003 if self.unconfined:
1004 klass = ' class="unconfined"'
1004 klass = ' class="unconfined"'
1005 if self.alt:
1005 if self.alt:
1006 alt = ' alt="%s"' % html.escape(self.alt)
1006 alt = ' alt="%s"' % html.escape(self.alt)
1007 return '<img src="{url}"{width}{height}{klass}{alt}/>'.format(
1007 return '<img src="{url}"{width}{height}{klass}{alt}/>'.format(
1008 url=self.url,
1008 url=self.url,
1009 width=width,
1009 width=width,
1010 height=height,
1010 height=height,
1011 klass=klass,
1011 klass=klass,
1012 alt=alt,
1012 alt=alt,
1013 )
1013 )
1014
1014
1015 def _repr_mimebundle_(self, include=None, exclude=None):
1015 def _repr_mimebundle_(self, include=None, exclude=None):
1016 """Return the image as a mimebundle
1016 """Return the image as a mimebundle
1017
1017
1018 Any new mimetype support should be implemented here.
1018 Any new mimetype support should be implemented here.
1019 """
1019 """
1020 if self.embed:
1020 if self.embed:
1021 mimetype = self._mimetype
1021 mimetype = self._mimetype
1022 data, metadata = self._data_and_metadata(always_both=True)
1022 data, metadata = self._data_and_metadata(always_both=True)
1023 if metadata:
1023 if metadata:
1024 metadata = {mimetype: metadata}
1024 metadata = {mimetype: metadata}
1025 return {mimetype: data}, metadata
1025 return {mimetype: data}, metadata
1026 else:
1026 else:
1027 return {'text/html': self._repr_html_()}
1027 return {'text/html': self._repr_html_()}
1028
1028
1029 def _data_and_metadata(self, always_both=False):
1029 def _data_and_metadata(self, always_both=False):
1030 """shortcut for returning metadata with shape information, if defined"""
1030 """shortcut for returning metadata with shape information, if defined"""
1031 try:
1031 try:
1032 b64_data = b2a_base64(self.data).decode('ascii')
1032 b64_data = b2a_base64(self.data).decode('ascii')
1033 except TypeError as e:
1033 except TypeError as e:
1034 raise FileNotFoundError(
1034 raise FileNotFoundError(
1035 "No such file or directory: '%s'" % (self.data)) from e
1035 "No such file or directory: '%s'" % (self.data)) from e
1036 md = {}
1036 md = {}
1037 if self.metadata:
1037 if self.metadata:
1038 md.update(self.metadata)
1038 md.update(self.metadata)
1039 if self.width:
1039 if self.width:
1040 md['width'] = self.width
1040 md['width'] = self.width
1041 if self.height:
1041 if self.height:
1042 md['height'] = self.height
1042 md['height'] = self.height
1043 if self.unconfined:
1043 if self.unconfined:
1044 md['unconfined'] = self.unconfined
1044 md['unconfined'] = self.unconfined
1045 if self.alt:
1045 if self.alt:
1046 md["alt"] = self.alt
1046 md["alt"] = self.alt
1047 if md or always_both:
1047 if md or always_both:
1048 return b64_data, md
1048 return b64_data, md
1049 else:
1049 else:
1050 return b64_data
1050 return b64_data
1051
1051
1052 def _repr_png_(self):
1052 def _repr_png_(self):
1053 if self.embed and self.format == self._FMT_PNG:
1053 if self.embed and self.format == self._FMT_PNG:
1054 return self._data_and_metadata()
1054 return self._data_and_metadata()
1055
1055
1056 def _repr_jpeg_(self):
1056 def _repr_jpeg_(self):
1057 if self.embed and self.format == self._FMT_JPEG:
1057 if self.embed and self.format == self._FMT_JPEG:
1058 return self._data_and_metadata()
1058 return self._data_and_metadata()
1059
1059
1060 def _find_ext(self, s):
1060 def _find_ext(self, s):
1061 base, ext = splitext(s)
1061 base, ext = splitext(s)
1062
1062
1063 if not ext:
1063 if not ext:
1064 return base
1064 return base
1065
1065
1066 # `splitext` includes leading period, so we skip it
1066 # `splitext` includes leading period, so we skip it
1067 return ext[1:].lower()
1067 return ext[1:].lower()
1068
1068
1069
1069
1070 class Video(DisplayObject):
1070 class Video(DisplayObject):
1071
1071
1072 def __init__(self, data=None, url=None, filename=None, embed=False,
1072 def __init__(self, data=None, url=None, filename=None, embed=False,
1073 mimetype=None, width=None, height=None, html_attributes="controls"):
1073 mimetype=None, width=None, height=None, html_attributes="controls"):
1074 """Create a video object given raw data or an URL.
1074 """Create a video object given raw data or an URL.
1075
1075
1076 When this object is returned by an input cell or passed to the
1076 When this object is returned by an input cell or passed to the
1077 display function, it will result in the video being displayed
1077 display function, it will result in the video being displayed
1078 in the frontend.
1078 in the frontend.
1079
1079
1080 Parameters
1080 Parameters
1081 ----------
1081 ----------
1082 data : unicode, str or bytes
1082 data : unicode, str or bytes
1083 The raw video data or a URL or filename to load the data from.
1083 The raw video data or a URL or filename to load the data from.
1084 Raw data will require passing ``embed=True``.
1084 Raw data will require passing ``embed=True``.
1085
1085
1086 url : unicode
1086 url : unicode
1087 A URL for the video. If you specify ``url=``,
1087 A URL for the video. If you specify ``url=``,
1088 the image data will not be embedded.
1088 the image data will not be embedded.
1089
1089
1090 filename : unicode
1090 filename : unicode
1091 Path to a local file containing the video.
1091 Path to a local file containing the video.
1092 Will be interpreted as a local URL unless ``embed=True``.
1092 Will be interpreted as a local URL unless ``embed=True``.
1093
1093
1094 embed : bool
1094 embed : bool
1095 Should the video be embedded using a data URI (True) or be
1095 Should the video be embedded using a data URI (True) or be
1096 loaded using a <video> tag (False).
1096 loaded using a <video> tag (False).
1097
1097
1098 Since videos are large, embedding them should be avoided, if possible.
1098 Since videos are large, embedding them should be avoided, if possible.
1099 You must confirm embedding as your intention by passing ``embed=True``.
1099 You must confirm embedding as your intention by passing ``embed=True``.
1100
1100
1101 Local files can be displayed with URLs without embedding the content, via::
1101 Local files can be displayed with URLs without embedding the content, via::
1102
1102
1103 Video('./video.mp4')
1103 Video('./video.mp4')
1104
1104
1105 mimetype : unicode
1105 mimetype : unicode
1106 Specify the mimetype for embedded videos.
1106 Specify the mimetype for embedded videos.
1107 Default will be guessed from file extension, if available.
1107 Default will be guessed from file extension, if available.
1108
1108
1109 width : int
1109 width : int
1110 Width in pixels to which to constrain the video in HTML.
1110 Width in pixels to which to constrain the video in HTML.
1111 If not supplied, defaults to the width of the video.
1111 If not supplied, defaults to the width of the video.
1112
1112
1113 height : int
1113 height : int
1114 Height in pixels to which to constrain the video in html.
1114 Height in pixels to which to constrain the video in html.
1115 If not supplied, defaults to the height of the video.
1115 If not supplied, defaults to the height of the video.
1116
1116
1117 html_attributes : str
1117 html_attributes : str
1118 Attributes for the HTML ``<video>`` block.
1118 Attributes for the HTML ``<video>`` block.
1119 Default: ``"controls"`` to get video controls.
1119 Default: ``"controls"`` to get video controls.
1120 Other examples: ``"controls muted"`` for muted video with controls,
1120 Other examples: ``"controls muted"`` for muted video with controls,
1121 ``"loop autoplay"`` for looping autoplaying video without controls.
1121 ``"loop autoplay"`` for looping autoplaying video without controls.
1122
1122
1123 Examples
1123 Examples
1124 --------
1124 --------
1125 ::
1125 ::
1126
1126
1127 Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4')
1127 Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4')
1128 Video('path/to/video.mp4')
1128 Video('path/to/video.mp4')
1129 Video('path/to/video.mp4', embed=True)
1129 Video('path/to/video.mp4', embed=True)
1130 Video('path/to/video.mp4', embed=True, html_attributes="controls muted autoplay")
1130 Video('path/to/video.mp4', embed=True, html_attributes="controls muted autoplay")
1131 Video(b'raw-videodata', embed=True)
1131 Video(b'raw-videodata', embed=True)
1132 """
1132 """
1133 if isinstance(data, (Path, PurePath)):
1133 if isinstance(data, (Path, PurePath)):
1134 data = str(data)
1134 data = str(data)
1135
1135
1136 if url is None and isinstance(data, str) and data.startswith(('http:', 'https:')):
1136 if url is None and isinstance(data, str) and data.startswith(('http:', 'https:')):
1137 url = data
1137 url = data
1138 data = None
1138 data = None
1139 elif data is not None and os.path.exists(data):
1139 elif data is not None and os.path.exists(data):
1140 filename = data
1140 filename = data
1141 data = None
1141 data = None
1142
1142
1143 if data and not embed:
1143 if data and not embed:
1144 msg = ''.join([
1144 msg = ''.join([
1145 "To embed videos, you must pass embed=True ",
1145 "To embed videos, you must pass embed=True ",
1146 "(this may make your notebook files huge)\n",
1146 "(this may make your notebook files huge)\n",
1147 "Consider passing Video(url='...')",
1147 "Consider passing Video(url='...')",
1148 ])
1148 ])
1149 raise ValueError(msg)
1149 raise ValueError(msg)
1150
1150
1151 self.mimetype = mimetype
1151 self.mimetype = mimetype
1152 self.embed = embed
1152 self.embed = embed
1153 self.width = width
1153 self.width = width
1154 self.height = height
1154 self.height = height
1155 self.html_attributes = html_attributes
1155 self.html_attributes = html_attributes
1156 super(Video, self).__init__(data=data, url=url, filename=filename)
1156 super(Video, self).__init__(data=data, url=url, filename=filename)
1157
1157
1158 def _repr_html_(self):
1158 def _repr_html_(self):
1159 width = height = ''
1159 width = height = ''
1160 if self.width:
1160 if self.width:
1161 width = ' width="%d"' % self.width
1161 width = ' width="%d"' % self.width
1162 if self.height:
1162 if self.height:
1163 height = ' height="%d"' % self.height
1163 height = ' height="%d"' % self.height
1164
1164
1165 # External URLs and potentially local files are not embedded into the
1165 # External URLs and potentially local files are not embedded into the
1166 # notebook output.
1166 # notebook output.
1167 if not self.embed:
1167 if not self.embed:
1168 url = self.url if self.url is not None else self.filename
1168 url = self.url if self.url is not None else self.filename
1169 output = """<video src="{0}" {1} {2} {3}>
1169 output = """<video src="{0}" {1} {2} {3}>
1170 Your browser does not support the <code>video</code> element.
1170 Your browser does not support the <code>video</code> element.
1171 </video>""".format(url, self.html_attributes, width, height)
1171 </video>""".format(url, self.html_attributes, width, height)
1172 return output
1172 return output
1173
1173
1174 # Embedded videos are base64-encoded.
1174 # Embedded videos are base64-encoded.
1175 mimetype = self.mimetype
1175 mimetype = self.mimetype
1176 if self.filename is not None:
1176 if self.filename is not None:
1177 if not mimetype:
1177 if not mimetype:
1178 mimetype, _ = mimetypes.guess_type(self.filename)
1178 mimetype, _ = mimetypes.guess_type(self.filename)
1179
1179
1180 with open(self.filename, 'rb') as f:
1180 with open(self.filename, 'rb') as f:
1181 video = f.read()
1181 video = f.read()
1182 else:
1182 else:
1183 video = self.data
1183 video = self.data
1184 if isinstance(video, str):
1184 if isinstance(video, str):
1185 # unicode input is already b64-encoded
1185 # unicode input is already b64-encoded
1186 b64_video = video
1186 b64_video = video
1187 else:
1187 else:
1188 b64_video = b2a_base64(video).decode('ascii').rstrip()
1188 b64_video = b2a_base64(video).decode('ascii').rstrip()
1189
1189
1190 output = """<video {0} {1} {2}>
1190 output = """<video {0} {1} {2}>
1191 <source src="data:{3};base64,{4}" type="{3}">
1191 <source src="data:{3};base64,{4}" type="{3}">
1192 Your browser does not support the video tag.
1192 Your browser does not support the video tag.
1193 </video>""".format(self.html_attributes, width, height, mimetype, b64_video)
1193 </video>""".format(self.html_attributes, width, height, mimetype, b64_video)
1194 return output
1194 return output
1195
1195
1196 def reload(self):
1196 def reload(self):
1197 # TODO
1197 # TODO
1198 pass
1198 pass
1199
1199
1200
1200
1201 @skip_doctest
1201 @skip_doctest
1202 def set_matplotlib_formats(*formats, **kwargs):
1202 def set_matplotlib_formats(*formats, **kwargs):
1203 """
1203 """
1204 .. deprecated:: 7.23
1204 .. deprecated:: 7.23
1205
1205
1206 use `matplotlib_inline.backend_inline.set_matplotlib_formats()`
1206 use `matplotlib_inline.backend_inline.set_matplotlib_formats()`
1207
1207
1208 Select figure formats for the inline backend. Optionally pass quality for JPEG.
1208 Select figure formats for the inline backend. Optionally pass quality for JPEG.
1209
1209
1210 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
1210 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
1211
1211
1212 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
1212 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
1213
1213
1214 To set this in your config files use the following::
1214 To set this in your config files use the following::
1215
1215
1216 c.InlineBackend.figure_formats = {'png', 'jpeg'}
1216 c.InlineBackend.figure_formats = {'png', 'jpeg'}
1217 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
1217 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
1218
1218
1219 Parameters
1219 Parameters
1220 ----------
1220 ----------
1221 *formats : strs
1221 *formats : strs
1222 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
1222 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
1223 **kwargs
1223 **kwargs
1224 Keyword args will be relayed to ``figure.canvas.print_figure``.
1224 Keyword args will be relayed to ``figure.canvas.print_figure``.
1225 """
1225 """
1226 warnings.warn(
1226 warnings.warn(
1227 "`set_matplotlib_formats` is deprecated since IPython 7.23, directly "
1227 "`set_matplotlib_formats` is deprecated since IPython 7.23, directly "
1228 "use `matplotlib_inline.backend_inline.set_matplotlib_formats()`",
1228 "use `matplotlib_inline.backend_inline.set_matplotlib_formats()`",
1229 DeprecationWarning,
1229 DeprecationWarning,
1230 stacklevel=2,
1230 stacklevel=2,
1231 )
1231 )
1232
1232
1233 from matplotlib_inline.backend_inline import (
1233 from matplotlib_inline.backend_inline import (
1234 set_matplotlib_formats as set_matplotlib_formats_orig,
1234 set_matplotlib_formats as set_matplotlib_formats_orig,
1235 )
1235 )
1236
1236
1237 set_matplotlib_formats_orig(*formats, **kwargs)
1237 set_matplotlib_formats_orig(*formats, **kwargs)
1238
1238
1239 @skip_doctest
1239 @skip_doctest
1240 def set_matplotlib_close(close=True):
1240 def set_matplotlib_close(close=True):
1241 """
1241 """
1242 .. deprecated:: 7.23
1242 .. deprecated:: 7.23
1243
1243
1244 use `matplotlib_inline.backend_inline.set_matplotlib_close()`
1244 use `matplotlib_inline.backend_inline.set_matplotlib_close()`
1245
1245
1246 Set whether the inline backend closes all figures automatically or not.
1246 Set whether the inline backend closes all figures automatically or not.
1247
1247
1248 By default, the inline backend used in the IPython Notebook will close all
1248 By default, the inline backend used in the IPython Notebook will close all
1249 matplotlib figures automatically after each cell is run. This means that
1249 matplotlib figures automatically after each cell is run. This means that
1250 plots in different cells won't interfere. Sometimes, you may want to make
1250 plots in different cells won't interfere. Sometimes, you may want to make
1251 a plot in one cell and then refine it in later cells. This can be accomplished
1251 a plot in one cell and then refine it in later cells. This can be accomplished
1252 by::
1252 by::
1253
1253
1254 In [1]: set_matplotlib_close(False)
1254 In [1]: set_matplotlib_close(False)
1255
1255
1256 To set this in your config files use the following::
1256 To set this in your config files use the following::
1257
1257
1258 c.InlineBackend.close_figures = False
1258 c.InlineBackend.close_figures = False
1259
1259
1260 Parameters
1260 Parameters
1261 ----------
1261 ----------
1262 close : bool
1262 close : bool
1263 Should all matplotlib figures be automatically closed after each cell is
1263 Should all matplotlib figures be automatically closed after each cell is
1264 run?
1264 run?
1265 """
1265 """
1266 warnings.warn(
1266 warnings.warn(
1267 "`set_matplotlib_close` is deprecated since IPython 7.23, directly "
1267 "`set_matplotlib_close` is deprecated since IPython 7.23, directly "
1268 "use `matplotlib_inline.backend_inline.set_matplotlib_close()`",
1268 "use `matplotlib_inline.backend_inline.set_matplotlib_close()`",
1269 DeprecationWarning,
1269 DeprecationWarning,
1270 stacklevel=2,
1270 stacklevel=2,
1271 )
1271 )
1272
1272
1273 from matplotlib_inline.backend_inline import (
1273 from matplotlib_inline.backend_inline import (
1274 set_matplotlib_close as set_matplotlib_close_orig,
1274 set_matplotlib_close as set_matplotlib_close_orig,
1275 )
1275 )
1276
1276
1277 set_matplotlib_close_orig(close)
1277 set_matplotlib_close_orig(close)
@@ -1,854 +1,854 b''
1 """Implementation of magic functions for interaction with the OS.
1 """Implementation of magic functions for interaction with the OS.
2
2
3 Note: this module is named 'osm' instead of 'os' to avoid a collision with the
3 Note: this module is named 'osm' instead of 'os' to avoid a collision with the
4 builtin.
4 builtin.
5 """
5 """
6 # Copyright (c) IPython Development Team.
6 # Copyright (c) IPython Development Team.
7 # Distributed under the terms of the Modified BSD License.
7 # Distributed under the terms of the Modified BSD License.
8
8
9 import io
9 import io
10 import os
10 import os
11 import re
11 import re
12 import sys
12 import sys
13 from pprint import pformat
13 from pprint import pformat
14
14
15 from IPython.core import magic_arguments
15 from IPython.core import magic_arguments
16 from IPython.core import oinspect
16 from IPython.core import oinspect
17 from IPython.core import page
17 from IPython.core import page
18 from IPython.core.alias import AliasError, Alias
18 from IPython.core.alias import AliasError, Alias
19 from IPython.core.error import UsageError
19 from IPython.core.error import UsageError
20 from IPython.core.magic import (
20 from IPython.core.magic import (
21 Magics, compress_dhist, magics_class, line_magic, cell_magic, line_cell_magic
21 Magics, compress_dhist, magics_class, line_magic, cell_magic, line_cell_magic
22 )
22 )
23 from IPython.testing.skipdoctest import skip_doctest
23 from IPython.testing.skipdoctest import skip_doctest
24 from IPython.utils.openpy import source_to_unicode
24 from IPython.utils.openpy import source_to_unicode
25 from IPython.utils.process import abbrev_cwd
25 from IPython.utils.process import abbrev_cwd
26 from IPython.utils.terminal import set_term_title
26 from IPython.utils.terminal import set_term_title
27 from traitlets import Bool
27 from traitlets import Bool
28 from warnings import warn
28 from warnings import warn
29
29
30
30
31 @magics_class
31 @magics_class
32 class OSMagics(Magics):
32 class OSMagics(Magics):
33 """Magics to interact with the underlying OS (shell-type functionality).
33 """Magics to interact with the underlying OS (shell-type functionality).
34 """
34 """
35
35
36 cd_force_quiet = Bool(False,
36 cd_force_quiet = Bool(False,
37 help="Force %cd magic to be quiet even if -q is not passed."
37 help="Force %cd magic to be quiet even if -q is not passed."
38 ).tag(config=True)
38 ).tag(config=True)
39
39
40 def __init__(self, shell=None, **kwargs):
40 def __init__(self, shell=None, **kwargs):
41
41
42 # Now define isexec in a cross platform manner.
42 # Now define isexec in a cross platform manner.
43 self.is_posix = False
43 self.is_posix = False
44 self.execre = None
44 self.execre = None
45 if os.name == 'posix':
45 if os.name == 'posix':
46 self.is_posix = True
46 self.is_posix = True
47 else:
47 else:
48 try:
48 try:
49 winext = os.environ['pathext'].replace(';','|').replace('.','')
49 winext = os.environ['pathext'].replace(';','|').replace('.','')
50 except KeyError:
50 except KeyError:
51 winext = 'exe|com|bat|py'
51 winext = 'exe|com|bat|py'
52 try:
52 try:
53 self.execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE)
53 self.execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE)
54 except re.error:
54 except re.error:
55 warn("Seems like your pathext environmental "
55 warn("Seems like your pathext environmental "
56 "variable is malformed. Please check it to "
56 "variable is malformed. Please check it to "
57 "enable a proper handle of file extensions "
57 "enable a proper handle of file extensions "
58 "managed for your system")
58 "managed for your system")
59 winext = 'exe|com|bat|py'
59 winext = 'exe|com|bat|py'
60 self.execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE)
60 self.execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE)
61
61
62 # call up the chain
62 # call up the chain
63 super().__init__(shell=shell, **kwargs)
63 super().__init__(shell=shell, **kwargs)
64
64
65
65
66 def _isexec_POSIX(self, file):
66 def _isexec_POSIX(self, file):
67 """
67 """
68 Test for executable on a POSIX system
68 Test for executable on a POSIX system
69 """
69 """
70 if os.access(file.path, os.X_OK):
70 if os.access(file.path, os.X_OK):
71 # will fail on maxOS if access is not X_OK
71 # will fail on maxOS if access is not X_OK
72 return file.is_file()
72 return file.is_file()
73 return False
73 return False
74
74
75
75
76
76
77 def _isexec_WIN(self, file):
77 def _isexec_WIN(self, file):
78 """
78 """
79 Test for executable file on non POSIX system
79 Test for executable file on non POSIX system
80 """
80 """
81 return file.is_file() and self.execre.match(file.name) is not None
81 return file.is_file() and self.execre.match(file.name) is not None
82
82
83 def isexec(self, file):
83 def isexec(self, file):
84 """
84 """
85 Test for executable file on non POSIX system
85 Test for executable file on non POSIX system
86 """
86 """
87 if self.is_posix:
87 if self.is_posix:
88 return self._isexec_POSIX(file)
88 return self._isexec_POSIX(file)
89 else:
89 else:
90 return self._isexec_WIN(file)
90 return self._isexec_WIN(file)
91
91
92
92
93 @skip_doctest
93 @skip_doctest
94 @line_magic
94 @line_magic
95 def alias(self, parameter_s=''):
95 def alias(self, parameter_s=''):
96 """Define an alias for a system command.
96 """Define an alias for a system command.
97
97
98 '%alias alias_name cmd' defines 'alias_name' as an alias for 'cmd'
98 '%alias alias_name cmd' defines 'alias_name' as an alias for 'cmd'
99
99
100 Then, typing 'alias_name params' will execute the system command 'cmd
100 Then, typing 'alias_name params' will execute the system command 'cmd
101 params' (from your underlying operating system).
101 params' (from your underlying operating system).
102
102
103 Aliases have lower precedence than magic functions and Python normal
103 Aliases have lower precedence than magic functions and Python normal
104 variables, so if 'foo' is both a Python variable and an alias, the
104 variables, so if 'foo' is both a Python variable and an alias, the
105 alias can not be executed until 'del foo' removes the Python variable.
105 alias can not be executed until 'del foo' removes the Python variable.
106
106
107 You can use the %l specifier in an alias definition to represent the
107 You can use the %l specifier in an alias definition to represent the
108 whole line when the alias is called. For example::
108 whole line when the alias is called. For example::
109
109
110 In [2]: alias bracket echo "Input in brackets: <%l>"
110 In [2]: alias bracket echo "Input in brackets: <%l>"
111 In [3]: bracket hello world
111 In [3]: bracket hello world
112 Input in brackets: <hello world>
112 Input in brackets: <hello world>
113
113
114 You can also define aliases with parameters using %s specifiers (one
114 You can also define aliases with parameters using %s specifiers (one
115 per parameter)::
115 per parameter)::
116
116
117 In [1]: alias parts echo first %s second %s
117 In [1]: alias parts echo first %s second %s
118 In [2]: %parts A B
118 In [2]: %parts A B
119 first A second B
119 first A second B
120 In [3]: %parts A
120 In [3]: %parts A
121 Incorrect number of arguments: 2 expected.
121 Incorrect number of arguments: 2 expected.
122 parts is an alias to: 'echo first %s second %s'
122 parts is an alias to: 'echo first %s second %s'
123
123
124 Note that %l and %s are mutually exclusive. You can only use one or
124 Note that %l and %s are mutually exclusive. You can only use one or
125 the other in your aliases.
125 the other in your aliases.
126
126
127 Aliases expand Python variables just like system calls using ! or !!
127 Aliases expand Python variables just like system calls using ! or !!
128 do: all expressions prefixed with '$' get expanded. For details of
128 do: all expressions prefixed with '$' get expanded. For details of
129 the semantic rules, see PEP-215:
129 the semantic rules, see PEP-215:
130 http://www.python.org/peps/pep-0215.html. This is the library used by
130 https://www.python.org/dev/peps/pep-0215/. This is the library used by
131 IPython for variable expansion. If you want to access a true shell
131 IPython for variable expansion. If you want to access a true shell
132 variable, an extra $ is necessary to prevent its expansion by
132 variable, an extra $ is necessary to prevent its expansion by
133 IPython::
133 IPython::
134
134
135 In [6]: alias show echo
135 In [6]: alias show echo
136 In [7]: PATH='A Python string'
136 In [7]: PATH='A Python string'
137 In [8]: show $PATH
137 In [8]: show $PATH
138 A Python string
138 A Python string
139 In [9]: show $$PATH
139 In [9]: show $$PATH
140 /usr/local/lf9560/bin:/usr/local/intel/compiler70/ia32/bin:...
140 /usr/local/lf9560/bin:/usr/local/intel/compiler70/ia32/bin:...
141
141
142 You can use the alias facility to access all of $PATH. See the %rehashx
142 You can use the alias facility to access all of $PATH. See the %rehashx
143 function, which automatically creates aliases for the contents of your
143 function, which automatically creates aliases for the contents of your
144 $PATH.
144 $PATH.
145
145
146 If called with no parameters, %alias prints the current alias table
146 If called with no parameters, %alias prints the current alias table
147 for your system. For posix systems, the default aliases are 'cat',
147 for your system. For posix systems, the default aliases are 'cat',
148 'cp', 'mv', 'rm', 'rmdir', and 'mkdir', and other platform-specific
148 'cp', 'mv', 'rm', 'rmdir', and 'mkdir', and other platform-specific
149 aliases are added. For windows-based systems, the default aliases are
149 aliases are added. For windows-based systems, the default aliases are
150 'copy', 'ddir', 'echo', 'ls', 'ldir', 'mkdir', 'ren', and 'rmdir'.
150 'copy', 'ddir', 'echo', 'ls', 'ldir', 'mkdir', 'ren', and 'rmdir'.
151
151
152 You can see the definition of alias by adding a question mark in the
152 You can see the definition of alias by adding a question mark in the
153 end::
153 end::
154
154
155 In [1]: cat?
155 In [1]: cat?
156 Repr: <alias cat for 'cat'>"""
156 Repr: <alias cat for 'cat'>"""
157
157
158 par = parameter_s.strip()
158 par = parameter_s.strip()
159 if not par:
159 if not par:
160 aliases = sorted(self.shell.alias_manager.aliases)
160 aliases = sorted(self.shell.alias_manager.aliases)
161 # stored = self.shell.db.get('stored_aliases', {} )
161 # stored = self.shell.db.get('stored_aliases', {} )
162 # for k, v in stored:
162 # for k, v in stored:
163 # atab.append(k, v[0])
163 # atab.append(k, v[0])
164
164
165 print("Total number of aliases:", len(aliases))
165 print("Total number of aliases:", len(aliases))
166 sys.stdout.flush()
166 sys.stdout.flush()
167 return aliases
167 return aliases
168
168
169 # Now try to define a new one
169 # Now try to define a new one
170 try:
170 try:
171 alias,cmd = par.split(None, 1)
171 alias,cmd = par.split(None, 1)
172 except TypeError:
172 except TypeError:
173 print(oinspect.getdoc(self.alias))
173 print(oinspect.getdoc(self.alias))
174 return
174 return
175
175
176 try:
176 try:
177 self.shell.alias_manager.define_alias(alias, cmd)
177 self.shell.alias_manager.define_alias(alias, cmd)
178 except AliasError as e:
178 except AliasError as e:
179 print(e)
179 print(e)
180 # end magic_alias
180 # end magic_alias
181
181
182 @line_magic
182 @line_magic
183 def unalias(self, parameter_s=''):
183 def unalias(self, parameter_s=''):
184 """Remove an alias"""
184 """Remove an alias"""
185
185
186 aname = parameter_s.strip()
186 aname = parameter_s.strip()
187 try:
187 try:
188 self.shell.alias_manager.undefine_alias(aname)
188 self.shell.alias_manager.undefine_alias(aname)
189 except ValueError as e:
189 except ValueError as e:
190 print(e)
190 print(e)
191 return
191 return
192
192
193 stored = self.shell.db.get('stored_aliases', {} )
193 stored = self.shell.db.get('stored_aliases', {} )
194 if aname in stored:
194 if aname in stored:
195 print("Removing %stored alias",aname)
195 print("Removing %stored alias",aname)
196 del stored[aname]
196 del stored[aname]
197 self.shell.db['stored_aliases'] = stored
197 self.shell.db['stored_aliases'] = stored
198
198
199 @line_magic
199 @line_magic
200 def rehashx(self, parameter_s=''):
200 def rehashx(self, parameter_s=''):
201 """Update the alias table with all executable files in $PATH.
201 """Update the alias table with all executable files in $PATH.
202
202
203 rehashx explicitly checks that every entry in $PATH is a file
203 rehashx explicitly checks that every entry in $PATH is a file
204 with execute access (os.X_OK).
204 with execute access (os.X_OK).
205
205
206 Under Windows, it checks executability as a match against a
206 Under Windows, it checks executability as a match against a
207 '|'-separated string of extensions, stored in the IPython config
207 '|'-separated string of extensions, stored in the IPython config
208 variable win_exec_ext. This defaults to 'exe|com|bat'.
208 variable win_exec_ext. This defaults to 'exe|com|bat'.
209
209
210 This function also resets the root module cache of module completer,
210 This function also resets the root module cache of module completer,
211 used on slow filesystems.
211 used on slow filesystems.
212 """
212 """
213 from IPython.core.alias import InvalidAliasError
213 from IPython.core.alias import InvalidAliasError
214
214
215 # for the benefit of module completer in ipy_completers.py
215 # for the benefit of module completer in ipy_completers.py
216 del self.shell.db['rootmodules_cache']
216 del self.shell.db['rootmodules_cache']
217
217
218 path = [os.path.abspath(os.path.expanduser(p)) for p in
218 path = [os.path.abspath(os.path.expanduser(p)) for p in
219 os.environ.get('PATH','').split(os.pathsep)]
219 os.environ.get('PATH','').split(os.pathsep)]
220
220
221 syscmdlist = []
221 syscmdlist = []
222 savedir = os.getcwd()
222 savedir = os.getcwd()
223
223
224 # Now walk the paths looking for executables to alias.
224 # Now walk the paths looking for executables to alias.
225 try:
225 try:
226 # write the whole loop for posix/Windows so we don't have an if in
226 # write the whole loop for posix/Windows so we don't have an if in
227 # the innermost part
227 # the innermost part
228 if self.is_posix:
228 if self.is_posix:
229 for pdir in path:
229 for pdir in path:
230 try:
230 try:
231 os.chdir(pdir)
231 os.chdir(pdir)
232 except OSError:
232 except OSError:
233 continue
233 continue
234
234
235 # for python 3.6+ rewrite to: with os.scandir(pdir) as dirlist:
235 # for python 3.6+ rewrite to: with os.scandir(pdir) as dirlist:
236 dirlist = os.scandir(path=pdir)
236 dirlist = os.scandir(path=pdir)
237 for ff in dirlist:
237 for ff in dirlist:
238 if self.isexec(ff):
238 if self.isexec(ff):
239 fname = ff.name
239 fname = ff.name
240 try:
240 try:
241 # Removes dots from the name since ipython
241 # Removes dots from the name since ipython
242 # will assume names with dots to be python.
242 # will assume names with dots to be python.
243 if not self.shell.alias_manager.is_alias(fname):
243 if not self.shell.alias_manager.is_alias(fname):
244 self.shell.alias_manager.define_alias(
244 self.shell.alias_manager.define_alias(
245 fname.replace('.',''), fname)
245 fname.replace('.',''), fname)
246 except InvalidAliasError:
246 except InvalidAliasError:
247 pass
247 pass
248 else:
248 else:
249 syscmdlist.append(fname)
249 syscmdlist.append(fname)
250 else:
250 else:
251 no_alias = Alias.blacklist
251 no_alias = Alias.blacklist
252 for pdir in path:
252 for pdir in path:
253 try:
253 try:
254 os.chdir(pdir)
254 os.chdir(pdir)
255 except OSError:
255 except OSError:
256 continue
256 continue
257
257
258 # for python 3.6+ rewrite to: with os.scandir(pdir) as dirlist:
258 # for python 3.6+ rewrite to: with os.scandir(pdir) as dirlist:
259 dirlist = os.scandir(pdir)
259 dirlist = os.scandir(pdir)
260 for ff in dirlist:
260 for ff in dirlist:
261 fname = ff.name
261 fname = ff.name
262 base, ext = os.path.splitext(fname)
262 base, ext = os.path.splitext(fname)
263 if self.isexec(ff) and base.lower() not in no_alias:
263 if self.isexec(ff) and base.lower() not in no_alias:
264 if ext.lower() == '.exe':
264 if ext.lower() == '.exe':
265 fname = base
265 fname = base
266 try:
266 try:
267 # Removes dots from the name since ipython
267 # Removes dots from the name since ipython
268 # will assume names with dots to be python.
268 # will assume names with dots to be python.
269 self.shell.alias_manager.define_alias(
269 self.shell.alias_manager.define_alias(
270 base.lower().replace('.',''), fname)
270 base.lower().replace('.',''), fname)
271 except InvalidAliasError:
271 except InvalidAliasError:
272 pass
272 pass
273 syscmdlist.append(fname)
273 syscmdlist.append(fname)
274
274
275 self.shell.db['syscmdlist'] = syscmdlist
275 self.shell.db['syscmdlist'] = syscmdlist
276 finally:
276 finally:
277 os.chdir(savedir)
277 os.chdir(savedir)
278
278
279 @skip_doctest
279 @skip_doctest
280 @line_magic
280 @line_magic
281 def pwd(self, parameter_s=''):
281 def pwd(self, parameter_s=''):
282 """Return the current working directory path.
282 """Return the current working directory path.
283
283
284 Examples
284 Examples
285 --------
285 --------
286 ::
286 ::
287
287
288 In [9]: pwd
288 In [9]: pwd
289 Out[9]: '/home/tsuser/sprint/ipython'
289 Out[9]: '/home/tsuser/sprint/ipython'
290 """
290 """
291 try:
291 try:
292 return os.getcwd()
292 return os.getcwd()
293 except FileNotFoundError as e:
293 except FileNotFoundError as e:
294 raise UsageError("CWD no longer exists - please use %cd to change directory.") from e
294 raise UsageError("CWD no longer exists - please use %cd to change directory.") from e
295
295
296 @skip_doctest
296 @skip_doctest
297 @line_magic
297 @line_magic
298 def cd(self, parameter_s=''):
298 def cd(self, parameter_s=''):
299 """Change the current working directory.
299 """Change the current working directory.
300
300
301 This command automatically maintains an internal list of directories
301 This command automatically maintains an internal list of directories
302 you visit during your IPython session, in the variable ``_dh``. The
302 you visit during your IPython session, in the variable ``_dh``. The
303 command :magic:`%dhist` shows this history nicely formatted. You can
303 command :magic:`%dhist` shows this history nicely formatted. You can
304 also do ``cd -<tab>`` to see directory history conveniently.
304 also do ``cd -<tab>`` to see directory history conveniently.
305 Usage:
305 Usage:
306
306
307 - ``cd 'dir'``: changes to directory 'dir'.
307 - ``cd 'dir'``: changes to directory 'dir'.
308 - ``cd -``: changes to the last visited directory.
308 - ``cd -``: changes to the last visited directory.
309 - ``cd -<n>``: changes to the n-th directory in the directory history.
309 - ``cd -<n>``: changes to the n-th directory in the directory history.
310 - ``cd --foo``: change to directory that matches 'foo' in history
310 - ``cd --foo``: change to directory that matches 'foo' in history
311 - ``cd -b <bookmark_name>``: jump to a bookmark set by %bookmark
311 - ``cd -b <bookmark_name>``: jump to a bookmark set by %bookmark
312 - Hitting a tab key after ``cd -b`` allows you to tab-complete
312 - Hitting a tab key after ``cd -b`` allows you to tab-complete
313 bookmark names.
313 bookmark names.
314
314
315 .. note::
315 .. note::
316 ``cd <bookmark_name>`` is enough if there is no directory
316 ``cd <bookmark_name>`` is enough if there is no directory
317 ``<bookmark_name>``, but a bookmark with the name exists.
317 ``<bookmark_name>``, but a bookmark with the name exists.
318
318
319 Options:
319 Options:
320
320
321 -q Be quiet. Do not print the working directory after the
321 -q Be quiet. Do not print the working directory after the
322 cd command is executed. By default IPython's cd
322 cd command is executed. By default IPython's cd
323 command does print this directory, since the default
323 command does print this directory, since the default
324 prompts do not display path information.
324 prompts do not display path information.
325
325
326 .. note::
326 .. note::
327 Note that ``!cd`` doesn't work for this purpose because the shell
327 Note that ``!cd`` doesn't work for this purpose because the shell
328 where ``!command`` runs is immediately discarded after executing
328 where ``!command`` runs is immediately discarded after executing
329 'command'.
329 'command'.
330
330
331 Examples
331 Examples
332 --------
332 --------
333 ::
333 ::
334
334
335 In [10]: cd parent/child
335 In [10]: cd parent/child
336 /home/tsuser/parent/child
336 /home/tsuser/parent/child
337 """
337 """
338
338
339 try:
339 try:
340 oldcwd = os.getcwd()
340 oldcwd = os.getcwd()
341 except FileNotFoundError:
341 except FileNotFoundError:
342 # Happens if the CWD has been deleted.
342 # Happens if the CWD has been deleted.
343 oldcwd = None
343 oldcwd = None
344
344
345 numcd = re.match(r'(-)(\d+)$',parameter_s)
345 numcd = re.match(r'(-)(\d+)$',parameter_s)
346 # jump in directory history by number
346 # jump in directory history by number
347 if numcd:
347 if numcd:
348 nn = int(numcd.group(2))
348 nn = int(numcd.group(2))
349 try:
349 try:
350 ps = self.shell.user_ns['_dh'][nn]
350 ps = self.shell.user_ns['_dh'][nn]
351 except IndexError:
351 except IndexError:
352 print('The requested directory does not exist in history.')
352 print('The requested directory does not exist in history.')
353 return
353 return
354 else:
354 else:
355 opts = {}
355 opts = {}
356 elif parameter_s.startswith('--'):
356 elif parameter_s.startswith('--'):
357 ps = None
357 ps = None
358 fallback = None
358 fallback = None
359 pat = parameter_s[2:]
359 pat = parameter_s[2:]
360 dh = self.shell.user_ns['_dh']
360 dh = self.shell.user_ns['_dh']
361 # first search only by basename (last component)
361 # first search only by basename (last component)
362 for ent in reversed(dh):
362 for ent in reversed(dh):
363 if pat in os.path.basename(ent) and os.path.isdir(ent):
363 if pat in os.path.basename(ent) and os.path.isdir(ent):
364 ps = ent
364 ps = ent
365 break
365 break
366
366
367 if fallback is None and pat in ent and os.path.isdir(ent):
367 if fallback is None and pat in ent and os.path.isdir(ent):
368 fallback = ent
368 fallback = ent
369
369
370 # if we have no last part match, pick the first full path match
370 # if we have no last part match, pick the first full path match
371 if ps is None:
371 if ps is None:
372 ps = fallback
372 ps = fallback
373
373
374 if ps is None:
374 if ps is None:
375 print("No matching entry in directory history")
375 print("No matching entry in directory history")
376 return
376 return
377 else:
377 else:
378 opts = {}
378 opts = {}
379
379
380
380
381 else:
381 else:
382 opts, ps = self.parse_options(parameter_s, 'qb', mode='string')
382 opts, ps = self.parse_options(parameter_s, 'qb', mode='string')
383 # jump to previous
383 # jump to previous
384 if ps == '-':
384 if ps == '-':
385 try:
385 try:
386 ps = self.shell.user_ns['_dh'][-2]
386 ps = self.shell.user_ns['_dh'][-2]
387 except IndexError as e:
387 except IndexError as e:
388 raise UsageError('%cd -: No previous directory to change to.') from e
388 raise UsageError('%cd -: No previous directory to change to.') from e
389 # jump to bookmark if needed
389 # jump to bookmark if needed
390 else:
390 else:
391 if not os.path.isdir(ps) or 'b' in opts:
391 if not os.path.isdir(ps) or 'b' in opts:
392 bkms = self.shell.db.get('bookmarks', {})
392 bkms = self.shell.db.get('bookmarks', {})
393
393
394 if ps in bkms:
394 if ps in bkms:
395 target = bkms[ps]
395 target = bkms[ps]
396 print('(bookmark:%s) -> %s' % (ps, target))
396 print('(bookmark:%s) -> %s' % (ps, target))
397 ps = target
397 ps = target
398 else:
398 else:
399 if 'b' in opts:
399 if 'b' in opts:
400 raise UsageError("Bookmark '%s' not found. "
400 raise UsageError("Bookmark '%s' not found. "
401 "Use '%%bookmark -l' to see your bookmarks." % ps)
401 "Use '%%bookmark -l' to see your bookmarks." % ps)
402
402
403 # at this point ps should point to the target dir
403 # at this point ps should point to the target dir
404 if ps:
404 if ps:
405 try:
405 try:
406 os.chdir(os.path.expanduser(ps))
406 os.chdir(os.path.expanduser(ps))
407 if hasattr(self.shell, 'term_title') and self.shell.term_title:
407 if hasattr(self.shell, 'term_title') and self.shell.term_title:
408 set_term_title(self.shell.term_title_format.format(cwd=abbrev_cwd()))
408 set_term_title(self.shell.term_title_format.format(cwd=abbrev_cwd()))
409 except OSError:
409 except OSError:
410 print(sys.exc_info()[1])
410 print(sys.exc_info()[1])
411 else:
411 else:
412 cwd = os.getcwd()
412 cwd = os.getcwd()
413 dhist = self.shell.user_ns['_dh']
413 dhist = self.shell.user_ns['_dh']
414 if oldcwd != cwd:
414 if oldcwd != cwd:
415 dhist.append(cwd)
415 dhist.append(cwd)
416 self.shell.db['dhist'] = compress_dhist(dhist)[-100:]
416 self.shell.db['dhist'] = compress_dhist(dhist)[-100:]
417
417
418 else:
418 else:
419 os.chdir(self.shell.home_dir)
419 os.chdir(self.shell.home_dir)
420 if hasattr(self.shell, 'term_title') and self.shell.term_title:
420 if hasattr(self.shell, 'term_title') and self.shell.term_title:
421 set_term_title(self.shell.term_title_format.format(cwd="~"))
421 set_term_title(self.shell.term_title_format.format(cwd="~"))
422 cwd = os.getcwd()
422 cwd = os.getcwd()
423 dhist = self.shell.user_ns['_dh']
423 dhist = self.shell.user_ns['_dh']
424
424
425 if oldcwd != cwd:
425 if oldcwd != cwd:
426 dhist.append(cwd)
426 dhist.append(cwd)
427 self.shell.db['dhist'] = compress_dhist(dhist)[-100:]
427 self.shell.db['dhist'] = compress_dhist(dhist)[-100:]
428 if not 'q' in opts and not self.cd_force_quiet and self.shell.user_ns['_dh']:
428 if not 'q' in opts and not self.cd_force_quiet and self.shell.user_ns['_dh']:
429 print(self.shell.user_ns['_dh'][-1])
429 print(self.shell.user_ns['_dh'][-1])
430
430
431 @line_magic
431 @line_magic
432 def env(self, parameter_s=''):
432 def env(self, parameter_s=''):
433 """Get, set, or list environment variables.
433 """Get, set, or list environment variables.
434
434
435 Usage:\\
435 Usage:\\
436
436
437 :``%env``: lists all environment variables/values
437 :``%env``: lists all environment variables/values
438 :``%env var``: get value for var
438 :``%env var``: get value for var
439 :``%env var val``: set value for var
439 :``%env var val``: set value for var
440 :``%env var=val``: set value for var
440 :``%env var=val``: set value for var
441 :``%env var=$val``: set value for var, using python expansion if possible
441 :``%env var=$val``: set value for var, using python expansion if possible
442 """
442 """
443 if parameter_s.strip():
443 if parameter_s.strip():
444 split = '=' if '=' in parameter_s else ' '
444 split = '=' if '=' in parameter_s else ' '
445 bits = parameter_s.split(split)
445 bits = parameter_s.split(split)
446 if len(bits) == 1:
446 if len(bits) == 1:
447 key = parameter_s.strip()
447 key = parameter_s.strip()
448 if key in os.environ:
448 if key in os.environ:
449 return os.environ[key]
449 return os.environ[key]
450 else:
450 else:
451 err = "Environment does not have key: {0}".format(key)
451 err = "Environment does not have key: {0}".format(key)
452 raise UsageError(err)
452 raise UsageError(err)
453 if len(bits) > 1:
453 if len(bits) > 1:
454 return self.set_env(parameter_s)
454 return self.set_env(parameter_s)
455 env = dict(os.environ)
455 env = dict(os.environ)
456 # hide likely secrets when printing the whole environment
456 # hide likely secrets when printing the whole environment
457 for key in list(env):
457 for key in list(env):
458 if any(s in key.lower() for s in ('key', 'token', 'secret')):
458 if any(s in key.lower() for s in ('key', 'token', 'secret')):
459 env[key] = '<hidden>'
459 env[key] = '<hidden>'
460
460
461 return env
461 return env
462
462
463 @line_magic
463 @line_magic
464 def set_env(self, parameter_s):
464 def set_env(self, parameter_s):
465 """Set environment variables. Assumptions are that either "val" is a
465 """Set environment variables. Assumptions are that either "val" is a
466 name in the user namespace, or val is something that evaluates to a
466 name in the user namespace, or val is something that evaluates to a
467 string.
467 string.
468
468
469 Usage:\\
469 Usage:\\
470 %set_env var val: set value for var
470 %set_env var val: set value for var
471 %set_env var=val: set value for var
471 %set_env var=val: set value for var
472 %set_env var=$val: set value for var, using python expansion if possible
472 %set_env var=$val: set value for var, using python expansion if possible
473 """
473 """
474 split = '=' if '=' in parameter_s else ' '
474 split = '=' if '=' in parameter_s else ' '
475 bits = parameter_s.split(split, 1)
475 bits = parameter_s.split(split, 1)
476 if not parameter_s.strip() or len(bits)<2:
476 if not parameter_s.strip() or len(bits)<2:
477 raise UsageError("usage is 'set_env var=val'")
477 raise UsageError("usage is 'set_env var=val'")
478 var = bits[0].strip()
478 var = bits[0].strip()
479 val = bits[1].strip()
479 val = bits[1].strip()
480 if re.match(r'.*\s.*', var):
480 if re.match(r'.*\s.*', var):
481 # an environment variable with whitespace is almost certainly
481 # an environment variable with whitespace is almost certainly
482 # not what the user intended. what's more likely is the wrong
482 # not what the user intended. what's more likely is the wrong
483 # split was chosen, ie for "set_env cmd_args A=B", we chose
483 # split was chosen, ie for "set_env cmd_args A=B", we chose
484 # '=' for the split and should have chosen ' '. to get around
484 # '=' for the split and should have chosen ' '. to get around
485 # this, users should just assign directly to os.environ or use
485 # this, users should just assign directly to os.environ or use
486 # standard magic {var} expansion.
486 # standard magic {var} expansion.
487 err = "refusing to set env var with whitespace: '{0}'"
487 err = "refusing to set env var with whitespace: '{0}'"
488 err = err.format(val)
488 err = err.format(val)
489 raise UsageError(err)
489 raise UsageError(err)
490 os.environ[var] = val
490 os.environ[var] = val
491 print('env: {0}={1}'.format(var,val))
491 print('env: {0}={1}'.format(var,val))
492
492
493 @line_magic
493 @line_magic
494 def pushd(self, parameter_s=''):
494 def pushd(self, parameter_s=''):
495 """Place the current dir on stack and change directory.
495 """Place the current dir on stack and change directory.
496
496
497 Usage:\\
497 Usage:\\
498 %pushd ['dirname']
498 %pushd ['dirname']
499 """
499 """
500
500
501 dir_s = self.shell.dir_stack
501 dir_s = self.shell.dir_stack
502 tgt = os.path.expanduser(parameter_s)
502 tgt = os.path.expanduser(parameter_s)
503 cwd = os.getcwd().replace(self.shell.home_dir,'~')
503 cwd = os.getcwd().replace(self.shell.home_dir,'~')
504 if tgt:
504 if tgt:
505 self.cd(parameter_s)
505 self.cd(parameter_s)
506 dir_s.insert(0,cwd)
506 dir_s.insert(0,cwd)
507 return self.shell.run_line_magic('dirs', '')
507 return self.shell.run_line_magic('dirs', '')
508
508
509 @line_magic
509 @line_magic
510 def popd(self, parameter_s=''):
510 def popd(self, parameter_s=''):
511 """Change to directory popped off the top of the stack.
511 """Change to directory popped off the top of the stack.
512 """
512 """
513 if not self.shell.dir_stack:
513 if not self.shell.dir_stack:
514 raise UsageError("%popd on empty stack")
514 raise UsageError("%popd on empty stack")
515 top = self.shell.dir_stack.pop(0)
515 top = self.shell.dir_stack.pop(0)
516 self.cd(top)
516 self.cd(top)
517 print("popd ->",top)
517 print("popd ->",top)
518
518
519 @line_magic
519 @line_magic
520 def dirs(self, parameter_s=''):
520 def dirs(self, parameter_s=''):
521 """Return the current directory stack."""
521 """Return the current directory stack."""
522
522
523 return self.shell.dir_stack
523 return self.shell.dir_stack
524
524
525 @line_magic
525 @line_magic
526 def dhist(self, parameter_s=''):
526 def dhist(self, parameter_s=''):
527 """Print your history of visited directories.
527 """Print your history of visited directories.
528
528
529 %dhist -> print full history\\
529 %dhist -> print full history\\
530 %dhist n -> print last n entries only\\
530 %dhist n -> print last n entries only\\
531 %dhist n1 n2 -> print entries between n1 and n2 (n2 not included)\\
531 %dhist n1 n2 -> print entries between n1 and n2 (n2 not included)\\
532
532
533 This history is automatically maintained by the %cd command, and
533 This history is automatically maintained by the %cd command, and
534 always available as the global list variable _dh. You can use %cd -<n>
534 always available as the global list variable _dh. You can use %cd -<n>
535 to go to directory number <n>.
535 to go to directory number <n>.
536
536
537 Note that most of time, you should view directory history by entering
537 Note that most of time, you should view directory history by entering
538 cd -<TAB>.
538 cd -<TAB>.
539
539
540 """
540 """
541
541
542 dh = self.shell.user_ns['_dh']
542 dh = self.shell.user_ns['_dh']
543 if parameter_s:
543 if parameter_s:
544 try:
544 try:
545 args = map(int,parameter_s.split())
545 args = map(int,parameter_s.split())
546 except:
546 except:
547 self.arg_err(self.dhist)
547 self.arg_err(self.dhist)
548 return
548 return
549 if len(args) == 1:
549 if len(args) == 1:
550 ini,fin = max(len(dh)-(args[0]),0),len(dh)
550 ini,fin = max(len(dh)-(args[0]),0),len(dh)
551 elif len(args) == 2:
551 elif len(args) == 2:
552 ini,fin = args
552 ini,fin = args
553 fin = min(fin, len(dh))
553 fin = min(fin, len(dh))
554 else:
554 else:
555 self.arg_err(self.dhist)
555 self.arg_err(self.dhist)
556 return
556 return
557 else:
557 else:
558 ini,fin = 0,len(dh)
558 ini,fin = 0,len(dh)
559 print('Directory history (kept in _dh)')
559 print('Directory history (kept in _dh)')
560 for i in range(ini, fin):
560 for i in range(ini, fin):
561 print("%d: %s" % (i, dh[i]))
561 print("%d: %s" % (i, dh[i]))
562
562
563 @skip_doctest
563 @skip_doctest
564 @line_magic
564 @line_magic
565 def sc(self, parameter_s=''):
565 def sc(self, parameter_s=''):
566 """Shell capture - run shell command and capture output (DEPRECATED use !).
566 """Shell capture - run shell command and capture output (DEPRECATED use !).
567
567
568 DEPRECATED. Suboptimal, retained for backwards compatibility.
568 DEPRECATED. Suboptimal, retained for backwards compatibility.
569
569
570 You should use the form 'var = !command' instead. Example:
570 You should use the form 'var = !command' instead. Example:
571
571
572 "%sc -l myfiles = ls ~" should now be written as
572 "%sc -l myfiles = ls ~" should now be written as
573
573
574 "myfiles = !ls ~"
574 "myfiles = !ls ~"
575
575
576 myfiles.s, myfiles.l and myfiles.n still apply as documented
576 myfiles.s, myfiles.l and myfiles.n still apply as documented
577 below.
577 below.
578
578
579 --
579 --
580 %sc [options] varname=command
580 %sc [options] varname=command
581
581
582 IPython will run the given command using commands.getoutput(), and
582 IPython will run the given command using commands.getoutput(), and
583 will then update the user's interactive namespace with a variable
583 will then update the user's interactive namespace with a variable
584 called varname, containing the value of the call. Your command can
584 called varname, containing the value of the call. Your command can
585 contain shell wildcards, pipes, etc.
585 contain shell wildcards, pipes, etc.
586
586
587 The '=' sign in the syntax is mandatory, and the variable name you
587 The '=' sign in the syntax is mandatory, and the variable name you
588 supply must follow Python's standard conventions for valid names.
588 supply must follow Python's standard conventions for valid names.
589
589
590 (A special format without variable name exists for internal use)
590 (A special format without variable name exists for internal use)
591
591
592 Options:
592 Options:
593
593
594 -l: list output. Split the output on newlines into a list before
594 -l: list output. Split the output on newlines into a list before
595 assigning it to the given variable. By default the output is stored
595 assigning it to the given variable. By default the output is stored
596 as a single string.
596 as a single string.
597
597
598 -v: verbose. Print the contents of the variable.
598 -v: verbose. Print the contents of the variable.
599
599
600 In most cases you should not need to split as a list, because the
600 In most cases you should not need to split as a list, because the
601 returned value is a special type of string which can automatically
601 returned value is a special type of string which can automatically
602 provide its contents either as a list (split on newlines) or as a
602 provide its contents either as a list (split on newlines) or as a
603 space-separated string. These are convenient, respectively, either
603 space-separated string. These are convenient, respectively, either
604 for sequential processing or to be passed to a shell command.
604 for sequential processing or to be passed to a shell command.
605
605
606 For example::
606 For example::
607
607
608 # Capture into variable a
608 # Capture into variable a
609 In [1]: sc a=ls *py
609 In [1]: sc a=ls *py
610
610
611 # a is a string with embedded newlines
611 # a is a string with embedded newlines
612 In [2]: a
612 In [2]: a
613 Out[2]: 'setup.py\\nwin32_manual_post_install.py'
613 Out[2]: 'setup.py\\nwin32_manual_post_install.py'
614
614
615 # which can be seen as a list:
615 # which can be seen as a list:
616 In [3]: a.l
616 In [3]: a.l
617 Out[3]: ['setup.py', 'win32_manual_post_install.py']
617 Out[3]: ['setup.py', 'win32_manual_post_install.py']
618
618
619 # or as a whitespace-separated string:
619 # or as a whitespace-separated string:
620 In [4]: a.s
620 In [4]: a.s
621 Out[4]: 'setup.py win32_manual_post_install.py'
621 Out[4]: 'setup.py win32_manual_post_install.py'
622
622
623 # a.s is useful to pass as a single command line:
623 # a.s is useful to pass as a single command line:
624 In [5]: !wc -l $a.s
624 In [5]: !wc -l $a.s
625 146 setup.py
625 146 setup.py
626 130 win32_manual_post_install.py
626 130 win32_manual_post_install.py
627 276 total
627 276 total
628
628
629 # while the list form is useful to loop over:
629 # while the list form is useful to loop over:
630 In [6]: for f in a.l:
630 In [6]: for f in a.l:
631 ...: !wc -l $f
631 ...: !wc -l $f
632 ...:
632 ...:
633 146 setup.py
633 146 setup.py
634 130 win32_manual_post_install.py
634 130 win32_manual_post_install.py
635
635
636 Similarly, the lists returned by the -l option are also special, in
636 Similarly, the lists returned by the -l option are also special, in
637 the sense that you can equally invoke the .s attribute on them to
637 the sense that you can equally invoke the .s attribute on them to
638 automatically get a whitespace-separated string from their contents::
638 automatically get a whitespace-separated string from their contents::
639
639
640 In [7]: sc -l b=ls *py
640 In [7]: sc -l b=ls *py
641
641
642 In [8]: b
642 In [8]: b
643 Out[8]: ['setup.py', 'win32_manual_post_install.py']
643 Out[8]: ['setup.py', 'win32_manual_post_install.py']
644
644
645 In [9]: b.s
645 In [9]: b.s
646 Out[9]: 'setup.py win32_manual_post_install.py'
646 Out[9]: 'setup.py win32_manual_post_install.py'
647
647
648 In summary, both the lists and strings used for output capture have
648 In summary, both the lists and strings used for output capture have
649 the following special attributes::
649 the following special attributes::
650
650
651 .l (or .list) : value as list.
651 .l (or .list) : value as list.
652 .n (or .nlstr): value as newline-separated string.
652 .n (or .nlstr): value as newline-separated string.
653 .s (or .spstr): value as space-separated string.
653 .s (or .spstr): value as space-separated string.
654 """
654 """
655
655
656 opts,args = self.parse_options(parameter_s, 'lv')
656 opts,args = self.parse_options(parameter_s, 'lv')
657 # Try to get a variable name and command to run
657 # Try to get a variable name and command to run
658 try:
658 try:
659 # the variable name must be obtained from the parse_options
659 # the variable name must be obtained from the parse_options
660 # output, which uses shlex.split to strip options out.
660 # output, which uses shlex.split to strip options out.
661 var,_ = args.split('=', 1)
661 var,_ = args.split('=', 1)
662 var = var.strip()
662 var = var.strip()
663 # But the command has to be extracted from the original input
663 # But the command has to be extracted from the original input
664 # parameter_s, not on what parse_options returns, to avoid the
664 # parameter_s, not on what parse_options returns, to avoid the
665 # quote stripping which shlex.split performs on it.
665 # quote stripping which shlex.split performs on it.
666 _,cmd = parameter_s.split('=', 1)
666 _,cmd = parameter_s.split('=', 1)
667 except ValueError:
667 except ValueError:
668 var,cmd = '',''
668 var,cmd = '',''
669 # If all looks ok, proceed
669 # If all looks ok, proceed
670 split = 'l' in opts
670 split = 'l' in opts
671 out = self.shell.getoutput(cmd, split=split)
671 out = self.shell.getoutput(cmd, split=split)
672 if 'v' in opts:
672 if 'v' in opts:
673 print('%s ==\n%s' % (var, pformat(out)))
673 print('%s ==\n%s' % (var, pformat(out)))
674 if var:
674 if var:
675 self.shell.user_ns.update({var:out})
675 self.shell.user_ns.update({var:out})
676 else:
676 else:
677 return out
677 return out
678
678
679 @line_cell_magic
679 @line_cell_magic
680 def sx(self, line='', cell=None):
680 def sx(self, line='', cell=None):
681 """Shell execute - run shell command and capture output (!! is short-hand).
681 """Shell execute - run shell command and capture output (!! is short-hand).
682
682
683 %sx command
683 %sx command
684
684
685 IPython will run the given command using commands.getoutput(), and
685 IPython will run the given command using commands.getoutput(), and
686 return the result formatted as a list (split on '\\n'). Since the
686 return the result formatted as a list (split on '\\n'). Since the
687 output is _returned_, it will be stored in ipython's regular output
687 output is _returned_, it will be stored in ipython's regular output
688 cache Out[N] and in the '_N' automatic variables.
688 cache Out[N] and in the '_N' automatic variables.
689
689
690 Notes:
690 Notes:
691
691
692 1) If an input line begins with '!!', then %sx is automatically
692 1) If an input line begins with '!!', then %sx is automatically
693 invoked. That is, while::
693 invoked. That is, while::
694
694
695 !ls
695 !ls
696
696
697 causes ipython to simply issue system('ls'), typing::
697 causes ipython to simply issue system('ls'), typing::
698
698
699 !!ls
699 !!ls
700
700
701 is a shorthand equivalent to::
701 is a shorthand equivalent to::
702
702
703 %sx ls
703 %sx ls
704
704
705 2) %sx differs from %sc in that %sx automatically splits into a list,
705 2) %sx differs from %sc in that %sx automatically splits into a list,
706 like '%sc -l'. The reason for this is to make it as easy as possible
706 like '%sc -l'. The reason for this is to make it as easy as possible
707 to process line-oriented shell output via further python commands.
707 to process line-oriented shell output via further python commands.
708 %sc is meant to provide much finer control, but requires more
708 %sc is meant to provide much finer control, but requires more
709 typing.
709 typing.
710
710
711 3) Just like %sc -l, this is a list with special attributes:
711 3) Just like %sc -l, this is a list with special attributes:
712 ::
712 ::
713
713
714 .l (or .list) : value as list.
714 .l (or .list) : value as list.
715 .n (or .nlstr): value as newline-separated string.
715 .n (or .nlstr): value as newline-separated string.
716 .s (or .spstr): value as whitespace-separated string.
716 .s (or .spstr): value as whitespace-separated string.
717
717
718 This is very useful when trying to use such lists as arguments to
718 This is very useful when trying to use such lists as arguments to
719 system commands."""
719 system commands."""
720
720
721 if cell is None:
721 if cell is None:
722 # line magic
722 # line magic
723 return self.shell.getoutput(line)
723 return self.shell.getoutput(line)
724 else:
724 else:
725 opts,args = self.parse_options(line, '', 'out=')
725 opts,args = self.parse_options(line, '', 'out=')
726 output = self.shell.getoutput(cell)
726 output = self.shell.getoutput(cell)
727 out_name = opts.get('out', opts.get('o'))
727 out_name = opts.get('out', opts.get('o'))
728 if out_name:
728 if out_name:
729 self.shell.user_ns[out_name] = output
729 self.shell.user_ns[out_name] = output
730 else:
730 else:
731 return output
731 return output
732
732
733 system = line_cell_magic('system')(sx)
733 system = line_cell_magic('system')(sx)
734 bang = cell_magic('!')(sx)
734 bang = cell_magic('!')(sx)
735
735
736 @line_magic
736 @line_magic
737 def bookmark(self, parameter_s=''):
737 def bookmark(self, parameter_s=''):
738 """Manage IPython's bookmark system.
738 """Manage IPython's bookmark system.
739
739
740 %bookmark <name> - set bookmark to current dir
740 %bookmark <name> - set bookmark to current dir
741 %bookmark <name> <dir> - set bookmark to <dir>
741 %bookmark <name> <dir> - set bookmark to <dir>
742 %bookmark -l - list all bookmarks
742 %bookmark -l - list all bookmarks
743 %bookmark -d <name> - remove bookmark
743 %bookmark -d <name> - remove bookmark
744 %bookmark -r - remove all bookmarks
744 %bookmark -r - remove all bookmarks
745
745
746 You can later on access a bookmarked folder with::
746 You can later on access a bookmarked folder with::
747
747
748 %cd -b <name>
748 %cd -b <name>
749
749
750 or simply '%cd <name>' if there is no directory called <name> AND
750 or simply '%cd <name>' if there is no directory called <name> AND
751 there is such a bookmark defined.
751 there is such a bookmark defined.
752
752
753 Your bookmarks persist through IPython sessions, but they are
753 Your bookmarks persist through IPython sessions, but they are
754 associated with each profile."""
754 associated with each profile."""
755
755
756 opts,args = self.parse_options(parameter_s,'drl',mode='list')
756 opts,args = self.parse_options(parameter_s,'drl',mode='list')
757 if len(args) > 2:
757 if len(args) > 2:
758 raise UsageError("%bookmark: too many arguments")
758 raise UsageError("%bookmark: too many arguments")
759
759
760 bkms = self.shell.db.get('bookmarks',{})
760 bkms = self.shell.db.get('bookmarks',{})
761
761
762 if 'd' in opts:
762 if 'd' in opts:
763 try:
763 try:
764 todel = args[0]
764 todel = args[0]
765 except IndexError as e:
765 except IndexError as e:
766 raise UsageError(
766 raise UsageError(
767 "%bookmark -d: must provide a bookmark to delete") from e
767 "%bookmark -d: must provide a bookmark to delete") from e
768 else:
768 else:
769 try:
769 try:
770 del bkms[todel]
770 del bkms[todel]
771 except KeyError as e:
771 except KeyError as e:
772 raise UsageError(
772 raise UsageError(
773 "%%bookmark -d: Can't delete bookmark '%s'" % todel) from e
773 "%%bookmark -d: Can't delete bookmark '%s'" % todel) from e
774
774
775 elif 'r' in opts:
775 elif 'r' in opts:
776 bkms = {}
776 bkms = {}
777 elif 'l' in opts:
777 elif 'l' in opts:
778 bks = sorted(bkms)
778 bks = sorted(bkms)
779 if bks:
779 if bks:
780 size = max(map(len, bks))
780 size = max(map(len, bks))
781 else:
781 else:
782 size = 0
782 size = 0
783 fmt = '%-'+str(size)+'s -> %s'
783 fmt = '%-'+str(size)+'s -> %s'
784 print('Current bookmarks:')
784 print('Current bookmarks:')
785 for bk in bks:
785 for bk in bks:
786 print(fmt % (bk, bkms[bk]))
786 print(fmt % (bk, bkms[bk]))
787 else:
787 else:
788 if not args:
788 if not args:
789 raise UsageError("%bookmark: You must specify the bookmark name")
789 raise UsageError("%bookmark: You must specify the bookmark name")
790 elif len(args)==1:
790 elif len(args)==1:
791 bkms[args[0]] = os.getcwd()
791 bkms[args[0]] = os.getcwd()
792 elif len(args)==2:
792 elif len(args)==2:
793 bkms[args[0]] = args[1]
793 bkms[args[0]] = args[1]
794 self.shell.db['bookmarks'] = bkms
794 self.shell.db['bookmarks'] = bkms
795
795
796 @line_magic
796 @line_magic
797 def pycat(self, parameter_s=''):
797 def pycat(self, parameter_s=''):
798 """Show a syntax-highlighted file through a pager.
798 """Show a syntax-highlighted file through a pager.
799
799
800 This magic is similar to the cat utility, but it will assume the file
800 This magic is similar to the cat utility, but it will assume the file
801 to be Python source and will show it with syntax highlighting.
801 to be Python source and will show it with syntax highlighting.
802
802
803 This magic command can either take a local filename, an url,
803 This magic command can either take a local filename, an url,
804 an history range (see %history) or a macro as argument.
804 an history range (see %history) or a macro as argument.
805
805
806 If no parameter is given, prints out history of current session up to
806 If no parameter is given, prints out history of current session up to
807 this point. ::
807 this point. ::
808
808
809 %pycat myscript.py
809 %pycat myscript.py
810 %pycat 7-27
810 %pycat 7-27
811 %pycat myMacro
811 %pycat myMacro
812 %pycat http://www.example.com/myscript.py
812 %pycat http://www.example.com/myscript.py
813 """
813 """
814 try:
814 try:
815 cont = self.shell.find_user_code(parameter_s, skip_encoding_cookie=False)
815 cont = self.shell.find_user_code(parameter_s, skip_encoding_cookie=False)
816 except (ValueError, IOError):
816 except (ValueError, IOError):
817 print("Error: no such file, variable, URL, history range or macro")
817 print("Error: no such file, variable, URL, history range or macro")
818 return
818 return
819
819
820 page.page(self.shell.pycolorize(source_to_unicode(cont)))
820 page.page(self.shell.pycolorize(source_to_unicode(cont)))
821
821
822 @magic_arguments.magic_arguments()
822 @magic_arguments.magic_arguments()
823 @magic_arguments.argument(
823 @magic_arguments.argument(
824 '-a', '--append', action='store_true', default=False,
824 '-a', '--append', action='store_true', default=False,
825 help='Append contents of the cell to an existing file. '
825 help='Append contents of the cell to an existing file. '
826 'The file will be created if it does not exist.'
826 'The file will be created if it does not exist.'
827 )
827 )
828 @magic_arguments.argument(
828 @magic_arguments.argument(
829 'filename', type=str,
829 'filename', type=str,
830 help='file to write'
830 help='file to write'
831 )
831 )
832 @cell_magic
832 @cell_magic
833 def writefile(self, line, cell):
833 def writefile(self, line, cell):
834 """Write the contents of the cell to a file.
834 """Write the contents of the cell to a file.
835
835
836 The file will be overwritten unless the -a (--append) flag is specified.
836 The file will be overwritten unless the -a (--append) flag is specified.
837 """
837 """
838 args = magic_arguments.parse_argstring(self.writefile, line)
838 args = magic_arguments.parse_argstring(self.writefile, line)
839 if re.match(r'^(\'.*\')|(".*")$', args.filename):
839 if re.match(r'^(\'.*\')|(".*")$', args.filename):
840 filename = os.path.expanduser(args.filename[1:-1])
840 filename = os.path.expanduser(args.filename[1:-1])
841 else:
841 else:
842 filename = os.path.expanduser(args.filename)
842 filename = os.path.expanduser(args.filename)
843
843
844 if os.path.exists(filename):
844 if os.path.exists(filename):
845 if args.append:
845 if args.append:
846 print("Appending to %s" % filename)
846 print("Appending to %s" % filename)
847 else:
847 else:
848 print("Overwriting %s" % filename)
848 print("Overwriting %s" % filename)
849 else:
849 else:
850 print("Writing %s" % filename)
850 print("Writing %s" % filename)
851
851
852 mode = 'a' if args.append else 'w'
852 mode = 'a' if args.append else 'w'
853 with io.open(filename, mode, encoding='utf-8') as f:
853 with io.open(filename, mode, encoding='utf-8') as f:
854 f.write(cell)
854 f.write(cell)
General Comments 0
You need to be logged in to leave comments. Login now