##// END OF EJS Templates
Merge pull request #4536 from minrk/msgspec5...
Thomas Kluyver -
r16692:a2329cd4 merge
parent child Browse files
Show More

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

1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,812 +1,812
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 Authors:
4 Authors:
5
5
6 * Brian Granger
6 * Brian Granger
7 """
7 """
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2013 The IPython Development Team
10 # Copyright (C) 2013 The IPython Development Team
11 #
11 #
12 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
13 # the file COPYING, distributed as part of this software.
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 from __future__ import print_function
20 from __future__ import print_function
21
21
22 import os
22 import os
23 import struct
23 import struct
24
24
25 from IPython.core.formatters import _safe_get_formatter_method
25 from IPython.core.formatters import _safe_get_formatter_method
26 from IPython.utils.py3compat import (string_types, cast_bytes_py2, cast_unicode,
26 from IPython.utils.py3compat import (string_types, cast_bytes_py2, cast_unicode,
27 unicode_type)
27 unicode_type)
28 from IPython.testing.skipdoctest import skip_doctest
28 from IPython.testing.skipdoctest import skip_doctest
29 from .displaypub import publish_display_data
29 from .displaypub import publish_display_data
30
30
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32 # utility functions
32 # utility functions
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34
34
35 def _safe_exists(path):
35 def _safe_exists(path):
36 """Check path, but don't let exceptions raise"""
36 """Check path, but don't let exceptions raise"""
37 try:
37 try:
38 return os.path.exists(path)
38 return os.path.exists(path)
39 except Exception:
39 except Exception:
40 return False
40 return False
41
41
42 def _merge(d1, d2):
42 def _merge(d1, d2):
43 """Like update, but merges sub-dicts instead of clobbering at the top level.
43 """Like update, but merges sub-dicts instead of clobbering at the top level.
44
44
45 Updates d1 in-place
45 Updates d1 in-place
46 """
46 """
47
47
48 if not isinstance(d2, dict) or not isinstance(d1, dict):
48 if not isinstance(d2, dict) or not isinstance(d1, dict):
49 return d2
49 return d2
50 for key, value in d2.items():
50 for key, value in d2.items():
51 d1[key] = _merge(d1.get(key), value)
51 d1[key] = _merge(d1.get(key), value)
52 return d1
52 return d1
53
53
54 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
54 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
55 """internal implementation of all display_foo methods
55 """internal implementation of all display_foo methods
56
56
57 Parameters
57 Parameters
58 ----------
58 ----------
59 mimetype : str
59 mimetype : str
60 The mimetype to be published (e.g. 'image/png')
60 The mimetype to be published (e.g. 'image/png')
61 objs : tuple of objects
61 objs : tuple of objects
62 The Python objects to display, or if raw=True raw text data to
62 The Python objects to display, or if raw=True raw text data to
63 display.
63 display.
64 raw : bool
64 raw : bool
65 Are the data objects raw data or Python objects that need to be
65 Are the data objects raw data or Python objects that need to be
66 formatted before display? [default: False]
66 formatted before display? [default: False]
67 metadata : dict (optional)
67 metadata : dict (optional)
68 Metadata to be associated with the specific mimetype output.
68 Metadata to be associated with the specific mimetype output.
69 """
69 """
70 if metadata:
70 if metadata:
71 metadata = {mimetype: metadata}
71 metadata = {mimetype: metadata}
72 if raw:
72 if raw:
73 # turn list of pngdata into list of { 'image/png': pngdata }
73 # turn list of pngdata into list of { 'image/png': pngdata }
74 objs = [ {mimetype: obj} for obj in objs ]
74 objs = [ {mimetype: obj} for obj in objs ]
75 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
75 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
76
76
77 #-----------------------------------------------------------------------------
77 #-----------------------------------------------------------------------------
78 # Main functions
78 # Main functions
79 #-----------------------------------------------------------------------------
79 #-----------------------------------------------------------------------------
80
80
81 def display(*objs, **kwargs):
81 def display(*objs, **kwargs):
82 """Display a Python object in all frontends.
82 """Display a Python object in all frontends.
83
83
84 By default all representations will be computed and sent to the frontends.
84 By default all representations will be computed and sent to the frontends.
85 Frontends can decide which representation is used and how.
85 Frontends can decide which representation is used and how.
86
86
87 Parameters
87 Parameters
88 ----------
88 ----------
89 objs : tuple of objects
89 objs : tuple of objects
90 The Python objects to display.
90 The Python objects to display.
91 raw : bool, optional
91 raw : bool, optional
92 Are the objects to be displayed already mimetype-keyed dicts of raw display data,
92 Are the objects to be displayed already mimetype-keyed dicts of raw display data,
93 or Python objects that need to be formatted before display? [default: False]
93 or Python objects that need to be formatted before display? [default: False]
94 include : list or tuple, optional
94 include : list or tuple, optional
95 A list of format type strings (MIME types) to include in the
95 A list of format type strings (MIME types) to include in the
96 format data dict. If this is set *only* the format types included
96 format data dict. If this is set *only* the format types included
97 in this list will be computed.
97 in this list will be computed.
98 exclude : list or tuple, optional
98 exclude : list or tuple, optional
99 A list of format type strings (MIME types) to exclude in the format
99 A list of format type strings (MIME types) to exclude in the format
100 data dict. If this is set all format types will be computed,
100 data dict. If this is set all format types will be computed,
101 except for those included in this argument.
101 except for those included in this argument.
102 metadata : dict, optional
102 metadata : dict, optional
103 A dictionary of metadata to associate with the output.
103 A dictionary of metadata to associate with the output.
104 mime-type keys in this dictionary will be associated with the individual
104 mime-type keys in this dictionary will be associated with the individual
105 representation formats, if they exist.
105 representation formats, if they exist.
106 """
106 """
107 raw = kwargs.get('raw', False)
107 raw = kwargs.get('raw', False)
108 include = kwargs.get('include')
108 include = kwargs.get('include')
109 exclude = kwargs.get('exclude')
109 exclude = kwargs.get('exclude')
110 metadata = kwargs.get('metadata')
110 metadata = kwargs.get('metadata')
111
111
112 from IPython.core.interactiveshell import InteractiveShell
112 from IPython.core.interactiveshell import InteractiveShell
113
113
114 if not raw:
114 if not raw:
115 format = InteractiveShell.instance().display_formatter.format
115 format = InteractiveShell.instance().display_formatter.format
116
116
117 for obj in objs:
117 for obj in objs:
118
118
119 # If _ipython_display_ is defined, use that to display this object.
119 # If _ipython_display_ is defined, use that to display this object.
120 display_method = _safe_get_formatter_method(obj, '_ipython_display_')
120 display_method = _safe_get_formatter_method(obj, '_ipython_display_')
121 if display_method is not None:
121 if display_method is not None:
122 try:
122 try:
123 display_method(**kwargs)
123 display_method(**kwargs)
124 except NotImplementedError:
124 except NotImplementedError:
125 pass
125 pass
126 else:
126 else:
127 continue
127 continue
128 if raw:
128 if raw:
129 publish_display_data('display', obj, metadata)
129 publish_display_data(data=obj, metadata=metadata)
130 else:
130 else:
131 format_dict, md_dict = format(obj, include=include, exclude=exclude)
131 format_dict, md_dict = format(obj, include=include, exclude=exclude)
132 if metadata:
132 if metadata:
133 # kwarg-specified metadata gets precedence
133 # kwarg-specified metadata gets precedence
134 _merge(md_dict, metadata)
134 _merge(md_dict, metadata)
135 publish_display_data('display', format_dict, md_dict)
135 publish_display_data(data=format_dict, metadata=md_dict)
136
136
137
137
138 def display_pretty(*objs, **kwargs):
138 def display_pretty(*objs, **kwargs):
139 """Display the pretty (default) representation of an object.
139 """Display the pretty (default) representation of an object.
140
140
141 Parameters
141 Parameters
142 ----------
142 ----------
143 objs : tuple of objects
143 objs : tuple of objects
144 The Python objects to display, or if raw=True raw text data to
144 The Python objects to display, or if raw=True raw text data to
145 display.
145 display.
146 raw : bool
146 raw : bool
147 Are the data objects raw data or Python objects that need to be
147 Are the data objects raw data or Python objects that need to be
148 formatted before display? [default: False]
148 formatted before display? [default: False]
149 metadata : dict (optional)
149 metadata : dict (optional)
150 Metadata to be associated with the specific mimetype output.
150 Metadata to be associated with the specific mimetype output.
151 """
151 """
152 _display_mimetype('text/plain', objs, **kwargs)
152 _display_mimetype('text/plain', objs, **kwargs)
153
153
154
154
155 def display_html(*objs, **kwargs):
155 def display_html(*objs, **kwargs):
156 """Display the HTML representation of an object.
156 """Display the HTML representation of an object.
157
157
158 Parameters
158 Parameters
159 ----------
159 ----------
160 objs : tuple of objects
160 objs : tuple of objects
161 The Python objects to display, or if raw=True raw HTML data to
161 The Python objects to display, or if raw=True raw HTML data to
162 display.
162 display.
163 raw : bool
163 raw : bool
164 Are the data objects raw data or Python objects that need to be
164 Are the data objects raw data or Python objects that need to be
165 formatted before display? [default: False]
165 formatted before display? [default: False]
166 metadata : dict (optional)
166 metadata : dict (optional)
167 Metadata to be associated with the specific mimetype output.
167 Metadata to be associated with the specific mimetype output.
168 """
168 """
169 _display_mimetype('text/html', objs, **kwargs)
169 _display_mimetype('text/html', objs, **kwargs)
170
170
171
171
172 def display_markdown(*objs, **kwargs):
172 def display_markdown(*objs, **kwargs):
173 """Displays the Markdown representation of an object.
173 """Displays the Markdown representation of an object.
174
174
175 Parameters
175 Parameters
176 ----------
176 ----------
177 objs : tuple of objects
177 objs : tuple of objects
178 The Python objects to display, or if raw=True raw markdown data to
178 The Python objects to display, or if raw=True raw markdown data to
179 display.
179 display.
180 raw : bool
180 raw : bool
181 Are the data objects raw data or Python objects that need to be
181 Are the data objects raw data or Python objects that need to be
182 formatted before display? [default: False]
182 formatted before display? [default: False]
183 metadata : dict (optional)
183 metadata : dict (optional)
184 Metadata to be associated with the specific mimetype output.
184 Metadata to be associated with the specific mimetype output.
185 """
185 """
186
186
187 _display_mimetype('text/markdown', objs, **kwargs)
187 _display_mimetype('text/markdown', objs, **kwargs)
188
188
189
189
190 def display_svg(*objs, **kwargs):
190 def display_svg(*objs, **kwargs):
191 """Display the SVG representation of an object.
191 """Display the SVG representation of an object.
192
192
193 Parameters
193 Parameters
194 ----------
194 ----------
195 objs : tuple of objects
195 objs : tuple of objects
196 The Python objects to display, or if raw=True raw svg data to
196 The Python objects to display, or if raw=True raw svg data to
197 display.
197 display.
198 raw : bool
198 raw : bool
199 Are the data objects raw data or Python objects that need to be
199 Are the data objects raw data or Python objects that need to be
200 formatted before display? [default: False]
200 formatted before display? [default: False]
201 metadata : dict (optional)
201 metadata : dict (optional)
202 Metadata to be associated with the specific mimetype output.
202 Metadata to be associated with the specific mimetype output.
203 """
203 """
204 _display_mimetype('image/svg+xml', objs, **kwargs)
204 _display_mimetype('image/svg+xml', objs, **kwargs)
205
205
206
206
207 def display_png(*objs, **kwargs):
207 def display_png(*objs, **kwargs):
208 """Display the PNG representation of an object.
208 """Display the PNG representation of an object.
209
209
210 Parameters
210 Parameters
211 ----------
211 ----------
212 objs : tuple of objects
212 objs : tuple of objects
213 The Python objects to display, or if raw=True raw png data to
213 The Python objects to display, or if raw=True raw png data to
214 display.
214 display.
215 raw : bool
215 raw : bool
216 Are the data objects raw data or Python objects that need to be
216 Are the data objects raw data or Python objects that need to be
217 formatted before display? [default: False]
217 formatted before display? [default: False]
218 metadata : dict (optional)
218 metadata : dict (optional)
219 Metadata to be associated with the specific mimetype output.
219 Metadata to be associated with the specific mimetype output.
220 """
220 """
221 _display_mimetype('image/png', objs, **kwargs)
221 _display_mimetype('image/png', objs, **kwargs)
222
222
223
223
224 def display_jpeg(*objs, **kwargs):
224 def display_jpeg(*objs, **kwargs):
225 """Display the JPEG representation of an object.
225 """Display the JPEG representation of an object.
226
226
227 Parameters
227 Parameters
228 ----------
228 ----------
229 objs : tuple of objects
229 objs : tuple of objects
230 The Python objects to display, or if raw=True raw JPEG data to
230 The Python objects to display, or if raw=True raw JPEG data to
231 display.
231 display.
232 raw : bool
232 raw : bool
233 Are the data objects raw data or Python objects that need to be
233 Are the data objects raw data or Python objects that need to be
234 formatted before display? [default: False]
234 formatted before display? [default: False]
235 metadata : dict (optional)
235 metadata : dict (optional)
236 Metadata to be associated with the specific mimetype output.
236 Metadata to be associated with the specific mimetype output.
237 """
237 """
238 _display_mimetype('image/jpeg', objs, **kwargs)
238 _display_mimetype('image/jpeg', objs, **kwargs)
239
239
240
240
241 def display_latex(*objs, **kwargs):
241 def display_latex(*objs, **kwargs):
242 """Display the LaTeX representation of an object.
242 """Display the LaTeX representation of an object.
243
243
244 Parameters
244 Parameters
245 ----------
245 ----------
246 objs : tuple of objects
246 objs : tuple of objects
247 The Python objects to display, or if raw=True raw latex data to
247 The Python objects to display, or if raw=True raw latex data to
248 display.
248 display.
249 raw : bool
249 raw : bool
250 Are the data objects raw data or Python objects that need to be
250 Are the data objects raw data or Python objects that need to be
251 formatted before display? [default: False]
251 formatted before display? [default: False]
252 metadata : dict (optional)
252 metadata : dict (optional)
253 Metadata to be associated with the specific mimetype output.
253 Metadata to be associated with the specific mimetype output.
254 """
254 """
255 _display_mimetype('text/latex', objs, **kwargs)
255 _display_mimetype('text/latex', objs, **kwargs)
256
256
257
257
258 def display_json(*objs, **kwargs):
258 def display_json(*objs, **kwargs):
259 """Display the JSON representation of an object.
259 """Display the JSON representation of an object.
260
260
261 Note that not many frontends support displaying JSON.
261 Note that not many frontends support displaying JSON.
262
262
263 Parameters
263 Parameters
264 ----------
264 ----------
265 objs : tuple of objects
265 objs : tuple of objects
266 The Python objects to display, or if raw=True raw json data to
266 The Python objects to display, or if raw=True raw json data to
267 display.
267 display.
268 raw : bool
268 raw : bool
269 Are the data objects raw data or Python objects that need to be
269 Are the data objects raw data or Python objects that need to be
270 formatted before display? [default: False]
270 formatted before display? [default: False]
271 metadata : dict (optional)
271 metadata : dict (optional)
272 Metadata to be associated with the specific mimetype output.
272 Metadata to be associated with the specific mimetype output.
273 """
273 """
274 _display_mimetype('application/json', objs, **kwargs)
274 _display_mimetype('application/json', objs, **kwargs)
275
275
276
276
277 def display_javascript(*objs, **kwargs):
277 def display_javascript(*objs, **kwargs):
278 """Display the Javascript representation of an object.
278 """Display the Javascript representation of an object.
279
279
280 Parameters
280 Parameters
281 ----------
281 ----------
282 objs : tuple of objects
282 objs : tuple of objects
283 The Python objects to display, or if raw=True raw javascript data to
283 The Python objects to display, or if raw=True raw javascript data to
284 display.
284 display.
285 raw : bool
285 raw : bool
286 Are the data objects raw data or Python objects that need to be
286 Are the data objects raw data or Python objects that need to be
287 formatted before display? [default: False]
287 formatted before display? [default: False]
288 metadata : dict (optional)
288 metadata : dict (optional)
289 Metadata to be associated with the specific mimetype output.
289 Metadata to be associated with the specific mimetype output.
290 """
290 """
291 _display_mimetype('application/javascript', objs, **kwargs)
291 _display_mimetype('application/javascript', objs, **kwargs)
292
292
293
293
294 def display_pdf(*objs, **kwargs):
294 def display_pdf(*objs, **kwargs):
295 """Display the PDF representation of an object.
295 """Display the PDF representation of an object.
296
296
297 Parameters
297 Parameters
298 ----------
298 ----------
299 objs : tuple of objects
299 objs : tuple of objects
300 The Python objects to display, or if raw=True raw javascript data to
300 The Python objects to display, or if raw=True raw javascript data to
301 display.
301 display.
302 raw : bool
302 raw : bool
303 Are the data objects raw data or Python objects that need to be
303 Are the data objects raw data or Python objects that need to be
304 formatted before display? [default: False]
304 formatted before display? [default: False]
305 metadata : dict (optional)
305 metadata : dict (optional)
306 Metadata to be associated with the specific mimetype output.
306 Metadata to be associated with the specific mimetype output.
307 """
307 """
308 _display_mimetype('application/pdf', objs, **kwargs)
308 _display_mimetype('application/pdf', objs, **kwargs)
309
309
310
310
311 #-----------------------------------------------------------------------------
311 #-----------------------------------------------------------------------------
312 # Smart classes
312 # Smart classes
313 #-----------------------------------------------------------------------------
313 #-----------------------------------------------------------------------------
314
314
315
315
316 class DisplayObject(object):
316 class DisplayObject(object):
317 """An object that wraps data to be displayed."""
317 """An object that wraps data to be displayed."""
318
318
319 _read_flags = 'r'
319 _read_flags = 'r'
320 _show_mem_addr = False
320 _show_mem_addr = False
321
321
322 def __init__(self, data=None, url=None, filename=None):
322 def __init__(self, data=None, url=None, filename=None):
323 """Create a display object given raw data.
323 """Create a display object given raw data.
324
324
325 When this object is returned by an expression or passed to the
325 When this object is returned by an expression or passed to the
326 display function, it will result in the data being displayed
326 display function, it will result in the data being displayed
327 in the frontend. The MIME type of the data should match the
327 in the frontend. The MIME type of the data should match the
328 subclasses used, so the Png subclass should be used for 'image/png'
328 subclasses used, so the Png subclass should be used for 'image/png'
329 data. If the data is a URL, the data will first be downloaded
329 data. If the data is a URL, the data will first be downloaded
330 and then displayed. If
330 and then displayed. If
331
331
332 Parameters
332 Parameters
333 ----------
333 ----------
334 data : unicode, str or bytes
334 data : unicode, str or bytes
335 The raw data or a URL or file to load the data from
335 The raw data or a URL or file to load the data from
336 url : unicode
336 url : unicode
337 A URL to download the data from.
337 A URL to download the data from.
338 filename : unicode
338 filename : unicode
339 Path to a local file to load the data from.
339 Path to a local file to load the data from.
340 """
340 """
341 if data is not None and isinstance(data, string_types):
341 if data is not None and isinstance(data, string_types):
342 if data.startswith('http') and url is None:
342 if data.startswith('http') and url is None:
343 url = data
343 url = data
344 filename = None
344 filename = None
345 data = None
345 data = None
346 elif _safe_exists(data) and filename is None:
346 elif _safe_exists(data) and filename is None:
347 url = None
347 url = None
348 filename = data
348 filename = data
349 data = None
349 data = None
350
350
351 self.data = data
351 self.data = data
352 self.url = url
352 self.url = url
353 self.filename = None if filename is None else unicode_type(filename)
353 self.filename = None if filename is None else unicode_type(filename)
354
354
355 self.reload()
355 self.reload()
356 self._check_data()
356 self._check_data()
357
357
358 def __repr__(self):
358 def __repr__(self):
359 if not self._show_mem_addr:
359 if not self._show_mem_addr:
360 cls = self.__class__
360 cls = self.__class__
361 r = "<%s.%s object>" % (cls.__module__, cls.__name__)
361 r = "<%s.%s object>" % (cls.__module__, cls.__name__)
362 else:
362 else:
363 r = super(DisplayObject, self).__repr__()
363 r = super(DisplayObject, self).__repr__()
364 return r
364 return r
365
365
366 def _check_data(self):
366 def _check_data(self):
367 """Override in subclasses if there's something to check."""
367 """Override in subclasses if there's something to check."""
368 pass
368 pass
369
369
370 def reload(self):
370 def reload(self):
371 """Reload the raw data from file or URL."""
371 """Reload the raw data from file or URL."""
372 if self.filename is not None:
372 if self.filename is not None:
373 with open(self.filename, self._read_flags) as f:
373 with open(self.filename, self._read_flags) as f:
374 self.data = f.read()
374 self.data = f.read()
375 elif self.url is not None:
375 elif self.url is not None:
376 try:
376 try:
377 try:
377 try:
378 from urllib.request import urlopen # Py3
378 from urllib.request import urlopen # Py3
379 except ImportError:
379 except ImportError:
380 from urllib2 import urlopen
380 from urllib2 import urlopen
381 response = urlopen(self.url)
381 response = urlopen(self.url)
382 self.data = response.read()
382 self.data = response.read()
383 # extract encoding from header, if there is one:
383 # extract encoding from header, if there is one:
384 encoding = None
384 encoding = None
385 for sub in response.headers['content-type'].split(';'):
385 for sub in response.headers['content-type'].split(';'):
386 sub = sub.strip()
386 sub = sub.strip()
387 if sub.startswith('charset'):
387 if sub.startswith('charset'):
388 encoding = sub.split('=')[-1].strip()
388 encoding = sub.split('=')[-1].strip()
389 break
389 break
390 # decode data, if an encoding was specified
390 # decode data, if an encoding was specified
391 if encoding:
391 if encoding:
392 self.data = self.data.decode(encoding, 'replace')
392 self.data = self.data.decode(encoding, 'replace')
393 except:
393 except:
394 self.data = None
394 self.data = None
395
395
396 class TextDisplayObject(DisplayObject):
396 class TextDisplayObject(DisplayObject):
397 """Validate that display data is text"""
397 """Validate that display data is text"""
398 def _check_data(self):
398 def _check_data(self):
399 if self.data is not None and not isinstance(self.data, string_types):
399 if self.data is not None and not isinstance(self.data, string_types):
400 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
400 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
401
401
402 class Pretty(TextDisplayObject):
402 class Pretty(TextDisplayObject):
403
403
404 def _repr_pretty_(self):
404 def _repr_pretty_(self):
405 return self.data
405 return self.data
406
406
407
407
408 class HTML(TextDisplayObject):
408 class HTML(TextDisplayObject):
409
409
410 def _repr_html_(self):
410 def _repr_html_(self):
411 return self.data
411 return self.data
412
412
413 def __html__(self):
413 def __html__(self):
414 """
414 """
415 This method exists to inform other HTML-using modules (e.g. Markupsafe,
415 This method exists to inform other HTML-using modules (e.g. Markupsafe,
416 htmltag, etc) that this object is HTML and does not need things like
416 htmltag, etc) that this object is HTML and does not need things like
417 special characters (<>&) escaped.
417 special characters (<>&) escaped.
418 """
418 """
419 return self._repr_html_()
419 return self._repr_html_()
420
420
421
421
422 class Markdown(TextDisplayObject):
422 class Markdown(TextDisplayObject):
423
423
424 def _repr_markdown_(self):
424 def _repr_markdown_(self):
425 return self.data
425 return self.data
426
426
427
427
428 class Math(TextDisplayObject):
428 class Math(TextDisplayObject):
429
429
430 def _repr_latex_(self):
430 def _repr_latex_(self):
431 s = self.data.strip('$')
431 s = self.data.strip('$')
432 return "$$%s$$" % s
432 return "$$%s$$" % s
433
433
434
434
435 class Latex(TextDisplayObject):
435 class Latex(TextDisplayObject):
436
436
437 def _repr_latex_(self):
437 def _repr_latex_(self):
438 return self.data
438 return self.data
439
439
440
440
441 class SVG(DisplayObject):
441 class SVG(DisplayObject):
442
442
443 # wrap data in a property, which extracts the <svg> tag, discarding
443 # wrap data in a property, which extracts the <svg> tag, discarding
444 # document headers
444 # document headers
445 _data = None
445 _data = None
446
446
447 @property
447 @property
448 def data(self):
448 def data(self):
449 return self._data
449 return self._data
450
450
451 @data.setter
451 @data.setter
452 def data(self, svg):
452 def data(self, svg):
453 if svg is None:
453 if svg is None:
454 self._data = None
454 self._data = None
455 return
455 return
456 # parse into dom object
456 # parse into dom object
457 from xml.dom import minidom
457 from xml.dom import minidom
458 svg = cast_bytes_py2(svg)
458 svg = cast_bytes_py2(svg)
459 x = minidom.parseString(svg)
459 x = minidom.parseString(svg)
460 # get svg tag (should be 1)
460 # get svg tag (should be 1)
461 found_svg = x.getElementsByTagName('svg')
461 found_svg = x.getElementsByTagName('svg')
462 if found_svg:
462 if found_svg:
463 svg = found_svg[0].toxml()
463 svg = found_svg[0].toxml()
464 else:
464 else:
465 # fallback on the input, trust the user
465 # fallback on the input, trust the user
466 # but this is probably an error.
466 # but this is probably an error.
467 pass
467 pass
468 svg = cast_unicode(svg)
468 svg = cast_unicode(svg)
469 self._data = svg
469 self._data = svg
470
470
471 def _repr_svg_(self):
471 def _repr_svg_(self):
472 return self.data
472 return self.data
473
473
474
474
475 class JSON(TextDisplayObject):
475 class JSON(TextDisplayObject):
476
476
477 def _repr_json_(self):
477 def _repr_json_(self):
478 return self.data
478 return self.data
479
479
480 css_t = """$("head").append($("<link/>").attr({
480 css_t = """$("head").append($("<link/>").attr({
481 rel: "stylesheet",
481 rel: "stylesheet",
482 type: "text/css",
482 type: "text/css",
483 href: "%s"
483 href: "%s"
484 }));
484 }));
485 """
485 """
486
486
487 lib_t1 = """$.getScript("%s", function () {
487 lib_t1 = """$.getScript("%s", function () {
488 """
488 """
489 lib_t2 = """});
489 lib_t2 = """});
490 """
490 """
491
491
492 class Javascript(TextDisplayObject):
492 class Javascript(TextDisplayObject):
493
493
494 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
494 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
495 """Create a Javascript display object given raw data.
495 """Create a Javascript display object given raw data.
496
496
497 When this object is returned by an expression or passed to the
497 When this object is returned by an expression or passed to the
498 display function, it will result in the data being displayed
498 display function, it will result in the data being displayed
499 in the frontend. If the data is a URL, the data will first be
499 in the frontend. If the data is a URL, the data will first be
500 downloaded and then displayed.
500 downloaded and then displayed.
501
501
502 In the Notebook, the containing element will be available as `element`,
502 In the Notebook, the containing element will be available as `element`,
503 and jQuery will be available. The output area starts hidden, so if
503 and jQuery will be available. The output area starts hidden, so if
504 the js appends content to `element` that should be visible, then
504 the js appends content to `element` that should be visible, then
505 it must call `container.show()` to unhide the area.
505 it must call `container.show()` to unhide the area.
506
506
507 Parameters
507 Parameters
508 ----------
508 ----------
509 data : unicode, str or bytes
509 data : unicode, str or bytes
510 The Javascript source code or a URL to download it from.
510 The Javascript source code or a URL to download it from.
511 url : unicode
511 url : unicode
512 A URL to download the data from.
512 A URL to download the data from.
513 filename : unicode
513 filename : unicode
514 Path to a local file to load the data from.
514 Path to a local file to load the data from.
515 lib : list or str
515 lib : list or str
516 A sequence of Javascript library URLs to load asynchronously before
516 A sequence of Javascript library URLs to load asynchronously before
517 running the source code. The full URLs of the libraries should
517 running the source code. The full URLs of the libraries should
518 be given. A single Javascript library URL can also be given as a
518 be given. A single Javascript library URL can also be given as a
519 string.
519 string.
520 css: : list or str
520 css: : list or str
521 A sequence of css files to load before running the source code.
521 A sequence of css files to load before running the source code.
522 The full URLs of the css files should be given. A single css URL
522 The full URLs of the css files should be given. A single css URL
523 can also be given as a string.
523 can also be given as a string.
524 """
524 """
525 if isinstance(lib, string_types):
525 if isinstance(lib, string_types):
526 lib = [lib]
526 lib = [lib]
527 elif lib is None:
527 elif lib is None:
528 lib = []
528 lib = []
529 if isinstance(css, string_types):
529 if isinstance(css, string_types):
530 css = [css]
530 css = [css]
531 elif css is None:
531 elif css is None:
532 css = []
532 css = []
533 if not isinstance(lib, (list,tuple)):
533 if not isinstance(lib, (list,tuple)):
534 raise TypeError('expected sequence, got: %r' % lib)
534 raise TypeError('expected sequence, got: %r' % lib)
535 if not isinstance(css, (list,tuple)):
535 if not isinstance(css, (list,tuple)):
536 raise TypeError('expected sequence, got: %r' % css)
536 raise TypeError('expected sequence, got: %r' % css)
537 self.lib = lib
537 self.lib = lib
538 self.css = css
538 self.css = css
539 super(Javascript, self).__init__(data=data, url=url, filename=filename)
539 super(Javascript, self).__init__(data=data, url=url, filename=filename)
540
540
541 def _repr_javascript_(self):
541 def _repr_javascript_(self):
542 r = ''
542 r = ''
543 for c in self.css:
543 for c in self.css:
544 r += css_t % c
544 r += css_t % c
545 for l in self.lib:
545 for l in self.lib:
546 r += lib_t1 % l
546 r += lib_t1 % l
547 r += self.data
547 r += self.data
548 r += lib_t2*len(self.lib)
548 r += lib_t2*len(self.lib)
549 return r
549 return r
550
550
551 # constants for identifying png/jpeg data
551 # constants for identifying png/jpeg data
552 _PNG = b'\x89PNG\r\n\x1a\n'
552 _PNG = b'\x89PNG\r\n\x1a\n'
553 _JPEG = b'\xff\xd8'
553 _JPEG = b'\xff\xd8'
554
554
555 def _pngxy(data):
555 def _pngxy(data):
556 """read the (width, height) from a PNG header"""
556 """read the (width, height) from a PNG header"""
557 ihdr = data.index(b'IHDR')
557 ihdr = data.index(b'IHDR')
558 # next 8 bytes are width/height
558 # next 8 bytes are width/height
559 w4h4 = data[ihdr+4:ihdr+12]
559 w4h4 = data[ihdr+4:ihdr+12]
560 return struct.unpack('>ii', w4h4)
560 return struct.unpack('>ii', w4h4)
561
561
562 def _jpegxy(data):
562 def _jpegxy(data):
563 """read the (width, height) from a JPEG header"""
563 """read the (width, height) from a JPEG header"""
564 # adapted from http://www.64lines.com/jpeg-width-height
564 # adapted from http://www.64lines.com/jpeg-width-height
565
565
566 idx = 4
566 idx = 4
567 while True:
567 while True:
568 block_size = struct.unpack('>H', data[idx:idx+2])[0]
568 block_size = struct.unpack('>H', data[idx:idx+2])[0]
569 idx = idx + block_size
569 idx = idx + block_size
570 if data[idx:idx+2] == b'\xFF\xC0':
570 if data[idx:idx+2] == b'\xFF\xC0':
571 # found Start of Frame
571 # found Start of Frame
572 iSOF = idx
572 iSOF = idx
573 break
573 break
574 else:
574 else:
575 # read another block
575 # read another block
576 idx += 2
576 idx += 2
577
577
578 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
578 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
579 return w, h
579 return w, h
580
580
581 class Image(DisplayObject):
581 class Image(DisplayObject):
582
582
583 _read_flags = 'rb'
583 _read_flags = 'rb'
584 _FMT_JPEG = u'jpeg'
584 _FMT_JPEG = u'jpeg'
585 _FMT_PNG = u'png'
585 _FMT_PNG = u'png'
586 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
586 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
587
587
588 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=None, width=None, height=None, retina=False):
588 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=None, width=None, height=None, retina=False):
589 """Create a PNG/JPEG image object given raw data.
589 """Create a PNG/JPEG image object given raw data.
590
590
591 When this object is returned by an input cell or passed to the
591 When this object is returned by an input cell or passed to the
592 display function, it will result in the image being displayed
592 display function, it will result in the image being displayed
593 in the frontend.
593 in the frontend.
594
594
595 Parameters
595 Parameters
596 ----------
596 ----------
597 data : unicode, str or bytes
597 data : unicode, str or bytes
598 The raw image data or a URL or filename to load the data from.
598 The raw image data or a URL or filename to load the data from.
599 This always results in embedded image data.
599 This always results in embedded image data.
600 url : unicode
600 url : unicode
601 A URL to download the data from. If you specify `url=`,
601 A URL to download the data from. If you specify `url=`,
602 the image data will not be embedded unless you also specify `embed=True`.
602 the image data will not be embedded unless you also specify `embed=True`.
603 filename : unicode
603 filename : unicode
604 Path to a local file to load the data from.
604 Path to a local file to load the data from.
605 Images from a file are always embedded.
605 Images from a file are always embedded.
606 format : unicode
606 format : unicode
607 The format of the image data (png/jpeg/jpg). If a filename or URL is given
607 The format of the image data (png/jpeg/jpg). If a filename or URL is given
608 for format will be inferred from the filename extension.
608 for format will be inferred from the filename extension.
609 embed : bool
609 embed : bool
610 Should the image data be embedded using a data URI (True) or be
610 Should the image data be embedded using a data URI (True) or be
611 loaded using an <img> tag. Set this to True if you want the image
611 loaded using an <img> tag. Set this to True if you want the image
612 to be viewable later with no internet connection in the notebook.
612 to be viewable later with no internet connection in the notebook.
613
613
614 Default is `True`, unless the keyword argument `url` is set, then
614 Default is `True`, unless the keyword argument `url` is set, then
615 default value is `False`.
615 default value is `False`.
616
616
617 Note that QtConsole is not able to display images if `embed` is set to `False`
617 Note that QtConsole is not able to display images if `embed` is set to `False`
618 width : int
618 width : int
619 Width to which to constrain the image in html
619 Width to which to constrain the image in html
620 height : int
620 height : int
621 Height to which to constrain the image in html
621 Height to which to constrain the image in html
622 retina : bool
622 retina : bool
623 Automatically set the width and height to half of the measured
623 Automatically set the width and height to half of the measured
624 width and height.
624 width and height.
625 This only works for embedded images because it reads the width/height
625 This only works for embedded images because it reads the width/height
626 from image data.
626 from image data.
627 For non-embedded images, you can just set the desired display width
627 For non-embedded images, you can just set the desired display width
628 and height directly.
628 and height directly.
629
629
630 Examples
630 Examples
631 --------
631 --------
632 # embedded image data, works in qtconsole and notebook
632 # embedded image data, works in qtconsole and notebook
633 # when passed positionally, the first arg can be any of raw image data,
633 # when passed positionally, the first arg can be any of raw image data,
634 # a URL, or a filename from which to load image data.
634 # a URL, or a filename from which to load image data.
635 # The result is always embedding image data for inline images.
635 # The result is always embedding image data for inline images.
636 Image('http://www.google.fr/images/srpr/logo3w.png')
636 Image('http://www.google.fr/images/srpr/logo3w.png')
637 Image('/path/to/image.jpg')
637 Image('/path/to/image.jpg')
638 Image(b'RAW_PNG_DATA...')
638 Image(b'RAW_PNG_DATA...')
639
639
640 # Specifying Image(url=...) does not embed the image data,
640 # Specifying Image(url=...) does not embed the image data,
641 # it only generates `<img>` tag with a link to the source.
641 # it only generates `<img>` tag with a link to the source.
642 # This will not work in the qtconsole or offline.
642 # This will not work in the qtconsole or offline.
643 Image(url='http://www.google.fr/images/srpr/logo3w.png')
643 Image(url='http://www.google.fr/images/srpr/logo3w.png')
644
644
645 """
645 """
646 if filename is not None:
646 if filename is not None:
647 ext = self._find_ext(filename)
647 ext = self._find_ext(filename)
648 elif url is not None:
648 elif url is not None:
649 ext = self._find_ext(url)
649 ext = self._find_ext(url)
650 elif data is None:
650 elif data is None:
651 raise ValueError("No image data found. Expecting filename, url, or data.")
651 raise ValueError("No image data found. Expecting filename, url, or data.")
652 elif isinstance(data, string_types) and (
652 elif isinstance(data, string_types) and (
653 data.startswith('http') or _safe_exists(data)
653 data.startswith('http') or _safe_exists(data)
654 ):
654 ):
655 ext = self._find_ext(data)
655 ext = self._find_ext(data)
656 else:
656 else:
657 ext = None
657 ext = None
658
658
659 if ext is not None:
659 if ext is not None:
660 format = ext.lower()
660 format = ext.lower()
661 if ext == u'jpg' or ext == u'jpeg':
661 if ext == u'jpg' or ext == u'jpeg':
662 format = self._FMT_JPEG
662 format = self._FMT_JPEG
663 if ext == u'png':
663 if ext == u'png':
664 format = self._FMT_PNG
664 format = self._FMT_PNG
665 elif isinstance(data, bytes) and format == 'png':
665 elif isinstance(data, bytes) and format == 'png':
666 # infer image type from image data header,
666 # infer image type from image data header,
667 # only if format might not have been specified.
667 # only if format might not have been specified.
668 if data[:2] == _JPEG:
668 if data[:2] == _JPEG:
669 format = 'jpeg'
669 format = 'jpeg'
670
670
671 self.format = unicode_type(format).lower()
671 self.format = unicode_type(format).lower()
672 self.embed = embed if embed is not None else (url is None)
672 self.embed = embed if embed is not None else (url is None)
673
673
674 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
674 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
675 raise ValueError("Cannot embed the '%s' image format" % (self.format))
675 raise ValueError("Cannot embed the '%s' image format" % (self.format))
676 self.width = width
676 self.width = width
677 self.height = height
677 self.height = height
678 self.retina = retina
678 self.retina = retina
679 super(Image, self).__init__(data=data, url=url, filename=filename)
679 super(Image, self).__init__(data=data, url=url, filename=filename)
680
680
681 if retina:
681 if retina:
682 self._retina_shape()
682 self._retina_shape()
683
683
684 def _retina_shape(self):
684 def _retina_shape(self):
685 """load pixel-doubled width and height from image data"""
685 """load pixel-doubled width and height from image data"""
686 if not self.embed:
686 if not self.embed:
687 return
687 return
688 if self.format == 'png':
688 if self.format == 'png':
689 w, h = _pngxy(self.data)
689 w, h = _pngxy(self.data)
690 elif self.format == 'jpeg':
690 elif self.format == 'jpeg':
691 w, h = _jpegxy(self.data)
691 w, h = _jpegxy(self.data)
692 else:
692 else:
693 # retina only supports png
693 # retina only supports png
694 return
694 return
695 self.width = w // 2
695 self.width = w // 2
696 self.height = h // 2
696 self.height = h // 2
697
697
698 def reload(self):
698 def reload(self):
699 """Reload the raw data from file or URL."""
699 """Reload the raw data from file or URL."""
700 if self.embed:
700 if self.embed:
701 super(Image,self).reload()
701 super(Image,self).reload()
702 if self.retina:
702 if self.retina:
703 self._retina_shape()
703 self._retina_shape()
704
704
705 def _repr_html_(self):
705 def _repr_html_(self):
706 if not self.embed:
706 if not self.embed:
707 width = height = ''
707 width = height = ''
708 if self.width:
708 if self.width:
709 width = ' width="%d"' % self.width
709 width = ' width="%d"' % self.width
710 if self.height:
710 if self.height:
711 height = ' height="%d"' % self.height
711 height = ' height="%d"' % self.height
712 return u'<img src="%s"%s%s/>' % (self.url, width, height)
712 return u'<img src="%s"%s%s/>' % (self.url, width, height)
713
713
714 def _data_and_metadata(self):
714 def _data_and_metadata(self):
715 """shortcut for returning metadata with shape information, if defined"""
715 """shortcut for returning metadata with shape information, if defined"""
716 md = {}
716 md = {}
717 if self.width:
717 if self.width:
718 md['width'] = self.width
718 md['width'] = self.width
719 if self.height:
719 if self.height:
720 md['height'] = self.height
720 md['height'] = self.height
721 if md:
721 if md:
722 return self.data, md
722 return self.data, md
723 else:
723 else:
724 return self.data
724 return self.data
725
725
726 def _repr_png_(self):
726 def _repr_png_(self):
727 if self.embed and self.format == u'png':
727 if self.embed and self.format == u'png':
728 return self._data_and_metadata()
728 return self._data_and_metadata()
729
729
730 def _repr_jpeg_(self):
730 def _repr_jpeg_(self):
731 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
731 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
732 return self._data_and_metadata()
732 return self._data_and_metadata()
733
733
734 def _find_ext(self, s):
734 def _find_ext(self, s):
735 return unicode_type(s.split('.')[-1].lower())
735 return unicode_type(s.split('.')[-1].lower())
736
736
737
737
738 def clear_output(wait=False):
738 def clear_output(wait=False):
739 """Clear the output of the current cell receiving output.
739 """Clear the output of the current cell receiving output.
740
740
741 Parameters
741 Parameters
742 ----------
742 ----------
743 wait : bool [default: false]
743 wait : bool [default: false]
744 Wait to clear the output until new output is available to replace it."""
744 Wait to clear the output until new output is available to replace it."""
745 from IPython.core.interactiveshell import InteractiveShell
745 from IPython.core.interactiveshell import InteractiveShell
746 if InteractiveShell.initialized():
746 if InteractiveShell.initialized():
747 InteractiveShell.instance().display_pub.clear_output(wait)
747 InteractiveShell.instance().display_pub.clear_output(wait)
748 else:
748 else:
749 from IPython.utils import io
749 from IPython.utils import io
750 print('\033[2K\r', file=io.stdout, end='')
750 print('\033[2K\r', file=io.stdout, end='')
751 io.stdout.flush()
751 io.stdout.flush()
752 print('\033[2K\r', file=io.stderr, end='')
752 print('\033[2K\r', file=io.stderr, end='')
753 io.stderr.flush()
753 io.stderr.flush()
754
754
755
755
756 @skip_doctest
756 @skip_doctest
757 def set_matplotlib_formats(*formats, **kwargs):
757 def set_matplotlib_formats(*formats, **kwargs):
758 """Select figure formats for the inline backend. Optionally pass quality for JPEG.
758 """Select figure formats for the inline backend. Optionally pass quality for JPEG.
759
759
760 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
760 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
761
761
762 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
762 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
763
763
764 To set this in your config files use the following::
764 To set this in your config files use the following::
765
765
766 c.InlineBackend.figure_formats = {'png', 'jpeg'}
766 c.InlineBackend.figure_formats = {'png', 'jpeg'}
767 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
767 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
768
768
769 Parameters
769 Parameters
770 ----------
770 ----------
771 *formats : strs
771 *formats : strs
772 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
772 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
773 **kwargs :
773 **kwargs :
774 Keyword args will be relayed to ``figure.canvas.print_figure``.
774 Keyword args will be relayed to ``figure.canvas.print_figure``.
775 """
775 """
776 from IPython.core.interactiveshell import InteractiveShell
776 from IPython.core.interactiveshell import InteractiveShell
777 from IPython.core.pylabtools import select_figure_formats
777 from IPython.core.pylabtools import select_figure_formats
778 from IPython.kernel.zmq.pylab.config import InlineBackend
778 from IPython.kernel.zmq.pylab.config import InlineBackend
779 # build kwargs, starting with InlineBackend config
779 # build kwargs, starting with InlineBackend config
780 kw = {}
780 kw = {}
781 cfg = InlineBackend.instance()
781 cfg = InlineBackend.instance()
782 kw.update(cfg.print_figure_kwargs)
782 kw.update(cfg.print_figure_kwargs)
783 kw.update(**kwargs)
783 kw.update(**kwargs)
784 shell = InteractiveShell.instance()
784 shell = InteractiveShell.instance()
785 select_figure_formats(shell, formats, **kw)
785 select_figure_formats(shell, formats, **kw)
786
786
787 @skip_doctest
787 @skip_doctest
788 def set_matplotlib_close(close=True):
788 def set_matplotlib_close(close=True):
789 """Set whether the inline backend closes all figures automatically or not.
789 """Set whether the inline backend closes all figures automatically or not.
790
790
791 By default, the inline backend used in the IPython Notebook will close all
791 By default, the inline backend used in the IPython Notebook will close all
792 matplotlib figures automatically after each cell is run. This means that
792 matplotlib figures automatically after each cell is run. This means that
793 plots in different cells won't interfere. Sometimes, you may want to make
793 plots in different cells won't interfere. Sometimes, you may want to make
794 a plot in one cell and then refine it in later cells. This can be accomplished
794 a plot in one cell and then refine it in later cells. This can be accomplished
795 by::
795 by::
796
796
797 In [1]: set_matplotlib_close(False)
797 In [1]: set_matplotlib_close(False)
798
798
799 To set this in your config files use the following::
799 To set this in your config files use the following::
800
800
801 c.InlineBackend.close_figures = False
801 c.InlineBackend.close_figures = False
802
802
803 Parameters
803 Parameters
804 ----------
804 ----------
805 close : bool
805 close : bool
806 Should all matplotlib figures be automatically closed after each cell is
806 Should all matplotlib figures be automatically closed after each cell is
807 run?
807 run?
808 """
808 """
809 from IPython.kernel.zmq.pylab.config import InlineBackend
809 from IPython.kernel.zmq.pylab.config import InlineBackend
810 cfg = InlineBackend.instance()
810 cfg = InlineBackend.instance()
811 cfg.close_figures = close
811 cfg.close_figures = close
812
812
@@ -1,179 +1,159
1 """An interface for publishing rich data to frontends.
1 """An interface for publishing rich data to frontends.
2
2
3 There are two components of the display system:
3 There are two components of the display system:
4
4
5 * Display formatters, which take a Python object and compute the
5 * Display formatters, which take a Python object and compute the
6 representation of the object in various formats (text, HTML, SVG, etc.).
6 representation of the object in various formats (text, HTML, SVG, etc.).
7 * The display publisher that is used to send the representation data to the
7 * The display publisher that is used to send the representation data to the
8 various frontends.
8 various frontends.
9
9
10 This module defines the logic display publishing. The display publisher uses
10 This module defines the logic display publishing. The display publisher uses
11 the ``display_data`` message type that is defined in the IPython messaging
11 the ``display_data`` message type that is defined in the IPython messaging
12 spec.
12 spec.
13
14 Authors:
15
16 * Brian Granger
17 """
13 """
18
14
19 #-----------------------------------------------------------------------------
15 # Copyright (c) IPython Development Team.
20 # Copyright (C) 2008-2011 The IPython Development Team
16 # Distributed under the terms of the Modified BSD License.
21 #
22 # Distributed under the terms of the BSD License. The full license is in
23 # the file COPYING, distributed as part of this software.
24 #-----------------------------------------------------------------------------
25
26 #-----------------------------------------------------------------------------
27 # Imports
28 #-----------------------------------------------------------------------------
29
17
30 from __future__ import print_function
18 from __future__ import print_function
31
19
32 from IPython.config.configurable import Configurable
20 from IPython.config.configurable import Configurable
33 from IPython.utils import io
21 from IPython.utils import io
34 from IPython.utils.py3compat import string_types
22 from IPython.utils.py3compat import string_types
35 from IPython.utils.traitlets import List
23 from IPython.utils.traitlets import List
36
24
37 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
38 # Main payload class
26 # Main payload class
39 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
40
28
41 class DisplayPublisher(Configurable):
29 class DisplayPublisher(Configurable):
42 """A traited class that publishes display data to frontends.
30 """A traited class that publishes display data to frontends.
43
31
44 Instances of this class are created by the main IPython object and should
32 Instances of this class are created by the main IPython object and should
45 be accessed there.
33 be accessed there.
46 """
34 """
47
35
48 def _validate_data(self, source, data, metadata=None):
36 def _validate_data(self, data, metadata=None):
49 """Validate the display data.
37 """Validate the display data.
50
38
51 Parameters
39 Parameters
52 ----------
40 ----------
53 source : str
54 The fully dotted name of the callable that created the data, like
55 :func:`foo.bar.my_formatter`.
56 data : dict
41 data : dict
57 The formata data dictionary.
42 The formata data dictionary.
58 metadata : dict
43 metadata : dict
59 Any metadata for the data.
44 Any metadata for the data.
60 """
45 """
61
46
62 if not isinstance(source, string_types):
63 raise TypeError('source must be a str, got: %r' % source)
64 if not isinstance(data, dict):
47 if not isinstance(data, dict):
65 raise TypeError('data must be a dict, got: %r' % data)
48 raise TypeError('data must be a dict, got: %r' % data)
66 if metadata is not None:
49 if metadata is not None:
67 if not isinstance(metadata, dict):
50 if not isinstance(metadata, dict):
68 raise TypeError('metadata must be a dict, got: %r' % data)
51 raise TypeError('metadata must be a dict, got: %r' % data)
69
52
70 def publish(self, source, data, metadata=None):
53 def publish(self, data, metadata=None, source=None):
71 """Publish data and metadata to all frontends.
54 """Publish data and metadata to all frontends.
72
55
73 See the ``display_data`` message in the messaging documentation for
56 See the ``display_data`` message in the messaging documentation for
74 more details about this message type.
57 more details about this message type.
75
58
76 The following MIME types are currently implemented:
59 The following MIME types are currently implemented:
77
60
78 * text/plain
61 * text/plain
79 * text/html
62 * text/html
80 * text/markdown
63 * text/markdown
81 * text/latex
64 * text/latex
82 * application/json
65 * application/json
83 * application/javascript
66 * application/javascript
84 * image/png
67 * image/png
85 * image/jpeg
68 * image/jpeg
86 * image/svg+xml
69 * image/svg+xml
87
70
88 Parameters
71 Parameters
89 ----------
72 ----------
90 source : str
91 A string that give the function or method that created the data,
92 such as 'IPython.core.page'.
93 data : dict
73 data : dict
94 A dictionary having keys that are valid MIME types (like
74 A dictionary having keys that are valid MIME types (like
95 'text/plain' or 'image/svg+xml') and values that are the data for
75 'text/plain' or 'image/svg+xml') and values that are the data for
96 that MIME type. The data itself must be a JSON'able data
76 that MIME type. The data itself must be a JSON'able data
97 structure. Minimally all data should have the 'text/plain' data,
77 structure. Minimally all data should have the 'text/plain' data,
98 which can be displayed by all frontends. If more than the plain
78 which can be displayed by all frontends. If more than the plain
99 text is given, it is up to the frontend to decide which
79 text is given, it is up to the frontend to decide which
100 representation to use.
80 representation to use.
101 metadata : dict
81 metadata : dict
102 A dictionary for metadata related to the data. This can contain
82 A dictionary for metadata related to the data. This can contain
103 arbitrary key, value pairs that frontends can use to interpret
83 arbitrary key, value pairs that frontends can use to interpret
104 the data. Metadata specific to each mime-type can be specified
84 the data. Metadata specific to each mime-type can be specified
105 in the metadata dict with the same mime-type keys as
85 in the metadata dict with the same mime-type keys as
106 the data itself.
86 the data itself.
87 source : str, deprecated
88 Unused.
107 """
89 """
108
90
109 # The default is to simply write the plain text data using io.stdout.
91 # The default is to simply write the plain text data using io.stdout.
110 if 'text/plain' in data:
92 if 'text/plain' in data:
111 print(data['text/plain'], file=io.stdout)
93 print(data['text/plain'], file=io.stdout)
112
94
113 def clear_output(self, wait=False):
95 def clear_output(self, wait=False):
114 """Clear the output of the cell receiving output."""
96 """Clear the output of the cell receiving output."""
115 print('\033[2K\r', file=io.stdout, end='')
97 print('\033[2K\r', file=io.stdout, end='')
116 io.stdout.flush()
98 io.stdout.flush()
117 print('\033[2K\r', file=io.stderr, end='')
99 print('\033[2K\r', file=io.stderr, end='')
118 io.stderr.flush()
100 io.stderr.flush()
119
101
120
102
121 class CapturingDisplayPublisher(DisplayPublisher):
103 class CapturingDisplayPublisher(DisplayPublisher):
122 """A DisplayPublisher that stores"""
104 """A DisplayPublisher that stores"""
123 outputs = List()
105 outputs = List()
124
106
125 def publish(self, source, data, metadata=None):
107 def publish(self, data, metadata=None, source=None):
126 self.outputs.append((source, data, metadata))
108 self.outputs.append((data, metadata))
127
109
128 def clear_output(self, wait=False):
110 def clear_output(self, wait=False):
129 super(CapturingDisplayPublisher, self).clear_output(wait)
111 super(CapturingDisplayPublisher, self).clear_output(wait)
130
112
131 # empty the list, *do not* reassign a new list
113 # empty the list, *do not* reassign a new list
132 del self.outputs[:]
114 del self.outputs[:]
133
115
134
116
135 def publish_display_data(source, data, metadata=None):
117 def publish_display_data(data, metadata=None, source=None):
136 """Publish data and metadata to all frontends.
118 """Publish data and metadata to all frontends.
137
119
138 See the ``display_data`` message in the messaging documentation for
120 See the ``display_data`` message in the messaging documentation for
139 more details about this message type.
121 more details about this message type.
140
122
141 The following MIME types are currently implemented:
123 The following MIME types are currently implemented:
142
124
143 * text/plain
125 * text/plain
144 * text/html
126 * text/html
145 * text/markdown
127 * text/markdown
146 * text/latex
128 * text/latex
147 * application/json
129 * application/json
148 * application/javascript
130 * application/javascript
149 * image/png
131 * image/png
150 * image/jpeg
132 * image/jpeg
151 * image/svg+xml
133 * image/svg+xml
152
134
153 Parameters
135 Parameters
154 ----------
136 ----------
155 source : str
156 A string that give the function or method that created the data,
157 such as 'IPython.core.page'.
158 data : dict
137 data : dict
159 A dictionary having keys that are valid MIME types (like
138 A dictionary having keys that are valid MIME types (like
160 'text/plain' or 'image/svg+xml') and values that are the data for
139 'text/plain' or 'image/svg+xml') and values that are the data for
161 that MIME type. The data itself must be a JSON'able data
140 that MIME type. The data itself must be a JSON'able data
162 structure. Minimally all data should have the 'text/plain' data,
141 structure. Minimally all data should have the 'text/plain' data,
163 which can be displayed by all frontends. If more than the plain
142 which can be displayed by all frontends. If more than the plain
164 text is given, it is up to the frontend to decide which
143 text is given, it is up to the frontend to decide which
165 representation to use.
144 representation to use.
166 metadata : dict
145 metadata : dict
167 A dictionary for metadata related to the data. This can contain
146 A dictionary for metadata related to the data. This can contain
168 arbitrary key, value pairs that frontends can use to interpret
147 arbitrary key, value pairs that frontends can use to interpret
169 the data. mime-type keys matching those in data can be used
148 the data. mime-type keys matching those in data can be used
170 to specify metadata about particular representations.
149 to specify metadata about particular representations.
150 source : str, deprecated
151 Unused.
171 """
152 """
172 from IPython.core.interactiveshell import InteractiveShell
153 from IPython.core.interactiveshell import InteractiveShell
173 InteractiveShell.instance().display_pub.publish(
154 InteractiveShell.instance().display_pub.publish(
174 source,
155 data=data,
175 data,
156 metadata=metadata,
176 metadata
177 )
157 )
178
158
179
159
@@ -1,3223 +1,3235
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Main IPython class."""
2 """Main IPython class."""
3
3
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de>
5 # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de>
6 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
6 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
7 # Copyright (C) 2008-2011 The IPython Development Team
7 # Copyright (C) 2008-2011 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 from __future__ import absolute_import, print_function
14 # Imports
15 #-----------------------------------------------------------------------------
16
17 from __future__ import absolute_import
18 from __future__ import print_function
19
14
20 import __future__
15 import __future__
21 import abc
16 import abc
22 import ast
17 import ast
23 import atexit
18 import atexit
24 import functools
19 import functools
25 import os
20 import os
26 import re
21 import re
27 import runpy
22 import runpy
28 import sys
23 import sys
29 import tempfile
24 import tempfile
30 import types
25 import types
31 import subprocess
26 import subprocess
32 from io import open as io_open
27 from io import open as io_open
33
28
34 from IPython.config.configurable import SingletonConfigurable
29 from IPython.config.configurable import SingletonConfigurable
35 from IPython.core import debugger, oinspect
30 from IPython.core import debugger, oinspect
36 from IPython.core import magic
31 from IPython.core import magic
37 from IPython.core import page
32 from IPython.core import page
38 from IPython.core import prefilter
33 from IPython.core import prefilter
39 from IPython.core import shadowns
34 from IPython.core import shadowns
40 from IPython.core import ultratb
35 from IPython.core import ultratb
41 from IPython.core.alias import AliasManager, AliasError
36 from IPython.core.alias import AliasManager, AliasError
42 from IPython.core.autocall import ExitAutocall
37 from IPython.core.autocall import ExitAutocall
43 from IPython.core.builtin_trap import BuiltinTrap
38 from IPython.core.builtin_trap import BuiltinTrap
44 from IPython.core.events import EventManager, available_events
39 from IPython.core.events import EventManager, available_events
45 from IPython.core.compilerop import CachingCompiler, check_linecache_ipython
40 from IPython.core.compilerop import CachingCompiler, check_linecache_ipython
46 from IPython.core.display_trap import DisplayTrap
41 from IPython.core.display_trap import DisplayTrap
47 from IPython.core.displayhook import DisplayHook
42 from IPython.core.displayhook import DisplayHook
48 from IPython.core.displaypub import DisplayPublisher
43 from IPython.core.displaypub import DisplayPublisher
49 from IPython.core.error import UsageError
44 from IPython.core.error import UsageError
50 from IPython.core.extensions import ExtensionManager
45 from IPython.core.extensions import ExtensionManager
51 from IPython.core.formatters import DisplayFormatter
46 from IPython.core.formatters import DisplayFormatter
52 from IPython.core.history import HistoryManager
47 from IPython.core.history import HistoryManager
53 from IPython.core.inputsplitter import IPythonInputSplitter, ESC_MAGIC, ESC_MAGIC2
48 from IPython.core.inputsplitter import IPythonInputSplitter, ESC_MAGIC, ESC_MAGIC2
54 from IPython.core.logger import Logger
49 from IPython.core.logger import Logger
55 from IPython.core.macro import Macro
50 from IPython.core.macro import Macro
56 from IPython.core.payload import PayloadManager
51 from IPython.core.payload import PayloadManager
57 from IPython.core.prefilter import PrefilterManager
52 from IPython.core.prefilter import PrefilterManager
58 from IPython.core.profiledir import ProfileDir
53 from IPython.core.profiledir import ProfileDir
59 from IPython.core.prompts import PromptManager
54 from IPython.core.prompts import PromptManager
55 from IPython.core.usage import default_banner
60 from IPython.lib.latextools import LaTeXTool
56 from IPython.lib.latextools import LaTeXTool
61 from IPython.testing.skipdoctest import skip_doctest
57 from IPython.testing.skipdoctest import skip_doctest
62 from IPython.utils import PyColorize
58 from IPython.utils import PyColorize
63 from IPython.utils import io
59 from IPython.utils import io
64 from IPython.utils import py3compat
60 from IPython.utils import py3compat
65 from IPython.utils import openpy
61 from IPython.utils import openpy
66 from IPython.utils.decorators import undoc
62 from IPython.utils.decorators import undoc
67 from IPython.utils.io import ask_yes_no
63 from IPython.utils.io import ask_yes_no
68 from IPython.utils.ipstruct import Struct
64 from IPython.utils.ipstruct import Struct
69 from IPython.utils.path import get_home_dir, get_ipython_dir, get_py_filename, unquote_filename, ensure_dir_exists
65 from IPython.utils.path import get_home_dir, get_ipython_dir, get_py_filename, unquote_filename, ensure_dir_exists
70 from IPython.utils.pickleshare import PickleShareDB
66 from IPython.utils.pickleshare import PickleShareDB
71 from IPython.utils.process import system, getoutput
67 from IPython.utils.process import system, getoutput
72 from IPython.utils.py3compat import (builtin_mod, unicode_type, string_types,
68 from IPython.utils.py3compat import (builtin_mod, unicode_type, string_types,
73 with_metaclass, iteritems)
69 with_metaclass, iteritems)
74 from IPython.utils.strdispatch import StrDispatch
70 from IPython.utils.strdispatch import StrDispatch
75 from IPython.utils.syspathcontext import prepended_to_syspath
71 from IPython.utils.syspathcontext import prepended_to_syspath
76 from IPython.utils.text import (format_screen, LSString, SList,
72 from IPython.utils.text import (format_screen, LSString, SList,
77 DollarFormatter)
73 DollarFormatter)
78 from IPython.utils.traitlets import (Integer, CBool, CaselessStrEnum, Enum,
74 from IPython.utils.traitlets import (Integer, CBool, CaselessStrEnum, Enum,
79 List, Unicode, Instance, Type)
75 List, Unicode, Instance, Type)
80 from IPython.utils.warn import warn, error
76 from IPython.utils.warn import warn, error
81 import IPython.core.hooks
77 import IPython.core.hooks
82
78
83 #-----------------------------------------------------------------------------
79 #-----------------------------------------------------------------------------
84 # Globals
80 # Globals
85 #-----------------------------------------------------------------------------
81 #-----------------------------------------------------------------------------
86
82
87 # compiled regexps for autoindent management
83 # compiled regexps for autoindent management
88 dedent_re = re.compile(r'^\s+raise|^\s+return|^\s+pass')
84 dedent_re = re.compile(r'^\s+raise|^\s+return|^\s+pass')
89
85
90 #-----------------------------------------------------------------------------
86 #-----------------------------------------------------------------------------
91 # Utilities
87 # Utilities
92 #-----------------------------------------------------------------------------
88 #-----------------------------------------------------------------------------
93
89
94 @undoc
90 @undoc
95 def softspace(file, newvalue):
91 def softspace(file, newvalue):
96 """Copied from code.py, to remove the dependency"""
92 """Copied from code.py, to remove the dependency"""
97
93
98 oldvalue = 0
94 oldvalue = 0
99 try:
95 try:
100 oldvalue = file.softspace
96 oldvalue = file.softspace
101 except AttributeError:
97 except AttributeError:
102 pass
98 pass
103 try:
99 try:
104 file.softspace = newvalue
100 file.softspace = newvalue
105 except (AttributeError, TypeError):
101 except (AttributeError, TypeError):
106 # "attribute-less object" or "read-only attributes"
102 # "attribute-less object" or "read-only attributes"
107 pass
103 pass
108 return oldvalue
104 return oldvalue
109
105
110 @undoc
106 @undoc
111 def no_op(*a, **kw): pass
107 def no_op(*a, **kw): pass
112
108
113 @undoc
109 @undoc
114 class NoOpContext(object):
110 class NoOpContext(object):
115 def __enter__(self): pass
111 def __enter__(self): pass
116 def __exit__(self, type, value, traceback): pass
112 def __exit__(self, type, value, traceback): pass
117 no_op_context = NoOpContext()
113 no_op_context = NoOpContext()
118
114
119 class SpaceInInput(Exception): pass
115 class SpaceInInput(Exception): pass
120
116
121 @undoc
117 @undoc
122 class Bunch: pass
118 class Bunch: pass
123
119
124
120
125 def get_default_colors():
121 def get_default_colors():
126 if sys.platform=='darwin':
122 if sys.platform=='darwin':
127 return "LightBG"
123 return "LightBG"
128 elif os.name=='nt':
124 elif os.name=='nt':
129 return 'Linux'
125 return 'Linux'
130 else:
126 else:
131 return 'Linux'
127 return 'Linux'
132
128
133
129
134 class SeparateUnicode(Unicode):
130 class SeparateUnicode(Unicode):
135 r"""A Unicode subclass to validate separate_in, separate_out, etc.
131 r"""A Unicode subclass to validate separate_in, separate_out, etc.
136
132
137 This is a Unicode based trait that converts '0'->'' and ``'\\n'->'\n'``.
133 This is a Unicode based trait that converts '0'->'' and ``'\\n'->'\n'``.
138 """
134 """
139
135
140 def validate(self, obj, value):
136 def validate(self, obj, value):
141 if value == '0': value = ''
137 if value == '0': value = ''
142 value = value.replace('\\n','\n')
138 value = value.replace('\\n','\n')
143 return super(SeparateUnicode, self).validate(obj, value)
139 return super(SeparateUnicode, self).validate(obj, value)
144
140
145
141
146 class ReadlineNoRecord(object):
142 class ReadlineNoRecord(object):
147 """Context manager to execute some code, then reload readline history
143 """Context manager to execute some code, then reload readline history
148 so that interactive input to the code doesn't appear when pressing up."""
144 so that interactive input to the code doesn't appear when pressing up."""
149 def __init__(self, shell):
145 def __init__(self, shell):
150 self.shell = shell
146 self.shell = shell
151 self._nested_level = 0
147 self._nested_level = 0
152
148
153 def __enter__(self):
149 def __enter__(self):
154 if self._nested_level == 0:
150 if self._nested_level == 0:
155 try:
151 try:
156 self.orig_length = self.current_length()
152 self.orig_length = self.current_length()
157 self.readline_tail = self.get_readline_tail()
153 self.readline_tail = self.get_readline_tail()
158 except (AttributeError, IndexError): # Can fail with pyreadline
154 except (AttributeError, IndexError): # Can fail with pyreadline
159 self.orig_length, self.readline_tail = 999999, []
155 self.orig_length, self.readline_tail = 999999, []
160 self._nested_level += 1
156 self._nested_level += 1
161
157
162 def __exit__(self, type, value, traceback):
158 def __exit__(self, type, value, traceback):
163 self._nested_level -= 1
159 self._nested_level -= 1
164 if self._nested_level == 0:
160 if self._nested_level == 0:
165 # Try clipping the end if it's got longer
161 # Try clipping the end if it's got longer
166 try:
162 try:
167 e = self.current_length() - self.orig_length
163 e = self.current_length() - self.orig_length
168 if e > 0:
164 if e > 0:
169 for _ in range(e):
165 for _ in range(e):
170 self.shell.readline.remove_history_item(self.orig_length)
166 self.shell.readline.remove_history_item(self.orig_length)
171
167
172 # If it still doesn't match, just reload readline history.
168 # If it still doesn't match, just reload readline history.
173 if self.current_length() != self.orig_length \
169 if self.current_length() != self.orig_length \
174 or self.get_readline_tail() != self.readline_tail:
170 or self.get_readline_tail() != self.readline_tail:
175 self.shell.refill_readline_hist()
171 self.shell.refill_readline_hist()
176 except (AttributeError, IndexError):
172 except (AttributeError, IndexError):
177 pass
173 pass
178 # Returning False will cause exceptions to propagate
174 # Returning False will cause exceptions to propagate
179 return False
175 return False
180
176
181 def current_length(self):
177 def current_length(self):
182 return self.shell.readline.get_current_history_length()
178 return self.shell.readline.get_current_history_length()
183
179
184 def get_readline_tail(self, n=10):
180 def get_readline_tail(self, n=10):
185 """Get the last n items in readline history."""
181 """Get the last n items in readline history."""
186 end = self.shell.readline.get_current_history_length() + 1
182 end = self.shell.readline.get_current_history_length() + 1
187 start = max(end-n, 1)
183 start = max(end-n, 1)
188 ghi = self.shell.readline.get_history_item
184 ghi = self.shell.readline.get_history_item
189 return [ghi(x) for x in range(start, end)]
185 return [ghi(x) for x in range(start, end)]
190
186
191
187
192 @undoc
188 @undoc
193 class DummyMod(object):
189 class DummyMod(object):
194 """A dummy module used for IPython's interactive module when
190 """A dummy module used for IPython's interactive module when
195 a namespace must be assigned to the module's __dict__."""
191 a namespace must be assigned to the module's __dict__."""
196 pass
192 pass
197
193
198 #-----------------------------------------------------------------------------
194 #-----------------------------------------------------------------------------
199 # Main IPython class
195 # Main IPython class
200 #-----------------------------------------------------------------------------
196 #-----------------------------------------------------------------------------
201
197
202 class InteractiveShell(SingletonConfigurable):
198 class InteractiveShell(SingletonConfigurable):
203 """An enhanced, interactive shell for Python."""
199 """An enhanced, interactive shell for Python."""
204
200
205 _instance = None
201 _instance = None
206
202
207 ast_transformers = List([], config=True, help=
203 ast_transformers = List([], config=True, help=
208 """
204 """
209 A list of ast.NodeTransformer subclass instances, which will be applied
205 A list of ast.NodeTransformer subclass instances, which will be applied
210 to user input before code is run.
206 to user input before code is run.
211 """
207 """
212 )
208 )
213
209
214 autocall = Enum((0,1,2), default_value=0, config=True, help=
210 autocall = Enum((0,1,2), default_value=0, config=True, help=
215 """
211 """
216 Make IPython automatically call any callable object even if you didn't
212 Make IPython automatically call any callable object even if you didn't
217 type explicit parentheses. For example, 'str 43' becomes 'str(43)'
213 type explicit parentheses. For example, 'str 43' becomes 'str(43)'
218 automatically. The value can be '0' to disable the feature, '1' for
214 automatically. The value can be '0' to disable the feature, '1' for
219 'smart' autocall, where it is not applied if there are no more
215 'smart' autocall, where it is not applied if there are no more
220 arguments on the line, and '2' for 'full' autocall, where all callable
216 arguments on the line, and '2' for 'full' autocall, where all callable
221 objects are automatically called (even if no arguments are present).
217 objects are automatically called (even if no arguments are present).
222 """
218 """
223 )
219 )
224 # TODO: remove all autoindent logic and put into frontends.
220 # TODO: remove all autoindent logic and put into frontends.
225 # We can't do this yet because even runlines uses the autoindent.
221 # We can't do this yet because even runlines uses the autoindent.
226 autoindent = CBool(True, config=True, help=
222 autoindent = CBool(True, config=True, help=
227 """
223 """
228 Autoindent IPython code entered interactively.
224 Autoindent IPython code entered interactively.
229 """
225 """
230 )
226 )
231 automagic = CBool(True, config=True, help=
227 automagic = CBool(True, config=True, help=
232 """
228 """
233 Enable magic commands to be called without the leading %.
229 Enable magic commands to be called without the leading %.
234 """
230 """
235 )
231 )
232
233 banner = Unicode('')
234
235 banner1 = Unicode(default_banner, config=True,
236 help="""The part of the banner to be printed before the profile"""
237 )
238 banner2 = Unicode('', config=True,
239 help="""The part of the banner to be printed after the profile"""
240 )
241
236 cache_size = Integer(1000, config=True, help=
242 cache_size = Integer(1000, config=True, help=
237 """
243 """
238 Set the size of the output cache. The default is 1000, you can
244 Set the size of the output cache. The default is 1000, you can
239 change it permanently in your config file. Setting it to 0 completely
245 change it permanently in your config file. Setting it to 0 completely
240 disables the caching system, and the minimum value accepted is 20 (if
246 disables the caching system, and the minimum value accepted is 20 (if
241 you provide a value less than 20, it is reset to 0 and a warning is
247 you provide a value less than 20, it is reset to 0 and a warning is
242 issued). This limit is defined because otherwise you'll spend more
248 issued). This limit is defined because otherwise you'll spend more
243 time re-flushing a too small cache than working
249 time re-flushing a too small cache than working
244 """
250 """
245 )
251 )
246 color_info = CBool(True, config=True, help=
252 color_info = CBool(True, config=True, help=
247 """
253 """
248 Use colors for displaying information about objects. Because this
254 Use colors for displaying information about objects. Because this
249 information is passed through a pager (like 'less'), and some pagers
255 information is passed through a pager (like 'less'), and some pagers
250 get confused with color codes, this capability can be turned off.
256 get confused with color codes, this capability can be turned off.
251 """
257 """
252 )
258 )
253 colors = CaselessStrEnum(('NoColor','LightBG','Linux'),
259 colors = CaselessStrEnum(('NoColor','LightBG','Linux'),
254 default_value=get_default_colors(), config=True,
260 default_value=get_default_colors(), config=True,
255 help="Set the color scheme (NoColor, Linux, or LightBG)."
261 help="Set the color scheme (NoColor, Linux, or LightBG)."
256 )
262 )
257 colors_force = CBool(False, help=
263 colors_force = CBool(False, help=
258 """
264 """
259 Force use of ANSI color codes, regardless of OS and readline
265 Force use of ANSI color codes, regardless of OS and readline
260 availability.
266 availability.
261 """
267 """
262 # FIXME: This is essentially a hack to allow ZMQShell to show colors
268 # FIXME: This is essentially a hack to allow ZMQShell to show colors
263 # without readline on Win32. When the ZMQ formatting system is
269 # without readline on Win32. When the ZMQ formatting system is
264 # refactored, this should be removed.
270 # refactored, this should be removed.
265 )
271 )
266 debug = CBool(False, config=True)
272 debug = CBool(False, config=True)
267 deep_reload = CBool(False, config=True, help=
273 deep_reload = CBool(False, config=True, help=
268 """
274 """
269 Enable deep (recursive) reloading by default. IPython can use the
275 Enable deep (recursive) reloading by default. IPython can use the
270 deep_reload module which reloads changes in modules recursively (it
276 deep_reload module which reloads changes in modules recursively (it
271 replaces the reload() function, so you don't need to change anything to
277 replaces the reload() function, so you don't need to change anything to
272 use it). deep_reload() forces a full reload of modules whose code may
278 use it). deep_reload() forces a full reload of modules whose code may
273 have changed, which the default reload() function does not. When
279 have changed, which the default reload() function does not. When
274 deep_reload is off, IPython will use the normal reload(), but
280 deep_reload is off, IPython will use the normal reload(), but
275 deep_reload will still be available as dreload().
281 deep_reload will still be available as dreload().
276 """
282 """
277 )
283 )
278 disable_failing_post_execute = CBool(False, config=True,
284 disable_failing_post_execute = CBool(False, config=True,
279 help="Don't call post-execute functions that have failed in the past."
285 help="Don't call post-execute functions that have failed in the past."
280 )
286 )
281 display_formatter = Instance(DisplayFormatter)
287 display_formatter = Instance(DisplayFormatter)
282 displayhook_class = Type(DisplayHook)
288 displayhook_class = Type(DisplayHook)
283 display_pub_class = Type(DisplayPublisher)
289 display_pub_class = Type(DisplayPublisher)
284 data_pub_class = None
290 data_pub_class = None
285
291
286 exit_now = CBool(False)
292 exit_now = CBool(False)
287 exiter = Instance(ExitAutocall)
293 exiter = Instance(ExitAutocall)
288 def _exiter_default(self):
294 def _exiter_default(self):
289 return ExitAutocall(self)
295 return ExitAutocall(self)
290 # Monotonically increasing execution counter
296 # Monotonically increasing execution counter
291 execution_count = Integer(1)
297 execution_count = Integer(1)
292 filename = Unicode("<ipython console>")
298 filename = Unicode("<ipython console>")
293 ipython_dir= Unicode('', config=True) # Set to get_ipython_dir() in __init__
299 ipython_dir= Unicode('', config=True) # Set to get_ipython_dir() in __init__
294
300
295 # Input splitter, to transform input line by line and detect when a block
301 # Input splitter, to transform input line by line and detect when a block
296 # is ready to be executed.
302 # is ready to be executed.
297 input_splitter = Instance('IPython.core.inputsplitter.IPythonInputSplitter',
303 input_splitter = Instance('IPython.core.inputsplitter.IPythonInputSplitter',
298 (), {'line_input_checker': True})
304 (), {'line_input_checker': True})
299
305
300 # This InputSplitter instance is used to transform completed cells before
306 # This InputSplitter instance is used to transform completed cells before
301 # running them. It allows cell magics to contain blank lines.
307 # running them. It allows cell magics to contain blank lines.
302 input_transformer_manager = Instance('IPython.core.inputsplitter.IPythonInputSplitter',
308 input_transformer_manager = Instance('IPython.core.inputsplitter.IPythonInputSplitter',
303 (), {'line_input_checker': False})
309 (), {'line_input_checker': False})
304
310
305 logstart = CBool(False, config=True, help=
311 logstart = CBool(False, config=True, help=
306 """
312 """
307 Start logging to the default log file.
313 Start logging to the default log file.
308 """
314 """
309 )
315 )
310 logfile = Unicode('', config=True, help=
316 logfile = Unicode('', config=True, help=
311 """
317 """
312 The name of the logfile to use.
318 The name of the logfile to use.
313 """
319 """
314 )
320 )
315 logappend = Unicode('', config=True, help=
321 logappend = Unicode('', config=True, help=
316 """
322 """
317 Start logging to the given file in append mode.
323 Start logging to the given file in append mode.
318 """
324 """
319 )
325 )
320 object_info_string_level = Enum((0,1,2), default_value=0,
326 object_info_string_level = Enum((0,1,2), default_value=0,
321 config=True)
327 config=True)
322 pdb = CBool(False, config=True, help=
328 pdb = CBool(False, config=True, help=
323 """
329 """
324 Automatically call the pdb debugger after every exception.
330 Automatically call the pdb debugger after every exception.
325 """
331 """
326 )
332 )
327 multiline_history = CBool(sys.platform != 'win32', config=True,
333 multiline_history = CBool(sys.platform != 'win32', config=True,
328 help="Save multi-line entries as one entry in readline history"
334 help="Save multi-line entries as one entry in readline history"
329 )
335 )
330
336
331 # deprecated prompt traits:
337 # deprecated prompt traits:
332
338
333 prompt_in1 = Unicode('In [\\#]: ', config=True,
339 prompt_in1 = Unicode('In [\\#]: ', config=True,
334 help="Deprecated, use PromptManager.in_template")
340 help="Deprecated, use PromptManager.in_template")
335 prompt_in2 = Unicode(' .\\D.: ', config=True,
341 prompt_in2 = Unicode(' .\\D.: ', config=True,
336 help="Deprecated, use PromptManager.in2_template")
342 help="Deprecated, use PromptManager.in2_template")
337 prompt_out = Unicode('Out[\\#]: ', config=True,
343 prompt_out = Unicode('Out[\\#]: ', config=True,
338 help="Deprecated, use PromptManager.out_template")
344 help="Deprecated, use PromptManager.out_template")
339 prompts_pad_left = CBool(True, config=True,
345 prompts_pad_left = CBool(True, config=True,
340 help="Deprecated, use PromptManager.justify")
346 help="Deprecated, use PromptManager.justify")
341
347
342 def _prompt_trait_changed(self, name, old, new):
348 def _prompt_trait_changed(self, name, old, new):
343 table = {
349 table = {
344 'prompt_in1' : 'in_template',
350 'prompt_in1' : 'in_template',
345 'prompt_in2' : 'in2_template',
351 'prompt_in2' : 'in2_template',
346 'prompt_out' : 'out_template',
352 'prompt_out' : 'out_template',
347 'prompts_pad_left' : 'justify',
353 'prompts_pad_left' : 'justify',
348 }
354 }
349 warn("InteractiveShell.{name} is deprecated, use PromptManager.{newname}".format(
355 warn("InteractiveShell.{name} is deprecated, use PromptManager.{newname}".format(
350 name=name, newname=table[name])
356 name=name, newname=table[name])
351 )
357 )
352 # protect against weird cases where self.config may not exist:
358 # protect against weird cases where self.config may not exist:
353 if self.config is not None:
359 if self.config is not None:
354 # propagate to corresponding PromptManager trait
360 # propagate to corresponding PromptManager trait
355 setattr(self.config.PromptManager, table[name], new)
361 setattr(self.config.PromptManager, table[name], new)
356
362
357 _prompt_in1_changed = _prompt_trait_changed
363 _prompt_in1_changed = _prompt_trait_changed
358 _prompt_in2_changed = _prompt_trait_changed
364 _prompt_in2_changed = _prompt_trait_changed
359 _prompt_out_changed = _prompt_trait_changed
365 _prompt_out_changed = _prompt_trait_changed
360 _prompt_pad_left_changed = _prompt_trait_changed
366 _prompt_pad_left_changed = _prompt_trait_changed
361
367
362 show_rewritten_input = CBool(True, config=True,
368 show_rewritten_input = CBool(True, config=True,
363 help="Show rewritten input, e.g. for autocall."
369 help="Show rewritten input, e.g. for autocall."
364 )
370 )
365
371
366 quiet = CBool(False, config=True)
372 quiet = CBool(False, config=True)
367
373
368 history_length = Integer(10000, config=True)
374 history_length = Integer(10000, config=True)
369
375
370 # The readline stuff will eventually be moved to the terminal subclass
376 # The readline stuff will eventually be moved to the terminal subclass
371 # but for now, we can't do that as readline is welded in everywhere.
377 # but for now, we can't do that as readline is welded in everywhere.
372 readline_use = CBool(True, config=True)
378 readline_use = CBool(True, config=True)
373 readline_remove_delims = Unicode('-/~', config=True)
379 readline_remove_delims = Unicode('-/~', config=True)
374 readline_delims = Unicode() # set by init_readline()
380 readline_delims = Unicode() # set by init_readline()
375 # don't use \M- bindings by default, because they
381 # don't use \M- bindings by default, because they
376 # conflict with 8-bit encodings. See gh-58,gh-88
382 # conflict with 8-bit encodings. See gh-58,gh-88
377 readline_parse_and_bind = List([
383 readline_parse_and_bind = List([
378 'tab: complete',
384 'tab: complete',
379 '"\C-l": clear-screen',
385 '"\C-l": clear-screen',
380 'set show-all-if-ambiguous on',
386 'set show-all-if-ambiguous on',
381 '"\C-o": tab-insert',
387 '"\C-o": tab-insert',
382 '"\C-r": reverse-search-history',
388 '"\C-r": reverse-search-history',
383 '"\C-s": forward-search-history',
389 '"\C-s": forward-search-history',
384 '"\C-p": history-search-backward',
390 '"\C-p": history-search-backward',
385 '"\C-n": history-search-forward',
391 '"\C-n": history-search-forward',
386 '"\e[A": history-search-backward',
392 '"\e[A": history-search-backward',
387 '"\e[B": history-search-forward',
393 '"\e[B": history-search-forward',
388 '"\C-k": kill-line',
394 '"\C-k": kill-line',
389 '"\C-u": unix-line-discard',
395 '"\C-u": unix-line-discard',
390 ], allow_none=False, config=True)
396 ], allow_none=False, config=True)
391
397
392 ast_node_interactivity = Enum(['all', 'last', 'last_expr', 'none'],
398 ast_node_interactivity = Enum(['all', 'last', 'last_expr', 'none'],
393 default_value='last_expr', config=True,
399 default_value='last_expr', config=True,
394 help="""
400 help="""
395 'all', 'last', 'last_expr' or 'none', specifying which nodes should be
401 'all', 'last', 'last_expr' or 'none', specifying which nodes should be
396 run interactively (displaying output from expressions).""")
402 run interactively (displaying output from expressions).""")
397
403
398 # TODO: this part of prompt management should be moved to the frontends.
404 # TODO: this part of prompt management should be moved to the frontends.
399 # Use custom TraitTypes that convert '0'->'' and '\\n'->'\n'
405 # Use custom TraitTypes that convert '0'->'' and '\\n'->'\n'
400 separate_in = SeparateUnicode('\n', config=True)
406 separate_in = SeparateUnicode('\n', config=True)
401 separate_out = SeparateUnicode('', config=True)
407 separate_out = SeparateUnicode('', config=True)
402 separate_out2 = SeparateUnicode('', config=True)
408 separate_out2 = SeparateUnicode('', config=True)
403 wildcards_case_sensitive = CBool(True, config=True)
409 wildcards_case_sensitive = CBool(True, config=True)
404 xmode = CaselessStrEnum(('Context','Plain', 'Verbose'),
410 xmode = CaselessStrEnum(('Context','Plain', 'Verbose'),
405 default_value='Context', config=True)
411 default_value='Context', config=True)
406
412
407 # Subcomponents of InteractiveShell
413 # Subcomponents of InteractiveShell
408 alias_manager = Instance('IPython.core.alias.AliasManager')
414 alias_manager = Instance('IPython.core.alias.AliasManager')
409 prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager')
415 prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager')
410 builtin_trap = Instance('IPython.core.builtin_trap.BuiltinTrap')
416 builtin_trap = Instance('IPython.core.builtin_trap.BuiltinTrap')
411 display_trap = Instance('IPython.core.display_trap.DisplayTrap')
417 display_trap = Instance('IPython.core.display_trap.DisplayTrap')
412 extension_manager = Instance('IPython.core.extensions.ExtensionManager')
418 extension_manager = Instance('IPython.core.extensions.ExtensionManager')
413 payload_manager = Instance('IPython.core.payload.PayloadManager')
419 payload_manager = Instance('IPython.core.payload.PayloadManager')
414 history_manager = Instance('IPython.core.history.HistoryManager')
420 history_manager = Instance('IPython.core.history.HistoryManager')
415 magics_manager = Instance('IPython.core.magic.MagicsManager')
421 magics_manager = Instance('IPython.core.magic.MagicsManager')
416
422
417 profile_dir = Instance('IPython.core.application.ProfileDir')
423 profile_dir = Instance('IPython.core.application.ProfileDir')
418 @property
424 @property
419 def profile(self):
425 def profile(self):
420 if self.profile_dir is not None:
426 if self.profile_dir is not None:
421 name = os.path.basename(self.profile_dir.location)
427 name = os.path.basename(self.profile_dir.location)
422 return name.replace('profile_','')
428 return name.replace('profile_','')
423
429
424
430
425 # Private interface
431 # Private interface
426 _post_execute = Instance(dict)
432 _post_execute = Instance(dict)
427
433
428 # Tracks any GUI loop loaded for pylab
434 # Tracks any GUI loop loaded for pylab
429 pylab_gui_select = None
435 pylab_gui_select = None
430
436
431 def __init__(self, ipython_dir=None, profile_dir=None,
437 def __init__(self, ipython_dir=None, profile_dir=None,
432 user_module=None, user_ns=None,
438 user_module=None, user_ns=None,
433 custom_exceptions=((), None), **kwargs):
439 custom_exceptions=((), None), **kwargs):
434
440
435 # This is where traits with a config_key argument are updated
441 # This is where traits with a config_key argument are updated
436 # from the values on config.
442 # from the values on config.
437 super(InteractiveShell, self).__init__(**kwargs)
443 super(InteractiveShell, self).__init__(**kwargs)
438 self.configurables = [self]
444 self.configurables = [self]
439
445
440 # These are relatively independent and stateless
446 # These are relatively independent and stateless
441 self.init_ipython_dir(ipython_dir)
447 self.init_ipython_dir(ipython_dir)
442 self.init_profile_dir(profile_dir)
448 self.init_profile_dir(profile_dir)
443 self.init_instance_attrs()
449 self.init_instance_attrs()
444 self.init_environment()
450 self.init_environment()
445
451
446 # Check if we're in a virtualenv, and set up sys.path.
452 # Check if we're in a virtualenv, and set up sys.path.
447 self.init_virtualenv()
453 self.init_virtualenv()
448
454
449 # Create namespaces (user_ns, user_global_ns, etc.)
455 # Create namespaces (user_ns, user_global_ns, etc.)
450 self.init_create_namespaces(user_module, user_ns)
456 self.init_create_namespaces(user_module, user_ns)
451 # This has to be done after init_create_namespaces because it uses
457 # This has to be done after init_create_namespaces because it uses
452 # something in self.user_ns, but before init_sys_modules, which
458 # something in self.user_ns, but before init_sys_modules, which
453 # is the first thing to modify sys.
459 # is the first thing to modify sys.
454 # TODO: When we override sys.stdout and sys.stderr before this class
460 # TODO: When we override sys.stdout and sys.stderr before this class
455 # is created, we are saving the overridden ones here. Not sure if this
461 # is created, we are saving the overridden ones here. Not sure if this
456 # is what we want to do.
462 # is what we want to do.
457 self.save_sys_module_state()
463 self.save_sys_module_state()
458 self.init_sys_modules()
464 self.init_sys_modules()
459
465
460 # While we're trying to have each part of the code directly access what
466 # While we're trying to have each part of the code directly access what
461 # it needs without keeping redundant references to objects, we have too
467 # it needs without keeping redundant references to objects, we have too
462 # much legacy code that expects ip.db to exist.
468 # much legacy code that expects ip.db to exist.
463 self.db = PickleShareDB(os.path.join(self.profile_dir.location, 'db'))
469 self.db = PickleShareDB(os.path.join(self.profile_dir.location, 'db'))
464
470
465 self.init_history()
471 self.init_history()
466 self.init_encoding()
472 self.init_encoding()
467 self.init_prefilter()
473 self.init_prefilter()
468
474
469 self.init_syntax_highlighting()
475 self.init_syntax_highlighting()
470 self.init_hooks()
476 self.init_hooks()
471 self.init_events()
477 self.init_events()
472 self.init_pushd_popd_magic()
478 self.init_pushd_popd_magic()
473 # self.init_traceback_handlers use to be here, but we moved it below
479 # self.init_traceback_handlers use to be here, but we moved it below
474 # because it and init_io have to come after init_readline.
480 # because it and init_io have to come after init_readline.
475 self.init_user_ns()
481 self.init_user_ns()
476 self.init_logger()
482 self.init_logger()
477 self.init_builtins()
483 self.init_builtins()
478
484
479 # The following was in post_config_initialization
485 # The following was in post_config_initialization
480 self.init_inspector()
486 self.init_inspector()
481 # init_readline() must come before init_io(), because init_io uses
487 # init_readline() must come before init_io(), because init_io uses
482 # readline related things.
488 # readline related things.
483 self.init_readline()
489 self.init_readline()
484 # We save this here in case user code replaces raw_input, but it needs
490 # We save this here in case user code replaces raw_input, but it needs
485 # to be after init_readline(), because PyPy's readline works by replacing
491 # to be after init_readline(), because PyPy's readline works by replacing
486 # raw_input.
492 # raw_input.
487 if py3compat.PY3:
493 if py3compat.PY3:
488 self.raw_input_original = input
494 self.raw_input_original = input
489 else:
495 else:
490 self.raw_input_original = raw_input
496 self.raw_input_original = raw_input
491 # init_completer must come after init_readline, because it needs to
497 # init_completer must come after init_readline, because it needs to
492 # know whether readline is present or not system-wide to configure the
498 # know whether readline is present or not system-wide to configure the
493 # completers, since the completion machinery can now operate
499 # completers, since the completion machinery can now operate
494 # independently of readline (e.g. over the network)
500 # independently of readline (e.g. over the network)
495 self.init_completer()
501 self.init_completer()
496 # TODO: init_io() needs to happen before init_traceback handlers
502 # TODO: init_io() needs to happen before init_traceback handlers
497 # because the traceback handlers hardcode the stdout/stderr streams.
503 # because the traceback handlers hardcode the stdout/stderr streams.
498 # This logic in in debugger.Pdb and should eventually be changed.
504 # This logic in in debugger.Pdb and should eventually be changed.
499 self.init_io()
505 self.init_io()
500 self.init_traceback_handlers(custom_exceptions)
506 self.init_traceback_handlers(custom_exceptions)
501 self.init_prompts()
507 self.init_prompts()
502 self.init_display_formatter()
508 self.init_display_formatter()
503 self.init_display_pub()
509 self.init_display_pub()
504 self.init_data_pub()
510 self.init_data_pub()
505 self.init_displayhook()
511 self.init_displayhook()
506 self.init_latextool()
512 self.init_latextool()
507 self.init_magics()
513 self.init_magics()
508 self.init_alias()
514 self.init_alias()
509 self.init_logstart()
515 self.init_logstart()
510 self.init_pdb()
516 self.init_pdb()
511 self.init_extension_manager()
517 self.init_extension_manager()
512 self.init_payload()
518 self.init_payload()
513 self.init_comms()
519 self.init_comms()
514 self.hooks.late_startup_hook()
520 self.hooks.late_startup_hook()
515 self.events.trigger('shell_initialized', self)
521 self.events.trigger('shell_initialized', self)
516 atexit.register(self.atexit_operations)
522 atexit.register(self.atexit_operations)
517
523
518 def get_ipython(self):
524 def get_ipython(self):
519 """Return the currently running IPython instance."""
525 """Return the currently running IPython instance."""
520 return self
526 return self
521
527
522 #-------------------------------------------------------------------------
528 #-------------------------------------------------------------------------
523 # Trait changed handlers
529 # Trait changed handlers
524 #-------------------------------------------------------------------------
530 #-------------------------------------------------------------------------
525
531
526 def _ipython_dir_changed(self, name, new):
532 def _ipython_dir_changed(self, name, new):
527 ensure_dir_exists(new)
533 ensure_dir_exists(new)
528
534
529 def set_autoindent(self,value=None):
535 def set_autoindent(self,value=None):
530 """Set the autoindent flag, checking for readline support.
536 """Set the autoindent flag, checking for readline support.
531
537
532 If called with no arguments, it acts as a toggle."""
538 If called with no arguments, it acts as a toggle."""
533
539
534 if value != 0 and not self.has_readline:
540 if value != 0 and not self.has_readline:
535 if os.name == 'posix':
541 if os.name == 'posix':
536 warn("The auto-indent feature requires the readline library")
542 warn("The auto-indent feature requires the readline library")
537 self.autoindent = 0
543 self.autoindent = 0
538 return
544 return
539 if value is None:
545 if value is None:
540 self.autoindent = not self.autoindent
546 self.autoindent = not self.autoindent
541 else:
547 else:
542 self.autoindent = value
548 self.autoindent = value
543
549
544 #-------------------------------------------------------------------------
550 #-------------------------------------------------------------------------
545 # init_* methods called by __init__
551 # init_* methods called by __init__
546 #-------------------------------------------------------------------------
552 #-------------------------------------------------------------------------
547
553
548 def init_ipython_dir(self, ipython_dir):
554 def init_ipython_dir(self, ipython_dir):
549 if ipython_dir is not None:
555 if ipython_dir is not None:
550 self.ipython_dir = ipython_dir
556 self.ipython_dir = ipython_dir
551 return
557 return
552
558
553 self.ipython_dir = get_ipython_dir()
559 self.ipython_dir = get_ipython_dir()
554
560
555 def init_profile_dir(self, profile_dir):
561 def init_profile_dir(self, profile_dir):
556 if profile_dir is not None:
562 if profile_dir is not None:
557 self.profile_dir = profile_dir
563 self.profile_dir = profile_dir
558 return
564 return
559 self.profile_dir =\
565 self.profile_dir =\
560 ProfileDir.create_profile_dir_by_name(self.ipython_dir, 'default')
566 ProfileDir.create_profile_dir_by_name(self.ipython_dir, 'default')
561
567
562 def init_instance_attrs(self):
568 def init_instance_attrs(self):
563 self.more = False
569 self.more = False
564
570
565 # command compiler
571 # command compiler
566 self.compile = CachingCompiler()
572 self.compile = CachingCompiler()
567
573
568 # Make an empty namespace, which extension writers can rely on both
574 # Make an empty namespace, which extension writers can rely on both
569 # existing and NEVER being used by ipython itself. This gives them a
575 # existing and NEVER being used by ipython itself. This gives them a
570 # convenient location for storing additional information and state
576 # convenient location for storing additional information and state
571 # their extensions may require, without fear of collisions with other
577 # their extensions may require, without fear of collisions with other
572 # ipython names that may develop later.
578 # ipython names that may develop later.
573 self.meta = Struct()
579 self.meta = Struct()
574
580
575 # Temporary files used for various purposes. Deleted at exit.
581 # Temporary files used for various purposes. Deleted at exit.
576 self.tempfiles = []
582 self.tempfiles = []
577 self.tempdirs = []
583 self.tempdirs = []
578
584
579 # Keep track of readline usage (later set by init_readline)
585 # Keep track of readline usage (later set by init_readline)
580 self.has_readline = False
586 self.has_readline = False
581
587
582 # keep track of where we started running (mainly for crash post-mortem)
588 # keep track of where we started running (mainly for crash post-mortem)
583 # This is not being used anywhere currently.
589 # This is not being used anywhere currently.
584 self.starting_dir = py3compat.getcwd()
590 self.starting_dir = py3compat.getcwd()
585
591
586 # Indentation management
592 # Indentation management
587 self.indent_current_nsp = 0
593 self.indent_current_nsp = 0
588
594
589 # Dict to track post-execution functions that have been registered
595 # Dict to track post-execution functions that have been registered
590 self._post_execute = {}
596 self._post_execute = {}
591
597
592 def init_environment(self):
598 def init_environment(self):
593 """Any changes we need to make to the user's environment."""
599 """Any changes we need to make to the user's environment."""
594 pass
600 pass
595
601
596 def init_encoding(self):
602 def init_encoding(self):
597 # Get system encoding at startup time. Certain terminals (like Emacs
603 # Get system encoding at startup time. Certain terminals (like Emacs
598 # under Win32 have it set to None, and we need to have a known valid
604 # under Win32 have it set to None, and we need to have a known valid
599 # encoding to use in the raw_input() method
605 # encoding to use in the raw_input() method
600 try:
606 try:
601 self.stdin_encoding = sys.stdin.encoding or 'ascii'
607 self.stdin_encoding = sys.stdin.encoding or 'ascii'
602 except AttributeError:
608 except AttributeError:
603 self.stdin_encoding = 'ascii'
609 self.stdin_encoding = 'ascii'
604
610
605 def init_syntax_highlighting(self):
611 def init_syntax_highlighting(self):
606 # Python source parser/formatter for syntax highlighting
612 # Python source parser/formatter for syntax highlighting
607 pyformat = PyColorize.Parser().format
613 pyformat = PyColorize.Parser().format
608 self.pycolorize = lambda src: pyformat(src,'str',self.colors)
614 self.pycolorize = lambda src: pyformat(src,'str',self.colors)
609
615
610 def init_pushd_popd_magic(self):
616 def init_pushd_popd_magic(self):
611 # for pushd/popd management
617 # for pushd/popd management
612 self.home_dir = get_home_dir()
618 self.home_dir = get_home_dir()
613
619
614 self.dir_stack = []
620 self.dir_stack = []
615
621
616 def init_logger(self):
622 def init_logger(self):
617 self.logger = Logger(self.home_dir, logfname='ipython_log.py',
623 self.logger = Logger(self.home_dir, logfname='ipython_log.py',
618 logmode='rotate')
624 logmode='rotate')
619
625
620 def init_logstart(self):
626 def init_logstart(self):
621 """Initialize logging in case it was requested at the command line.
627 """Initialize logging in case it was requested at the command line.
622 """
628 """
623 if self.logappend:
629 if self.logappend:
624 self.magic('logstart %s append' % self.logappend)
630 self.magic('logstart %s append' % self.logappend)
625 elif self.logfile:
631 elif self.logfile:
626 self.magic('logstart %s' % self.logfile)
632 self.magic('logstart %s' % self.logfile)
627 elif self.logstart:
633 elif self.logstart:
628 self.magic('logstart')
634 self.magic('logstart')
629
635
630 def init_builtins(self):
636 def init_builtins(self):
631 # A single, static flag that we set to True. Its presence indicates
637 # A single, static flag that we set to True. Its presence indicates
632 # that an IPython shell has been created, and we make no attempts at
638 # that an IPython shell has been created, and we make no attempts at
633 # removing on exit or representing the existence of more than one
639 # removing on exit or representing the existence of more than one
634 # IPython at a time.
640 # IPython at a time.
635 builtin_mod.__dict__['__IPYTHON__'] = True
641 builtin_mod.__dict__['__IPYTHON__'] = True
636
642
637 # In 0.11 we introduced '__IPYTHON__active' as an integer we'd try to
643 # In 0.11 we introduced '__IPYTHON__active' as an integer we'd try to
638 # manage on enter/exit, but with all our shells it's virtually
644 # manage on enter/exit, but with all our shells it's virtually
639 # impossible to get all the cases right. We're leaving the name in for
645 # impossible to get all the cases right. We're leaving the name in for
640 # those who adapted their codes to check for this flag, but will
646 # those who adapted their codes to check for this flag, but will
641 # eventually remove it after a few more releases.
647 # eventually remove it after a few more releases.
642 builtin_mod.__dict__['__IPYTHON__active'] = \
648 builtin_mod.__dict__['__IPYTHON__active'] = \
643 'Deprecated, check for __IPYTHON__'
649 'Deprecated, check for __IPYTHON__'
644
650
645 self.builtin_trap = BuiltinTrap(shell=self)
651 self.builtin_trap = BuiltinTrap(shell=self)
646
652
647 def init_inspector(self):
653 def init_inspector(self):
648 # Object inspector
654 # Object inspector
649 self.inspector = oinspect.Inspector(oinspect.InspectColors,
655 self.inspector = oinspect.Inspector(oinspect.InspectColors,
650 PyColorize.ANSICodeColors,
656 PyColorize.ANSICodeColors,
651 'NoColor',
657 'NoColor',
652 self.object_info_string_level)
658 self.object_info_string_level)
653
659
654 def init_io(self):
660 def init_io(self):
655 # This will just use sys.stdout and sys.stderr. If you want to
661 # This will just use sys.stdout and sys.stderr. If you want to
656 # override sys.stdout and sys.stderr themselves, you need to do that
662 # override sys.stdout and sys.stderr themselves, you need to do that
657 # *before* instantiating this class, because io holds onto
663 # *before* instantiating this class, because io holds onto
658 # references to the underlying streams.
664 # references to the underlying streams.
659 if (sys.platform == 'win32' or sys.platform == 'cli') and self.has_readline:
665 if (sys.platform == 'win32' or sys.platform == 'cli') and self.has_readline:
660 io.stdout = io.stderr = io.IOStream(self.readline._outputfile)
666 io.stdout = io.stderr = io.IOStream(self.readline._outputfile)
661 else:
667 else:
662 io.stdout = io.IOStream(sys.stdout)
668 io.stdout = io.IOStream(sys.stdout)
663 io.stderr = io.IOStream(sys.stderr)
669 io.stderr = io.IOStream(sys.stderr)
664
670
665 def init_prompts(self):
671 def init_prompts(self):
666 self.prompt_manager = PromptManager(shell=self, parent=self)
672 self.prompt_manager = PromptManager(shell=self, parent=self)
667 self.configurables.append(self.prompt_manager)
673 self.configurables.append(self.prompt_manager)
668 # Set system prompts, so that scripts can decide if they are running
674 # Set system prompts, so that scripts can decide if they are running
669 # interactively.
675 # interactively.
670 sys.ps1 = 'In : '
676 sys.ps1 = 'In : '
671 sys.ps2 = '...: '
677 sys.ps2 = '...: '
672 sys.ps3 = 'Out: '
678 sys.ps3 = 'Out: '
673
679
674 def init_display_formatter(self):
680 def init_display_formatter(self):
675 self.display_formatter = DisplayFormatter(parent=self)
681 self.display_formatter = DisplayFormatter(parent=self)
676 self.configurables.append(self.display_formatter)
682 self.configurables.append(self.display_formatter)
677
683
678 def init_display_pub(self):
684 def init_display_pub(self):
679 self.display_pub = self.display_pub_class(parent=self)
685 self.display_pub = self.display_pub_class(parent=self)
680 self.configurables.append(self.display_pub)
686 self.configurables.append(self.display_pub)
681
687
682 def init_data_pub(self):
688 def init_data_pub(self):
683 if not self.data_pub_class:
689 if not self.data_pub_class:
684 self.data_pub = None
690 self.data_pub = None
685 return
691 return
686 self.data_pub = self.data_pub_class(parent=self)
692 self.data_pub = self.data_pub_class(parent=self)
687 self.configurables.append(self.data_pub)
693 self.configurables.append(self.data_pub)
688
694
689 def init_displayhook(self):
695 def init_displayhook(self):
690 # Initialize displayhook, set in/out prompts and printing system
696 # Initialize displayhook, set in/out prompts and printing system
691 self.displayhook = self.displayhook_class(
697 self.displayhook = self.displayhook_class(
692 parent=self,
698 parent=self,
693 shell=self,
699 shell=self,
694 cache_size=self.cache_size,
700 cache_size=self.cache_size,
695 )
701 )
696 self.configurables.append(self.displayhook)
702 self.configurables.append(self.displayhook)
697 # This is a context manager that installs/revmoes the displayhook at
703 # This is a context manager that installs/revmoes the displayhook at
698 # the appropriate time.
704 # the appropriate time.
699 self.display_trap = DisplayTrap(hook=self.displayhook)
705 self.display_trap = DisplayTrap(hook=self.displayhook)
700
706
701 def init_latextool(self):
707 def init_latextool(self):
702 """Configure LaTeXTool."""
708 """Configure LaTeXTool."""
703 cfg = LaTeXTool.instance(parent=self)
709 cfg = LaTeXTool.instance(parent=self)
704 if cfg not in self.configurables:
710 if cfg not in self.configurables:
705 self.configurables.append(cfg)
711 self.configurables.append(cfg)
706
712
707 def init_virtualenv(self):
713 def init_virtualenv(self):
708 """Add a virtualenv to sys.path so the user can import modules from it.
714 """Add a virtualenv to sys.path so the user can import modules from it.
709 This isn't perfect: it doesn't use the Python interpreter with which the
715 This isn't perfect: it doesn't use the Python interpreter with which the
710 virtualenv was built, and it ignores the --no-site-packages option. A
716 virtualenv was built, and it ignores the --no-site-packages option. A
711 warning will appear suggesting the user installs IPython in the
717 warning will appear suggesting the user installs IPython in the
712 virtualenv, but for many cases, it probably works well enough.
718 virtualenv, but for many cases, it probably works well enough.
713
719
714 Adapted from code snippets online.
720 Adapted from code snippets online.
715
721
716 http://blog.ufsoft.org/2009/1/29/ipython-and-virtualenv
722 http://blog.ufsoft.org/2009/1/29/ipython-and-virtualenv
717 """
723 """
718 if 'VIRTUAL_ENV' not in os.environ:
724 if 'VIRTUAL_ENV' not in os.environ:
719 # Not in a virtualenv
725 # Not in a virtualenv
720 return
726 return
721
727
722 # venv detection:
728 # venv detection:
723 # stdlib venv may symlink sys.executable, so we can't use realpath.
729 # stdlib venv may symlink sys.executable, so we can't use realpath.
724 # but others can symlink *to* the venv Python, so we can't just use sys.executable.
730 # but others can symlink *to* the venv Python, so we can't just use sys.executable.
725 # So we just check every item in the symlink tree (generally <= 3)
731 # So we just check every item in the symlink tree (generally <= 3)
726 p = sys.executable
732 p = sys.executable
727 paths = [p]
733 paths = [p]
728 while os.path.islink(p):
734 while os.path.islink(p):
729 p = os.path.join(os.path.dirname(p), os.readlink(p))
735 p = os.path.join(os.path.dirname(p), os.readlink(p))
730 paths.append(p)
736 paths.append(p)
731 if any(p.startswith(os.environ['VIRTUAL_ENV']) for p in paths):
737 if any(p.startswith(os.environ['VIRTUAL_ENV']) for p in paths):
732 # Running properly in the virtualenv, don't need to do anything
738 # Running properly in the virtualenv, don't need to do anything
733 return
739 return
734
740
735 warn("Attempting to work in a virtualenv. If you encounter problems, please "
741 warn("Attempting to work in a virtualenv. If you encounter problems, please "
736 "install IPython inside the virtualenv.")
742 "install IPython inside the virtualenv.")
737 if sys.platform == "win32":
743 if sys.platform == "win32":
738 virtual_env = os.path.join(os.environ['VIRTUAL_ENV'], 'Lib', 'site-packages')
744 virtual_env = os.path.join(os.environ['VIRTUAL_ENV'], 'Lib', 'site-packages')
739 else:
745 else:
740 virtual_env = os.path.join(os.environ['VIRTUAL_ENV'], 'lib',
746 virtual_env = os.path.join(os.environ['VIRTUAL_ENV'], 'lib',
741 'python%d.%d' % sys.version_info[:2], 'site-packages')
747 'python%d.%d' % sys.version_info[:2], 'site-packages')
742
748
743 import site
749 import site
744 sys.path.insert(0, virtual_env)
750 sys.path.insert(0, virtual_env)
745 site.addsitedir(virtual_env)
751 site.addsitedir(virtual_env)
746
752
747 #-------------------------------------------------------------------------
753 #-------------------------------------------------------------------------
748 # Things related to injections into the sys module
754 # Things related to injections into the sys module
749 #-------------------------------------------------------------------------
755 #-------------------------------------------------------------------------
750
756
751 def save_sys_module_state(self):
757 def save_sys_module_state(self):
752 """Save the state of hooks in the sys module.
758 """Save the state of hooks in the sys module.
753
759
754 This has to be called after self.user_module is created.
760 This has to be called after self.user_module is created.
755 """
761 """
756 self._orig_sys_module_state = {}
762 self._orig_sys_module_state = {}
757 self._orig_sys_module_state['stdin'] = sys.stdin
763 self._orig_sys_module_state['stdin'] = sys.stdin
758 self._orig_sys_module_state['stdout'] = sys.stdout
764 self._orig_sys_module_state['stdout'] = sys.stdout
759 self._orig_sys_module_state['stderr'] = sys.stderr
765 self._orig_sys_module_state['stderr'] = sys.stderr
760 self._orig_sys_module_state['excepthook'] = sys.excepthook
766 self._orig_sys_module_state['excepthook'] = sys.excepthook
761 self._orig_sys_modules_main_name = self.user_module.__name__
767 self._orig_sys_modules_main_name = self.user_module.__name__
762 self._orig_sys_modules_main_mod = sys.modules.get(self.user_module.__name__)
768 self._orig_sys_modules_main_mod = sys.modules.get(self.user_module.__name__)
763
769
764 def restore_sys_module_state(self):
770 def restore_sys_module_state(self):
765 """Restore the state of the sys module."""
771 """Restore the state of the sys module."""
766 try:
772 try:
767 for k, v in iteritems(self._orig_sys_module_state):
773 for k, v in iteritems(self._orig_sys_module_state):
768 setattr(sys, k, v)
774 setattr(sys, k, v)
769 except AttributeError:
775 except AttributeError:
770 pass
776 pass
771 # Reset what what done in self.init_sys_modules
777 # Reset what what done in self.init_sys_modules
772 if self._orig_sys_modules_main_mod is not None:
778 if self._orig_sys_modules_main_mod is not None:
773 sys.modules[self._orig_sys_modules_main_name] = self._orig_sys_modules_main_mod
779 sys.modules[self._orig_sys_modules_main_name] = self._orig_sys_modules_main_mod
774
780
775 #-------------------------------------------------------------------------
781 #-------------------------------------------------------------------------
782 # Things related to the banner
783 #-------------------------------------------------------------------------
784
785 @property
786 def banner(self):
787 banner = self.banner1
788 if self.profile and self.profile != 'default':
789 banner += '\nIPython profile: %s\n' % self.profile
790 if self.banner2:
791 banner += '\n' + self.banner2
792 return banner
793
794 def show_banner(self, banner=None):
795 if banner is None:
796 banner = self.banner
797 self.write(banner)
798
799 #-------------------------------------------------------------------------
776 # Things related to hooks
800 # Things related to hooks
777 #-------------------------------------------------------------------------
801 #-------------------------------------------------------------------------
778
802
779 def init_hooks(self):
803 def init_hooks(self):
780 # hooks holds pointers used for user-side customizations
804 # hooks holds pointers used for user-side customizations
781 self.hooks = Struct()
805 self.hooks = Struct()
782
806
783 self.strdispatchers = {}
807 self.strdispatchers = {}
784
808
785 # Set all default hooks, defined in the IPython.hooks module.
809 # Set all default hooks, defined in the IPython.hooks module.
786 hooks = IPython.core.hooks
810 hooks = IPython.core.hooks
787 for hook_name in hooks.__all__:
811 for hook_name in hooks.__all__:
788 # default hooks have priority 100, i.e. low; user hooks should have
812 # default hooks have priority 100, i.e. low; user hooks should have
789 # 0-100 priority
813 # 0-100 priority
790 self.set_hook(hook_name,getattr(hooks,hook_name), 100, _warn_deprecated=False)
814 self.set_hook(hook_name,getattr(hooks,hook_name), 100, _warn_deprecated=False)
791
815
792 def set_hook(self,name,hook, priority=50, str_key=None, re_key=None,
816 def set_hook(self,name,hook, priority=50, str_key=None, re_key=None,
793 _warn_deprecated=True):
817 _warn_deprecated=True):
794 """set_hook(name,hook) -> sets an internal IPython hook.
818 """set_hook(name,hook) -> sets an internal IPython hook.
795
819
796 IPython exposes some of its internal API as user-modifiable hooks. By
820 IPython exposes some of its internal API as user-modifiable hooks. By
797 adding your function to one of these hooks, you can modify IPython's
821 adding your function to one of these hooks, you can modify IPython's
798 behavior to call at runtime your own routines."""
822 behavior to call at runtime your own routines."""
799
823
800 # At some point in the future, this should validate the hook before it
824 # At some point in the future, this should validate the hook before it
801 # accepts it. Probably at least check that the hook takes the number
825 # accepts it. Probably at least check that the hook takes the number
802 # of args it's supposed to.
826 # of args it's supposed to.
803
827
804 f = types.MethodType(hook,self)
828 f = types.MethodType(hook,self)
805
829
806 # check if the hook is for strdispatcher first
830 # check if the hook is for strdispatcher first
807 if str_key is not None:
831 if str_key is not None:
808 sdp = self.strdispatchers.get(name, StrDispatch())
832 sdp = self.strdispatchers.get(name, StrDispatch())
809 sdp.add_s(str_key, f, priority )
833 sdp.add_s(str_key, f, priority )
810 self.strdispatchers[name] = sdp
834 self.strdispatchers[name] = sdp
811 return
835 return
812 if re_key is not None:
836 if re_key is not None:
813 sdp = self.strdispatchers.get(name, StrDispatch())
837 sdp = self.strdispatchers.get(name, StrDispatch())
814 sdp.add_re(re.compile(re_key), f, priority )
838 sdp.add_re(re.compile(re_key), f, priority )
815 self.strdispatchers[name] = sdp
839 self.strdispatchers[name] = sdp
816 return
840 return
817
841
818 dp = getattr(self.hooks, name, None)
842 dp = getattr(self.hooks, name, None)
819 if name not in IPython.core.hooks.__all__:
843 if name not in IPython.core.hooks.__all__:
820 print("Warning! Hook '%s' is not one of %s" % \
844 print("Warning! Hook '%s' is not one of %s" % \
821 (name, IPython.core.hooks.__all__ ))
845 (name, IPython.core.hooks.__all__ ))
822
846
823 if _warn_deprecated and (name in IPython.core.hooks.deprecated):
847 if _warn_deprecated and (name in IPython.core.hooks.deprecated):
824 alternative = IPython.core.hooks.deprecated[name]
848 alternative = IPython.core.hooks.deprecated[name]
825 warn("Hook {} is deprecated. Use {} instead.".format(name, alternative))
849 warn("Hook {} is deprecated. Use {} instead.".format(name, alternative))
826
850
827 if not dp:
851 if not dp:
828 dp = IPython.core.hooks.CommandChainDispatcher()
852 dp = IPython.core.hooks.CommandChainDispatcher()
829
853
830 try:
854 try:
831 dp.add(f,priority)
855 dp.add(f,priority)
832 except AttributeError:
856 except AttributeError:
833 # it was not commandchain, plain old func - replace
857 # it was not commandchain, plain old func - replace
834 dp = f
858 dp = f
835
859
836 setattr(self.hooks,name, dp)
860 setattr(self.hooks,name, dp)
837
861
838 #-------------------------------------------------------------------------
862 #-------------------------------------------------------------------------
839 # Things related to events
863 # Things related to events
840 #-------------------------------------------------------------------------
864 #-------------------------------------------------------------------------
841
865
842 def init_events(self):
866 def init_events(self):
843 self.events = EventManager(self, available_events)
867 self.events = EventManager(self, available_events)
844
868
845 def register_post_execute(self, func):
869 def register_post_execute(self, func):
846 """DEPRECATED: Use ip.events.register('post_run_cell', func)
870 """DEPRECATED: Use ip.events.register('post_run_cell', func)
847
871
848 Register a function for calling after code execution.
872 Register a function for calling after code execution.
849 """
873 """
850 warn("ip.register_post_execute is deprecated, use "
874 warn("ip.register_post_execute is deprecated, use "
851 "ip.events.register('post_run_cell', func) instead.")
875 "ip.events.register('post_run_cell', func) instead.")
852 self.events.register('post_run_cell', func)
876 self.events.register('post_run_cell', func)
853
877
854 #-------------------------------------------------------------------------
878 #-------------------------------------------------------------------------
855 # Things related to the "main" module
879 # Things related to the "main" module
856 #-------------------------------------------------------------------------
880 #-------------------------------------------------------------------------
857
881
858 def new_main_mod(self, filename, modname):
882 def new_main_mod(self, filename, modname):
859 """Return a new 'main' module object for user code execution.
883 """Return a new 'main' module object for user code execution.
860
884
861 ``filename`` should be the path of the script which will be run in the
885 ``filename`` should be the path of the script which will be run in the
862 module. Requests with the same filename will get the same module, with
886 module. Requests with the same filename will get the same module, with
863 its namespace cleared.
887 its namespace cleared.
864
888
865 ``modname`` should be the module name - normally either '__main__' or
889 ``modname`` should be the module name - normally either '__main__' or
866 the basename of the file without the extension.
890 the basename of the file without the extension.
867
891
868 When scripts are executed via %run, we must keep a reference to their
892 When scripts are executed via %run, we must keep a reference to their
869 __main__ module around so that Python doesn't
893 __main__ module around so that Python doesn't
870 clear it, rendering references to module globals useless.
894 clear it, rendering references to module globals useless.
871
895
872 This method keeps said reference in a private dict, keyed by the
896 This method keeps said reference in a private dict, keyed by the
873 absolute path of the script. This way, for multiple executions of the
897 absolute path of the script. This way, for multiple executions of the
874 same script we only keep one copy of the namespace (the last one),
898 same script we only keep one copy of the namespace (the last one),
875 thus preventing memory leaks from old references while allowing the
899 thus preventing memory leaks from old references while allowing the
876 objects from the last execution to be accessible.
900 objects from the last execution to be accessible.
877 """
901 """
878 filename = os.path.abspath(filename)
902 filename = os.path.abspath(filename)
879 try:
903 try:
880 main_mod = self._main_mod_cache[filename]
904 main_mod = self._main_mod_cache[filename]
881 except KeyError:
905 except KeyError:
882 main_mod = self._main_mod_cache[filename] = types.ModuleType(modname,
906 main_mod = self._main_mod_cache[filename] = types.ModuleType(modname,
883 doc="Module created for script run in IPython")
907 doc="Module created for script run in IPython")
884 else:
908 else:
885 main_mod.__dict__.clear()
909 main_mod.__dict__.clear()
886 main_mod.__name__ = modname
910 main_mod.__name__ = modname
887
911
888 main_mod.__file__ = filename
912 main_mod.__file__ = filename
889 # It seems pydoc (and perhaps others) needs any module instance to
913 # It seems pydoc (and perhaps others) needs any module instance to
890 # implement a __nonzero__ method
914 # implement a __nonzero__ method
891 main_mod.__nonzero__ = lambda : True
915 main_mod.__nonzero__ = lambda : True
892
916
893 return main_mod
917 return main_mod
894
918
895 def clear_main_mod_cache(self):
919 def clear_main_mod_cache(self):
896 """Clear the cache of main modules.
920 """Clear the cache of main modules.
897
921
898 Mainly for use by utilities like %reset.
922 Mainly for use by utilities like %reset.
899
923
900 Examples
924 Examples
901 --------
925 --------
902
926
903 In [15]: import IPython
927 In [15]: import IPython
904
928
905 In [16]: m = _ip.new_main_mod(IPython.__file__, 'IPython')
929 In [16]: m = _ip.new_main_mod(IPython.__file__, 'IPython')
906
930
907 In [17]: len(_ip._main_mod_cache) > 0
931 In [17]: len(_ip._main_mod_cache) > 0
908 Out[17]: True
932 Out[17]: True
909
933
910 In [18]: _ip.clear_main_mod_cache()
934 In [18]: _ip.clear_main_mod_cache()
911
935
912 In [19]: len(_ip._main_mod_cache) == 0
936 In [19]: len(_ip._main_mod_cache) == 0
913 Out[19]: True
937 Out[19]: True
914 """
938 """
915 self._main_mod_cache.clear()
939 self._main_mod_cache.clear()
916
940
917 #-------------------------------------------------------------------------
941 #-------------------------------------------------------------------------
918 # Things related to debugging
942 # Things related to debugging
919 #-------------------------------------------------------------------------
943 #-------------------------------------------------------------------------
920
944
921 def init_pdb(self):
945 def init_pdb(self):
922 # Set calling of pdb on exceptions
946 # Set calling of pdb on exceptions
923 # self.call_pdb is a property
947 # self.call_pdb is a property
924 self.call_pdb = self.pdb
948 self.call_pdb = self.pdb
925
949
926 def _get_call_pdb(self):
950 def _get_call_pdb(self):
927 return self._call_pdb
951 return self._call_pdb
928
952
929 def _set_call_pdb(self,val):
953 def _set_call_pdb(self,val):
930
954
931 if val not in (0,1,False,True):
955 if val not in (0,1,False,True):
932 raise ValueError('new call_pdb value must be boolean')
956 raise ValueError('new call_pdb value must be boolean')
933
957
934 # store value in instance
958 # store value in instance
935 self._call_pdb = val
959 self._call_pdb = val
936
960
937 # notify the actual exception handlers
961 # notify the actual exception handlers
938 self.InteractiveTB.call_pdb = val
962 self.InteractiveTB.call_pdb = val
939
963
940 call_pdb = property(_get_call_pdb,_set_call_pdb,None,
964 call_pdb = property(_get_call_pdb,_set_call_pdb,None,
941 'Control auto-activation of pdb at exceptions')
965 'Control auto-activation of pdb at exceptions')
942
966
943 def debugger(self,force=False):
967 def debugger(self,force=False):
944 """Call the pydb/pdb debugger.
968 """Call the pydb/pdb debugger.
945
969
946 Keywords:
970 Keywords:
947
971
948 - force(False): by default, this routine checks the instance call_pdb
972 - force(False): by default, this routine checks the instance call_pdb
949 flag and does not actually invoke the debugger if the flag is false.
973 flag and does not actually invoke the debugger if the flag is false.
950 The 'force' option forces the debugger to activate even if the flag
974 The 'force' option forces the debugger to activate even if the flag
951 is false.
975 is false.
952 """
976 """
953
977
954 if not (force or self.call_pdb):
978 if not (force or self.call_pdb):
955 return
979 return
956
980
957 if not hasattr(sys,'last_traceback'):
981 if not hasattr(sys,'last_traceback'):
958 error('No traceback has been produced, nothing to debug.')
982 error('No traceback has been produced, nothing to debug.')
959 return
983 return
960
984
961 # use pydb if available
985 # use pydb if available
962 if debugger.has_pydb:
986 if debugger.has_pydb:
963 from pydb import pm
987 from pydb import pm
964 else:
988 else:
965 # fallback to our internal debugger
989 # fallback to our internal debugger
966 pm = lambda : self.InteractiveTB.debugger(force=True)
990 pm = lambda : self.InteractiveTB.debugger(force=True)
967
991
968 with self.readline_no_record:
992 with self.readline_no_record:
969 pm()
993 pm()
970
994
971 #-------------------------------------------------------------------------
995 #-------------------------------------------------------------------------
972 # Things related to IPython's various namespaces
996 # Things related to IPython's various namespaces
973 #-------------------------------------------------------------------------
997 #-------------------------------------------------------------------------
974 default_user_namespaces = True
998 default_user_namespaces = True
975
999
976 def init_create_namespaces(self, user_module=None, user_ns=None):
1000 def init_create_namespaces(self, user_module=None, user_ns=None):
977 # Create the namespace where the user will operate. user_ns is
1001 # Create the namespace where the user will operate. user_ns is
978 # normally the only one used, and it is passed to the exec calls as
1002 # normally the only one used, and it is passed to the exec calls as
979 # the locals argument. But we do carry a user_global_ns namespace
1003 # the locals argument. But we do carry a user_global_ns namespace
980 # given as the exec 'globals' argument, This is useful in embedding
1004 # given as the exec 'globals' argument, This is useful in embedding
981 # situations where the ipython shell opens in a context where the
1005 # situations where the ipython shell opens in a context where the
982 # distinction between locals and globals is meaningful. For
1006 # distinction between locals and globals is meaningful. For
983 # non-embedded contexts, it is just the same object as the user_ns dict.
1007 # non-embedded contexts, it is just the same object as the user_ns dict.
984
1008
985 # FIXME. For some strange reason, __builtins__ is showing up at user
1009 # FIXME. For some strange reason, __builtins__ is showing up at user
986 # level as a dict instead of a module. This is a manual fix, but I
1010 # level as a dict instead of a module. This is a manual fix, but I
987 # should really track down where the problem is coming from. Alex
1011 # should really track down where the problem is coming from. Alex
988 # Schmolck reported this problem first.
1012 # Schmolck reported this problem first.
989
1013
990 # A useful post by Alex Martelli on this topic:
1014 # A useful post by Alex Martelli on this topic:
991 # Re: inconsistent value from __builtins__
1015 # Re: inconsistent value from __builtins__
992 # Von: Alex Martelli <aleaxit@yahoo.com>
1016 # Von: Alex Martelli <aleaxit@yahoo.com>
993 # Datum: Freitag 01 Oktober 2004 04:45:34 nachmittags/abends
1017 # Datum: Freitag 01 Oktober 2004 04:45:34 nachmittags/abends
994 # Gruppen: comp.lang.python
1018 # Gruppen: comp.lang.python
995
1019
996 # Michael Hohn <hohn@hooknose.lbl.gov> wrote:
1020 # Michael Hohn <hohn@hooknose.lbl.gov> wrote:
997 # > >>> print type(builtin_check.get_global_binding('__builtins__'))
1021 # > >>> print type(builtin_check.get_global_binding('__builtins__'))
998 # > <type 'dict'>
1022 # > <type 'dict'>
999 # > >>> print type(__builtins__)
1023 # > >>> print type(__builtins__)
1000 # > <type 'module'>
1024 # > <type 'module'>
1001 # > Is this difference in return value intentional?
1025 # > Is this difference in return value intentional?
1002
1026
1003 # Well, it's documented that '__builtins__' can be either a dictionary
1027 # Well, it's documented that '__builtins__' can be either a dictionary
1004 # or a module, and it's been that way for a long time. Whether it's
1028 # or a module, and it's been that way for a long time. Whether it's
1005 # intentional (or sensible), I don't know. In any case, the idea is
1029 # intentional (or sensible), I don't know. In any case, the idea is
1006 # that if you need to access the built-in namespace directly, you
1030 # that if you need to access the built-in namespace directly, you
1007 # should start with "import __builtin__" (note, no 's') which will
1031 # should start with "import __builtin__" (note, no 's') which will
1008 # definitely give you a module. Yeah, it's somewhat confusing:-(.
1032 # definitely give you a module. Yeah, it's somewhat confusing:-(.
1009
1033
1010 # These routines return a properly built module and dict as needed by
1034 # These routines return a properly built module and dict as needed by
1011 # the rest of the code, and can also be used by extension writers to
1035 # the rest of the code, and can also be used by extension writers to
1012 # generate properly initialized namespaces.
1036 # generate properly initialized namespaces.
1013 if (user_ns is not None) or (user_module is not None):
1037 if (user_ns is not None) or (user_module is not None):
1014 self.default_user_namespaces = False
1038 self.default_user_namespaces = False
1015 self.user_module, self.user_ns = self.prepare_user_module(user_module, user_ns)
1039 self.user_module, self.user_ns = self.prepare_user_module(user_module, user_ns)
1016
1040
1017 # A record of hidden variables we have added to the user namespace, so
1041 # A record of hidden variables we have added to the user namespace, so
1018 # we can list later only variables defined in actual interactive use.
1042 # we can list later only variables defined in actual interactive use.
1019 self.user_ns_hidden = {}
1043 self.user_ns_hidden = {}
1020
1044
1021 # Now that FakeModule produces a real module, we've run into a nasty
1045 # Now that FakeModule produces a real module, we've run into a nasty
1022 # problem: after script execution (via %run), the module where the user
1046 # problem: after script execution (via %run), the module where the user
1023 # code ran is deleted. Now that this object is a true module (needed
1047 # code ran is deleted. Now that this object is a true module (needed
1024 # so docetst and other tools work correctly), the Python module
1048 # so docetst and other tools work correctly), the Python module
1025 # teardown mechanism runs over it, and sets to None every variable
1049 # teardown mechanism runs over it, and sets to None every variable
1026 # present in that module. Top-level references to objects from the
1050 # present in that module. Top-level references to objects from the
1027 # script survive, because the user_ns is updated with them. However,
1051 # script survive, because the user_ns is updated with them. However,
1028 # calling functions defined in the script that use other things from
1052 # calling functions defined in the script that use other things from
1029 # the script will fail, because the function's closure had references
1053 # the script will fail, because the function's closure had references
1030 # to the original objects, which are now all None. So we must protect
1054 # to the original objects, which are now all None. So we must protect
1031 # these modules from deletion by keeping a cache.
1055 # these modules from deletion by keeping a cache.
1032 #
1056 #
1033 # To avoid keeping stale modules around (we only need the one from the
1057 # To avoid keeping stale modules around (we only need the one from the
1034 # last run), we use a dict keyed with the full path to the script, so
1058 # last run), we use a dict keyed with the full path to the script, so
1035 # only the last version of the module is held in the cache. Note,
1059 # only the last version of the module is held in the cache. Note,
1036 # however, that we must cache the module *namespace contents* (their
1060 # however, that we must cache the module *namespace contents* (their
1037 # __dict__). Because if we try to cache the actual modules, old ones
1061 # __dict__). Because if we try to cache the actual modules, old ones
1038 # (uncached) could be destroyed while still holding references (such as
1062 # (uncached) could be destroyed while still holding references (such as
1039 # those held by GUI objects that tend to be long-lived)>
1063 # those held by GUI objects that tend to be long-lived)>
1040 #
1064 #
1041 # The %reset command will flush this cache. See the cache_main_mod()
1065 # The %reset command will flush this cache. See the cache_main_mod()
1042 # and clear_main_mod_cache() methods for details on use.
1066 # and clear_main_mod_cache() methods for details on use.
1043
1067
1044 # This is the cache used for 'main' namespaces
1068 # This is the cache used for 'main' namespaces
1045 self._main_mod_cache = {}
1069 self._main_mod_cache = {}
1046
1070
1047 # A table holding all the namespaces IPython deals with, so that
1071 # A table holding all the namespaces IPython deals with, so that
1048 # introspection facilities can search easily.
1072 # introspection facilities can search easily.
1049 self.ns_table = {'user_global':self.user_module.__dict__,
1073 self.ns_table = {'user_global':self.user_module.__dict__,
1050 'user_local':self.user_ns,
1074 'user_local':self.user_ns,
1051 'builtin':builtin_mod.__dict__
1075 'builtin':builtin_mod.__dict__
1052 }
1076 }
1053
1077
1054 @property
1078 @property
1055 def user_global_ns(self):
1079 def user_global_ns(self):
1056 return self.user_module.__dict__
1080 return self.user_module.__dict__
1057
1081
1058 def prepare_user_module(self, user_module=None, user_ns=None):
1082 def prepare_user_module(self, user_module=None, user_ns=None):
1059 """Prepare the module and namespace in which user code will be run.
1083 """Prepare the module and namespace in which user code will be run.
1060
1084
1061 When IPython is started normally, both parameters are None: a new module
1085 When IPython is started normally, both parameters are None: a new module
1062 is created automatically, and its __dict__ used as the namespace.
1086 is created automatically, and its __dict__ used as the namespace.
1063
1087
1064 If only user_module is provided, its __dict__ is used as the namespace.
1088 If only user_module is provided, its __dict__ is used as the namespace.
1065 If only user_ns is provided, a dummy module is created, and user_ns
1089 If only user_ns is provided, a dummy module is created, and user_ns
1066 becomes the global namespace. If both are provided (as they may be
1090 becomes the global namespace. If both are provided (as they may be
1067 when embedding), user_ns is the local namespace, and user_module
1091 when embedding), user_ns is the local namespace, and user_module
1068 provides the global namespace.
1092 provides the global namespace.
1069
1093
1070 Parameters
1094 Parameters
1071 ----------
1095 ----------
1072 user_module : module, optional
1096 user_module : module, optional
1073 The current user module in which IPython is being run. If None,
1097 The current user module in which IPython is being run. If None,
1074 a clean module will be created.
1098 a clean module will be created.
1075 user_ns : dict, optional
1099 user_ns : dict, optional
1076 A namespace in which to run interactive commands.
1100 A namespace in which to run interactive commands.
1077
1101
1078 Returns
1102 Returns
1079 -------
1103 -------
1080 A tuple of user_module and user_ns, each properly initialised.
1104 A tuple of user_module and user_ns, each properly initialised.
1081 """
1105 """
1082 if user_module is None and user_ns is not None:
1106 if user_module is None and user_ns is not None:
1083 user_ns.setdefault("__name__", "__main__")
1107 user_ns.setdefault("__name__", "__main__")
1084 user_module = DummyMod()
1108 user_module = DummyMod()
1085 user_module.__dict__ = user_ns
1109 user_module.__dict__ = user_ns
1086
1110
1087 if user_module is None:
1111 if user_module is None:
1088 user_module = types.ModuleType("__main__",
1112 user_module = types.ModuleType("__main__",
1089 doc="Automatically created module for IPython interactive environment")
1113 doc="Automatically created module for IPython interactive environment")
1090
1114
1091 # We must ensure that __builtin__ (without the final 's') is always
1115 # We must ensure that __builtin__ (without the final 's') is always
1092 # available and pointing to the __builtin__ *module*. For more details:
1116 # available and pointing to the __builtin__ *module*. For more details:
1093 # http://mail.python.org/pipermail/python-dev/2001-April/014068.html
1117 # http://mail.python.org/pipermail/python-dev/2001-April/014068.html
1094 user_module.__dict__.setdefault('__builtin__', builtin_mod)
1118 user_module.__dict__.setdefault('__builtin__', builtin_mod)
1095 user_module.__dict__.setdefault('__builtins__', builtin_mod)
1119 user_module.__dict__.setdefault('__builtins__', builtin_mod)
1096
1120
1097 if user_ns is None:
1121 if user_ns is None:
1098 user_ns = user_module.__dict__
1122 user_ns = user_module.__dict__
1099
1123
1100 return user_module, user_ns
1124 return user_module, user_ns
1101
1125
1102 def init_sys_modules(self):
1126 def init_sys_modules(self):
1103 # We need to insert into sys.modules something that looks like a
1127 # We need to insert into sys.modules something that looks like a
1104 # module but which accesses the IPython namespace, for shelve and
1128 # module but which accesses the IPython namespace, for shelve and
1105 # pickle to work interactively. Normally they rely on getting
1129 # pickle to work interactively. Normally they rely on getting
1106 # everything out of __main__, but for embedding purposes each IPython
1130 # everything out of __main__, but for embedding purposes each IPython
1107 # instance has its own private namespace, so we can't go shoving
1131 # instance has its own private namespace, so we can't go shoving
1108 # everything into __main__.
1132 # everything into __main__.
1109
1133
1110 # note, however, that we should only do this for non-embedded
1134 # note, however, that we should only do this for non-embedded
1111 # ipythons, which really mimic the __main__.__dict__ with their own
1135 # ipythons, which really mimic the __main__.__dict__ with their own
1112 # namespace. Embedded instances, on the other hand, should not do
1136 # namespace. Embedded instances, on the other hand, should not do
1113 # this because they need to manage the user local/global namespaces
1137 # this because they need to manage the user local/global namespaces
1114 # only, but they live within a 'normal' __main__ (meaning, they
1138 # only, but they live within a 'normal' __main__ (meaning, they
1115 # shouldn't overtake the execution environment of the script they're
1139 # shouldn't overtake the execution environment of the script they're
1116 # embedded in).
1140 # embedded in).
1117
1141
1118 # This is overridden in the InteractiveShellEmbed subclass to a no-op.
1142 # This is overridden in the InteractiveShellEmbed subclass to a no-op.
1119 main_name = self.user_module.__name__
1143 main_name = self.user_module.__name__
1120 sys.modules[main_name] = self.user_module
1144 sys.modules[main_name] = self.user_module
1121
1145
1122 def init_user_ns(self):
1146 def init_user_ns(self):
1123 """Initialize all user-visible namespaces to their minimum defaults.
1147 """Initialize all user-visible namespaces to their minimum defaults.
1124
1148
1125 Certain history lists are also initialized here, as they effectively
1149 Certain history lists are also initialized here, as they effectively
1126 act as user namespaces.
1150 act as user namespaces.
1127
1151
1128 Notes
1152 Notes
1129 -----
1153 -----
1130 All data structures here are only filled in, they are NOT reset by this
1154 All data structures here are only filled in, they are NOT reset by this
1131 method. If they were not empty before, data will simply be added to
1155 method. If they were not empty before, data will simply be added to
1132 therm.
1156 therm.
1133 """
1157 """
1134 # This function works in two parts: first we put a few things in
1158 # This function works in two parts: first we put a few things in
1135 # user_ns, and we sync that contents into user_ns_hidden so that these
1159 # user_ns, and we sync that contents into user_ns_hidden so that these
1136 # initial variables aren't shown by %who. After the sync, we add the
1160 # initial variables aren't shown by %who. After the sync, we add the
1137 # rest of what we *do* want the user to see with %who even on a new
1161 # rest of what we *do* want the user to see with %who even on a new
1138 # session (probably nothing, so theye really only see their own stuff)
1162 # session (probably nothing, so theye really only see their own stuff)
1139
1163
1140 # The user dict must *always* have a __builtin__ reference to the
1164 # The user dict must *always* have a __builtin__ reference to the
1141 # Python standard __builtin__ namespace, which must be imported.
1165 # Python standard __builtin__ namespace, which must be imported.
1142 # This is so that certain operations in prompt evaluation can be
1166 # This is so that certain operations in prompt evaluation can be
1143 # reliably executed with builtins. Note that we can NOT use
1167 # reliably executed with builtins. Note that we can NOT use
1144 # __builtins__ (note the 's'), because that can either be a dict or a
1168 # __builtins__ (note the 's'), because that can either be a dict or a
1145 # module, and can even mutate at runtime, depending on the context
1169 # module, and can even mutate at runtime, depending on the context
1146 # (Python makes no guarantees on it). In contrast, __builtin__ is
1170 # (Python makes no guarantees on it). In contrast, __builtin__ is
1147 # always a module object, though it must be explicitly imported.
1171 # always a module object, though it must be explicitly imported.
1148
1172
1149 # For more details:
1173 # For more details:
1150 # http://mail.python.org/pipermail/python-dev/2001-April/014068.html
1174 # http://mail.python.org/pipermail/python-dev/2001-April/014068.html
1151 ns = dict()
1175 ns = dict()
1152
1176
1153 # make global variables for user access to the histories
1177 # make global variables for user access to the histories
1154 ns['_ih'] = self.history_manager.input_hist_parsed
1178 ns['_ih'] = self.history_manager.input_hist_parsed
1155 ns['_oh'] = self.history_manager.output_hist
1179 ns['_oh'] = self.history_manager.output_hist
1156 ns['_dh'] = self.history_manager.dir_hist
1180 ns['_dh'] = self.history_manager.dir_hist
1157
1181
1158 ns['_sh'] = shadowns
1182 ns['_sh'] = shadowns
1159
1183
1160 # user aliases to input and output histories. These shouldn't show up
1184 # user aliases to input and output histories. These shouldn't show up
1161 # in %who, as they can have very large reprs.
1185 # in %who, as they can have very large reprs.
1162 ns['In'] = self.history_manager.input_hist_parsed
1186 ns['In'] = self.history_manager.input_hist_parsed
1163 ns['Out'] = self.history_manager.output_hist
1187 ns['Out'] = self.history_manager.output_hist
1164
1188
1165 # Store myself as the public api!!!
1189 # Store myself as the public api!!!
1166 ns['get_ipython'] = self.get_ipython
1190 ns['get_ipython'] = self.get_ipython
1167
1191
1168 ns['exit'] = self.exiter
1192 ns['exit'] = self.exiter
1169 ns['quit'] = self.exiter
1193 ns['quit'] = self.exiter
1170
1194
1171 # Sync what we've added so far to user_ns_hidden so these aren't seen
1195 # Sync what we've added so far to user_ns_hidden so these aren't seen
1172 # by %who
1196 # by %who
1173 self.user_ns_hidden.update(ns)
1197 self.user_ns_hidden.update(ns)
1174
1198
1175 # Anything put into ns now would show up in %who. Think twice before
1199 # Anything put into ns now would show up in %who. Think twice before
1176 # putting anything here, as we really want %who to show the user their
1200 # putting anything here, as we really want %who to show the user their
1177 # stuff, not our variables.
1201 # stuff, not our variables.
1178
1202
1179 # Finally, update the real user's namespace
1203 # Finally, update the real user's namespace
1180 self.user_ns.update(ns)
1204 self.user_ns.update(ns)
1181
1205
1182 @property
1206 @property
1183 def all_ns_refs(self):
1207 def all_ns_refs(self):
1184 """Get a list of references to all the namespace dictionaries in which
1208 """Get a list of references to all the namespace dictionaries in which
1185 IPython might store a user-created object.
1209 IPython might store a user-created object.
1186
1210
1187 Note that this does not include the displayhook, which also caches
1211 Note that this does not include the displayhook, which also caches
1188 objects from the output."""
1212 objects from the output."""
1189 return [self.user_ns, self.user_global_ns, self.user_ns_hidden] + \
1213 return [self.user_ns, self.user_global_ns, self.user_ns_hidden] + \
1190 [m.__dict__ for m in self._main_mod_cache.values()]
1214 [m.__dict__ for m in self._main_mod_cache.values()]
1191
1215
1192 def reset(self, new_session=True):
1216 def reset(self, new_session=True):
1193 """Clear all internal namespaces, and attempt to release references to
1217 """Clear all internal namespaces, and attempt to release references to
1194 user objects.
1218 user objects.
1195
1219
1196 If new_session is True, a new history session will be opened.
1220 If new_session is True, a new history session will be opened.
1197 """
1221 """
1198 # Clear histories
1222 # Clear histories
1199 self.history_manager.reset(new_session)
1223 self.history_manager.reset(new_session)
1200 # Reset counter used to index all histories
1224 # Reset counter used to index all histories
1201 if new_session:
1225 if new_session:
1202 self.execution_count = 1
1226 self.execution_count = 1
1203
1227
1204 # Flush cached output items
1228 # Flush cached output items
1205 if self.displayhook.do_full_cache:
1229 if self.displayhook.do_full_cache:
1206 self.displayhook.flush()
1230 self.displayhook.flush()
1207
1231
1208 # The main execution namespaces must be cleared very carefully,
1232 # The main execution namespaces must be cleared very carefully,
1209 # skipping the deletion of the builtin-related keys, because doing so
1233 # skipping the deletion of the builtin-related keys, because doing so
1210 # would cause errors in many object's __del__ methods.
1234 # would cause errors in many object's __del__ methods.
1211 if self.user_ns is not self.user_global_ns:
1235 if self.user_ns is not self.user_global_ns:
1212 self.user_ns.clear()
1236 self.user_ns.clear()
1213 ns = self.user_global_ns
1237 ns = self.user_global_ns
1214 drop_keys = set(ns.keys())
1238 drop_keys = set(ns.keys())
1215 drop_keys.discard('__builtin__')
1239 drop_keys.discard('__builtin__')
1216 drop_keys.discard('__builtins__')
1240 drop_keys.discard('__builtins__')
1217 drop_keys.discard('__name__')
1241 drop_keys.discard('__name__')
1218 for k in drop_keys:
1242 for k in drop_keys:
1219 del ns[k]
1243 del ns[k]
1220
1244
1221 self.user_ns_hidden.clear()
1245 self.user_ns_hidden.clear()
1222
1246
1223 # Restore the user namespaces to minimal usability
1247 # Restore the user namespaces to minimal usability
1224 self.init_user_ns()
1248 self.init_user_ns()
1225
1249
1226 # Restore the default and user aliases
1250 # Restore the default and user aliases
1227 self.alias_manager.clear_aliases()
1251 self.alias_manager.clear_aliases()
1228 self.alias_manager.init_aliases()
1252 self.alias_manager.init_aliases()
1229
1253
1230 # Flush the private list of module references kept for script
1254 # Flush the private list of module references kept for script
1231 # execution protection
1255 # execution protection
1232 self.clear_main_mod_cache()
1256 self.clear_main_mod_cache()
1233
1257
1234 def del_var(self, varname, by_name=False):
1258 def del_var(self, varname, by_name=False):
1235 """Delete a variable from the various namespaces, so that, as
1259 """Delete a variable from the various namespaces, so that, as
1236 far as possible, we're not keeping any hidden references to it.
1260 far as possible, we're not keeping any hidden references to it.
1237
1261
1238 Parameters
1262 Parameters
1239 ----------
1263 ----------
1240 varname : str
1264 varname : str
1241 The name of the variable to delete.
1265 The name of the variable to delete.
1242 by_name : bool
1266 by_name : bool
1243 If True, delete variables with the given name in each
1267 If True, delete variables with the given name in each
1244 namespace. If False (default), find the variable in the user
1268 namespace. If False (default), find the variable in the user
1245 namespace, and delete references to it.
1269 namespace, and delete references to it.
1246 """
1270 """
1247 if varname in ('__builtin__', '__builtins__'):
1271 if varname in ('__builtin__', '__builtins__'):
1248 raise ValueError("Refusing to delete %s" % varname)
1272 raise ValueError("Refusing to delete %s" % varname)
1249
1273
1250 ns_refs = self.all_ns_refs
1274 ns_refs = self.all_ns_refs
1251
1275
1252 if by_name: # Delete by name
1276 if by_name: # Delete by name
1253 for ns in ns_refs:
1277 for ns in ns_refs:
1254 try:
1278 try:
1255 del ns[varname]
1279 del ns[varname]
1256 except KeyError:
1280 except KeyError:
1257 pass
1281 pass
1258 else: # Delete by object
1282 else: # Delete by object
1259 try:
1283 try:
1260 obj = self.user_ns[varname]
1284 obj = self.user_ns[varname]
1261 except KeyError:
1285 except KeyError:
1262 raise NameError("name '%s' is not defined" % varname)
1286 raise NameError("name '%s' is not defined" % varname)
1263 # Also check in output history
1287 # Also check in output history
1264 ns_refs.append(self.history_manager.output_hist)
1288 ns_refs.append(self.history_manager.output_hist)
1265 for ns in ns_refs:
1289 for ns in ns_refs:
1266 to_delete = [n for n, o in iteritems(ns) if o is obj]
1290 to_delete = [n for n, o in iteritems(ns) if o is obj]
1267 for name in to_delete:
1291 for name in to_delete:
1268 del ns[name]
1292 del ns[name]
1269
1293
1270 # displayhook keeps extra references, but not in a dictionary
1294 # displayhook keeps extra references, but not in a dictionary
1271 for name in ('_', '__', '___'):
1295 for name in ('_', '__', '___'):
1272 if getattr(self.displayhook, name) is obj:
1296 if getattr(self.displayhook, name) is obj:
1273 setattr(self.displayhook, name, None)
1297 setattr(self.displayhook, name, None)
1274
1298
1275 def reset_selective(self, regex=None):
1299 def reset_selective(self, regex=None):
1276 """Clear selective variables from internal namespaces based on a
1300 """Clear selective variables from internal namespaces based on a
1277 specified regular expression.
1301 specified regular expression.
1278
1302
1279 Parameters
1303 Parameters
1280 ----------
1304 ----------
1281 regex : string or compiled pattern, optional
1305 regex : string or compiled pattern, optional
1282 A regular expression pattern that will be used in searching
1306 A regular expression pattern that will be used in searching
1283 variable names in the users namespaces.
1307 variable names in the users namespaces.
1284 """
1308 """
1285 if regex is not None:
1309 if regex is not None:
1286 try:
1310 try:
1287 m = re.compile(regex)
1311 m = re.compile(regex)
1288 except TypeError:
1312 except TypeError:
1289 raise TypeError('regex must be a string or compiled pattern')
1313 raise TypeError('regex must be a string or compiled pattern')
1290 # Search for keys in each namespace that match the given regex
1314 # Search for keys in each namespace that match the given regex
1291 # If a match is found, delete the key/value pair.
1315 # If a match is found, delete the key/value pair.
1292 for ns in self.all_ns_refs:
1316 for ns in self.all_ns_refs:
1293 for var in ns:
1317 for var in ns:
1294 if m.search(var):
1318 if m.search(var):
1295 del ns[var]
1319 del ns[var]
1296
1320
1297 def push(self, variables, interactive=True):
1321 def push(self, variables, interactive=True):
1298 """Inject a group of variables into the IPython user namespace.
1322 """Inject a group of variables into the IPython user namespace.
1299
1323
1300 Parameters
1324 Parameters
1301 ----------
1325 ----------
1302 variables : dict, str or list/tuple of str
1326 variables : dict, str or list/tuple of str
1303 The variables to inject into the user's namespace. If a dict, a
1327 The variables to inject into the user's namespace. If a dict, a
1304 simple update is done. If a str, the string is assumed to have
1328 simple update is done. If a str, the string is assumed to have
1305 variable names separated by spaces. A list/tuple of str can also
1329 variable names separated by spaces. A list/tuple of str can also
1306 be used to give the variable names. If just the variable names are
1330 be used to give the variable names. If just the variable names are
1307 give (list/tuple/str) then the variable values looked up in the
1331 give (list/tuple/str) then the variable values looked up in the
1308 callers frame.
1332 callers frame.
1309 interactive : bool
1333 interactive : bool
1310 If True (default), the variables will be listed with the ``who``
1334 If True (default), the variables will be listed with the ``who``
1311 magic.
1335 magic.
1312 """
1336 """
1313 vdict = None
1337 vdict = None
1314
1338
1315 # We need a dict of name/value pairs to do namespace updates.
1339 # We need a dict of name/value pairs to do namespace updates.
1316 if isinstance(variables, dict):
1340 if isinstance(variables, dict):
1317 vdict = variables
1341 vdict = variables
1318 elif isinstance(variables, string_types+(list, tuple)):
1342 elif isinstance(variables, string_types+(list, tuple)):
1319 if isinstance(variables, string_types):
1343 if isinstance(variables, string_types):
1320 vlist = variables.split()
1344 vlist = variables.split()
1321 else:
1345 else:
1322 vlist = variables
1346 vlist = variables
1323 vdict = {}
1347 vdict = {}
1324 cf = sys._getframe(1)
1348 cf = sys._getframe(1)
1325 for name in vlist:
1349 for name in vlist:
1326 try:
1350 try:
1327 vdict[name] = eval(name, cf.f_globals, cf.f_locals)
1351 vdict[name] = eval(name, cf.f_globals, cf.f_locals)
1328 except:
1352 except:
1329 print('Could not get variable %s from %s' %
1353 print('Could not get variable %s from %s' %
1330 (name,cf.f_code.co_name))
1354 (name,cf.f_code.co_name))
1331 else:
1355 else:
1332 raise ValueError('variables must be a dict/str/list/tuple')
1356 raise ValueError('variables must be a dict/str/list/tuple')
1333
1357
1334 # Propagate variables to user namespace
1358 # Propagate variables to user namespace
1335 self.user_ns.update(vdict)
1359 self.user_ns.update(vdict)
1336
1360
1337 # And configure interactive visibility
1361 # And configure interactive visibility
1338 user_ns_hidden = self.user_ns_hidden
1362 user_ns_hidden = self.user_ns_hidden
1339 if interactive:
1363 if interactive:
1340 for name in vdict:
1364 for name in vdict:
1341 user_ns_hidden.pop(name, None)
1365 user_ns_hidden.pop(name, None)
1342 else:
1366 else:
1343 user_ns_hidden.update(vdict)
1367 user_ns_hidden.update(vdict)
1344
1368
1345 def drop_by_id(self, variables):
1369 def drop_by_id(self, variables):
1346 """Remove a dict of variables from the user namespace, if they are the
1370 """Remove a dict of variables from the user namespace, if they are the
1347 same as the values in the dictionary.
1371 same as the values in the dictionary.
1348
1372
1349 This is intended for use by extensions: variables that they've added can
1373 This is intended for use by extensions: variables that they've added can
1350 be taken back out if they are unloaded, without removing any that the
1374 be taken back out if they are unloaded, without removing any that the
1351 user has overwritten.
1375 user has overwritten.
1352
1376
1353 Parameters
1377 Parameters
1354 ----------
1378 ----------
1355 variables : dict
1379 variables : dict
1356 A dictionary mapping object names (as strings) to the objects.
1380 A dictionary mapping object names (as strings) to the objects.
1357 """
1381 """
1358 for name, obj in iteritems(variables):
1382 for name, obj in iteritems(variables):
1359 if name in self.user_ns and self.user_ns[name] is obj:
1383 if name in self.user_ns and self.user_ns[name] is obj:
1360 del self.user_ns[name]
1384 del self.user_ns[name]
1361 self.user_ns_hidden.pop(name, None)
1385 self.user_ns_hidden.pop(name, None)
1362
1386
1363 #-------------------------------------------------------------------------
1387 #-------------------------------------------------------------------------
1364 # Things related to object introspection
1388 # Things related to object introspection
1365 #-------------------------------------------------------------------------
1389 #-------------------------------------------------------------------------
1366
1390
1367 def _ofind(self, oname, namespaces=None):
1391 def _ofind(self, oname, namespaces=None):
1368 """Find an object in the available namespaces.
1392 """Find an object in the available namespaces.
1369
1393
1370 self._ofind(oname) -> dict with keys: found,obj,ospace,ismagic
1394 self._ofind(oname) -> dict with keys: found,obj,ospace,ismagic
1371
1395
1372 Has special code to detect magic functions.
1396 Has special code to detect magic functions.
1373 """
1397 """
1374 oname = oname.strip()
1398 oname = oname.strip()
1375 #print '1- oname: <%r>' % oname # dbg
1399 #print '1- oname: <%r>' % oname # dbg
1376 if not oname.startswith(ESC_MAGIC) and \
1400 if not oname.startswith(ESC_MAGIC) and \
1377 not oname.startswith(ESC_MAGIC2) and \
1401 not oname.startswith(ESC_MAGIC2) and \
1378 not py3compat.isidentifier(oname, dotted=True):
1402 not py3compat.isidentifier(oname, dotted=True):
1379 return dict(found=False)
1403 return dict(found=False)
1380
1404
1381 alias_ns = None
1405 alias_ns = None
1382 if namespaces is None:
1406 if namespaces is None:
1383 # Namespaces to search in:
1407 # Namespaces to search in:
1384 # Put them in a list. The order is important so that we
1408 # Put them in a list. The order is important so that we
1385 # find things in the same order that Python finds them.
1409 # find things in the same order that Python finds them.
1386 namespaces = [ ('Interactive', self.user_ns),
1410 namespaces = [ ('Interactive', self.user_ns),
1387 ('Interactive (global)', self.user_global_ns),
1411 ('Interactive (global)', self.user_global_ns),
1388 ('Python builtin', builtin_mod.__dict__),
1412 ('Python builtin', builtin_mod.__dict__),
1389 ]
1413 ]
1390
1414
1391 # initialize results to 'null'
1415 # initialize results to 'null'
1392 found = False; obj = None; ospace = None; ds = None;
1416 found = False; obj = None; ospace = None; ds = None;
1393 ismagic = False; isalias = False; parent = None
1417 ismagic = False; isalias = False; parent = None
1394
1418
1395 # We need to special-case 'print', which as of python2.6 registers as a
1419 # We need to special-case 'print', which as of python2.6 registers as a
1396 # function but should only be treated as one if print_function was
1420 # function but should only be treated as one if print_function was
1397 # loaded with a future import. In this case, just bail.
1421 # loaded with a future import. In this case, just bail.
1398 if (oname == 'print' and not py3compat.PY3 and not \
1422 if (oname == 'print' and not py3compat.PY3 and not \
1399 (self.compile.compiler_flags & __future__.CO_FUTURE_PRINT_FUNCTION)):
1423 (self.compile.compiler_flags & __future__.CO_FUTURE_PRINT_FUNCTION)):
1400 return {'found':found, 'obj':obj, 'namespace':ospace,
1424 return {'found':found, 'obj':obj, 'namespace':ospace,
1401 'ismagic':ismagic, 'isalias':isalias, 'parent':parent}
1425 'ismagic':ismagic, 'isalias':isalias, 'parent':parent}
1402
1426
1403 # Look for the given name by splitting it in parts. If the head is
1427 # Look for the given name by splitting it in parts. If the head is
1404 # found, then we look for all the remaining parts as members, and only
1428 # found, then we look for all the remaining parts as members, and only
1405 # declare success if we can find them all.
1429 # declare success if we can find them all.
1406 oname_parts = oname.split('.')
1430 oname_parts = oname.split('.')
1407 oname_head, oname_rest = oname_parts[0],oname_parts[1:]
1431 oname_head, oname_rest = oname_parts[0],oname_parts[1:]
1408 for nsname,ns in namespaces:
1432 for nsname,ns in namespaces:
1409 try:
1433 try:
1410 obj = ns[oname_head]
1434 obj = ns[oname_head]
1411 except KeyError:
1435 except KeyError:
1412 continue
1436 continue
1413 else:
1437 else:
1414 #print 'oname_rest:', oname_rest # dbg
1438 #print 'oname_rest:', oname_rest # dbg
1415 for part in oname_rest:
1439 for part in oname_rest:
1416 try:
1440 try:
1417 parent = obj
1441 parent = obj
1418 obj = getattr(obj,part)
1442 obj = getattr(obj,part)
1419 except:
1443 except:
1420 # Blanket except b/c some badly implemented objects
1444 # Blanket except b/c some badly implemented objects
1421 # allow __getattr__ to raise exceptions other than
1445 # allow __getattr__ to raise exceptions other than
1422 # AttributeError, which then crashes IPython.
1446 # AttributeError, which then crashes IPython.
1423 break
1447 break
1424 else:
1448 else:
1425 # If we finish the for loop (no break), we got all members
1449 # If we finish the for loop (no break), we got all members
1426 found = True
1450 found = True
1427 ospace = nsname
1451 ospace = nsname
1428 break # namespace loop
1452 break # namespace loop
1429
1453
1430 # Try to see if it's magic
1454 # Try to see if it's magic
1431 if not found:
1455 if not found:
1432 obj = None
1456 obj = None
1433 if oname.startswith(ESC_MAGIC2):
1457 if oname.startswith(ESC_MAGIC2):
1434 oname = oname.lstrip(ESC_MAGIC2)
1458 oname = oname.lstrip(ESC_MAGIC2)
1435 obj = self.find_cell_magic(oname)
1459 obj = self.find_cell_magic(oname)
1436 elif oname.startswith(ESC_MAGIC):
1460 elif oname.startswith(ESC_MAGIC):
1437 oname = oname.lstrip(ESC_MAGIC)
1461 oname = oname.lstrip(ESC_MAGIC)
1438 obj = self.find_line_magic(oname)
1462 obj = self.find_line_magic(oname)
1439 else:
1463 else:
1440 # search without prefix, so run? will find %run?
1464 # search without prefix, so run? will find %run?
1441 obj = self.find_line_magic(oname)
1465 obj = self.find_line_magic(oname)
1442 if obj is None:
1466 if obj is None:
1443 obj = self.find_cell_magic(oname)
1467 obj = self.find_cell_magic(oname)
1444 if obj is not None:
1468 if obj is not None:
1445 found = True
1469 found = True
1446 ospace = 'IPython internal'
1470 ospace = 'IPython internal'
1447 ismagic = True
1471 ismagic = True
1448
1472
1449 # Last try: special-case some literals like '', [], {}, etc:
1473 # Last try: special-case some literals like '', [], {}, etc:
1450 if not found and oname_head in ["''",'""','[]','{}','()']:
1474 if not found and oname_head in ["''",'""','[]','{}','()']:
1451 obj = eval(oname_head)
1475 obj = eval(oname_head)
1452 found = True
1476 found = True
1453 ospace = 'Interactive'
1477 ospace = 'Interactive'
1454
1478
1455 return {'found':found, 'obj':obj, 'namespace':ospace,
1479 return {'found':found, 'obj':obj, 'namespace':ospace,
1456 'ismagic':ismagic, 'isalias':isalias, 'parent':parent}
1480 'ismagic':ismagic, 'isalias':isalias, 'parent':parent}
1457
1481
1458 def _ofind_property(self, oname, info):
1482 def _ofind_property(self, oname, info):
1459 """Second part of object finding, to look for property details."""
1483 """Second part of object finding, to look for property details."""
1460 if info.found:
1484 if info.found:
1461 # Get the docstring of the class property if it exists.
1485 # Get the docstring of the class property if it exists.
1462 path = oname.split('.')
1486 path = oname.split('.')
1463 root = '.'.join(path[:-1])
1487 root = '.'.join(path[:-1])
1464 if info.parent is not None:
1488 if info.parent is not None:
1465 try:
1489 try:
1466 target = getattr(info.parent, '__class__')
1490 target = getattr(info.parent, '__class__')
1467 # The object belongs to a class instance.
1491 # The object belongs to a class instance.
1468 try:
1492 try:
1469 target = getattr(target, path[-1])
1493 target = getattr(target, path[-1])
1470 # The class defines the object.
1494 # The class defines the object.
1471 if isinstance(target, property):
1495 if isinstance(target, property):
1472 oname = root + '.__class__.' + path[-1]
1496 oname = root + '.__class__.' + path[-1]
1473 info = Struct(self._ofind(oname))
1497 info = Struct(self._ofind(oname))
1474 except AttributeError: pass
1498 except AttributeError: pass
1475 except AttributeError: pass
1499 except AttributeError: pass
1476
1500
1477 # We return either the new info or the unmodified input if the object
1501 # We return either the new info or the unmodified input if the object
1478 # hadn't been found
1502 # hadn't been found
1479 return info
1503 return info
1480
1504
1481 def _object_find(self, oname, namespaces=None):
1505 def _object_find(self, oname, namespaces=None):
1482 """Find an object and return a struct with info about it."""
1506 """Find an object and return a struct with info about it."""
1483 inf = Struct(self._ofind(oname, namespaces))
1507 inf = Struct(self._ofind(oname, namespaces))
1484 return Struct(self._ofind_property(oname, inf))
1508 return Struct(self._ofind_property(oname, inf))
1485
1509
1486 def _inspect(self, meth, oname, namespaces=None, **kw):
1510 def _inspect(self, meth, oname, namespaces=None, **kw):
1487 """Generic interface to the inspector system.
1511 """Generic interface to the inspector system.
1488
1512
1489 This function is meant to be called by pdef, pdoc & friends."""
1513 This function is meant to be called by pdef, pdoc & friends."""
1490 info = self._object_find(oname, namespaces)
1514 info = self._object_find(oname, namespaces)
1491 if info.found:
1515 if info.found:
1492 pmethod = getattr(self.inspector, meth)
1516 pmethod = getattr(self.inspector, meth)
1493 formatter = format_screen if info.ismagic else None
1517 formatter = format_screen if info.ismagic else None
1494 if meth == 'pdoc':
1518 if meth == 'pdoc':
1495 pmethod(info.obj, oname, formatter)
1519 pmethod(info.obj, oname, formatter)
1496 elif meth == 'pinfo':
1520 elif meth == 'pinfo':
1497 pmethod(info.obj, oname, formatter, info, **kw)
1521 pmethod(info.obj, oname, formatter, info, **kw)
1498 else:
1522 else:
1499 pmethod(info.obj, oname)
1523 pmethod(info.obj, oname)
1500 else:
1524 else:
1501 print('Object `%s` not found.' % oname)
1525 print('Object `%s` not found.' % oname)
1502 return 'not found' # so callers can take other action
1526 return 'not found' # so callers can take other action
1503
1527
1504 def object_inspect(self, oname, detail_level=0):
1528 def object_inspect(self, oname, detail_level=0):
1529 """Get object info about oname"""
1505 with self.builtin_trap:
1530 with self.builtin_trap:
1506 info = self._object_find(oname)
1531 info = self._object_find(oname)
1507 if info.found:
1532 if info.found:
1508 return self.inspector.info(info.obj, oname, info=info,
1533 return self.inspector.info(info.obj, oname, info=info,
1509 detail_level=detail_level
1534 detail_level=detail_level
1510 )
1535 )
1511 else:
1536 else:
1512 return oinspect.object_info(name=oname, found=False)
1537 return oinspect.object_info(name=oname, found=False)
1513
1538
1539 def object_inspect_text(self, oname, detail_level=0):
1540 """Get object info as formatted text"""
1541 with self.builtin_trap:
1542 info = self._object_find(oname)
1543 if info.found:
1544 return self.inspector._format_info(info.obj, oname, info=info,
1545 detail_level=detail_level
1546 )
1547 else:
1548 raise KeyError(oname)
1549
1514 #-------------------------------------------------------------------------
1550 #-------------------------------------------------------------------------
1515 # Things related to history management
1551 # Things related to history management
1516 #-------------------------------------------------------------------------
1552 #-------------------------------------------------------------------------
1517
1553
1518 def init_history(self):
1554 def init_history(self):
1519 """Sets up the command history, and starts regular autosaves."""
1555 """Sets up the command history, and starts regular autosaves."""
1520 self.history_manager = HistoryManager(shell=self, parent=self)
1556 self.history_manager = HistoryManager(shell=self, parent=self)
1521 self.configurables.append(self.history_manager)
1557 self.configurables.append(self.history_manager)
1522
1558
1523 #-------------------------------------------------------------------------
1559 #-------------------------------------------------------------------------
1524 # Things related to exception handling and tracebacks (not debugging)
1560 # Things related to exception handling and tracebacks (not debugging)
1525 #-------------------------------------------------------------------------
1561 #-------------------------------------------------------------------------
1526
1562
1527 def init_traceback_handlers(self, custom_exceptions):
1563 def init_traceback_handlers(self, custom_exceptions):
1528 # Syntax error handler.
1564 # Syntax error handler.
1529 self.SyntaxTB = ultratb.SyntaxTB(color_scheme='NoColor')
1565 self.SyntaxTB = ultratb.SyntaxTB(color_scheme='NoColor')
1530
1566
1531 # The interactive one is initialized with an offset, meaning we always
1567 # The interactive one is initialized with an offset, meaning we always
1532 # want to remove the topmost item in the traceback, which is our own
1568 # want to remove the topmost item in the traceback, which is our own
1533 # internal code. Valid modes: ['Plain','Context','Verbose']
1569 # internal code. Valid modes: ['Plain','Context','Verbose']
1534 self.InteractiveTB = ultratb.AutoFormattedTB(mode = 'Plain',
1570 self.InteractiveTB = ultratb.AutoFormattedTB(mode = 'Plain',
1535 color_scheme='NoColor',
1571 color_scheme='NoColor',
1536 tb_offset = 1,
1572 tb_offset = 1,
1537 check_cache=check_linecache_ipython)
1573 check_cache=check_linecache_ipython)
1538
1574
1539 # The instance will store a pointer to the system-wide exception hook,
1575 # The instance will store a pointer to the system-wide exception hook,
1540 # so that runtime code (such as magics) can access it. This is because
1576 # so that runtime code (such as magics) can access it. This is because
1541 # during the read-eval loop, it may get temporarily overwritten.
1577 # during the read-eval loop, it may get temporarily overwritten.
1542 self.sys_excepthook = sys.excepthook
1578 self.sys_excepthook = sys.excepthook
1543
1579
1544 # and add any custom exception handlers the user may have specified
1580 # and add any custom exception handlers the user may have specified
1545 self.set_custom_exc(*custom_exceptions)
1581 self.set_custom_exc(*custom_exceptions)
1546
1582
1547 # Set the exception mode
1583 # Set the exception mode
1548 self.InteractiveTB.set_mode(mode=self.xmode)
1584 self.InteractiveTB.set_mode(mode=self.xmode)
1549
1585
1550 def set_custom_exc(self, exc_tuple, handler):
1586 def set_custom_exc(self, exc_tuple, handler):
1551 """set_custom_exc(exc_tuple,handler)
1587 """set_custom_exc(exc_tuple,handler)
1552
1588
1553 Set a custom exception handler, which will be called if any of the
1589 Set a custom exception handler, which will be called if any of the
1554 exceptions in exc_tuple occur in the mainloop (specifically, in the
1590 exceptions in exc_tuple occur in the mainloop (specifically, in the
1555 run_code() method).
1591 run_code() method).
1556
1592
1557 Parameters
1593 Parameters
1558 ----------
1594 ----------
1559
1595
1560 exc_tuple : tuple of exception classes
1596 exc_tuple : tuple of exception classes
1561 A *tuple* of exception classes, for which to call the defined
1597 A *tuple* of exception classes, for which to call the defined
1562 handler. It is very important that you use a tuple, and NOT A
1598 handler. It is very important that you use a tuple, and NOT A
1563 LIST here, because of the way Python's except statement works. If
1599 LIST here, because of the way Python's except statement works. If
1564 you only want to trap a single exception, use a singleton tuple::
1600 you only want to trap a single exception, use a singleton tuple::
1565
1601
1566 exc_tuple == (MyCustomException,)
1602 exc_tuple == (MyCustomException,)
1567
1603
1568 handler : callable
1604 handler : callable
1569 handler must have the following signature::
1605 handler must have the following signature::
1570
1606
1571 def my_handler(self, etype, value, tb, tb_offset=None):
1607 def my_handler(self, etype, value, tb, tb_offset=None):
1572 ...
1608 ...
1573 return structured_traceback
1609 return structured_traceback
1574
1610
1575 Your handler must return a structured traceback (a list of strings),
1611 Your handler must return a structured traceback (a list of strings),
1576 or None.
1612 or None.
1577
1613
1578 This will be made into an instance method (via types.MethodType)
1614 This will be made into an instance method (via types.MethodType)
1579 of IPython itself, and it will be called if any of the exceptions
1615 of IPython itself, and it will be called if any of the exceptions
1580 listed in the exc_tuple are caught. If the handler is None, an
1616 listed in the exc_tuple are caught. If the handler is None, an
1581 internal basic one is used, which just prints basic info.
1617 internal basic one is used, which just prints basic info.
1582
1618
1583 To protect IPython from crashes, if your handler ever raises an
1619 To protect IPython from crashes, if your handler ever raises an
1584 exception or returns an invalid result, it will be immediately
1620 exception or returns an invalid result, it will be immediately
1585 disabled.
1621 disabled.
1586
1622
1587 WARNING: by putting in your own exception handler into IPython's main
1623 WARNING: by putting in your own exception handler into IPython's main
1588 execution loop, you run a very good chance of nasty crashes. This
1624 execution loop, you run a very good chance of nasty crashes. This
1589 facility should only be used if you really know what you are doing."""
1625 facility should only be used if you really know what you are doing."""
1590
1626
1591 assert type(exc_tuple)==type(()) , \
1627 assert type(exc_tuple)==type(()) , \
1592 "The custom exceptions must be given AS A TUPLE."
1628 "The custom exceptions must be given AS A TUPLE."
1593
1629
1594 def dummy_handler(self,etype,value,tb,tb_offset=None):
1630 def dummy_handler(self,etype,value,tb,tb_offset=None):
1595 print('*** Simple custom exception handler ***')
1631 print('*** Simple custom exception handler ***')
1596 print('Exception type :',etype)
1632 print('Exception type :',etype)
1597 print('Exception value:',value)
1633 print('Exception value:',value)
1598 print('Traceback :',tb)
1634 print('Traceback :',tb)
1599 #print 'Source code :','\n'.join(self.buffer)
1635 #print 'Source code :','\n'.join(self.buffer)
1600
1636
1601 def validate_stb(stb):
1637 def validate_stb(stb):
1602 """validate structured traceback return type
1638 """validate structured traceback return type
1603
1639
1604 return type of CustomTB *should* be a list of strings, but allow
1640 return type of CustomTB *should* be a list of strings, but allow
1605 single strings or None, which are harmless.
1641 single strings or None, which are harmless.
1606
1642
1607 This function will *always* return a list of strings,
1643 This function will *always* return a list of strings,
1608 and will raise a TypeError if stb is inappropriate.
1644 and will raise a TypeError if stb is inappropriate.
1609 """
1645 """
1610 msg = "CustomTB must return list of strings, not %r" % stb
1646 msg = "CustomTB must return list of strings, not %r" % stb
1611 if stb is None:
1647 if stb is None:
1612 return []
1648 return []
1613 elif isinstance(stb, string_types):
1649 elif isinstance(stb, string_types):
1614 return [stb]
1650 return [stb]
1615 elif not isinstance(stb, list):
1651 elif not isinstance(stb, list):
1616 raise TypeError(msg)
1652 raise TypeError(msg)
1617 # it's a list
1653 # it's a list
1618 for line in stb:
1654 for line in stb:
1619 # check every element
1655 # check every element
1620 if not isinstance(line, string_types):
1656 if not isinstance(line, string_types):
1621 raise TypeError(msg)
1657 raise TypeError(msg)
1622 return stb
1658 return stb
1623
1659
1624 if handler is None:
1660 if handler is None:
1625 wrapped = dummy_handler
1661 wrapped = dummy_handler
1626 else:
1662 else:
1627 def wrapped(self,etype,value,tb,tb_offset=None):
1663 def wrapped(self,etype,value,tb,tb_offset=None):
1628 """wrap CustomTB handler, to protect IPython from user code
1664 """wrap CustomTB handler, to protect IPython from user code
1629
1665
1630 This makes it harder (but not impossible) for custom exception
1666 This makes it harder (but not impossible) for custom exception
1631 handlers to crash IPython.
1667 handlers to crash IPython.
1632 """
1668 """
1633 try:
1669 try:
1634 stb = handler(self,etype,value,tb,tb_offset=tb_offset)
1670 stb = handler(self,etype,value,tb,tb_offset=tb_offset)
1635 return validate_stb(stb)
1671 return validate_stb(stb)
1636 except:
1672 except:
1637 # clear custom handler immediately
1673 # clear custom handler immediately
1638 self.set_custom_exc((), None)
1674 self.set_custom_exc((), None)
1639 print("Custom TB Handler failed, unregistering", file=io.stderr)
1675 print("Custom TB Handler failed, unregistering", file=io.stderr)
1640 # show the exception in handler first
1676 # show the exception in handler first
1641 stb = self.InteractiveTB.structured_traceback(*sys.exc_info())
1677 stb = self.InteractiveTB.structured_traceback(*sys.exc_info())
1642 print(self.InteractiveTB.stb2text(stb), file=io.stdout)
1678 print(self.InteractiveTB.stb2text(stb), file=io.stdout)
1643 print("The original exception:", file=io.stdout)
1679 print("The original exception:", file=io.stdout)
1644 stb = self.InteractiveTB.structured_traceback(
1680 stb = self.InteractiveTB.structured_traceback(
1645 (etype,value,tb), tb_offset=tb_offset
1681 (etype,value,tb), tb_offset=tb_offset
1646 )
1682 )
1647 return stb
1683 return stb
1648
1684
1649 self.CustomTB = types.MethodType(wrapped,self)
1685 self.CustomTB = types.MethodType(wrapped,self)
1650 self.custom_exceptions = exc_tuple
1686 self.custom_exceptions = exc_tuple
1651
1687
1652 def excepthook(self, etype, value, tb):
1688 def excepthook(self, etype, value, tb):
1653 """One more defense for GUI apps that call sys.excepthook.
1689 """One more defense for GUI apps that call sys.excepthook.
1654
1690
1655 GUI frameworks like wxPython trap exceptions and call
1691 GUI frameworks like wxPython trap exceptions and call
1656 sys.excepthook themselves. I guess this is a feature that
1692 sys.excepthook themselves. I guess this is a feature that
1657 enables them to keep running after exceptions that would
1693 enables them to keep running after exceptions that would
1658 otherwise kill their mainloop. This is a bother for IPython
1694 otherwise kill their mainloop. This is a bother for IPython
1659 which excepts to catch all of the program exceptions with a try:
1695 which excepts to catch all of the program exceptions with a try:
1660 except: statement.
1696 except: statement.
1661
1697
1662 Normally, IPython sets sys.excepthook to a CrashHandler instance, so if
1698 Normally, IPython sets sys.excepthook to a CrashHandler instance, so if
1663 any app directly invokes sys.excepthook, it will look to the user like
1699 any app directly invokes sys.excepthook, it will look to the user like
1664 IPython crashed. In order to work around this, we can disable the
1700 IPython crashed. In order to work around this, we can disable the
1665 CrashHandler and replace it with this excepthook instead, which prints a
1701 CrashHandler and replace it with this excepthook instead, which prints a
1666 regular traceback using our InteractiveTB. In this fashion, apps which
1702 regular traceback using our InteractiveTB. In this fashion, apps which
1667 call sys.excepthook will generate a regular-looking exception from
1703 call sys.excepthook will generate a regular-looking exception from
1668 IPython, and the CrashHandler will only be triggered by real IPython
1704 IPython, and the CrashHandler will only be triggered by real IPython
1669 crashes.
1705 crashes.
1670
1706
1671 This hook should be used sparingly, only in places which are not likely
1707 This hook should be used sparingly, only in places which are not likely
1672 to be true IPython errors.
1708 to be true IPython errors.
1673 """
1709 """
1674 self.showtraceback((etype,value,tb),tb_offset=0)
1710 self.showtraceback((etype,value,tb),tb_offset=0)
1675
1711
1676 def _get_exc_info(self, exc_tuple=None):
1712 def _get_exc_info(self, exc_tuple=None):
1677 """get exc_info from a given tuple, sys.exc_info() or sys.last_type etc.
1713 """get exc_info from a given tuple, sys.exc_info() or sys.last_type etc.
1678
1714
1679 Ensures sys.last_type,value,traceback hold the exc_info we found,
1715 Ensures sys.last_type,value,traceback hold the exc_info we found,
1680 from whichever source.
1716 from whichever source.
1681
1717
1682 raises ValueError if none of these contain any information
1718 raises ValueError if none of these contain any information
1683 """
1719 """
1684 if exc_tuple is None:
1720 if exc_tuple is None:
1685 etype, value, tb = sys.exc_info()
1721 etype, value, tb = sys.exc_info()
1686 else:
1722 else:
1687 etype, value, tb = exc_tuple
1723 etype, value, tb = exc_tuple
1688
1724
1689 if etype is None:
1725 if etype is None:
1690 if hasattr(sys, 'last_type'):
1726 if hasattr(sys, 'last_type'):
1691 etype, value, tb = sys.last_type, sys.last_value, \
1727 etype, value, tb = sys.last_type, sys.last_value, \
1692 sys.last_traceback
1728 sys.last_traceback
1693
1729
1694 if etype is None:
1730 if etype is None:
1695 raise ValueError("No exception to find")
1731 raise ValueError("No exception to find")
1696
1732
1697 # Now store the exception info in sys.last_type etc.
1733 # Now store the exception info in sys.last_type etc.
1698 # WARNING: these variables are somewhat deprecated and not
1734 # WARNING: these variables are somewhat deprecated and not
1699 # necessarily safe to use in a threaded environment, but tools
1735 # necessarily safe to use in a threaded environment, but tools
1700 # like pdb depend on their existence, so let's set them. If we
1736 # like pdb depend on their existence, so let's set them. If we
1701 # find problems in the field, we'll need to revisit their use.
1737 # find problems in the field, we'll need to revisit their use.
1702 sys.last_type = etype
1738 sys.last_type = etype
1703 sys.last_value = value
1739 sys.last_value = value
1704 sys.last_traceback = tb
1740 sys.last_traceback = tb
1705
1741
1706 return etype, value, tb
1742 return etype, value, tb
1707
1743
1708 def show_usage_error(self, exc):
1744 def show_usage_error(self, exc):
1709 """Show a short message for UsageErrors
1745 """Show a short message for UsageErrors
1710
1746
1711 These are special exceptions that shouldn't show a traceback.
1747 These are special exceptions that shouldn't show a traceback.
1712 """
1748 """
1713 self.write_err("UsageError: %s" % exc)
1749 self.write_err("UsageError: %s" % exc)
1714
1750
1715 def showtraceback(self,exc_tuple = None,filename=None,tb_offset=None,
1751 def showtraceback(self,exc_tuple = None,filename=None,tb_offset=None,
1716 exception_only=False):
1752 exception_only=False):
1717 """Display the exception that just occurred.
1753 """Display the exception that just occurred.
1718
1754
1719 If nothing is known about the exception, this is the method which
1755 If nothing is known about the exception, this is the method which
1720 should be used throughout the code for presenting user tracebacks,
1756 should be used throughout the code for presenting user tracebacks,
1721 rather than directly invoking the InteractiveTB object.
1757 rather than directly invoking the InteractiveTB object.
1722
1758
1723 A specific showsyntaxerror() also exists, but this method can take
1759 A specific showsyntaxerror() also exists, but this method can take
1724 care of calling it if needed, so unless you are explicitly catching a
1760 care of calling it if needed, so unless you are explicitly catching a
1725 SyntaxError exception, don't try to analyze the stack manually and
1761 SyntaxError exception, don't try to analyze the stack manually and
1726 simply call this method."""
1762 simply call this method."""
1727
1763
1728 try:
1764 try:
1729 try:
1765 try:
1730 etype, value, tb = self._get_exc_info(exc_tuple)
1766 etype, value, tb = self._get_exc_info(exc_tuple)
1731 except ValueError:
1767 except ValueError:
1732 self.write_err('No traceback available to show.\n')
1768 self.write_err('No traceback available to show.\n')
1733 return
1769 return
1734
1770
1735 if issubclass(etype, SyntaxError):
1771 if issubclass(etype, SyntaxError):
1736 # Though this won't be called by syntax errors in the input
1772 # Though this won't be called by syntax errors in the input
1737 # line, there may be SyntaxError cases with imported code.
1773 # line, there may be SyntaxError cases with imported code.
1738 self.showsyntaxerror(filename)
1774 self.showsyntaxerror(filename)
1739 elif etype is UsageError:
1775 elif etype is UsageError:
1740 self.show_usage_error(value)
1776 self.show_usage_error(value)
1741 else:
1777 else:
1742 if exception_only:
1778 if exception_only:
1743 stb = ['An exception has occurred, use %tb to see '
1779 stb = ['An exception has occurred, use %tb to see '
1744 'the full traceback.\n']
1780 'the full traceback.\n']
1745 stb.extend(self.InteractiveTB.get_exception_only(etype,
1781 stb.extend(self.InteractiveTB.get_exception_only(etype,
1746 value))
1782 value))
1747 else:
1783 else:
1748 try:
1784 try:
1749 # Exception classes can customise their traceback - we
1785 # Exception classes can customise their traceback - we
1750 # use this in IPython.parallel for exceptions occurring
1786 # use this in IPython.parallel for exceptions occurring
1751 # in the engines. This should return a list of strings.
1787 # in the engines. This should return a list of strings.
1752 stb = value._render_traceback_()
1788 stb = value._render_traceback_()
1753 except Exception:
1789 except Exception:
1754 stb = self.InteractiveTB.structured_traceback(etype,
1790 stb = self.InteractiveTB.structured_traceback(etype,
1755 value, tb, tb_offset=tb_offset)
1791 value, tb, tb_offset=tb_offset)
1756
1792
1757 self._showtraceback(etype, value, stb)
1793 self._showtraceback(etype, value, stb)
1758 if self.call_pdb:
1794 if self.call_pdb:
1759 # drop into debugger
1795 # drop into debugger
1760 self.debugger(force=True)
1796 self.debugger(force=True)
1761 return
1797 return
1762
1798
1763 # Actually show the traceback
1799 # Actually show the traceback
1764 self._showtraceback(etype, value, stb)
1800 self._showtraceback(etype, value, stb)
1765
1801
1766 except KeyboardInterrupt:
1802 except KeyboardInterrupt:
1767 self.write_err("\nKeyboardInterrupt\n")
1803 self.write_err("\nKeyboardInterrupt\n")
1768
1804
1769 def _showtraceback(self, etype, evalue, stb):
1805 def _showtraceback(self, etype, evalue, stb):
1770 """Actually show a traceback.
1806 """Actually show a traceback.
1771
1807
1772 Subclasses may override this method to put the traceback on a different
1808 Subclasses may override this method to put the traceback on a different
1773 place, like a side channel.
1809 place, like a side channel.
1774 """
1810 """
1775 print(self.InteractiveTB.stb2text(stb), file=io.stdout)
1811 print(self.InteractiveTB.stb2text(stb), file=io.stdout)
1776
1812
1777 def showsyntaxerror(self, filename=None):
1813 def showsyntaxerror(self, filename=None):
1778 """Display the syntax error that just occurred.
1814 """Display the syntax error that just occurred.
1779
1815
1780 This doesn't display a stack trace because there isn't one.
1816 This doesn't display a stack trace because there isn't one.
1781
1817
1782 If a filename is given, it is stuffed in the exception instead
1818 If a filename is given, it is stuffed in the exception instead
1783 of what was there before (because Python's parser always uses
1819 of what was there before (because Python's parser always uses
1784 "<string>" when reading from a string).
1820 "<string>" when reading from a string).
1785 """
1821 """
1786 etype, value, last_traceback = self._get_exc_info()
1822 etype, value, last_traceback = self._get_exc_info()
1787
1823
1788 if filename and issubclass(etype, SyntaxError):
1824 if filename and issubclass(etype, SyntaxError):
1789 try:
1825 try:
1790 value.filename = filename
1826 value.filename = filename
1791 except:
1827 except:
1792 # Not the format we expect; leave it alone
1828 # Not the format we expect; leave it alone
1793 pass
1829 pass
1794
1830
1795 stb = self.SyntaxTB.structured_traceback(etype, value, [])
1831 stb = self.SyntaxTB.structured_traceback(etype, value, [])
1796 self._showtraceback(etype, value, stb)
1832 self._showtraceback(etype, value, stb)
1797
1833
1798 # This is overridden in TerminalInteractiveShell to show a message about
1834 # This is overridden in TerminalInteractiveShell to show a message about
1799 # the %paste magic.
1835 # the %paste magic.
1800 def showindentationerror(self):
1836 def showindentationerror(self):
1801 """Called by run_cell when there's an IndentationError in code entered
1837 """Called by run_cell when there's an IndentationError in code entered
1802 at the prompt.
1838 at the prompt.
1803
1839
1804 This is overridden in TerminalInteractiveShell to show a message about
1840 This is overridden in TerminalInteractiveShell to show a message about
1805 the %paste magic."""
1841 the %paste magic."""
1806 self.showsyntaxerror()
1842 self.showsyntaxerror()
1807
1843
1808 #-------------------------------------------------------------------------
1844 #-------------------------------------------------------------------------
1809 # Things related to readline
1845 # Things related to readline
1810 #-------------------------------------------------------------------------
1846 #-------------------------------------------------------------------------
1811
1847
1812 def init_readline(self):
1848 def init_readline(self):
1813 """Command history completion/saving/reloading."""
1849 """Command history completion/saving/reloading."""
1814
1850
1815 if self.readline_use:
1851 if self.readline_use:
1816 import IPython.utils.rlineimpl as readline
1852 import IPython.utils.rlineimpl as readline
1817
1853
1818 self.rl_next_input = None
1854 self.rl_next_input = None
1819 self.rl_do_indent = False
1855 self.rl_do_indent = False
1820
1856
1821 if not self.readline_use or not readline.have_readline:
1857 if not self.readline_use or not readline.have_readline:
1822 self.has_readline = False
1858 self.has_readline = False
1823 self.readline = None
1859 self.readline = None
1824 # Set a number of methods that depend on readline to be no-op
1860 # Set a number of methods that depend on readline to be no-op
1825 self.readline_no_record = no_op_context
1861 self.readline_no_record = no_op_context
1826 self.set_readline_completer = no_op
1862 self.set_readline_completer = no_op
1827 self.set_custom_completer = no_op
1863 self.set_custom_completer = no_op
1828 if self.readline_use:
1864 if self.readline_use:
1829 warn('Readline services not available or not loaded.')
1865 warn('Readline services not available or not loaded.')
1830 else:
1866 else:
1831 self.has_readline = True
1867 self.has_readline = True
1832 self.readline = readline
1868 self.readline = readline
1833 sys.modules['readline'] = readline
1869 sys.modules['readline'] = readline
1834
1870
1835 # Platform-specific configuration
1871 # Platform-specific configuration
1836 if os.name == 'nt':
1872 if os.name == 'nt':
1837 # FIXME - check with Frederick to see if we can harmonize
1873 # FIXME - check with Frederick to see if we can harmonize
1838 # naming conventions with pyreadline to avoid this
1874 # naming conventions with pyreadline to avoid this
1839 # platform-dependent check
1875 # platform-dependent check
1840 self.readline_startup_hook = readline.set_pre_input_hook
1876 self.readline_startup_hook = readline.set_pre_input_hook
1841 else:
1877 else:
1842 self.readline_startup_hook = readline.set_startup_hook
1878 self.readline_startup_hook = readline.set_startup_hook
1843
1879
1844 # Load user's initrc file (readline config)
1880 # Load user's initrc file (readline config)
1845 # Or if libedit is used, load editrc.
1881 # Or if libedit is used, load editrc.
1846 inputrc_name = os.environ.get('INPUTRC')
1882 inputrc_name = os.environ.get('INPUTRC')
1847 if inputrc_name is None:
1883 if inputrc_name is None:
1848 inputrc_name = '.inputrc'
1884 inputrc_name = '.inputrc'
1849 if readline.uses_libedit:
1885 if readline.uses_libedit:
1850 inputrc_name = '.editrc'
1886 inputrc_name = '.editrc'
1851 inputrc_name = os.path.join(self.home_dir, inputrc_name)
1887 inputrc_name = os.path.join(self.home_dir, inputrc_name)
1852 if os.path.isfile(inputrc_name):
1888 if os.path.isfile(inputrc_name):
1853 try:
1889 try:
1854 readline.read_init_file(inputrc_name)
1890 readline.read_init_file(inputrc_name)
1855 except:
1891 except:
1856 warn('Problems reading readline initialization file <%s>'
1892 warn('Problems reading readline initialization file <%s>'
1857 % inputrc_name)
1893 % inputrc_name)
1858
1894
1859 # Configure readline according to user's prefs
1895 # Configure readline according to user's prefs
1860 # This is only done if GNU readline is being used. If libedit
1896 # This is only done if GNU readline is being used. If libedit
1861 # is being used (as on Leopard) the readline config is
1897 # is being used (as on Leopard) the readline config is
1862 # not run as the syntax for libedit is different.
1898 # not run as the syntax for libedit is different.
1863 if not readline.uses_libedit:
1899 if not readline.uses_libedit:
1864 for rlcommand in self.readline_parse_and_bind:
1900 for rlcommand in self.readline_parse_and_bind:
1865 #print "loading rl:",rlcommand # dbg
1901 #print "loading rl:",rlcommand # dbg
1866 readline.parse_and_bind(rlcommand)
1902 readline.parse_and_bind(rlcommand)
1867
1903
1868 # Remove some chars from the delimiters list. If we encounter
1904 # Remove some chars from the delimiters list. If we encounter
1869 # unicode chars, discard them.
1905 # unicode chars, discard them.
1870 delims = readline.get_completer_delims()
1906 delims = readline.get_completer_delims()
1871 if not py3compat.PY3:
1907 if not py3compat.PY3:
1872 delims = delims.encode("ascii", "ignore")
1908 delims = delims.encode("ascii", "ignore")
1873 for d in self.readline_remove_delims:
1909 for d in self.readline_remove_delims:
1874 delims = delims.replace(d, "")
1910 delims = delims.replace(d, "")
1875 delims = delims.replace(ESC_MAGIC, '')
1911 delims = delims.replace(ESC_MAGIC, '')
1876 readline.set_completer_delims(delims)
1912 readline.set_completer_delims(delims)
1877 # Store these so we can restore them if something like rpy2 modifies
1913 # Store these so we can restore them if something like rpy2 modifies
1878 # them.
1914 # them.
1879 self.readline_delims = delims
1915 self.readline_delims = delims
1880 # otherwise we end up with a monster history after a while:
1916 # otherwise we end up with a monster history after a while:
1881 readline.set_history_length(self.history_length)
1917 readline.set_history_length(self.history_length)
1882
1918
1883 self.refill_readline_hist()
1919 self.refill_readline_hist()
1884 self.readline_no_record = ReadlineNoRecord(self)
1920 self.readline_no_record = ReadlineNoRecord(self)
1885
1921
1886 # Configure auto-indent for all platforms
1922 # Configure auto-indent for all platforms
1887 self.set_autoindent(self.autoindent)
1923 self.set_autoindent(self.autoindent)
1888
1924
1889 def refill_readline_hist(self):
1925 def refill_readline_hist(self):
1890 # Load the last 1000 lines from history
1926 # Load the last 1000 lines from history
1891 self.readline.clear_history()
1927 self.readline.clear_history()
1892 stdin_encoding = sys.stdin.encoding or "utf-8"
1928 stdin_encoding = sys.stdin.encoding or "utf-8"
1893 last_cell = u""
1929 last_cell = u""
1894 for _, _, cell in self.history_manager.get_tail(1000,
1930 for _, _, cell in self.history_manager.get_tail(1000,
1895 include_latest=True):
1931 include_latest=True):
1896 # Ignore blank lines and consecutive duplicates
1932 # Ignore blank lines and consecutive duplicates
1897 cell = cell.rstrip()
1933 cell = cell.rstrip()
1898 if cell and (cell != last_cell):
1934 if cell and (cell != last_cell):
1899 try:
1935 try:
1900 if self.multiline_history:
1936 if self.multiline_history:
1901 self.readline.add_history(py3compat.unicode_to_str(cell,
1937 self.readline.add_history(py3compat.unicode_to_str(cell,
1902 stdin_encoding))
1938 stdin_encoding))
1903 else:
1939 else:
1904 for line in cell.splitlines():
1940 for line in cell.splitlines():
1905 self.readline.add_history(py3compat.unicode_to_str(line,
1941 self.readline.add_history(py3compat.unicode_to_str(line,
1906 stdin_encoding))
1942 stdin_encoding))
1907 last_cell = cell
1943 last_cell = cell
1908
1944
1909 except TypeError:
1945 except TypeError:
1910 # The history DB can get corrupted so it returns strings
1946 # The history DB can get corrupted so it returns strings
1911 # containing null bytes, which readline objects to.
1947 # containing null bytes, which readline objects to.
1912 continue
1948 continue
1913
1949
1914 @skip_doctest
1950 @skip_doctest
1915 def set_next_input(self, s):
1951 def set_next_input(self, s):
1916 """ Sets the 'default' input string for the next command line.
1952 """ Sets the 'default' input string for the next command line.
1917
1953
1918 Requires readline.
1954 Requires readline.
1919
1955
1920 Example::
1956 Example::
1921
1957
1922 In [1]: _ip.set_next_input("Hello Word")
1958 In [1]: _ip.set_next_input("Hello Word")
1923 In [2]: Hello Word_ # cursor is here
1959 In [2]: Hello Word_ # cursor is here
1924 """
1960 """
1925 self.rl_next_input = py3compat.cast_bytes_py2(s)
1961 self.rl_next_input = py3compat.cast_bytes_py2(s)
1926
1962
1927 # Maybe move this to the terminal subclass?
1963 # Maybe move this to the terminal subclass?
1928 def pre_readline(self):
1964 def pre_readline(self):
1929 """readline hook to be used at the start of each line.
1965 """readline hook to be used at the start of each line.
1930
1966
1931 Currently it handles auto-indent only."""
1967 Currently it handles auto-indent only."""
1932
1968
1933 if self.rl_do_indent:
1969 if self.rl_do_indent:
1934 self.readline.insert_text(self._indent_current_str())
1970 self.readline.insert_text(self._indent_current_str())
1935 if self.rl_next_input is not None:
1971 if self.rl_next_input is not None:
1936 self.readline.insert_text(self.rl_next_input)
1972 self.readline.insert_text(self.rl_next_input)
1937 self.rl_next_input = None
1973 self.rl_next_input = None
1938
1974
1939 def _indent_current_str(self):
1975 def _indent_current_str(self):
1940 """return the current level of indentation as a string"""
1976 """return the current level of indentation as a string"""
1941 return self.input_splitter.indent_spaces * ' '
1977 return self.input_splitter.indent_spaces * ' '
1942
1978
1943 #-------------------------------------------------------------------------
1979 #-------------------------------------------------------------------------
1944 # Things related to text completion
1980 # Things related to text completion
1945 #-------------------------------------------------------------------------
1981 #-------------------------------------------------------------------------
1946
1982
1947 def init_completer(self):
1983 def init_completer(self):
1948 """Initialize the completion machinery.
1984 """Initialize the completion machinery.
1949
1985
1950 This creates completion machinery that can be used by client code,
1986 This creates completion machinery that can be used by client code,
1951 either interactively in-process (typically triggered by the readline
1987 either interactively in-process (typically triggered by the readline
1952 library), programatically (such as in test suites) or out-of-prcess
1988 library), programatically (such as in test suites) or out-of-prcess
1953 (typically over the network by remote frontends).
1989 (typically over the network by remote frontends).
1954 """
1990 """
1955 from IPython.core.completer import IPCompleter
1991 from IPython.core.completer import IPCompleter
1956 from IPython.core.completerlib import (module_completer,
1992 from IPython.core.completerlib import (module_completer,
1957 magic_run_completer, cd_completer, reset_completer)
1993 magic_run_completer, cd_completer, reset_completer)
1958
1994
1959 self.Completer = IPCompleter(shell=self,
1995 self.Completer = IPCompleter(shell=self,
1960 namespace=self.user_ns,
1996 namespace=self.user_ns,
1961 global_namespace=self.user_global_ns,
1997 global_namespace=self.user_global_ns,
1962 use_readline=self.has_readline,
1998 use_readline=self.has_readline,
1963 parent=self,
1999 parent=self,
1964 )
2000 )
1965 self.configurables.append(self.Completer)
2001 self.configurables.append(self.Completer)
1966
2002
1967 # Add custom completers to the basic ones built into IPCompleter
2003 # Add custom completers to the basic ones built into IPCompleter
1968 sdisp = self.strdispatchers.get('complete_command', StrDispatch())
2004 sdisp = self.strdispatchers.get('complete_command', StrDispatch())
1969 self.strdispatchers['complete_command'] = sdisp
2005 self.strdispatchers['complete_command'] = sdisp
1970 self.Completer.custom_completers = sdisp
2006 self.Completer.custom_completers = sdisp
1971
2007
1972 self.set_hook('complete_command', module_completer, str_key = 'import')
2008 self.set_hook('complete_command', module_completer, str_key = 'import')
1973 self.set_hook('complete_command', module_completer, str_key = 'from')
2009 self.set_hook('complete_command', module_completer, str_key = 'from')
1974 self.set_hook('complete_command', magic_run_completer, str_key = '%run')
2010 self.set_hook('complete_command', magic_run_completer, str_key = '%run')
1975 self.set_hook('complete_command', cd_completer, str_key = '%cd')
2011 self.set_hook('complete_command', cd_completer, str_key = '%cd')
1976 self.set_hook('complete_command', reset_completer, str_key = '%reset')
2012 self.set_hook('complete_command', reset_completer, str_key = '%reset')
1977
2013
1978 # Only configure readline if we truly are using readline. IPython can
2014 # Only configure readline if we truly are using readline. IPython can
1979 # do tab-completion over the network, in GUIs, etc, where readline
2015 # do tab-completion over the network, in GUIs, etc, where readline
1980 # itself may be absent
2016 # itself may be absent
1981 if self.has_readline:
2017 if self.has_readline:
1982 self.set_readline_completer()
2018 self.set_readline_completer()
1983
2019
1984 def complete(self, text, line=None, cursor_pos=None):
2020 def complete(self, text, line=None, cursor_pos=None):
1985 """Return the completed text and a list of completions.
2021 """Return the completed text and a list of completions.
1986
2022
1987 Parameters
2023 Parameters
1988 ----------
2024 ----------
1989
2025
1990 text : string
2026 text : string
1991 A string of text to be completed on. It can be given as empty and
2027 A string of text to be completed on. It can be given as empty and
1992 instead a line/position pair are given. In this case, the
2028 instead a line/position pair are given. In this case, the
1993 completer itself will split the line like readline does.
2029 completer itself will split the line like readline does.
1994
2030
1995 line : string, optional
2031 line : string, optional
1996 The complete line that text is part of.
2032 The complete line that text is part of.
1997
2033
1998 cursor_pos : int, optional
2034 cursor_pos : int, optional
1999 The position of the cursor on the input line.
2035 The position of the cursor on the input line.
2000
2036
2001 Returns
2037 Returns
2002 -------
2038 -------
2003 text : string
2039 text : string
2004 The actual text that was completed.
2040 The actual text that was completed.
2005
2041
2006 matches : list
2042 matches : list
2007 A sorted list with all possible completions.
2043 A sorted list with all possible completions.
2008
2044
2009 The optional arguments allow the completion to take more context into
2045 The optional arguments allow the completion to take more context into
2010 account, and are part of the low-level completion API.
2046 account, and are part of the low-level completion API.
2011
2047
2012 This is a wrapper around the completion mechanism, similar to what
2048 This is a wrapper around the completion mechanism, similar to what
2013 readline does at the command line when the TAB key is hit. By
2049 readline does at the command line when the TAB key is hit. By
2014 exposing it as a method, it can be used by other non-readline
2050 exposing it as a method, it can be used by other non-readline
2015 environments (such as GUIs) for text completion.
2051 environments (such as GUIs) for text completion.
2016
2052
2017 Simple usage example:
2053 Simple usage example:
2018
2054
2019 In [1]: x = 'hello'
2055 In [1]: x = 'hello'
2020
2056
2021 In [2]: _ip.complete('x.l')
2057 In [2]: _ip.complete('x.l')
2022 Out[2]: ('x.l', ['x.ljust', 'x.lower', 'x.lstrip'])
2058 Out[2]: ('x.l', ['x.ljust', 'x.lower', 'x.lstrip'])
2023 """
2059 """
2024
2060
2025 # Inject names into __builtin__ so we can complete on the added names.
2061 # Inject names into __builtin__ so we can complete on the added names.
2026 with self.builtin_trap:
2062 with self.builtin_trap:
2027 return self.Completer.complete(text, line, cursor_pos)
2063 return self.Completer.complete(text, line, cursor_pos)
2028
2064
2029 def set_custom_completer(self, completer, pos=0):
2065 def set_custom_completer(self, completer, pos=0):
2030 """Adds a new custom completer function.
2066 """Adds a new custom completer function.
2031
2067
2032 The position argument (defaults to 0) is the index in the completers
2068 The position argument (defaults to 0) is the index in the completers
2033 list where you want the completer to be inserted."""
2069 list where you want the completer to be inserted."""
2034
2070
2035 newcomp = types.MethodType(completer,self.Completer)
2071 newcomp = types.MethodType(completer,self.Completer)
2036 self.Completer.matchers.insert(pos,newcomp)
2072 self.Completer.matchers.insert(pos,newcomp)
2037
2073
2038 def set_readline_completer(self):
2074 def set_readline_completer(self):
2039 """Reset readline's completer to be our own."""
2075 """Reset readline's completer to be our own."""
2040 self.readline.set_completer(self.Completer.rlcomplete)
2076 self.readline.set_completer(self.Completer.rlcomplete)
2041
2077
2042 def set_completer_frame(self, frame=None):
2078 def set_completer_frame(self, frame=None):
2043 """Set the frame of the completer."""
2079 """Set the frame of the completer."""
2044 if frame:
2080 if frame:
2045 self.Completer.namespace = frame.f_locals
2081 self.Completer.namespace = frame.f_locals
2046 self.Completer.global_namespace = frame.f_globals
2082 self.Completer.global_namespace = frame.f_globals
2047 else:
2083 else:
2048 self.Completer.namespace = self.user_ns
2084 self.Completer.namespace = self.user_ns
2049 self.Completer.global_namespace = self.user_global_ns
2085 self.Completer.global_namespace = self.user_global_ns
2050
2086
2051 #-------------------------------------------------------------------------
2087 #-------------------------------------------------------------------------
2052 # Things related to magics
2088 # Things related to magics
2053 #-------------------------------------------------------------------------
2089 #-------------------------------------------------------------------------
2054
2090
2055 def init_magics(self):
2091 def init_magics(self):
2056 from IPython.core import magics as m
2092 from IPython.core import magics as m
2057 self.magics_manager = magic.MagicsManager(shell=self,
2093 self.magics_manager = magic.MagicsManager(shell=self,
2058 parent=self,
2094 parent=self,
2059 user_magics=m.UserMagics(self))
2095 user_magics=m.UserMagics(self))
2060 self.configurables.append(self.magics_manager)
2096 self.configurables.append(self.magics_manager)
2061
2097
2062 # Expose as public API from the magics manager
2098 # Expose as public API from the magics manager
2063 self.register_magics = self.magics_manager.register
2099 self.register_magics = self.magics_manager.register
2064 self.define_magic = self.magics_manager.define_magic
2100 self.define_magic = self.magics_manager.define_magic
2065
2101
2066 self.register_magics(m.AutoMagics, m.BasicMagics, m.CodeMagics,
2102 self.register_magics(m.AutoMagics, m.BasicMagics, m.CodeMagics,
2067 m.ConfigMagics, m.DeprecatedMagics, m.DisplayMagics, m.ExecutionMagics,
2103 m.ConfigMagics, m.DeprecatedMagics, m.DisplayMagics, m.ExecutionMagics,
2068 m.ExtensionMagics, m.HistoryMagics, m.LoggingMagics,
2104 m.ExtensionMagics, m.HistoryMagics, m.LoggingMagics,
2069 m.NamespaceMagics, m.OSMagics, m.PylabMagics, m.ScriptMagics,
2105 m.NamespaceMagics, m.OSMagics, m.PylabMagics, m.ScriptMagics,
2070 )
2106 )
2071
2107
2072 # Register Magic Aliases
2108 # Register Magic Aliases
2073 mman = self.magics_manager
2109 mman = self.magics_manager
2074 # FIXME: magic aliases should be defined by the Magics classes
2110 # FIXME: magic aliases should be defined by the Magics classes
2075 # or in MagicsManager, not here
2111 # or in MagicsManager, not here
2076 mman.register_alias('ed', 'edit')
2112 mman.register_alias('ed', 'edit')
2077 mman.register_alias('hist', 'history')
2113 mman.register_alias('hist', 'history')
2078 mman.register_alias('rep', 'recall')
2114 mman.register_alias('rep', 'recall')
2079 mman.register_alias('SVG', 'svg', 'cell')
2115 mman.register_alias('SVG', 'svg', 'cell')
2080 mman.register_alias('HTML', 'html', 'cell')
2116 mman.register_alias('HTML', 'html', 'cell')
2081 mman.register_alias('file', 'writefile', 'cell')
2117 mman.register_alias('file', 'writefile', 'cell')
2082
2118
2083 # FIXME: Move the color initialization to the DisplayHook, which
2119 # FIXME: Move the color initialization to the DisplayHook, which
2084 # should be split into a prompt manager and displayhook. We probably
2120 # should be split into a prompt manager and displayhook. We probably
2085 # even need a centralize colors management object.
2121 # even need a centralize colors management object.
2086 self.magic('colors %s' % self.colors)
2122 self.magic('colors %s' % self.colors)
2087
2123
2088 # Defined here so that it's included in the documentation
2124 # Defined here so that it's included in the documentation
2089 @functools.wraps(magic.MagicsManager.register_function)
2125 @functools.wraps(magic.MagicsManager.register_function)
2090 def register_magic_function(self, func, magic_kind='line', magic_name=None):
2126 def register_magic_function(self, func, magic_kind='line', magic_name=None):
2091 self.magics_manager.register_function(func,
2127 self.magics_manager.register_function(func,
2092 magic_kind=magic_kind, magic_name=magic_name)
2128 magic_kind=magic_kind, magic_name=magic_name)
2093
2129
2094 def run_line_magic(self, magic_name, line):
2130 def run_line_magic(self, magic_name, line):
2095 """Execute the given line magic.
2131 """Execute the given line magic.
2096
2132
2097 Parameters
2133 Parameters
2098 ----------
2134 ----------
2099 magic_name : str
2135 magic_name : str
2100 Name of the desired magic function, without '%' prefix.
2136 Name of the desired magic function, without '%' prefix.
2101
2137
2102 line : str
2138 line : str
2103 The rest of the input line as a single string.
2139 The rest of the input line as a single string.
2104 """
2140 """
2105 fn = self.find_line_magic(magic_name)
2141 fn = self.find_line_magic(magic_name)
2106 if fn is None:
2142 if fn is None:
2107 cm = self.find_cell_magic(magic_name)
2143 cm = self.find_cell_magic(magic_name)
2108 etpl = "Line magic function `%%%s` not found%s."
2144 etpl = "Line magic function `%%%s` not found%s."
2109 extra = '' if cm is None else (' (But cell magic `%%%%%s` exists, '
2145 extra = '' if cm is None else (' (But cell magic `%%%%%s` exists, '
2110 'did you mean that instead?)' % magic_name )
2146 'did you mean that instead?)' % magic_name )
2111 error(etpl % (magic_name, extra))
2147 error(etpl % (magic_name, extra))
2112 else:
2148 else:
2113 # Note: this is the distance in the stack to the user's frame.
2149 # Note: this is the distance in the stack to the user's frame.
2114 # This will need to be updated if the internal calling logic gets
2150 # This will need to be updated if the internal calling logic gets
2115 # refactored, or else we'll be expanding the wrong variables.
2151 # refactored, or else we'll be expanding the wrong variables.
2116 stack_depth = 2
2152 stack_depth = 2
2117 magic_arg_s = self.var_expand(line, stack_depth)
2153 magic_arg_s = self.var_expand(line, stack_depth)
2118 # Put magic args in a list so we can call with f(*a) syntax
2154 # Put magic args in a list so we can call with f(*a) syntax
2119 args = [magic_arg_s]
2155 args = [magic_arg_s]
2120 kwargs = {}
2156 kwargs = {}
2121 # Grab local namespace if we need it:
2157 # Grab local namespace if we need it:
2122 if getattr(fn, "needs_local_scope", False):
2158 if getattr(fn, "needs_local_scope", False):
2123 kwargs['local_ns'] = sys._getframe(stack_depth).f_locals
2159 kwargs['local_ns'] = sys._getframe(stack_depth).f_locals
2124 with self.builtin_trap:
2160 with self.builtin_trap:
2125 result = fn(*args,**kwargs)
2161 result = fn(*args,**kwargs)
2126 return result
2162 return result
2127
2163
2128 def run_cell_magic(self, magic_name, line, cell):
2164 def run_cell_magic(self, magic_name, line, cell):
2129 """Execute the given cell magic.
2165 """Execute the given cell magic.
2130
2166
2131 Parameters
2167 Parameters
2132 ----------
2168 ----------
2133 magic_name : str
2169 magic_name : str
2134 Name of the desired magic function, without '%' prefix.
2170 Name of the desired magic function, without '%' prefix.
2135
2171
2136 line : str
2172 line : str
2137 The rest of the first input line as a single string.
2173 The rest of the first input line as a single string.
2138
2174
2139 cell : str
2175 cell : str
2140 The body of the cell as a (possibly multiline) string.
2176 The body of the cell as a (possibly multiline) string.
2141 """
2177 """
2142 fn = self.find_cell_magic(magic_name)
2178 fn = self.find_cell_magic(magic_name)
2143 if fn is None:
2179 if fn is None:
2144 lm = self.find_line_magic(magic_name)
2180 lm = self.find_line_magic(magic_name)
2145 etpl = "Cell magic `%%{0}` not found{1}."
2181 etpl = "Cell magic `%%{0}` not found{1}."
2146 extra = '' if lm is None else (' (But line magic `%{0}` exists, '
2182 extra = '' if lm is None else (' (But line magic `%{0}` exists, '
2147 'did you mean that instead?)'.format(magic_name))
2183 'did you mean that instead?)'.format(magic_name))
2148 error(etpl.format(magic_name, extra))
2184 error(etpl.format(magic_name, extra))
2149 elif cell == '':
2185 elif cell == '':
2150 message = '%%{0} is a cell magic, but the cell body is empty.'.format(magic_name)
2186 message = '%%{0} is a cell magic, but the cell body is empty.'.format(magic_name)
2151 if self.find_line_magic(magic_name) is not None:
2187 if self.find_line_magic(magic_name) is not None:
2152 message += ' Did you mean the line magic %{0} (single %)?'.format(magic_name)
2188 message += ' Did you mean the line magic %{0} (single %)?'.format(magic_name)
2153 raise UsageError(message)
2189 raise UsageError(message)
2154 else:
2190 else:
2155 # Note: this is the distance in the stack to the user's frame.
2191 # Note: this is the distance in the stack to the user's frame.
2156 # This will need to be updated if the internal calling logic gets
2192 # This will need to be updated if the internal calling logic gets
2157 # refactored, or else we'll be expanding the wrong variables.
2193 # refactored, or else we'll be expanding the wrong variables.
2158 stack_depth = 2
2194 stack_depth = 2
2159 magic_arg_s = self.var_expand(line, stack_depth)
2195 magic_arg_s = self.var_expand(line, stack_depth)
2160 with self.builtin_trap:
2196 with self.builtin_trap:
2161 result = fn(magic_arg_s, cell)
2197 result = fn(magic_arg_s, cell)
2162 return result
2198 return result
2163
2199
2164 def find_line_magic(self, magic_name):
2200 def find_line_magic(self, magic_name):
2165 """Find and return a line magic by name.
2201 """Find and return a line magic by name.
2166
2202
2167 Returns None if the magic isn't found."""
2203 Returns None if the magic isn't found."""
2168 return self.magics_manager.magics['line'].get(magic_name)
2204 return self.magics_manager.magics['line'].get(magic_name)
2169
2205
2170 def find_cell_magic(self, magic_name):
2206 def find_cell_magic(self, magic_name):
2171 """Find and return a cell magic by name.
2207 """Find and return a cell magic by name.
2172
2208
2173 Returns None if the magic isn't found."""
2209 Returns None if the magic isn't found."""
2174 return self.magics_manager.magics['cell'].get(magic_name)
2210 return self.magics_manager.magics['cell'].get(magic_name)
2175
2211
2176 def find_magic(self, magic_name, magic_kind='line'):
2212 def find_magic(self, magic_name, magic_kind='line'):
2177 """Find and return a magic of the given type by name.
2213 """Find and return a magic of the given type by name.
2178
2214
2179 Returns None if the magic isn't found."""
2215 Returns None if the magic isn't found."""
2180 return self.magics_manager.magics[magic_kind].get(magic_name)
2216 return self.magics_manager.magics[magic_kind].get(magic_name)
2181
2217
2182 def magic(self, arg_s):
2218 def magic(self, arg_s):
2183 """DEPRECATED. Use run_line_magic() instead.
2219 """DEPRECATED. Use run_line_magic() instead.
2184
2220
2185 Call a magic function by name.
2221 Call a magic function by name.
2186
2222
2187 Input: a string containing the name of the magic function to call and
2223 Input: a string containing the name of the magic function to call and
2188 any additional arguments to be passed to the magic.
2224 any additional arguments to be passed to the magic.
2189
2225
2190 magic('name -opt foo bar') is equivalent to typing at the ipython
2226 magic('name -opt foo bar') is equivalent to typing at the ipython
2191 prompt:
2227 prompt:
2192
2228
2193 In[1]: %name -opt foo bar
2229 In[1]: %name -opt foo bar
2194
2230
2195 To call a magic without arguments, simply use magic('name').
2231 To call a magic without arguments, simply use magic('name').
2196
2232
2197 This provides a proper Python function to call IPython's magics in any
2233 This provides a proper Python function to call IPython's magics in any
2198 valid Python code you can type at the interpreter, including loops and
2234 valid Python code you can type at the interpreter, including loops and
2199 compound statements.
2235 compound statements.
2200 """
2236 """
2201 # TODO: should we issue a loud deprecation warning here?
2237 # TODO: should we issue a loud deprecation warning here?
2202 magic_name, _, magic_arg_s = arg_s.partition(' ')
2238 magic_name, _, magic_arg_s = arg_s.partition(' ')
2203 magic_name = magic_name.lstrip(prefilter.ESC_MAGIC)
2239 magic_name = magic_name.lstrip(prefilter.ESC_MAGIC)
2204 return self.run_line_magic(magic_name, magic_arg_s)
2240 return self.run_line_magic(magic_name, magic_arg_s)
2205
2241
2206 #-------------------------------------------------------------------------
2242 #-------------------------------------------------------------------------
2207 # Things related to macros
2243 # Things related to macros
2208 #-------------------------------------------------------------------------
2244 #-------------------------------------------------------------------------
2209
2245
2210 def define_macro(self, name, themacro):
2246 def define_macro(self, name, themacro):
2211 """Define a new macro
2247 """Define a new macro
2212
2248
2213 Parameters
2249 Parameters
2214 ----------
2250 ----------
2215 name : str
2251 name : str
2216 The name of the macro.
2252 The name of the macro.
2217 themacro : str or Macro
2253 themacro : str or Macro
2218 The action to do upon invoking the macro. If a string, a new
2254 The action to do upon invoking the macro. If a string, a new
2219 Macro object is created by passing the string to it.
2255 Macro object is created by passing the string to it.
2220 """
2256 """
2221
2257
2222 from IPython.core import macro
2258 from IPython.core import macro
2223
2259
2224 if isinstance(themacro, string_types):
2260 if isinstance(themacro, string_types):
2225 themacro = macro.Macro(themacro)
2261 themacro = macro.Macro(themacro)
2226 if not isinstance(themacro, macro.Macro):
2262 if not isinstance(themacro, macro.Macro):
2227 raise ValueError('A macro must be a string or a Macro instance.')
2263 raise ValueError('A macro must be a string or a Macro instance.')
2228 self.user_ns[name] = themacro
2264 self.user_ns[name] = themacro
2229
2265
2230 #-------------------------------------------------------------------------
2266 #-------------------------------------------------------------------------
2231 # Things related to the running of system commands
2267 # Things related to the running of system commands
2232 #-------------------------------------------------------------------------
2268 #-------------------------------------------------------------------------
2233
2269
2234 def system_piped(self, cmd):
2270 def system_piped(self, cmd):
2235 """Call the given cmd in a subprocess, piping stdout/err
2271 """Call the given cmd in a subprocess, piping stdout/err
2236
2272
2237 Parameters
2273 Parameters
2238 ----------
2274 ----------
2239 cmd : str
2275 cmd : str
2240 Command to execute (can not end in '&', as background processes are
2276 Command to execute (can not end in '&', as background processes are
2241 not supported. Should not be a command that expects input
2277 not supported. Should not be a command that expects input
2242 other than simple text.
2278 other than simple text.
2243 """
2279 """
2244 if cmd.rstrip().endswith('&'):
2280 if cmd.rstrip().endswith('&'):
2245 # this is *far* from a rigorous test
2281 # this is *far* from a rigorous test
2246 # We do not support backgrounding processes because we either use
2282 # We do not support backgrounding processes because we either use
2247 # pexpect or pipes to read from. Users can always just call
2283 # pexpect or pipes to read from. Users can always just call
2248 # os.system() or use ip.system=ip.system_raw
2284 # os.system() or use ip.system=ip.system_raw
2249 # if they really want a background process.
2285 # if they really want a background process.
2250 raise OSError("Background processes not supported.")
2286 raise OSError("Background processes not supported.")
2251
2287
2252 # we explicitly do NOT return the subprocess status code, because
2288 # we explicitly do NOT return the subprocess status code, because
2253 # a non-None value would trigger :func:`sys.displayhook` calls.
2289 # a non-None value would trigger :func:`sys.displayhook` calls.
2254 # Instead, we store the exit_code in user_ns.
2290 # Instead, we store the exit_code in user_ns.
2255 self.user_ns['_exit_code'] = system(self.var_expand(cmd, depth=1))
2291 self.user_ns['_exit_code'] = system(self.var_expand(cmd, depth=1))
2256
2292
2257 def system_raw(self, cmd):
2293 def system_raw(self, cmd):
2258 """Call the given cmd in a subprocess using os.system on Windows or
2294 """Call the given cmd in a subprocess using os.system on Windows or
2259 subprocess.call using the system shell on other platforms.
2295 subprocess.call using the system shell on other platforms.
2260
2296
2261 Parameters
2297 Parameters
2262 ----------
2298 ----------
2263 cmd : str
2299 cmd : str
2264 Command to execute.
2300 Command to execute.
2265 """
2301 """
2266 cmd = self.var_expand(cmd, depth=1)
2302 cmd = self.var_expand(cmd, depth=1)
2267 # protect os.system from UNC paths on Windows, which it can't handle:
2303 # protect os.system from UNC paths on Windows, which it can't handle:
2268 if sys.platform == 'win32':
2304 if sys.platform == 'win32':
2269 from IPython.utils._process_win32 import AvoidUNCPath
2305 from IPython.utils._process_win32 import AvoidUNCPath
2270 with AvoidUNCPath() as path:
2306 with AvoidUNCPath() as path:
2271 if path is not None:
2307 if path is not None:
2272 cmd = '"pushd %s &&"%s' % (path, cmd)
2308 cmd = '"pushd %s &&"%s' % (path, cmd)
2273 cmd = py3compat.unicode_to_str(cmd)
2309 cmd = py3compat.unicode_to_str(cmd)
2274 ec = os.system(cmd)
2310 ec = os.system(cmd)
2275 else:
2311 else:
2276 cmd = py3compat.unicode_to_str(cmd)
2312 cmd = py3compat.unicode_to_str(cmd)
2277 # Call the cmd using the OS shell, instead of the default /bin/sh, if set.
2313 # Call the cmd using the OS shell, instead of the default /bin/sh, if set.
2278 ec = subprocess.call(cmd, shell=True, executable=os.environ.get('SHELL', None))
2314 ec = subprocess.call(cmd, shell=True, executable=os.environ.get('SHELL', None))
2279 # exit code is positive for program failure, or negative for
2315 # exit code is positive for program failure, or negative for
2280 # terminating signal number.
2316 # terminating signal number.
2281
2317
2282 # We explicitly do NOT return the subprocess status code, because
2318 # We explicitly do NOT return the subprocess status code, because
2283 # a non-None value would trigger :func:`sys.displayhook` calls.
2319 # a non-None value would trigger :func:`sys.displayhook` calls.
2284 # Instead, we store the exit_code in user_ns.
2320 # Instead, we store the exit_code in user_ns.
2285 self.user_ns['_exit_code'] = ec
2321 self.user_ns['_exit_code'] = ec
2286
2322
2287 # use piped system by default, because it is better behaved
2323 # use piped system by default, because it is better behaved
2288 system = system_piped
2324 system = system_piped
2289
2325
2290 def getoutput(self, cmd, split=True, depth=0):
2326 def getoutput(self, cmd, split=True, depth=0):
2291 """Get output (possibly including stderr) from a subprocess.
2327 """Get output (possibly including stderr) from a subprocess.
2292
2328
2293 Parameters
2329 Parameters
2294 ----------
2330 ----------
2295 cmd : str
2331 cmd : str
2296 Command to execute (can not end in '&', as background processes are
2332 Command to execute (can not end in '&', as background processes are
2297 not supported.
2333 not supported.
2298 split : bool, optional
2334 split : bool, optional
2299 If True, split the output into an IPython SList. Otherwise, an
2335 If True, split the output into an IPython SList. Otherwise, an
2300 IPython LSString is returned. These are objects similar to normal
2336 IPython LSString is returned. These are objects similar to normal
2301 lists and strings, with a few convenience attributes for easier
2337 lists and strings, with a few convenience attributes for easier
2302 manipulation of line-based output. You can use '?' on them for
2338 manipulation of line-based output. You can use '?' on them for
2303 details.
2339 details.
2304 depth : int, optional
2340 depth : int, optional
2305 How many frames above the caller are the local variables which should
2341 How many frames above the caller are the local variables which should
2306 be expanded in the command string? The default (0) assumes that the
2342 be expanded in the command string? The default (0) assumes that the
2307 expansion variables are in the stack frame calling this function.
2343 expansion variables are in the stack frame calling this function.
2308 """
2344 """
2309 if cmd.rstrip().endswith('&'):
2345 if cmd.rstrip().endswith('&'):
2310 # this is *far* from a rigorous test
2346 # this is *far* from a rigorous test
2311 raise OSError("Background processes not supported.")
2347 raise OSError("Background processes not supported.")
2312 out = getoutput(self.var_expand(cmd, depth=depth+1))
2348 out = getoutput(self.var_expand(cmd, depth=depth+1))
2313 if split:
2349 if split:
2314 out = SList(out.splitlines())
2350 out = SList(out.splitlines())
2315 else:
2351 else:
2316 out = LSString(out)
2352 out = LSString(out)
2317 return out
2353 return out
2318
2354
2319 #-------------------------------------------------------------------------
2355 #-------------------------------------------------------------------------
2320 # Things related to aliases
2356 # Things related to aliases
2321 #-------------------------------------------------------------------------
2357 #-------------------------------------------------------------------------
2322
2358
2323 def init_alias(self):
2359 def init_alias(self):
2324 self.alias_manager = AliasManager(shell=self, parent=self)
2360 self.alias_manager = AliasManager(shell=self, parent=self)
2325 self.configurables.append(self.alias_manager)
2361 self.configurables.append(self.alias_manager)
2326
2362
2327 #-------------------------------------------------------------------------
2363 #-------------------------------------------------------------------------
2328 # Things related to extensions
2364 # Things related to extensions
2329 #-------------------------------------------------------------------------
2365 #-------------------------------------------------------------------------
2330
2366
2331 def init_extension_manager(self):
2367 def init_extension_manager(self):
2332 self.extension_manager = ExtensionManager(shell=self, parent=self)
2368 self.extension_manager = ExtensionManager(shell=self, parent=self)
2333 self.configurables.append(self.extension_manager)
2369 self.configurables.append(self.extension_manager)
2334
2370
2335 #-------------------------------------------------------------------------
2371 #-------------------------------------------------------------------------
2336 # Things related to payloads
2372 # Things related to payloads
2337 #-------------------------------------------------------------------------
2373 #-------------------------------------------------------------------------
2338
2374
2339 def init_payload(self):
2375 def init_payload(self):
2340 self.payload_manager = PayloadManager(parent=self)
2376 self.payload_manager = PayloadManager(parent=self)
2341 self.configurables.append(self.payload_manager)
2377 self.configurables.append(self.payload_manager)
2342
2378
2343 #-------------------------------------------------------------------------
2379 #-------------------------------------------------------------------------
2344 # Things related to widgets
2380 # Things related to widgets
2345 #-------------------------------------------------------------------------
2381 #-------------------------------------------------------------------------
2346
2382
2347 def init_comms(self):
2383 def init_comms(self):
2348 # not implemented in the base class
2384 # not implemented in the base class
2349 pass
2385 pass
2350
2386
2351 #-------------------------------------------------------------------------
2387 #-------------------------------------------------------------------------
2352 # Things related to the prefilter
2388 # Things related to the prefilter
2353 #-------------------------------------------------------------------------
2389 #-------------------------------------------------------------------------
2354
2390
2355 def init_prefilter(self):
2391 def init_prefilter(self):
2356 self.prefilter_manager = PrefilterManager(shell=self, parent=self)
2392 self.prefilter_manager = PrefilterManager(shell=self, parent=self)
2357 self.configurables.append(self.prefilter_manager)
2393 self.configurables.append(self.prefilter_manager)
2358 # Ultimately this will be refactored in the new interpreter code, but
2394 # Ultimately this will be refactored in the new interpreter code, but
2359 # for now, we should expose the main prefilter method (there's legacy
2395 # for now, we should expose the main prefilter method (there's legacy
2360 # code out there that may rely on this).
2396 # code out there that may rely on this).
2361 self.prefilter = self.prefilter_manager.prefilter_lines
2397 self.prefilter = self.prefilter_manager.prefilter_lines
2362
2398
2363 def auto_rewrite_input(self, cmd):
2399 def auto_rewrite_input(self, cmd):
2364 """Print to the screen the rewritten form of the user's command.
2400 """Print to the screen the rewritten form of the user's command.
2365
2401
2366 This shows visual feedback by rewriting input lines that cause
2402 This shows visual feedback by rewriting input lines that cause
2367 automatic calling to kick in, like::
2403 automatic calling to kick in, like::
2368
2404
2369 /f x
2405 /f x
2370
2406
2371 into::
2407 into::
2372
2408
2373 ------> f(x)
2409 ------> f(x)
2374
2410
2375 after the user's input prompt. This helps the user understand that the
2411 after the user's input prompt. This helps the user understand that the
2376 input line was transformed automatically by IPython.
2412 input line was transformed automatically by IPython.
2377 """
2413 """
2378 if not self.show_rewritten_input:
2414 if not self.show_rewritten_input:
2379 return
2415 return
2380
2416
2381 rw = self.prompt_manager.render('rewrite') + cmd
2417 rw = self.prompt_manager.render('rewrite') + cmd
2382
2418
2383 try:
2419 try:
2384 # plain ascii works better w/ pyreadline, on some machines, so
2420 # plain ascii works better w/ pyreadline, on some machines, so
2385 # we use it and only print uncolored rewrite if we have unicode
2421 # we use it and only print uncolored rewrite if we have unicode
2386 rw = str(rw)
2422 rw = str(rw)
2387 print(rw, file=io.stdout)
2423 print(rw, file=io.stdout)
2388 except UnicodeEncodeError:
2424 except UnicodeEncodeError:
2389 print("------> " + cmd)
2425 print("------> " + cmd)
2390
2426
2391 #-------------------------------------------------------------------------
2427 #-------------------------------------------------------------------------
2392 # Things related to extracting values/expressions from kernel and user_ns
2428 # Things related to extracting values/expressions from kernel and user_ns
2393 #-------------------------------------------------------------------------
2429 #-------------------------------------------------------------------------
2394
2430
2395 def _user_obj_error(self):
2431 def _user_obj_error(self):
2396 """return simple exception dict
2432 """return simple exception dict
2397
2433
2398 for use in user_variables / expressions
2434 for use in user_expressions
2399 """
2435 """
2400
2436
2401 etype, evalue, tb = self._get_exc_info()
2437 etype, evalue, tb = self._get_exc_info()
2402 stb = self.InteractiveTB.get_exception_only(etype, evalue)
2438 stb = self.InteractiveTB.get_exception_only(etype, evalue)
2403
2439
2404 exc_info = {
2440 exc_info = {
2405 u'status' : 'error',
2441 u'status' : 'error',
2406 u'traceback' : stb,
2442 u'traceback' : stb,
2407 u'ename' : unicode_type(etype.__name__),
2443 u'ename' : unicode_type(etype.__name__),
2408 u'evalue' : py3compat.safe_unicode(evalue),
2444 u'evalue' : py3compat.safe_unicode(evalue),
2409 }
2445 }
2410
2446
2411 return exc_info
2447 return exc_info
2412
2448
2413 def _format_user_obj(self, obj):
2449 def _format_user_obj(self, obj):
2414 """format a user object to display dict
2450 """format a user object to display dict
2415
2451
2416 for use in user_expressions / variables
2452 for use in user_expressions
2417 """
2453 """
2418
2454
2419 data, md = self.display_formatter.format(obj)
2455 data, md = self.display_formatter.format(obj)
2420 value = {
2456 value = {
2421 'status' : 'ok',
2457 'status' : 'ok',
2422 'data' : data,
2458 'data' : data,
2423 'metadata' : md,
2459 'metadata' : md,
2424 }
2460 }
2425 return value
2461 return value
2426
2462
2427 def user_variables(self, names):
2428 """Get a list of variable names from the user's namespace.
2429
2430 Parameters
2431 ----------
2432 names : list of strings
2433 A list of names of variables to be read from the user namespace.
2434
2435 Returns
2436 -------
2437 A dict, keyed by the input names and with the rich mime-type repr(s) of each value.
2438 Each element will be a sub-dict of the same form as a display_data message.
2439 """
2440 out = {}
2441 user_ns = self.user_ns
2442
2443 for varname in names:
2444 try:
2445 value = self._format_user_obj(user_ns[varname])
2446 except:
2447 value = self._user_obj_error()
2448 out[varname] = value
2449 return out
2450
2451 def user_expressions(self, expressions):
2463 def user_expressions(self, expressions):
2452 """Evaluate a dict of expressions in the user's namespace.
2464 """Evaluate a dict of expressions in the user's namespace.
2453
2465
2454 Parameters
2466 Parameters
2455 ----------
2467 ----------
2456 expressions : dict
2468 expressions : dict
2457 A dict with string keys and string values. The expression values
2469 A dict with string keys and string values. The expression values
2458 should be valid Python expressions, each of which will be evaluated
2470 should be valid Python expressions, each of which will be evaluated
2459 in the user namespace.
2471 in the user namespace.
2460
2472
2461 Returns
2473 Returns
2462 -------
2474 -------
2463 A dict, keyed like the input expressions dict, with the rich mime-typed
2475 A dict, keyed like the input expressions dict, with the rich mime-typed
2464 display_data of each value.
2476 display_data of each value.
2465 """
2477 """
2466 out = {}
2478 out = {}
2467 user_ns = self.user_ns
2479 user_ns = self.user_ns
2468 global_ns = self.user_global_ns
2480 global_ns = self.user_global_ns
2469
2481
2470 for key, expr in iteritems(expressions):
2482 for key, expr in iteritems(expressions):
2471 try:
2483 try:
2472 value = self._format_user_obj(eval(expr, global_ns, user_ns))
2484 value = self._format_user_obj(eval(expr, global_ns, user_ns))
2473 except:
2485 except:
2474 value = self._user_obj_error()
2486 value = self._user_obj_error()
2475 out[key] = value
2487 out[key] = value
2476 return out
2488 return out
2477
2489
2478 #-------------------------------------------------------------------------
2490 #-------------------------------------------------------------------------
2479 # Things related to the running of code
2491 # Things related to the running of code
2480 #-------------------------------------------------------------------------
2492 #-------------------------------------------------------------------------
2481
2493
2482 def ex(self, cmd):
2494 def ex(self, cmd):
2483 """Execute a normal python statement in user namespace."""
2495 """Execute a normal python statement in user namespace."""
2484 with self.builtin_trap:
2496 with self.builtin_trap:
2485 exec(cmd, self.user_global_ns, self.user_ns)
2497 exec(cmd, self.user_global_ns, self.user_ns)
2486
2498
2487 def ev(self, expr):
2499 def ev(self, expr):
2488 """Evaluate python expression expr in user namespace.
2500 """Evaluate python expression expr in user namespace.
2489
2501
2490 Returns the result of evaluation
2502 Returns the result of evaluation
2491 """
2503 """
2492 with self.builtin_trap:
2504 with self.builtin_trap:
2493 return eval(expr, self.user_global_ns, self.user_ns)
2505 return eval(expr, self.user_global_ns, self.user_ns)
2494
2506
2495 def safe_execfile(self, fname, *where, **kw):
2507 def safe_execfile(self, fname, *where, **kw):
2496 """A safe version of the builtin execfile().
2508 """A safe version of the builtin execfile().
2497
2509
2498 This version will never throw an exception, but instead print
2510 This version will never throw an exception, but instead print
2499 helpful error messages to the screen. This only works on pure
2511 helpful error messages to the screen. This only works on pure
2500 Python files with the .py extension.
2512 Python files with the .py extension.
2501
2513
2502 Parameters
2514 Parameters
2503 ----------
2515 ----------
2504 fname : string
2516 fname : string
2505 The name of the file to be executed.
2517 The name of the file to be executed.
2506 where : tuple
2518 where : tuple
2507 One or two namespaces, passed to execfile() as (globals,locals).
2519 One or two namespaces, passed to execfile() as (globals,locals).
2508 If only one is given, it is passed as both.
2520 If only one is given, it is passed as both.
2509 exit_ignore : bool (False)
2521 exit_ignore : bool (False)
2510 If True, then silence SystemExit for non-zero status (it is always
2522 If True, then silence SystemExit for non-zero status (it is always
2511 silenced for zero status, as it is so common).
2523 silenced for zero status, as it is so common).
2512 raise_exceptions : bool (False)
2524 raise_exceptions : bool (False)
2513 If True raise exceptions everywhere. Meant for testing.
2525 If True raise exceptions everywhere. Meant for testing.
2514
2526
2515 """
2527 """
2516 kw.setdefault('exit_ignore', False)
2528 kw.setdefault('exit_ignore', False)
2517 kw.setdefault('raise_exceptions', False)
2529 kw.setdefault('raise_exceptions', False)
2518
2530
2519 fname = os.path.abspath(os.path.expanduser(fname))
2531 fname = os.path.abspath(os.path.expanduser(fname))
2520
2532
2521 # Make sure we can open the file
2533 # Make sure we can open the file
2522 try:
2534 try:
2523 with open(fname) as thefile:
2535 with open(fname) as thefile:
2524 pass
2536 pass
2525 except:
2537 except:
2526 warn('Could not open file <%s> for safe execution.' % fname)
2538 warn('Could not open file <%s> for safe execution.' % fname)
2527 return
2539 return
2528
2540
2529 # Find things also in current directory. This is needed to mimic the
2541 # Find things also in current directory. This is needed to mimic the
2530 # behavior of running a script from the system command line, where
2542 # behavior of running a script from the system command line, where
2531 # Python inserts the script's directory into sys.path
2543 # Python inserts the script's directory into sys.path
2532 dname = os.path.dirname(fname)
2544 dname = os.path.dirname(fname)
2533
2545
2534 with prepended_to_syspath(dname):
2546 with prepended_to_syspath(dname):
2535 try:
2547 try:
2536 py3compat.execfile(fname,*where)
2548 py3compat.execfile(fname,*where)
2537 except SystemExit as status:
2549 except SystemExit as status:
2538 # If the call was made with 0 or None exit status (sys.exit(0)
2550 # If the call was made with 0 or None exit status (sys.exit(0)
2539 # or sys.exit() ), don't bother showing a traceback, as both of
2551 # or sys.exit() ), don't bother showing a traceback, as both of
2540 # these are considered normal by the OS:
2552 # these are considered normal by the OS:
2541 # > python -c'import sys;sys.exit(0)'; echo $?
2553 # > python -c'import sys;sys.exit(0)'; echo $?
2542 # 0
2554 # 0
2543 # > python -c'import sys;sys.exit()'; echo $?
2555 # > python -c'import sys;sys.exit()'; echo $?
2544 # 0
2556 # 0
2545 # For other exit status, we show the exception unless
2557 # For other exit status, we show the exception unless
2546 # explicitly silenced, but only in short form.
2558 # explicitly silenced, but only in short form.
2547 if kw['raise_exceptions']:
2559 if kw['raise_exceptions']:
2548 raise
2560 raise
2549 if status.code and not kw['exit_ignore']:
2561 if status.code and not kw['exit_ignore']:
2550 self.showtraceback(exception_only=True)
2562 self.showtraceback(exception_only=True)
2551 except:
2563 except:
2552 if kw['raise_exceptions']:
2564 if kw['raise_exceptions']:
2553 raise
2565 raise
2554 # tb offset is 2 because we wrap execfile
2566 # tb offset is 2 because we wrap execfile
2555 self.showtraceback(tb_offset=2)
2567 self.showtraceback(tb_offset=2)
2556
2568
2557 def safe_execfile_ipy(self, fname):
2569 def safe_execfile_ipy(self, fname):
2558 """Like safe_execfile, but for .ipy or .ipynb files with IPython syntax.
2570 """Like safe_execfile, but for .ipy or .ipynb files with IPython syntax.
2559
2571
2560 Parameters
2572 Parameters
2561 ----------
2573 ----------
2562 fname : str
2574 fname : str
2563 The name of the file to execute. The filename must have a
2575 The name of the file to execute. The filename must have a
2564 .ipy or .ipynb extension.
2576 .ipy or .ipynb extension.
2565 """
2577 """
2566 fname = os.path.abspath(os.path.expanduser(fname))
2578 fname = os.path.abspath(os.path.expanduser(fname))
2567
2579
2568 # Make sure we can open the file
2580 # Make sure we can open the file
2569 try:
2581 try:
2570 with open(fname) as thefile:
2582 with open(fname) as thefile:
2571 pass
2583 pass
2572 except:
2584 except:
2573 warn('Could not open file <%s> for safe execution.' % fname)
2585 warn('Could not open file <%s> for safe execution.' % fname)
2574 return
2586 return
2575
2587
2576 # Find things also in current directory. This is needed to mimic the
2588 # Find things also in current directory. This is needed to mimic the
2577 # behavior of running a script from the system command line, where
2589 # behavior of running a script from the system command line, where
2578 # Python inserts the script's directory into sys.path
2590 # Python inserts the script's directory into sys.path
2579 dname = os.path.dirname(fname)
2591 dname = os.path.dirname(fname)
2580
2592
2581 def get_cells():
2593 def get_cells():
2582 """generator for sequence of code blocks to run"""
2594 """generator for sequence of code blocks to run"""
2583 if fname.endswith('.ipynb'):
2595 if fname.endswith('.ipynb'):
2584 from IPython.nbformat import current
2596 from IPython.nbformat import current
2585 with open(fname) as f:
2597 with open(fname) as f:
2586 nb = current.read(f, 'json')
2598 nb = current.read(f, 'json')
2587 if not nb.worksheets:
2599 if not nb.worksheets:
2588 return
2600 return
2589 for cell in nb.worksheets[0].cells:
2601 for cell in nb.worksheets[0].cells:
2590 if cell.cell_type == 'code':
2602 if cell.cell_type == 'code':
2591 yield cell.input
2603 yield cell.input
2592 else:
2604 else:
2593 with open(fname) as f:
2605 with open(fname) as f:
2594 yield f.read()
2606 yield f.read()
2595
2607
2596 with prepended_to_syspath(dname):
2608 with prepended_to_syspath(dname):
2597 try:
2609 try:
2598 for cell in get_cells():
2610 for cell in get_cells():
2599 # self.run_cell currently captures all exceptions
2611 # self.run_cell currently captures all exceptions
2600 # raised in user code. It would be nice if there were
2612 # raised in user code. It would be nice if there were
2601 # versions of run_cell that did raise, so
2613 # versions of run_cell that did raise, so
2602 # we could catch the errors.
2614 # we could catch the errors.
2603 self.run_cell(cell, silent=True, shell_futures=False)
2615 self.run_cell(cell, silent=True, shell_futures=False)
2604 except:
2616 except:
2605 self.showtraceback()
2617 self.showtraceback()
2606 warn('Unknown failure executing file: <%s>' % fname)
2618 warn('Unknown failure executing file: <%s>' % fname)
2607
2619
2608 def safe_run_module(self, mod_name, where):
2620 def safe_run_module(self, mod_name, where):
2609 """A safe version of runpy.run_module().
2621 """A safe version of runpy.run_module().
2610
2622
2611 This version will never throw an exception, but instead print
2623 This version will never throw an exception, but instead print
2612 helpful error messages to the screen.
2624 helpful error messages to the screen.
2613
2625
2614 `SystemExit` exceptions with status code 0 or None are ignored.
2626 `SystemExit` exceptions with status code 0 or None are ignored.
2615
2627
2616 Parameters
2628 Parameters
2617 ----------
2629 ----------
2618 mod_name : string
2630 mod_name : string
2619 The name of the module to be executed.
2631 The name of the module to be executed.
2620 where : dict
2632 where : dict
2621 The globals namespace.
2633 The globals namespace.
2622 """
2634 """
2623 try:
2635 try:
2624 try:
2636 try:
2625 where.update(
2637 where.update(
2626 runpy.run_module(str(mod_name), run_name="__main__",
2638 runpy.run_module(str(mod_name), run_name="__main__",
2627 alter_sys=True)
2639 alter_sys=True)
2628 )
2640 )
2629 except SystemExit as status:
2641 except SystemExit as status:
2630 if status.code:
2642 if status.code:
2631 raise
2643 raise
2632 except:
2644 except:
2633 self.showtraceback()
2645 self.showtraceback()
2634 warn('Unknown failure executing module: <%s>' % mod_name)
2646 warn('Unknown failure executing module: <%s>' % mod_name)
2635
2647
2636 def _run_cached_cell_magic(self, magic_name, line):
2648 def _run_cached_cell_magic(self, magic_name, line):
2637 """Special method to call a cell magic with the data stored in self.
2649 """Special method to call a cell magic with the data stored in self.
2638 """
2650 """
2639 cell = self._current_cell_magic_body
2651 cell = self._current_cell_magic_body
2640 self._current_cell_magic_body = None
2652 self._current_cell_magic_body = None
2641 return self.run_cell_magic(magic_name, line, cell)
2653 return self.run_cell_magic(magic_name, line, cell)
2642
2654
2643 def run_cell(self, raw_cell, store_history=False, silent=False, shell_futures=True):
2655 def run_cell(self, raw_cell, store_history=False, silent=False, shell_futures=True):
2644 """Run a complete IPython cell.
2656 """Run a complete IPython cell.
2645
2657
2646 Parameters
2658 Parameters
2647 ----------
2659 ----------
2648 raw_cell : str
2660 raw_cell : str
2649 The code (including IPython code such as %magic functions) to run.
2661 The code (including IPython code such as %magic functions) to run.
2650 store_history : bool
2662 store_history : bool
2651 If True, the raw and translated cell will be stored in IPython's
2663 If True, the raw and translated cell will be stored in IPython's
2652 history. For user code calling back into IPython's machinery, this
2664 history. For user code calling back into IPython's machinery, this
2653 should be set to False.
2665 should be set to False.
2654 silent : bool
2666 silent : bool
2655 If True, avoid side-effects, such as implicit displayhooks and
2667 If True, avoid side-effects, such as implicit displayhooks and
2656 and logging. silent=True forces store_history=False.
2668 and logging. silent=True forces store_history=False.
2657 shell_futures : bool
2669 shell_futures : bool
2658 If True, the code will share future statements with the interactive
2670 If True, the code will share future statements with the interactive
2659 shell. It will both be affected by previous __future__ imports, and
2671 shell. It will both be affected by previous __future__ imports, and
2660 any __future__ imports in the code will affect the shell. If False,
2672 any __future__ imports in the code will affect the shell. If False,
2661 __future__ imports are not shared in either direction.
2673 __future__ imports are not shared in either direction.
2662 """
2674 """
2663 if (not raw_cell) or raw_cell.isspace():
2675 if (not raw_cell) or raw_cell.isspace():
2664 return
2676 return
2665
2677
2666 if silent:
2678 if silent:
2667 store_history = False
2679 store_history = False
2668
2680
2669 self.events.trigger('pre_execute')
2681 self.events.trigger('pre_execute')
2670 if not silent:
2682 if not silent:
2671 self.events.trigger('pre_run_cell')
2683 self.events.trigger('pre_run_cell')
2672
2684
2673 # If any of our input transformation (input_transformer_manager or
2685 # If any of our input transformation (input_transformer_manager or
2674 # prefilter_manager) raises an exception, we store it in this variable
2686 # prefilter_manager) raises an exception, we store it in this variable
2675 # so that we can display the error after logging the input and storing
2687 # so that we can display the error after logging the input and storing
2676 # it in the history.
2688 # it in the history.
2677 preprocessing_exc_tuple = None
2689 preprocessing_exc_tuple = None
2678 try:
2690 try:
2679 # Static input transformations
2691 # Static input transformations
2680 cell = self.input_transformer_manager.transform_cell(raw_cell)
2692 cell = self.input_transformer_manager.transform_cell(raw_cell)
2681 except SyntaxError:
2693 except SyntaxError:
2682 preprocessing_exc_tuple = sys.exc_info()
2694 preprocessing_exc_tuple = sys.exc_info()
2683 cell = raw_cell # cell has to exist so it can be stored/logged
2695 cell = raw_cell # cell has to exist so it can be stored/logged
2684 else:
2696 else:
2685 if len(cell.splitlines()) == 1:
2697 if len(cell.splitlines()) == 1:
2686 # Dynamic transformations - only applied for single line commands
2698 # Dynamic transformations - only applied for single line commands
2687 with self.builtin_trap:
2699 with self.builtin_trap:
2688 try:
2700 try:
2689 # use prefilter_lines to handle trailing newlines
2701 # use prefilter_lines to handle trailing newlines
2690 # restore trailing newline for ast.parse
2702 # restore trailing newline for ast.parse
2691 cell = self.prefilter_manager.prefilter_lines(cell) + '\n'
2703 cell = self.prefilter_manager.prefilter_lines(cell) + '\n'
2692 except Exception:
2704 except Exception:
2693 # don't allow prefilter errors to crash IPython
2705 # don't allow prefilter errors to crash IPython
2694 preprocessing_exc_tuple = sys.exc_info()
2706 preprocessing_exc_tuple = sys.exc_info()
2695
2707
2696 # Store raw and processed history
2708 # Store raw and processed history
2697 if store_history:
2709 if store_history:
2698 self.history_manager.store_inputs(self.execution_count,
2710 self.history_manager.store_inputs(self.execution_count,
2699 cell, raw_cell)
2711 cell, raw_cell)
2700 if not silent:
2712 if not silent:
2701 self.logger.log(cell, raw_cell)
2713 self.logger.log(cell, raw_cell)
2702
2714
2703 # Display the exception if input processing failed.
2715 # Display the exception if input processing failed.
2704 if preprocessing_exc_tuple is not None:
2716 if preprocessing_exc_tuple is not None:
2705 self.showtraceback(preprocessing_exc_tuple)
2717 self.showtraceback(preprocessing_exc_tuple)
2706 if store_history:
2718 if store_history:
2707 self.execution_count += 1
2719 self.execution_count += 1
2708 return
2720 return
2709
2721
2710 # Our own compiler remembers the __future__ environment. If we want to
2722 # Our own compiler remembers the __future__ environment. If we want to
2711 # run code with a separate __future__ environment, use the default
2723 # run code with a separate __future__ environment, use the default
2712 # compiler
2724 # compiler
2713 compiler = self.compile if shell_futures else CachingCompiler()
2725 compiler = self.compile if shell_futures else CachingCompiler()
2714
2726
2715 with self.builtin_trap:
2727 with self.builtin_trap:
2716 cell_name = self.compile.cache(cell, self.execution_count)
2728 cell_name = self.compile.cache(cell, self.execution_count)
2717
2729
2718 with self.display_trap:
2730 with self.display_trap:
2719 # Compile to bytecode
2731 # Compile to bytecode
2720 try:
2732 try:
2721 code_ast = compiler.ast_parse(cell, filename=cell_name)
2733 code_ast = compiler.ast_parse(cell, filename=cell_name)
2722 except IndentationError:
2734 except IndentationError:
2723 self.showindentationerror()
2735 self.showindentationerror()
2724 if store_history:
2736 if store_history:
2725 self.execution_count += 1
2737 self.execution_count += 1
2726 return None
2738 return None
2727 except (OverflowError, SyntaxError, ValueError, TypeError,
2739 except (OverflowError, SyntaxError, ValueError, TypeError,
2728 MemoryError):
2740 MemoryError):
2729 self.showsyntaxerror()
2741 self.showsyntaxerror()
2730 if store_history:
2742 if store_history:
2731 self.execution_count += 1
2743 self.execution_count += 1
2732 return None
2744 return None
2733
2745
2734 # Apply AST transformations
2746 # Apply AST transformations
2735 code_ast = self.transform_ast(code_ast)
2747 code_ast = self.transform_ast(code_ast)
2736
2748
2737 # Execute the user code
2749 # Execute the user code
2738 interactivity = "none" if silent else self.ast_node_interactivity
2750 interactivity = "none" if silent else self.ast_node_interactivity
2739 self.run_ast_nodes(code_ast.body, cell_name,
2751 self.run_ast_nodes(code_ast.body, cell_name,
2740 interactivity=interactivity, compiler=compiler)
2752 interactivity=interactivity, compiler=compiler)
2741
2753
2742 self.events.trigger('post_execute')
2754 self.events.trigger('post_execute')
2743 if not silent:
2755 if not silent:
2744 self.events.trigger('post_run_cell')
2756 self.events.trigger('post_run_cell')
2745
2757
2746 if store_history:
2758 if store_history:
2747 # Write output to the database. Does nothing unless
2759 # Write output to the database. Does nothing unless
2748 # history output logging is enabled.
2760 # history output logging is enabled.
2749 self.history_manager.store_output(self.execution_count)
2761 self.history_manager.store_output(self.execution_count)
2750 # Each cell is a *single* input, regardless of how many lines it has
2762 # Each cell is a *single* input, regardless of how many lines it has
2751 self.execution_count += 1
2763 self.execution_count += 1
2752
2764
2753 def transform_ast(self, node):
2765 def transform_ast(self, node):
2754 """Apply the AST transformations from self.ast_transformers
2766 """Apply the AST transformations from self.ast_transformers
2755
2767
2756 Parameters
2768 Parameters
2757 ----------
2769 ----------
2758 node : ast.Node
2770 node : ast.Node
2759 The root node to be transformed. Typically called with the ast.Module
2771 The root node to be transformed. Typically called with the ast.Module
2760 produced by parsing user input.
2772 produced by parsing user input.
2761
2773
2762 Returns
2774 Returns
2763 -------
2775 -------
2764 An ast.Node corresponding to the node it was called with. Note that it
2776 An ast.Node corresponding to the node it was called with. Note that it
2765 may also modify the passed object, so don't rely on references to the
2777 may also modify the passed object, so don't rely on references to the
2766 original AST.
2778 original AST.
2767 """
2779 """
2768 for transformer in self.ast_transformers:
2780 for transformer in self.ast_transformers:
2769 try:
2781 try:
2770 node = transformer.visit(node)
2782 node = transformer.visit(node)
2771 except Exception:
2783 except Exception:
2772 warn("AST transformer %r threw an error. It will be unregistered." % transformer)
2784 warn("AST transformer %r threw an error. It will be unregistered." % transformer)
2773 self.ast_transformers.remove(transformer)
2785 self.ast_transformers.remove(transformer)
2774
2786
2775 if self.ast_transformers:
2787 if self.ast_transformers:
2776 ast.fix_missing_locations(node)
2788 ast.fix_missing_locations(node)
2777 return node
2789 return node
2778
2790
2779
2791
2780 def run_ast_nodes(self, nodelist, cell_name, interactivity='last_expr',
2792 def run_ast_nodes(self, nodelist, cell_name, interactivity='last_expr',
2781 compiler=compile):
2793 compiler=compile):
2782 """Run a sequence of AST nodes. The execution mode depends on the
2794 """Run a sequence of AST nodes. The execution mode depends on the
2783 interactivity parameter.
2795 interactivity parameter.
2784
2796
2785 Parameters
2797 Parameters
2786 ----------
2798 ----------
2787 nodelist : list
2799 nodelist : list
2788 A sequence of AST nodes to run.
2800 A sequence of AST nodes to run.
2789 cell_name : str
2801 cell_name : str
2790 Will be passed to the compiler as the filename of the cell. Typically
2802 Will be passed to the compiler as the filename of the cell. Typically
2791 the value returned by ip.compile.cache(cell).
2803 the value returned by ip.compile.cache(cell).
2792 interactivity : str
2804 interactivity : str
2793 'all', 'last', 'last_expr' or 'none', specifying which nodes should be
2805 'all', 'last', 'last_expr' or 'none', specifying which nodes should be
2794 run interactively (displaying output from expressions). 'last_expr'
2806 run interactively (displaying output from expressions). 'last_expr'
2795 will run the last node interactively only if it is an expression (i.e.
2807 will run the last node interactively only if it is an expression (i.e.
2796 expressions in loops or other blocks are not displayed. Other values
2808 expressions in loops or other blocks are not displayed. Other values
2797 for this parameter will raise a ValueError.
2809 for this parameter will raise a ValueError.
2798 compiler : callable
2810 compiler : callable
2799 A function with the same interface as the built-in compile(), to turn
2811 A function with the same interface as the built-in compile(), to turn
2800 the AST nodes into code objects. Default is the built-in compile().
2812 the AST nodes into code objects. Default is the built-in compile().
2801 """
2813 """
2802 if not nodelist:
2814 if not nodelist:
2803 return
2815 return
2804
2816
2805 if interactivity == 'last_expr':
2817 if interactivity == 'last_expr':
2806 if isinstance(nodelist[-1], ast.Expr):
2818 if isinstance(nodelist[-1], ast.Expr):
2807 interactivity = "last"
2819 interactivity = "last"
2808 else:
2820 else:
2809 interactivity = "none"
2821 interactivity = "none"
2810
2822
2811 if interactivity == 'none':
2823 if interactivity == 'none':
2812 to_run_exec, to_run_interactive = nodelist, []
2824 to_run_exec, to_run_interactive = nodelist, []
2813 elif interactivity == 'last':
2825 elif interactivity == 'last':
2814 to_run_exec, to_run_interactive = nodelist[:-1], nodelist[-1:]
2826 to_run_exec, to_run_interactive = nodelist[:-1], nodelist[-1:]
2815 elif interactivity == 'all':
2827 elif interactivity == 'all':
2816 to_run_exec, to_run_interactive = [], nodelist
2828 to_run_exec, to_run_interactive = [], nodelist
2817 else:
2829 else:
2818 raise ValueError("Interactivity was %r" % interactivity)
2830 raise ValueError("Interactivity was %r" % interactivity)
2819
2831
2820 exec_count = self.execution_count
2832 exec_count = self.execution_count
2821
2833
2822 try:
2834 try:
2823 for i, node in enumerate(to_run_exec):
2835 for i, node in enumerate(to_run_exec):
2824 mod = ast.Module([node])
2836 mod = ast.Module([node])
2825 code = compiler(mod, cell_name, "exec")
2837 code = compiler(mod, cell_name, "exec")
2826 if self.run_code(code):
2838 if self.run_code(code):
2827 return True
2839 return True
2828
2840
2829 for i, node in enumerate(to_run_interactive):
2841 for i, node in enumerate(to_run_interactive):
2830 mod = ast.Interactive([node])
2842 mod = ast.Interactive([node])
2831 code = compiler(mod, cell_name, "single")
2843 code = compiler(mod, cell_name, "single")
2832 if self.run_code(code):
2844 if self.run_code(code):
2833 return True
2845 return True
2834
2846
2835 # Flush softspace
2847 # Flush softspace
2836 if softspace(sys.stdout, 0):
2848 if softspace(sys.stdout, 0):
2837 print()
2849 print()
2838
2850
2839 except:
2851 except:
2840 # It's possible to have exceptions raised here, typically by
2852 # It's possible to have exceptions raised here, typically by
2841 # compilation of odd code (such as a naked 'return' outside a
2853 # compilation of odd code (such as a naked 'return' outside a
2842 # function) that did parse but isn't valid. Typically the exception
2854 # function) that did parse but isn't valid. Typically the exception
2843 # is a SyntaxError, but it's safest just to catch anything and show
2855 # is a SyntaxError, but it's safest just to catch anything and show
2844 # the user a traceback.
2856 # the user a traceback.
2845
2857
2846 # We do only one try/except outside the loop to minimize the impact
2858 # We do only one try/except outside the loop to minimize the impact
2847 # on runtime, and also because if any node in the node list is
2859 # on runtime, and also because if any node in the node list is
2848 # broken, we should stop execution completely.
2860 # broken, we should stop execution completely.
2849 self.showtraceback()
2861 self.showtraceback()
2850
2862
2851 return False
2863 return False
2852
2864
2853 def run_code(self, code_obj):
2865 def run_code(self, code_obj):
2854 """Execute a code object.
2866 """Execute a code object.
2855
2867
2856 When an exception occurs, self.showtraceback() is called to display a
2868 When an exception occurs, self.showtraceback() is called to display a
2857 traceback.
2869 traceback.
2858
2870
2859 Parameters
2871 Parameters
2860 ----------
2872 ----------
2861 code_obj : code object
2873 code_obj : code object
2862 A compiled code object, to be executed
2874 A compiled code object, to be executed
2863
2875
2864 Returns
2876 Returns
2865 -------
2877 -------
2866 False : successful execution.
2878 False : successful execution.
2867 True : an error occurred.
2879 True : an error occurred.
2868 """
2880 """
2869
2881
2870 # Set our own excepthook in case the user code tries to call it
2882 # Set our own excepthook in case the user code tries to call it
2871 # directly, so that the IPython crash handler doesn't get triggered
2883 # directly, so that the IPython crash handler doesn't get triggered
2872 old_excepthook,sys.excepthook = sys.excepthook, self.excepthook
2884 old_excepthook,sys.excepthook = sys.excepthook, self.excepthook
2873
2885
2874 # we save the original sys.excepthook in the instance, in case config
2886 # we save the original sys.excepthook in the instance, in case config
2875 # code (such as magics) needs access to it.
2887 # code (such as magics) needs access to it.
2876 self.sys_excepthook = old_excepthook
2888 self.sys_excepthook = old_excepthook
2877 outflag = 1 # happens in more places, so it's easier as default
2889 outflag = 1 # happens in more places, so it's easier as default
2878 try:
2890 try:
2879 try:
2891 try:
2880 self.hooks.pre_run_code_hook()
2892 self.hooks.pre_run_code_hook()
2881 #rprint('Running code', repr(code_obj)) # dbg
2893 #rprint('Running code', repr(code_obj)) # dbg
2882 exec(code_obj, self.user_global_ns, self.user_ns)
2894 exec(code_obj, self.user_global_ns, self.user_ns)
2883 finally:
2895 finally:
2884 # Reset our crash handler in place
2896 # Reset our crash handler in place
2885 sys.excepthook = old_excepthook
2897 sys.excepthook = old_excepthook
2886 except SystemExit:
2898 except SystemExit:
2887 self.showtraceback(exception_only=True)
2899 self.showtraceback(exception_only=True)
2888 warn("To exit: use 'exit', 'quit', or Ctrl-D.", level=1)
2900 warn("To exit: use 'exit', 'quit', or Ctrl-D.", level=1)
2889 except self.custom_exceptions:
2901 except self.custom_exceptions:
2890 etype,value,tb = sys.exc_info()
2902 etype,value,tb = sys.exc_info()
2891 self.CustomTB(etype,value,tb)
2903 self.CustomTB(etype,value,tb)
2892 except:
2904 except:
2893 self.showtraceback()
2905 self.showtraceback()
2894 else:
2906 else:
2895 outflag = 0
2907 outflag = 0
2896 return outflag
2908 return outflag
2897
2909
2898 # For backwards compatibility
2910 # For backwards compatibility
2899 runcode = run_code
2911 runcode = run_code
2900
2912
2901 #-------------------------------------------------------------------------
2913 #-------------------------------------------------------------------------
2902 # Things related to GUI support and pylab
2914 # Things related to GUI support and pylab
2903 #-------------------------------------------------------------------------
2915 #-------------------------------------------------------------------------
2904
2916
2905 def enable_gui(self, gui=None):
2917 def enable_gui(self, gui=None):
2906 raise NotImplementedError('Implement enable_gui in a subclass')
2918 raise NotImplementedError('Implement enable_gui in a subclass')
2907
2919
2908 def enable_matplotlib(self, gui=None):
2920 def enable_matplotlib(self, gui=None):
2909 """Enable interactive matplotlib and inline figure support.
2921 """Enable interactive matplotlib and inline figure support.
2910
2922
2911 This takes the following steps:
2923 This takes the following steps:
2912
2924
2913 1. select the appropriate eventloop and matplotlib backend
2925 1. select the appropriate eventloop and matplotlib backend
2914 2. set up matplotlib for interactive use with that backend
2926 2. set up matplotlib for interactive use with that backend
2915 3. configure formatters for inline figure display
2927 3. configure formatters for inline figure display
2916 4. enable the selected gui eventloop
2928 4. enable the selected gui eventloop
2917
2929
2918 Parameters
2930 Parameters
2919 ----------
2931 ----------
2920 gui : optional, string
2932 gui : optional, string
2921 If given, dictates the choice of matplotlib GUI backend to use
2933 If given, dictates the choice of matplotlib GUI backend to use
2922 (should be one of IPython's supported backends, 'qt', 'osx', 'tk',
2934 (should be one of IPython's supported backends, 'qt', 'osx', 'tk',
2923 'gtk', 'wx' or 'inline'), otherwise we use the default chosen by
2935 'gtk', 'wx' or 'inline'), otherwise we use the default chosen by
2924 matplotlib (as dictated by the matplotlib build-time options plus the
2936 matplotlib (as dictated by the matplotlib build-time options plus the
2925 user's matplotlibrc configuration file). Note that not all backends
2937 user's matplotlibrc configuration file). Note that not all backends
2926 make sense in all contexts, for example a terminal ipython can't
2938 make sense in all contexts, for example a terminal ipython can't
2927 display figures inline.
2939 display figures inline.
2928 """
2940 """
2929 from IPython.core import pylabtools as pt
2941 from IPython.core import pylabtools as pt
2930 gui, backend = pt.find_gui_and_backend(gui, self.pylab_gui_select)
2942 gui, backend = pt.find_gui_and_backend(gui, self.pylab_gui_select)
2931
2943
2932 if gui != 'inline':
2944 if gui != 'inline':
2933 # If we have our first gui selection, store it
2945 # If we have our first gui selection, store it
2934 if self.pylab_gui_select is None:
2946 if self.pylab_gui_select is None:
2935 self.pylab_gui_select = gui
2947 self.pylab_gui_select = gui
2936 # Otherwise if they are different
2948 # Otherwise if they are different
2937 elif gui != self.pylab_gui_select:
2949 elif gui != self.pylab_gui_select:
2938 print ('Warning: Cannot change to a different GUI toolkit: %s.'
2950 print ('Warning: Cannot change to a different GUI toolkit: %s.'
2939 ' Using %s instead.' % (gui, self.pylab_gui_select))
2951 ' Using %s instead.' % (gui, self.pylab_gui_select))
2940 gui, backend = pt.find_gui_and_backend(self.pylab_gui_select)
2952 gui, backend = pt.find_gui_and_backend(self.pylab_gui_select)
2941
2953
2942 pt.activate_matplotlib(backend)
2954 pt.activate_matplotlib(backend)
2943 pt.configure_inline_support(self, backend)
2955 pt.configure_inline_support(self, backend)
2944
2956
2945 # Now we must activate the gui pylab wants to use, and fix %run to take
2957 # Now we must activate the gui pylab wants to use, and fix %run to take
2946 # plot updates into account
2958 # plot updates into account
2947 self.enable_gui(gui)
2959 self.enable_gui(gui)
2948 self.magics_manager.registry['ExecutionMagics'].default_runner = \
2960 self.magics_manager.registry['ExecutionMagics'].default_runner = \
2949 pt.mpl_runner(self.safe_execfile)
2961 pt.mpl_runner(self.safe_execfile)
2950
2962
2951 return gui, backend
2963 return gui, backend
2952
2964
2953 def enable_pylab(self, gui=None, import_all=True, welcome_message=False):
2965 def enable_pylab(self, gui=None, import_all=True, welcome_message=False):
2954 """Activate pylab support at runtime.
2966 """Activate pylab support at runtime.
2955
2967
2956 This turns on support for matplotlib, preloads into the interactive
2968 This turns on support for matplotlib, preloads into the interactive
2957 namespace all of numpy and pylab, and configures IPython to correctly
2969 namespace all of numpy and pylab, and configures IPython to correctly
2958 interact with the GUI event loop. The GUI backend to be used can be
2970 interact with the GUI event loop. The GUI backend to be used can be
2959 optionally selected with the optional ``gui`` argument.
2971 optionally selected with the optional ``gui`` argument.
2960
2972
2961 This method only adds preloading the namespace to InteractiveShell.enable_matplotlib.
2973 This method only adds preloading the namespace to InteractiveShell.enable_matplotlib.
2962
2974
2963 Parameters
2975 Parameters
2964 ----------
2976 ----------
2965 gui : optional, string
2977 gui : optional, string
2966 If given, dictates the choice of matplotlib GUI backend to use
2978 If given, dictates the choice of matplotlib GUI backend to use
2967 (should be one of IPython's supported backends, 'qt', 'osx', 'tk',
2979 (should be one of IPython's supported backends, 'qt', 'osx', 'tk',
2968 'gtk', 'wx' or 'inline'), otherwise we use the default chosen by
2980 'gtk', 'wx' or 'inline'), otherwise we use the default chosen by
2969 matplotlib (as dictated by the matplotlib build-time options plus the
2981 matplotlib (as dictated by the matplotlib build-time options plus the
2970 user's matplotlibrc configuration file). Note that not all backends
2982 user's matplotlibrc configuration file). Note that not all backends
2971 make sense in all contexts, for example a terminal ipython can't
2983 make sense in all contexts, for example a terminal ipython can't
2972 display figures inline.
2984 display figures inline.
2973 import_all : optional, bool, default: True
2985 import_all : optional, bool, default: True
2974 Whether to do `from numpy import *` and `from pylab import *`
2986 Whether to do `from numpy import *` and `from pylab import *`
2975 in addition to module imports.
2987 in addition to module imports.
2976 welcome_message : deprecated
2988 welcome_message : deprecated
2977 This argument is ignored, no welcome message will be displayed.
2989 This argument is ignored, no welcome message will be displayed.
2978 """
2990 """
2979 from IPython.core.pylabtools import import_pylab
2991 from IPython.core.pylabtools import import_pylab
2980
2992
2981 gui, backend = self.enable_matplotlib(gui)
2993 gui, backend = self.enable_matplotlib(gui)
2982
2994
2983 # We want to prevent the loading of pylab to pollute the user's
2995 # We want to prevent the loading of pylab to pollute the user's
2984 # namespace as shown by the %who* magics, so we execute the activation
2996 # namespace as shown by the %who* magics, so we execute the activation
2985 # code in an empty namespace, and we update *both* user_ns and
2997 # code in an empty namespace, and we update *both* user_ns and
2986 # user_ns_hidden with this information.
2998 # user_ns_hidden with this information.
2987 ns = {}
2999 ns = {}
2988 import_pylab(ns, import_all)
3000 import_pylab(ns, import_all)
2989 # warn about clobbered names
3001 # warn about clobbered names
2990 ignored = set(["__builtins__"])
3002 ignored = set(["__builtins__"])
2991 both = set(ns).intersection(self.user_ns).difference(ignored)
3003 both = set(ns).intersection(self.user_ns).difference(ignored)
2992 clobbered = [ name for name in both if self.user_ns[name] is not ns[name] ]
3004 clobbered = [ name for name in both if self.user_ns[name] is not ns[name] ]
2993 self.user_ns.update(ns)
3005 self.user_ns.update(ns)
2994 self.user_ns_hidden.update(ns)
3006 self.user_ns_hidden.update(ns)
2995 return gui, backend, clobbered
3007 return gui, backend, clobbered
2996
3008
2997 #-------------------------------------------------------------------------
3009 #-------------------------------------------------------------------------
2998 # Utilities
3010 # Utilities
2999 #-------------------------------------------------------------------------
3011 #-------------------------------------------------------------------------
3000
3012
3001 def var_expand(self, cmd, depth=0, formatter=DollarFormatter()):
3013 def var_expand(self, cmd, depth=0, formatter=DollarFormatter()):
3002 """Expand python variables in a string.
3014 """Expand python variables in a string.
3003
3015
3004 The depth argument indicates how many frames above the caller should
3016 The depth argument indicates how many frames above the caller should
3005 be walked to look for the local namespace where to expand variables.
3017 be walked to look for the local namespace where to expand variables.
3006
3018
3007 The global namespace for expansion is always the user's interactive
3019 The global namespace for expansion is always the user's interactive
3008 namespace.
3020 namespace.
3009 """
3021 """
3010 ns = self.user_ns.copy()
3022 ns = self.user_ns.copy()
3011 ns.update(sys._getframe(depth+1).f_locals)
3023 ns.update(sys._getframe(depth+1).f_locals)
3012 try:
3024 try:
3013 # We have to use .vformat() here, because 'self' is a valid and common
3025 # We have to use .vformat() here, because 'self' is a valid and common
3014 # name, and expanding **ns for .format() would make it collide with
3026 # name, and expanding **ns for .format() would make it collide with
3015 # the 'self' argument of the method.
3027 # the 'self' argument of the method.
3016 cmd = formatter.vformat(cmd, args=[], kwargs=ns)
3028 cmd = formatter.vformat(cmd, args=[], kwargs=ns)
3017 except Exception:
3029 except Exception:
3018 # if formatter couldn't format, just let it go untransformed
3030 # if formatter couldn't format, just let it go untransformed
3019 pass
3031 pass
3020 return cmd
3032 return cmd
3021
3033
3022 def mktempfile(self, data=None, prefix='ipython_edit_'):
3034 def mktempfile(self, data=None, prefix='ipython_edit_'):
3023 """Make a new tempfile and return its filename.
3035 """Make a new tempfile and return its filename.
3024
3036
3025 This makes a call to tempfile.mkstemp (created in a tempfile.mkdtemp),
3037 This makes a call to tempfile.mkstemp (created in a tempfile.mkdtemp),
3026 but it registers the created filename internally so ipython cleans it up
3038 but it registers the created filename internally so ipython cleans it up
3027 at exit time.
3039 at exit time.
3028
3040
3029 Optional inputs:
3041 Optional inputs:
3030
3042
3031 - data(None): if data is given, it gets written out to the temp file
3043 - data(None): if data is given, it gets written out to the temp file
3032 immediately, and the file is closed again."""
3044 immediately, and the file is closed again."""
3033
3045
3034 dirname = tempfile.mkdtemp(prefix=prefix)
3046 dirname = tempfile.mkdtemp(prefix=prefix)
3035 self.tempdirs.append(dirname)
3047 self.tempdirs.append(dirname)
3036
3048
3037 handle, filename = tempfile.mkstemp('.py', prefix, dir=dirname)
3049 handle, filename = tempfile.mkstemp('.py', prefix, dir=dirname)
3038 self.tempfiles.append(filename)
3050 self.tempfiles.append(filename)
3039
3051
3040 if data:
3052 if data:
3041 tmp_file = open(filename,'w')
3053 tmp_file = open(filename,'w')
3042 tmp_file.write(data)
3054 tmp_file.write(data)
3043 tmp_file.close()
3055 tmp_file.close()
3044 return filename
3056 return filename
3045
3057
3046 # TODO: This should be removed when Term is refactored.
3058 # TODO: This should be removed when Term is refactored.
3047 def write(self,data):
3059 def write(self,data):
3048 """Write a string to the default output"""
3060 """Write a string to the default output"""
3049 io.stdout.write(data)
3061 io.stdout.write(data)
3050
3062
3051 # TODO: This should be removed when Term is refactored.
3063 # TODO: This should be removed when Term is refactored.
3052 def write_err(self,data):
3064 def write_err(self,data):
3053 """Write a string to the default error output"""
3065 """Write a string to the default error output"""
3054 io.stderr.write(data)
3066 io.stderr.write(data)
3055
3067
3056 def ask_yes_no(self, prompt, default=None):
3068 def ask_yes_no(self, prompt, default=None):
3057 if self.quiet:
3069 if self.quiet:
3058 return True
3070 return True
3059 return ask_yes_no(prompt,default)
3071 return ask_yes_no(prompt,default)
3060
3072
3061 def show_usage(self):
3073 def show_usage(self):
3062 """Show a usage message"""
3074 """Show a usage message"""
3063 page.page(IPython.core.usage.interactive_usage)
3075 page.page(IPython.core.usage.interactive_usage)
3064
3076
3065 def extract_input_lines(self, range_str, raw=False):
3077 def extract_input_lines(self, range_str, raw=False):
3066 """Return as a string a set of input history slices.
3078 """Return as a string a set of input history slices.
3067
3079
3068 Parameters
3080 Parameters
3069 ----------
3081 ----------
3070 range_str : string
3082 range_str : string
3071 The set of slices is given as a string, like "~5/6-~4/2 4:8 9",
3083 The set of slices is given as a string, like "~5/6-~4/2 4:8 9",
3072 since this function is for use by magic functions which get their
3084 since this function is for use by magic functions which get their
3073 arguments as strings. The number before the / is the session
3085 arguments as strings. The number before the / is the session
3074 number: ~n goes n back from the current session.
3086 number: ~n goes n back from the current session.
3075
3087
3076 raw : bool, optional
3088 raw : bool, optional
3077 By default, the processed input is used. If this is true, the raw
3089 By default, the processed input is used. If this is true, the raw
3078 input history is used instead.
3090 input history is used instead.
3079
3091
3080 Notes
3092 Notes
3081 -----
3093 -----
3082
3094
3083 Slices can be described with two notations:
3095 Slices can be described with two notations:
3084
3096
3085 * ``N:M`` -> standard python form, means including items N...(M-1).
3097 * ``N:M`` -> standard python form, means including items N...(M-1).
3086 * ``N-M`` -> include items N..M (closed endpoint).
3098 * ``N-M`` -> include items N..M (closed endpoint).
3087 """
3099 """
3088 lines = self.history_manager.get_range_by_str(range_str, raw=raw)
3100 lines = self.history_manager.get_range_by_str(range_str, raw=raw)
3089 return "\n".join(x for _, _, x in lines)
3101 return "\n".join(x for _, _, x in lines)
3090
3102
3091 def find_user_code(self, target, raw=True, py_only=False, skip_encoding_cookie=True, search_ns=False):
3103 def find_user_code(self, target, raw=True, py_only=False, skip_encoding_cookie=True, search_ns=False):
3092 """Get a code string from history, file, url, or a string or macro.
3104 """Get a code string from history, file, url, or a string or macro.
3093
3105
3094 This is mainly used by magic functions.
3106 This is mainly used by magic functions.
3095
3107
3096 Parameters
3108 Parameters
3097 ----------
3109 ----------
3098
3110
3099 target : str
3111 target : str
3100
3112
3101 A string specifying code to retrieve. This will be tried respectively
3113 A string specifying code to retrieve. This will be tried respectively
3102 as: ranges of input history (see %history for syntax), url,
3114 as: ranges of input history (see %history for syntax), url,
3103 correspnding .py file, filename, or an expression evaluating to a
3115 correspnding .py file, filename, or an expression evaluating to a
3104 string or Macro in the user namespace.
3116 string or Macro in the user namespace.
3105
3117
3106 raw : bool
3118 raw : bool
3107 If true (default), retrieve raw history. Has no effect on the other
3119 If true (default), retrieve raw history. Has no effect on the other
3108 retrieval mechanisms.
3120 retrieval mechanisms.
3109
3121
3110 py_only : bool (default False)
3122 py_only : bool (default False)
3111 Only try to fetch python code, do not try alternative methods to decode file
3123 Only try to fetch python code, do not try alternative methods to decode file
3112 if unicode fails.
3124 if unicode fails.
3113
3125
3114 Returns
3126 Returns
3115 -------
3127 -------
3116 A string of code.
3128 A string of code.
3117
3129
3118 ValueError is raised if nothing is found, and TypeError if it evaluates
3130 ValueError is raised if nothing is found, and TypeError if it evaluates
3119 to an object of another type. In each case, .args[0] is a printable
3131 to an object of another type. In each case, .args[0] is a printable
3120 message.
3132 message.
3121 """
3133 """
3122 code = self.extract_input_lines(target, raw=raw) # Grab history
3134 code = self.extract_input_lines(target, raw=raw) # Grab history
3123 if code:
3135 if code:
3124 return code
3136 return code
3125 utarget = unquote_filename(target)
3137 utarget = unquote_filename(target)
3126 try:
3138 try:
3127 if utarget.startswith(('http://', 'https://')):
3139 if utarget.startswith(('http://', 'https://')):
3128 return openpy.read_py_url(utarget, skip_encoding_cookie=skip_encoding_cookie)
3140 return openpy.read_py_url(utarget, skip_encoding_cookie=skip_encoding_cookie)
3129 except UnicodeDecodeError:
3141 except UnicodeDecodeError:
3130 if not py_only :
3142 if not py_only :
3131 # Deferred import
3143 # Deferred import
3132 try:
3144 try:
3133 from urllib.request import urlopen # Py3
3145 from urllib.request import urlopen # Py3
3134 except ImportError:
3146 except ImportError:
3135 from urllib import urlopen
3147 from urllib import urlopen
3136 response = urlopen(target)
3148 response = urlopen(target)
3137 return response.read().decode('latin1')
3149 return response.read().decode('latin1')
3138 raise ValueError(("'%s' seem to be unreadable.") % utarget)
3150 raise ValueError(("'%s' seem to be unreadable.") % utarget)
3139
3151
3140 potential_target = [target]
3152 potential_target = [target]
3141 try :
3153 try :
3142 potential_target.insert(0,get_py_filename(target))
3154 potential_target.insert(0,get_py_filename(target))
3143 except IOError:
3155 except IOError:
3144 pass
3156 pass
3145
3157
3146 for tgt in potential_target :
3158 for tgt in potential_target :
3147 if os.path.isfile(tgt): # Read file
3159 if os.path.isfile(tgt): # Read file
3148 try :
3160 try :
3149 return openpy.read_py_file(tgt, skip_encoding_cookie=skip_encoding_cookie)
3161 return openpy.read_py_file(tgt, skip_encoding_cookie=skip_encoding_cookie)
3150 except UnicodeDecodeError :
3162 except UnicodeDecodeError :
3151 if not py_only :
3163 if not py_only :
3152 with io_open(tgt,'r', encoding='latin1') as f :
3164 with io_open(tgt,'r', encoding='latin1') as f :
3153 return f.read()
3165 return f.read()
3154 raise ValueError(("'%s' seem to be unreadable.") % target)
3166 raise ValueError(("'%s' seem to be unreadable.") % target)
3155 elif os.path.isdir(os.path.expanduser(tgt)):
3167 elif os.path.isdir(os.path.expanduser(tgt)):
3156 raise ValueError("'%s' is a directory, not a regular file." % target)
3168 raise ValueError("'%s' is a directory, not a regular file." % target)
3157
3169
3158 if search_ns:
3170 if search_ns:
3159 # Inspect namespace to load object source
3171 # Inspect namespace to load object source
3160 object_info = self.object_inspect(target, detail_level=1)
3172 object_info = self.object_inspect(target, detail_level=1)
3161 if object_info['found'] and object_info['source']:
3173 if object_info['found'] and object_info['source']:
3162 return object_info['source']
3174 return object_info['source']
3163
3175
3164 try: # User namespace
3176 try: # User namespace
3165 codeobj = eval(target, self.user_ns)
3177 codeobj = eval(target, self.user_ns)
3166 except Exception:
3178 except Exception:
3167 raise ValueError(("'%s' was not found in history, as a file, url, "
3179 raise ValueError(("'%s' was not found in history, as a file, url, "
3168 "nor in the user namespace.") % target)
3180 "nor in the user namespace.") % target)
3169
3181
3170 if isinstance(codeobj, string_types):
3182 if isinstance(codeobj, string_types):
3171 return codeobj
3183 return codeobj
3172 elif isinstance(codeobj, Macro):
3184 elif isinstance(codeobj, Macro):
3173 return codeobj.value
3185 return codeobj.value
3174
3186
3175 raise TypeError("%s is neither a string nor a macro." % target,
3187 raise TypeError("%s is neither a string nor a macro." % target,
3176 codeobj)
3188 codeobj)
3177
3189
3178 #-------------------------------------------------------------------------
3190 #-------------------------------------------------------------------------
3179 # Things related to IPython exiting
3191 # Things related to IPython exiting
3180 #-------------------------------------------------------------------------
3192 #-------------------------------------------------------------------------
3181 def atexit_operations(self):
3193 def atexit_operations(self):
3182 """This will be executed at the time of exit.
3194 """This will be executed at the time of exit.
3183
3195
3184 Cleanup operations and saving of persistent data that is done
3196 Cleanup operations and saving of persistent data that is done
3185 unconditionally by IPython should be performed here.
3197 unconditionally by IPython should be performed here.
3186
3198
3187 For things that may depend on startup flags or platform specifics (such
3199 For things that may depend on startup flags or platform specifics (such
3188 as having readline or not), register a separate atexit function in the
3200 as having readline or not), register a separate atexit function in the
3189 code that has the appropriate information, rather than trying to
3201 code that has the appropriate information, rather than trying to
3190 clutter
3202 clutter
3191 """
3203 """
3192 # Close the history session (this stores the end time and line count)
3204 # Close the history session (this stores the end time and line count)
3193 # this must be *before* the tempfile cleanup, in case of temporary
3205 # this must be *before* the tempfile cleanup, in case of temporary
3194 # history db
3206 # history db
3195 self.history_manager.end_session()
3207 self.history_manager.end_session()
3196
3208
3197 # Cleanup all tempfiles and folders left around
3209 # Cleanup all tempfiles and folders left around
3198 for tfile in self.tempfiles:
3210 for tfile in self.tempfiles:
3199 try:
3211 try:
3200 os.unlink(tfile)
3212 os.unlink(tfile)
3201 except OSError:
3213 except OSError:
3202 pass
3214 pass
3203
3215
3204 for tdir in self.tempdirs:
3216 for tdir in self.tempdirs:
3205 try:
3217 try:
3206 os.rmdir(tdir)
3218 os.rmdir(tdir)
3207 except OSError:
3219 except OSError:
3208 pass
3220 pass
3209
3221
3210 # Clear all user namespaces to release all references cleanly.
3222 # Clear all user namespaces to release all references cleanly.
3211 self.reset(new_session=False)
3223 self.reset(new_session=False)
3212
3224
3213 # Run user hooks
3225 # Run user hooks
3214 self.hooks.shutdown_hook()
3226 self.hooks.shutdown_hook()
3215
3227
3216 def cleanup(self):
3228 def cleanup(self):
3217 self.restore_sys_module_state()
3229 self.restore_sys_module_state()
3218
3230
3219
3231
3220 class InteractiveShellABC(with_metaclass(abc.ABCMeta, object)):
3232 class InteractiveShellABC(with_metaclass(abc.ABCMeta, object)):
3221 """An abstract base class for InteractiveShell."""
3233 """An abstract base class for InteractiveShell."""
3222
3234
3223 InteractiveShellABC.register(InteractiveShell)
3235 InteractiveShellABC.register(InteractiveShell)
@@ -1,881 +1,885
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tools for inspecting Python objects.
2 """Tools for inspecting Python objects.
3
3
4 Uses syntax highlighting for presenting the various information elements.
4 Uses syntax highlighting for presenting the various information elements.
5
5
6 Similar in spirit to the inspect module, but all calls take a name argument to
6 Similar in spirit to the inspect module, but all calls take a name argument to
7 reference the name under which an object is being read.
7 reference the name under which an object is being read.
8 """
8 """
9
9
10 #*****************************************************************************
10 # Copyright (c) IPython Development Team.
11 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
11 # Distributed under the terms of the Modified BSD License.
12 #
12
13 # Distributed under the terms of the BSD License. The full license is in
14 # the file COPYING, distributed as part of this software.
15 #*****************************************************************************
16 from __future__ import print_function
13 from __future__ import print_function
17
14
18 __all__ = ['Inspector','InspectColors']
15 __all__ = ['Inspector','InspectColors']
19
16
20 # stdlib modules
17 # stdlib modules
21 import inspect
18 import inspect
22 import linecache
19 import linecache
23 import os
20 import os
24 import types
21 import types
25 import io as stdlib_io
22 import io as stdlib_io
26
23
27 try:
24 try:
28 from itertools import izip_longest
25 from itertools import izip_longest
29 except ImportError:
26 except ImportError:
30 from itertools import zip_longest as izip_longest
27 from itertools import zip_longest as izip_longest
31
28
32 # IPython's own
29 # IPython's own
33 from IPython.core import page
30 from IPython.core import page
34 from IPython.testing.skipdoctest import skip_doctest_py3
31 from IPython.testing.skipdoctest import skip_doctest_py3
35 from IPython.utils import PyColorize
32 from IPython.utils import PyColorize
36 from IPython.utils import io
33 from IPython.utils import io
37 from IPython.utils import openpy
34 from IPython.utils import openpy
38 from IPython.utils import py3compat
35 from IPython.utils import py3compat
39 from IPython.utils.dir2 import safe_hasattr
36 from IPython.utils.dir2 import safe_hasattr
40 from IPython.utils.text import indent
37 from IPython.utils.text import indent
41 from IPython.utils.wildcard import list_namespace
38 from IPython.utils.wildcard import list_namespace
42 from IPython.utils.coloransi import TermColors, ColorScheme, ColorSchemeTable
39 from IPython.utils.coloransi import TermColors, ColorScheme, ColorSchemeTable
43 from IPython.utils.py3compat import cast_unicode, string_types, PY3
40 from IPython.utils.py3compat import cast_unicode, string_types, PY3
44
41
45 # builtin docstrings to ignore
42 # builtin docstrings to ignore
46 _func_call_docstring = types.FunctionType.__call__.__doc__
43 _func_call_docstring = types.FunctionType.__call__.__doc__
47 _object_init_docstring = object.__init__.__doc__
44 _object_init_docstring = object.__init__.__doc__
48 _builtin_type_docstrings = {
45 _builtin_type_docstrings = {
49 t.__doc__ for t in (types.ModuleType, types.MethodType, types.FunctionType)
46 t.__doc__ for t in (types.ModuleType, types.MethodType, types.FunctionType)
50 }
47 }
51
48
52 _builtin_func_type = type(all)
49 _builtin_func_type = type(all)
53 _builtin_meth_type = type(str.upper) # Bound methods have the same type as builtin functions
50 _builtin_meth_type = type(str.upper) # Bound methods have the same type as builtin functions
54 #****************************************************************************
51 #****************************************************************************
55 # Builtin color schemes
52 # Builtin color schemes
56
53
57 Colors = TermColors # just a shorthand
54 Colors = TermColors # just a shorthand
58
55
59 # Build a few color schemes
56 # Build a few color schemes
60 NoColor = ColorScheme(
57 NoColor = ColorScheme(
61 'NoColor',{
58 'NoColor',{
62 'header' : Colors.NoColor,
59 'header' : Colors.NoColor,
63 'normal' : Colors.NoColor # color off (usu. Colors.Normal)
60 'normal' : Colors.NoColor # color off (usu. Colors.Normal)
64 } )
61 } )
65
62
66 LinuxColors = ColorScheme(
63 LinuxColors = ColorScheme(
67 'Linux',{
64 'Linux',{
68 'header' : Colors.LightRed,
65 'header' : Colors.LightRed,
69 'normal' : Colors.Normal # color off (usu. Colors.Normal)
66 'normal' : Colors.Normal # color off (usu. Colors.Normal)
70 } )
67 } )
71
68
72 LightBGColors = ColorScheme(
69 LightBGColors = ColorScheme(
73 'LightBG',{
70 'LightBG',{
74 'header' : Colors.Red,
71 'header' : Colors.Red,
75 'normal' : Colors.Normal # color off (usu. Colors.Normal)
72 'normal' : Colors.Normal # color off (usu. Colors.Normal)
76 } )
73 } )
77
74
78 # Build table of color schemes (needed by the parser)
75 # Build table of color schemes (needed by the parser)
79 InspectColors = ColorSchemeTable([NoColor,LinuxColors,LightBGColors],
76 InspectColors = ColorSchemeTable([NoColor,LinuxColors,LightBGColors],
80 'Linux')
77 'Linux')
81
78
82 #****************************************************************************
79 #****************************************************************************
83 # Auxiliary functions and objects
80 # Auxiliary functions and objects
84
81
85 # See the messaging spec for the definition of all these fields. This list
82 # See the messaging spec for the definition of all these fields. This list
86 # effectively defines the order of display
83 # effectively defines the order of display
87 info_fields = ['type_name', 'base_class', 'string_form', 'namespace',
84 info_fields = ['type_name', 'base_class', 'string_form', 'namespace',
88 'length', 'file', 'definition', 'docstring', 'source',
85 'length', 'file', 'definition', 'docstring', 'source',
89 'init_definition', 'class_docstring', 'init_docstring',
86 'init_definition', 'class_docstring', 'init_docstring',
90 'call_def', 'call_docstring',
87 'call_def', 'call_docstring',
91 # These won't be printed but will be used to determine how to
88 # These won't be printed but will be used to determine how to
92 # format the object
89 # format the object
93 'ismagic', 'isalias', 'isclass', 'argspec', 'found', 'name'
90 'ismagic', 'isalias', 'isclass', 'argspec', 'found', 'name'
94 ]
91 ]
95
92
96
93
97 def object_info(**kw):
94 def object_info(**kw):
98 """Make an object info dict with all fields present."""
95 """Make an object info dict with all fields present."""
99 infodict = dict(izip_longest(info_fields, [None]))
96 infodict = dict(izip_longest(info_fields, [None]))
100 infodict.update(kw)
97 infodict.update(kw)
101 return infodict
98 return infodict
102
99
103
100
104 def get_encoding(obj):
101 def get_encoding(obj):
105 """Get encoding for python source file defining obj
102 """Get encoding for python source file defining obj
106
103
107 Returns None if obj is not defined in a sourcefile.
104 Returns None if obj is not defined in a sourcefile.
108 """
105 """
109 ofile = find_file(obj)
106 ofile = find_file(obj)
110 # run contents of file through pager starting at line where the object
107 # run contents of file through pager starting at line where the object
111 # is defined, as long as the file isn't binary and is actually on the
108 # is defined, as long as the file isn't binary and is actually on the
112 # filesystem.
109 # filesystem.
113 if ofile is None:
110 if ofile is None:
114 return None
111 return None
115 elif ofile.endswith(('.so', '.dll', '.pyd')):
112 elif ofile.endswith(('.so', '.dll', '.pyd')):
116 return None
113 return None
117 elif not os.path.isfile(ofile):
114 elif not os.path.isfile(ofile):
118 return None
115 return None
119 else:
116 else:
120 # Print only text files, not extension binaries. Note that
117 # Print only text files, not extension binaries. Note that
121 # getsourcelines returns lineno with 1-offset and page() uses
118 # getsourcelines returns lineno with 1-offset and page() uses
122 # 0-offset, so we must adjust.
119 # 0-offset, so we must adjust.
123 with stdlib_io.open(ofile, 'rb') as buffer: # Tweaked to use io.open for Python 2
120 with stdlib_io.open(ofile, 'rb') as buffer: # Tweaked to use io.open for Python 2
124 encoding, lines = openpy.detect_encoding(buffer.readline)
121 encoding, lines = openpy.detect_encoding(buffer.readline)
125 return encoding
122 return encoding
126
123
127 def getdoc(obj):
124 def getdoc(obj):
128 """Stable wrapper around inspect.getdoc.
125 """Stable wrapper around inspect.getdoc.
129
126
130 This can't crash because of attribute problems.
127 This can't crash because of attribute problems.
131
128
132 It also attempts to call a getdoc() method on the given object. This
129 It also attempts to call a getdoc() method on the given object. This
133 allows objects which provide their docstrings via non-standard mechanisms
130 allows objects which provide their docstrings via non-standard mechanisms
134 (like Pyro proxies) to still be inspected by ipython's ? system."""
131 (like Pyro proxies) to still be inspected by ipython's ? system."""
135 # Allow objects to offer customized documentation via a getdoc method:
132 # Allow objects to offer customized documentation via a getdoc method:
136 try:
133 try:
137 ds = obj.getdoc()
134 ds = obj.getdoc()
138 except Exception:
135 except Exception:
139 pass
136 pass
140 else:
137 else:
141 # if we get extra info, we add it to the normal docstring.
138 # if we get extra info, we add it to the normal docstring.
142 if isinstance(ds, string_types):
139 if isinstance(ds, string_types):
143 return inspect.cleandoc(ds)
140 return inspect.cleandoc(ds)
144
141
145 try:
142 try:
146 docstr = inspect.getdoc(obj)
143 docstr = inspect.getdoc(obj)
147 encoding = get_encoding(obj)
144 encoding = get_encoding(obj)
148 return py3compat.cast_unicode(docstr, encoding=encoding)
145 return py3compat.cast_unicode(docstr, encoding=encoding)
149 except Exception:
146 except Exception:
150 # Harden against an inspect failure, which can occur with
147 # Harden against an inspect failure, which can occur with
151 # SWIG-wrapped extensions.
148 # SWIG-wrapped extensions.
152 raise
149 raise
153 return None
150 return None
154
151
155
152
156 def getsource(obj,is_binary=False):
153 def getsource(obj,is_binary=False):
157 """Wrapper around inspect.getsource.
154 """Wrapper around inspect.getsource.
158
155
159 This can be modified by other projects to provide customized source
156 This can be modified by other projects to provide customized source
160 extraction.
157 extraction.
161
158
162 Inputs:
159 Inputs:
163
160
164 - obj: an object whose source code we will attempt to extract.
161 - obj: an object whose source code we will attempt to extract.
165
162
166 Optional inputs:
163 Optional inputs:
167
164
168 - is_binary: whether the object is known to come from a binary source.
165 - is_binary: whether the object is known to come from a binary source.
169 This implementation will skip returning any output for binary objects, but
166 This implementation will skip returning any output for binary objects, but
170 custom extractors may know how to meaningfully process them."""
167 custom extractors may know how to meaningfully process them."""
171
168
172 if is_binary:
169 if is_binary:
173 return None
170 return None
174 else:
171 else:
175 # get source if obj was decorated with @decorator
172 # get source if obj was decorated with @decorator
176 if hasattr(obj,"__wrapped__"):
173 if hasattr(obj,"__wrapped__"):
177 obj = obj.__wrapped__
174 obj = obj.__wrapped__
178 try:
175 try:
179 src = inspect.getsource(obj)
176 src = inspect.getsource(obj)
180 except TypeError:
177 except TypeError:
181 if hasattr(obj,'__class__'):
178 if hasattr(obj,'__class__'):
182 src = inspect.getsource(obj.__class__)
179 src = inspect.getsource(obj.__class__)
183 encoding = get_encoding(obj)
180 encoding = get_encoding(obj)
184 return cast_unicode(src, encoding=encoding)
181 return cast_unicode(src, encoding=encoding)
185
182
186
183
187 def is_simple_callable(obj):
184 def is_simple_callable(obj):
188 """True if obj is a function ()"""
185 """True if obj is a function ()"""
189 return (inspect.isfunction(obj) or inspect.ismethod(obj) or \
186 return (inspect.isfunction(obj) or inspect.ismethod(obj) or \
190 isinstance(obj, _builtin_func_type) or isinstance(obj, _builtin_meth_type))
187 isinstance(obj, _builtin_func_type) or isinstance(obj, _builtin_meth_type))
191
188
192
189
193 def getargspec(obj):
190 def getargspec(obj):
194 """Wrapper around :func:`inspect.getfullargspec` on Python 3, and
191 """Wrapper around :func:`inspect.getfullargspec` on Python 3, and
195 :func:inspect.getargspec` on Python 2.
192 :func:inspect.getargspec` on Python 2.
196
193
197 In addition to functions and methods, this can also handle objects with a
194 In addition to functions and methods, this can also handle objects with a
198 ``__call__`` attribute.
195 ``__call__`` attribute.
199 """
196 """
200 if safe_hasattr(obj, '__call__') and not is_simple_callable(obj):
197 if safe_hasattr(obj, '__call__') and not is_simple_callable(obj):
201 obj = obj.__call__
198 obj = obj.__call__
202
199
203 return inspect.getfullargspec(obj) if PY3 else inspect.getargspec(obj)
200 return inspect.getfullargspec(obj) if PY3 else inspect.getargspec(obj)
204
201
205
202
206 def format_argspec(argspec):
203 def format_argspec(argspec):
207 """Format argspect, convenience wrapper around inspect's.
204 """Format argspect, convenience wrapper around inspect's.
208
205
209 This takes a dict instead of ordered arguments and calls
206 This takes a dict instead of ordered arguments and calls
210 inspect.format_argspec with the arguments in the necessary order.
207 inspect.format_argspec with the arguments in the necessary order.
211 """
208 """
212 return inspect.formatargspec(argspec['args'], argspec['varargs'],
209 return inspect.formatargspec(argspec['args'], argspec['varargs'],
213 argspec['varkw'], argspec['defaults'])
210 argspec['varkw'], argspec['defaults'])
214
211
215
212
216 def call_tip(oinfo, format_call=True):
213 def call_tip(oinfo, format_call=True):
217 """Extract call tip data from an oinfo dict.
214 """Extract call tip data from an oinfo dict.
218
215
219 Parameters
216 Parameters
220 ----------
217 ----------
221 oinfo : dict
218 oinfo : dict
222
219
223 format_call : bool, optional
220 format_call : bool, optional
224 If True, the call line is formatted and returned as a string. If not, a
221 If True, the call line is formatted and returned as a string. If not, a
225 tuple of (name, argspec) is returned.
222 tuple of (name, argspec) is returned.
226
223
227 Returns
224 Returns
228 -------
225 -------
229 call_info : None, str or (str, dict) tuple.
226 call_info : None, str or (str, dict) tuple.
230 When format_call is True, the whole call information is formattted as a
227 When format_call is True, the whole call information is formattted as a
231 single string. Otherwise, the object's name and its argspec dict are
228 single string. Otherwise, the object's name and its argspec dict are
232 returned. If no call information is available, None is returned.
229 returned. If no call information is available, None is returned.
233
230
234 docstring : str or None
231 docstring : str or None
235 The most relevant docstring for calling purposes is returned, if
232 The most relevant docstring for calling purposes is returned, if
236 available. The priority is: call docstring for callable instances, then
233 available. The priority is: call docstring for callable instances, then
237 constructor docstring for classes, then main object's docstring otherwise
234 constructor docstring for classes, then main object's docstring otherwise
238 (regular functions).
235 (regular functions).
239 """
236 """
240 # Get call definition
237 # Get call definition
241 argspec = oinfo.get('argspec')
238 argspec = oinfo.get('argspec')
242 if argspec is None:
239 if argspec is None:
243 call_line = None
240 call_line = None
244 else:
241 else:
245 # Callable objects will have 'self' as their first argument, prune
242 # Callable objects will have 'self' as their first argument, prune
246 # it out if it's there for clarity (since users do *not* pass an
243 # it out if it's there for clarity (since users do *not* pass an
247 # extra first argument explicitly).
244 # extra first argument explicitly).
248 try:
245 try:
249 has_self = argspec['args'][0] == 'self'
246 has_self = argspec['args'][0] == 'self'
250 except (KeyError, IndexError):
247 except (KeyError, IndexError):
251 pass
248 pass
252 else:
249 else:
253 if has_self:
250 if has_self:
254 argspec['args'] = argspec['args'][1:]
251 argspec['args'] = argspec['args'][1:]
255
252
256 call_line = oinfo['name']+format_argspec(argspec)
253 call_line = oinfo['name']+format_argspec(argspec)
257
254
258 # Now get docstring.
255 # Now get docstring.
259 # The priority is: call docstring, constructor docstring, main one.
256 # The priority is: call docstring, constructor docstring, main one.
260 doc = oinfo.get('call_docstring')
257 doc = oinfo.get('call_docstring')
261 if doc is None:
258 if doc is None:
262 doc = oinfo.get('init_docstring')
259 doc = oinfo.get('init_docstring')
263 if doc is None:
260 if doc is None:
264 doc = oinfo.get('docstring','')
261 doc = oinfo.get('docstring','')
265
262
266 return call_line, doc
263 return call_line, doc
267
264
268
265
269 def find_file(obj):
266 def find_file(obj):
270 """Find the absolute path to the file where an object was defined.
267 """Find the absolute path to the file where an object was defined.
271
268
272 This is essentially a robust wrapper around `inspect.getabsfile`.
269 This is essentially a robust wrapper around `inspect.getabsfile`.
273
270
274 Returns None if no file can be found.
271 Returns None if no file can be found.
275
272
276 Parameters
273 Parameters
277 ----------
274 ----------
278 obj : any Python object
275 obj : any Python object
279
276
280 Returns
277 Returns
281 -------
278 -------
282 fname : str
279 fname : str
283 The absolute path to the file where the object was defined.
280 The absolute path to the file where the object was defined.
284 """
281 """
285 # get source if obj was decorated with @decorator
282 # get source if obj was decorated with @decorator
286 if safe_hasattr(obj, '__wrapped__'):
283 if safe_hasattr(obj, '__wrapped__'):
287 obj = obj.__wrapped__
284 obj = obj.__wrapped__
288
285
289 fname = None
286 fname = None
290 try:
287 try:
291 fname = inspect.getabsfile(obj)
288 fname = inspect.getabsfile(obj)
292 except TypeError:
289 except TypeError:
293 # For an instance, the file that matters is where its class was
290 # For an instance, the file that matters is where its class was
294 # declared.
291 # declared.
295 if hasattr(obj, '__class__'):
292 if hasattr(obj, '__class__'):
296 try:
293 try:
297 fname = inspect.getabsfile(obj.__class__)
294 fname = inspect.getabsfile(obj.__class__)
298 except TypeError:
295 except TypeError:
299 # Can happen for builtins
296 # Can happen for builtins
300 pass
297 pass
301 except:
298 except:
302 pass
299 pass
303 return cast_unicode(fname)
300 return cast_unicode(fname)
304
301
305
302
306 def find_source_lines(obj):
303 def find_source_lines(obj):
307 """Find the line number in a file where an object was defined.
304 """Find the line number in a file where an object was defined.
308
305
309 This is essentially a robust wrapper around `inspect.getsourcelines`.
306 This is essentially a robust wrapper around `inspect.getsourcelines`.
310
307
311 Returns None if no file can be found.
308 Returns None if no file can be found.
312
309
313 Parameters
310 Parameters
314 ----------
311 ----------
315 obj : any Python object
312 obj : any Python object
316
313
317 Returns
314 Returns
318 -------
315 -------
319 lineno : int
316 lineno : int
320 The line number where the object definition starts.
317 The line number where the object definition starts.
321 """
318 """
322 # get source if obj was decorated with @decorator
319 # get source if obj was decorated with @decorator
323 if safe_hasattr(obj, '__wrapped__'):
320 if safe_hasattr(obj, '__wrapped__'):
324 obj = obj.__wrapped__
321 obj = obj.__wrapped__
325
322
326 try:
323 try:
327 try:
324 try:
328 lineno = inspect.getsourcelines(obj)[1]
325 lineno = inspect.getsourcelines(obj)[1]
329 except TypeError:
326 except TypeError:
330 # For instances, try the class object like getsource() does
327 # For instances, try the class object like getsource() does
331 if hasattr(obj, '__class__'):
328 if hasattr(obj, '__class__'):
332 lineno = inspect.getsourcelines(obj.__class__)[1]
329 lineno = inspect.getsourcelines(obj.__class__)[1]
333 else:
330 else:
334 lineno = None
331 lineno = None
335 except:
332 except:
336 return None
333 return None
337
334
338 return lineno
335 return lineno
339
336
340
337
341 class Inspector:
338 class Inspector:
342 def __init__(self, color_table=InspectColors,
339 def __init__(self, color_table=InspectColors,
343 code_color_table=PyColorize.ANSICodeColors,
340 code_color_table=PyColorize.ANSICodeColors,
344 scheme='NoColor',
341 scheme='NoColor',
345 str_detail_level=0):
342 str_detail_level=0):
346 self.color_table = color_table
343 self.color_table = color_table
347 self.parser = PyColorize.Parser(code_color_table,out='str')
344 self.parser = PyColorize.Parser(code_color_table,out='str')
348 self.format = self.parser.format
345 self.format = self.parser.format
349 self.str_detail_level = str_detail_level
346 self.str_detail_level = str_detail_level
350 self.set_active_scheme(scheme)
347 self.set_active_scheme(scheme)
351
348
352 def _getdef(self,obj,oname=''):
349 def _getdef(self,obj,oname=''):
353 """Return the call signature for any callable object.
350 """Return the call signature for any callable object.
354
351
355 If any exception is generated, None is returned instead and the
352 If any exception is generated, None is returned instead and the
356 exception is suppressed."""
353 exception is suppressed."""
357 try:
354 try:
358 hdef = oname + inspect.formatargspec(*getargspec(obj))
355 hdef = oname + inspect.formatargspec(*getargspec(obj))
359 return cast_unicode(hdef)
356 return cast_unicode(hdef)
360 except:
357 except:
361 return None
358 return None
362
359
363 def __head(self,h):
360 def __head(self,h):
364 """Return a header string with proper colors."""
361 """Return a header string with proper colors."""
365 return '%s%s%s' % (self.color_table.active_colors.header,h,
362 return '%s%s%s' % (self.color_table.active_colors.header,h,
366 self.color_table.active_colors.normal)
363 self.color_table.active_colors.normal)
367
364
368 def set_active_scheme(self, scheme):
365 def set_active_scheme(self, scheme):
369 self.color_table.set_active_scheme(scheme)
366 self.color_table.set_active_scheme(scheme)
370 self.parser.color_table.set_active_scheme(scheme)
367 self.parser.color_table.set_active_scheme(scheme)
371
368
372 def noinfo(self, msg, oname):
369 def noinfo(self, msg, oname):
373 """Generic message when no information is found."""
370 """Generic message when no information is found."""
374 print('No %s found' % msg, end=' ')
371 print('No %s found' % msg, end=' ')
375 if oname:
372 if oname:
376 print('for %s' % oname)
373 print('for %s' % oname)
377 else:
374 else:
378 print()
375 print()
379
376
380 def pdef(self, obj, oname=''):
377 def pdef(self, obj, oname=''):
381 """Print the call signature for any callable object.
378 """Print the call signature for any callable object.
382
379
383 If the object is a class, print the constructor information."""
380 If the object is a class, print the constructor information."""
384
381
385 if not callable(obj):
382 if not callable(obj):
386 print('Object is not callable.')
383 print('Object is not callable.')
387 return
384 return
388
385
389 header = ''
386 header = ''
390
387
391 if inspect.isclass(obj):
388 if inspect.isclass(obj):
392 header = self.__head('Class constructor information:\n')
389 header = self.__head('Class constructor information:\n')
393 obj = obj.__init__
390 obj = obj.__init__
394 elif (not py3compat.PY3) and type(obj) is types.InstanceType:
391 elif (not py3compat.PY3) and type(obj) is types.InstanceType:
395 obj = obj.__call__
392 obj = obj.__call__
396
393
397 output = self._getdef(obj,oname)
394 output = self._getdef(obj,oname)
398 if output is None:
395 if output is None:
399 self.noinfo('definition header',oname)
396 self.noinfo('definition header',oname)
400 else:
397 else:
401 print(header,self.format(output), end=' ', file=io.stdout)
398 print(header,self.format(output), end=' ', file=io.stdout)
402
399
403 # In Python 3, all classes are new-style, so they all have __init__.
400 # In Python 3, all classes are new-style, so they all have __init__.
404 @skip_doctest_py3
401 @skip_doctest_py3
405 def pdoc(self,obj,oname='',formatter = None):
402 def pdoc(self,obj,oname='',formatter = None):
406 """Print the docstring for any object.
403 """Print the docstring for any object.
407
404
408 Optional:
405 Optional:
409 -formatter: a function to run the docstring through for specially
406 -formatter: a function to run the docstring through for specially
410 formatted docstrings.
407 formatted docstrings.
411
408
412 Examples
409 Examples
413 --------
410 --------
414
411
415 In [1]: class NoInit:
412 In [1]: class NoInit:
416 ...: pass
413 ...: pass
417
414
418 In [2]: class NoDoc:
415 In [2]: class NoDoc:
419 ...: def __init__(self):
416 ...: def __init__(self):
420 ...: pass
417 ...: pass
421
418
422 In [3]: %pdoc NoDoc
419 In [3]: %pdoc NoDoc
423 No documentation found for NoDoc
420 No documentation found for NoDoc
424
421
425 In [4]: %pdoc NoInit
422 In [4]: %pdoc NoInit
426 No documentation found for NoInit
423 No documentation found for NoInit
427
424
428 In [5]: obj = NoInit()
425 In [5]: obj = NoInit()
429
426
430 In [6]: %pdoc obj
427 In [6]: %pdoc obj
431 No documentation found for obj
428 No documentation found for obj
432
429
433 In [5]: obj2 = NoDoc()
430 In [5]: obj2 = NoDoc()
434
431
435 In [6]: %pdoc obj2
432 In [6]: %pdoc obj2
436 No documentation found for obj2
433 No documentation found for obj2
437 """
434 """
438
435
439 head = self.__head # For convenience
436 head = self.__head # For convenience
440 lines = []
437 lines = []
441 ds = getdoc(obj)
438 ds = getdoc(obj)
442 if formatter:
439 if formatter:
443 ds = formatter(ds)
440 ds = formatter(ds)
444 if ds:
441 if ds:
445 lines.append(head("Class docstring:"))
442 lines.append(head("Class docstring:"))
446 lines.append(indent(ds))
443 lines.append(indent(ds))
447 if inspect.isclass(obj) and hasattr(obj, '__init__'):
444 if inspect.isclass(obj) and hasattr(obj, '__init__'):
448 init_ds = getdoc(obj.__init__)
445 init_ds = getdoc(obj.__init__)
449 if init_ds is not None:
446 if init_ds is not None:
450 lines.append(head("Init docstring:"))
447 lines.append(head("Init docstring:"))
451 lines.append(indent(init_ds))
448 lines.append(indent(init_ds))
452 elif hasattr(obj,'__call__'):
449 elif hasattr(obj,'__call__'):
453 call_ds = getdoc(obj.__call__)
450 call_ds = getdoc(obj.__call__)
454 if call_ds:
451 if call_ds:
455 lines.append(head("Call docstring:"))
452 lines.append(head("Call docstring:"))
456 lines.append(indent(call_ds))
453 lines.append(indent(call_ds))
457
454
458 if not lines:
455 if not lines:
459 self.noinfo('documentation',oname)
456 self.noinfo('documentation',oname)
460 else:
457 else:
461 page.page('\n'.join(lines))
458 page.page('\n'.join(lines))
462
459
463 def psource(self,obj,oname=''):
460 def psource(self,obj,oname=''):
464 """Print the source code for an object."""
461 """Print the source code for an object."""
465
462
466 # Flush the source cache because inspect can return out-of-date source
463 # Flush the source cache because inspect can return out-of-date source
467 linecache.checkcache()
464 linecache.checkcache()
468 try:
465 try:
469 src = getsource(obj)
466 src = getsource(obj)
470 except:
467 except:
471 self.noinfo('source',oname)
468 self.noinfo('source',oname)
472 else:
469 else:
473 page.page(self.format(src))
470 page.page(self.format(src))
474
471
475 def pfile(self, obj, oname=''):
472 def pfile(self, obj, oname=''):
476 """Show the whole file where an object was defined."""
473 """Show the whole file where an object was defined."""
477
474
478 lineno = find_source_lines(obj)
475 lineno = find_source_lines(obj)
479 if lineno is None:
476 if lineno is None:
480 self.noinfo('file', oname)
477 self.noinfo('file', oname)
481 return
478 return
482
479
483 ofile = find_file(obj)
480 ofile = find_file(obj)
484 # run contents of file through pager starting at line where the object
481 # run contents of file through pager starting at line where the object
485 # is defined, as long as the file isn't binary and is actually on the
482 # is defined, as long as the file isn't binary and is actually on the
486 # filesystem.
483 # filesystem.
487 if ofile.endswith(('.so', '.dll', '.pyd')):
484 if ofile.endswith(('.so', '.dll', '.pyd')):
488 print('File %r is binary, not printing.' % ofile)
485 print('File %r is binary, not printing.' % ofile)
489 elif not os.path.isfile(ofile):
486 elif not os.path.isfile(ofile):
490 print('File %r does not exist, not printing.' % ofile)
487 print('File %r does not exist, not printing.' % ofile)
491 else:
488 else:
492 # Print only text files, not extension binaries. Note that
489 # Print only text files, not extension binaries. Note that
493 # getsourcelines returns lineno with 1-offset and page() uses
490 # getsourcelines returns lineno with 1-offset and page() uses
494 # 0-offset, so we must adjust.
491 # 0-offset, so we must adjust.
495 page.page(self.format(openpy.read_py_file(ofile, skip_encoding_cookie=False)), lineno - 1)
492 page.page(self.format(openpy.read_py_file(ofile, skip_encoding_cookie=False)), lineno - 1)
496
493
497 def _format_fields(self, fields, title_width=0):
494 def _format_fields(self, fields, title_width=0):
498 """Formats a list of fields for display.
495 """Formats a list of fields for display.
499
496
500 Parameters
497 Parameters
501 ----------
498 ----------
502 fields : list
499 fields : list
503 A list of 2-tuples: (field_title, field_content)
500 A list of 2-tuples: (field_title, field_content)
504 title_width : int
501 title_width : int
505 How many characters to pad titles to. Default to longest title.
502 How many characters to pad titles to. Default to longest title.
506 """
503 """
507 out = []
504 out = []
508 header = self.__head
505 header = self.__head
509 if title_width == 0:
506 if title_width == 0:
510 title_width = max(len(title) + 2 for title, _ in fields)
507 title_width = max(len(title) + 2 for title, _ in fields)
511 for title, content in fields:
508 for title, content in fields:
512 if len(content.splitlines()) > 1:
509 if len(content.splitlines()) > 1:
513 title = header(title + ":") + "\n"
510 title = header(title + ":") + "\n"
514 else:
511 else:
515 title = header((title+":").ljust(title_width))
512 title = header((title+":").ljust(title_width))
516 out.append(cast_unicode(title) + cast_unicode(content))
513 out.append(cast_unicode(title) + cast_unicode(content))
517 return "\n".join(out)
514 return "\n".join(out)
518
515
519 # The fields to be displayed by pinfo: (fancy_name, key_in_info_dict)
516 # The fields to be displayed by pinfo: (fancy_name, key_in_info_dict)
520 pinfo_fields1 = [("Type", "type_name"),
517 pinfo_fields1 = [("Type", "type_name"),
521 ]
518 ]
522
519
523 pinfo_fields2 = [("String form", "string_form"),
520 pinfo_fields2 = [("String form", "string_form"),
524 ]
521 ]
525
522
526 pinfo_fields3 = [("Length", "length"),
523 pinfo_fields3 = [("Length", "length"),
527 ("File", "file"),
524 ("File", "file"),
528 ("Definition", "definition"),
525 ("Definition", "definition"),
529 ]
526 ]
530
527
531 pinfo_fields_obj = [("Class docstring", "class_docstring"),
528 pinfo_fields_obj = [("Class docstring", "class_docstring"),
532 ("Init docstring", "init_docstring"),
529 ("Init docstring", "init_docstring"),
533 ("Call def", "call_def"),
530 ("Call def", "call_def"),
534 ("Call docstring", "call_docstring")]
531 ("Call docstring", "call_docstring")]
535
532
536 def pinfo(self,obj,oname='',formatter=None,info=None,detail_level=0):
533 def _format_info(self, obj, oname='', formatter=None, info=None, detail_level=0):
537 """Show detailed information about an object.
534 """Format an info dict as text"""
538
539 Optional arguments:
540
541 - oname: name of the variable pointing to the object.
542
543 - formatter: special formatter for docstrings (see pdoc)
544
545 - info: a structure with some information fields which may have been
546 precomputed already.
547
548 - detail_level: if set to 1, more information is given.
549 """
550 info = self.info(obj, oname=oname, formatter=formatter,
535 info = self.info(obj, oname=oname, formatter=formatter,
551 info=info, detail_level=detail_level)
536 info=info, detail_level=detail_level)
552 displayfields = []
537 displayfields = []
553 def add_fields(fields):
538 def add_fields(fields):
554 for title, key in fields:
539 for title, key in fields:
555 field = info[key]
540 field = info[key]
556 if field is not None:
541 if field is not None:
557 displayfields.append((title, field.rstrip()))
542 displayfields.append((title, field.rstrip()))
558
543
559 add_fields(self.pinfo_fields1)
544 add_fields(self.pinfo_fields1)
560
545
561 # Base class for old-style instances
546 # Base class for old-style instances
562 if (not py3compat.PY3) and isinstance(obj, types.InstanceType) and info['base_class']:
547 if (not py3compat.PY3) and isinstance(obj, types.InstanceType) and info['base_class']:
563 displayfields.append(("Base Class", info['base_class'].rstrip()))
548 displayfields.append(("Base Class", info['base_class'].rstrip()))
564
549
565 add_fields(self.pinfo_fields2)
550 add_fields(self.pinfo_fields2)
566
551
567 # Namespace
552 # Namespace
568 if info['namespace'] != 'Interactive':
553 if info['namespace'] != 'Interactive':
569 displayfields.append(("Namespace", info['namespace'].rstrip()))
554 displayfields.append(("Namespace", info['namespace'].rstrip()))
570
555
571 add_fields(self.pinfo_fields3)
556 add_fields(self.pinfo_fields3)
572 if info['isclass'] and info['init_definition']:
557 if info['isclass'] and info['init_definition']:
573 displayfields.append(("Init definition",
558 displayfields.append(("Init definition",
574 info['init_definition'].rstrip()))
559 info['init_definition'].rstrip()))
575
560
576 # Source or docstring, depending on detail level and whether
561 # Source or docstring, depending on detail level and whether
577 # source found.
562 # source found.
578 if detail_level > 0 and info['source'] is not None:
563 if detail_level > 0 and info['source'] is not None:
579 displayfields.append(("Source",
564 displayfields.append(("Source",
580 self.format(cast_unicode(info['source']))))
565 self.format(cast_unicode(info['source']))))
581 elif info['docstring'] is not None:
566 elif info['docstring'] is not None:
582 displayfields.append(("Docstring", info["docstring"]))
567 displayfields.append(("Docstring", info["docstring"]))
583
568
584 # Constructor info for classes
569 # Constructor info for classes
585 if info['isclass']:
570 if info['isclass']:
586 if info['init_docstring'] is not None:
571 if info['init_docstring'] is not None:
587 displayfields.append(("Init docstring",
572 displayfields.append(("Init docstring",
588 info['init_docstring']))
573 info['init_docstring']))
589
574
590 # Info for objects:
575 # Info for objects:
591 else:
576 else:
592 add_fields(self.pinfo_fields_obj)
577 add_fields(self.pinfo_fields_obj)
593
578
594 # Finally send to printer/pager:
595 if displayfields:
579 if displayfields:
596 page.page(self._format_fields(displayfields))
580 return self._format_fields(displayfields)
581 else:
582 return u''
583
584 def pinfo(self, obj, oname='', formatter=None, info=None, detail_level=0):
585 """Show detailed information about an object.
586
587 Optional arguments:
588
589 - oname: name of the variable pointing to the object.
597
590
591 - formatter: special formatter for docstrings (see pdoc)
592
593 - info: a structure with some information fields which may have been
594 precomputed already.
595
596 - detail_level: if set to 1, more information is given.
597 """
598 text = self._format_info(obj, oname, formatter, info, detail_level)
599 if text:
600 page.page(text)
601
598 def info(self, obj, oname='', formatter=None, info=None, detail_level=0):
602 def info(self, obj, oname='', formatter=None, info=None, detail_level=0):
599 """Compute a dict with detailed information about an object.
603 """Compute a dict with detailed information about an object.
600
604
601 Optional arguments:
605 Optional arguments:
602
606
603 - oname: name of the variable pointing to the object.
607 - oname: name of the variable pointing to the object.
604
608
605 - formatter: special formatter for docstrings (see pdoc)
609 - formatter: special formatter for docstrings (see pdoc)
606
610
607 - info: a structure with some information fields which may have been
611 - info: a structure with some information fields which may have been
608 precomputed already.
612 precomputed already.
609
613
610 - detail_level: if set to 1, more information is given.
614 - detail_level: if set to 1, more information is given.
611 """
615 """
612
616
613 obj_type = type(obj)
617 obj_type = type(obj)
614
618
615 if info is None:
619 if info is None:
616 ismagic = 0
620 ismagic = 0
617 isalias = 0
621 isalias = 0
618 ospace = ''
622 ospace = ''
619 else:
623 else:
620 ismagic = info.ismagic
624 ismagic = info.ismagic
621 isalias = info.isalias
625 isalias = info.isalias
622 ospace = info.namespace
626 ospace = info.namespace
623
627
624 # Get docstring, special-casing aliases:
628 # Get docstring, special-casing aliases:
625 if isalias:
629 if isalias:
626 if not callable(obj):
630 if not callable(obj):
627 try:
631 try:
628 ds = "Alias to the system command:\n %s" % obj[1]
632 ds = "Alias to the system command:\n %s" % obj[1]
629 except:
633 except:
630 ds = "Alias: " + str(obj)
634 ds = "Alias: " + str(obj)
631 else:
635 else:
632 ds = "Alias to " + str(obj)
636 ds = "Alias to " + str(obj)
633 if obj.__doc__:
637 if obj.__doc__:
634 ds += "\nDocstring:\n" + obj.__doc__
638 ds += "\nDocstring:\n" + obj.__doc__
635 else:
639 else:
636 ds = getdoc(obj)
640 ds = getdoc(obj)
637 if ds is None:
641 if ds is None:
638 ds = '<no docstring>'
642 ds = '<no docstring>'
639 if formatter is not None:
643 if formatter is not None:
640 ds = formatter(ds)
644 ds = formatter(ds)
641
645
642 # store output in a dict, we initialize it here and fill it as we go
646 # store output in a dict, we initialize it here and fill it as we go
643 out = dict(name=oname, found=True, isalias=isalias, ismagic=ismagic)
647 out = dict(name=oname, found=True, isalias=isalias, ismagic=ismagic)
644
648
645 string_max = 200 # max size of strings to show (snipped if longer)
649 string_max = 200 # max size of strings to show (snipped if longer)
646 shalf = int((string_max -5)/2)
650 shalf = int((string_max -5)/2)
647
651
648 if ismagic:
652 if ismagic:
649 obj_type_name = 'Magic function'
653 obj_type_name = 'Magic function'
650 elif isalias:
654 elif isalias:
651 obj_type_name = 'System alias'
655 obj_type_name = 'System alias'
652 else:
656 else:
653 obj_type_name = obj_type.__name__
657 obj_type_name = obj_type.__name__
654 out['type_name'] = obj_type_name
658 out['type_name'] = obj_type_name
655
659
656 try:
660 try:
657 bclass = obj.__class__
661 bclass = obj.__class__
658 out['base_class'] = str(bclass)
662 out['base_class'] = str(bclass)
659 except: pass
663 except: pass
660
664
661 # String form, but snip if too long in ? form (full in ??)
665 # String form, but snip if too long in ? form (full in ??)
662 if detail_level >= self.str_detail_level:
666 if detail_level >= self.str_detail_level:
663 try:
667 try:
664 ostr = str(obj)
668 ostr = str(obj)
665 str_head = 'string_form'
669 str_head = 'string_form'
666 if not detail_level and len(ostr)>string_max:
670 if not detail_level and len(ostr)>string_max:
667 ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:]
671 ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:]
668 ostr = ("\n" + " " * len(str_head.expandtabs())).\
672 ostr = ("\n" + " " * len(str_head.expandtabs())).\
669 join(q.strip() for q in ostr.split("\n"))
673 join(q.strip() for q in ostr.split("\n"))
670 out[str_head] = ostr
674 out[str_head] = ostr
671 except:
675 except:
672 pass
676 pass
673
677
674 if ospace:
678 if ospace:
675 out['namespace'] = ospace
679 out['namespace'] = ospace
676
680
677 # Length (for strings and lists)
681 # Length (for strings and lists)
678 try:
682 try:
679 out['length'] = str(len(obj))
683 out['length'] = str(len(obj))
680 except: pass
684 except: pass
681
685
682 # Filename where object was defined
686 # Filename where object was defined
683 binary_file = False
687 binary_file = False
684 fname = find_file(obj)
688 fname = find_file(obj)
685 if fname is None:
689 if fname is None:
686 # if anything goes wrong, we don't want to show source, so it's as
690 # if anything goes wrong, we don't want to show source, so it's as
687 # if the file was binary
691 # if the file was binary
688 binary_file = True
692 binary_file = True
689 else:
693 else:
690 if fname.endswith(('.so', '.dll', '.pyd')):
694 if fname.endswith(('.so', '.dll', '.pyd')):
691 binary_file = True
695 binary_file = True
692 elif fname.endswith('<string>'):
696 elif fname.endswith('<string>'):
693 fname = 'Dynamically generated function. No source code available.'
697 fname = 'Dynamically generated function. No source code available.'
694 out['file'] = fname
698 out['file'] = fname
695
699
696 # Docstrings only in detail 0 mode, since source contains them (we
700 # Docstrings only in detail 0 mode, since source contains them (we
697 # avoid repetitions). If source fails, we add them back, see below.
701 # avoid repetitions). If source fails, we add them back, see below.
698 if ds and detail_level == 0:
702 if ds and detail_level == 0:
699 out['docstring'] = ds
703 out['docstring'] = ds
700
704
701 # Original source code for any callable
705 # Original source code for any callable
702 if detail_level:
706 if detail_level:
703 # Flush the source cache because inspect can return out-of-date
707 # Flush the source cache because inspect can return out-of-date
704 # source
708 # source
705 linecache.checkcache()
709 linecache.checkcache()
706 source = None
710 source = None
707 try:
711 try:
708 try:
712 try:
709 source = getsource(obj, binary_file)
713 source = getsource(obj, binary_file)
710 except TypeError:
714 except TypeError:
711 if hasattr(obj, '__class__'):
715 if hasattr(obj, '__class__'):
712 source = getsource(obj.__class__, binary_file)
716 source = getsource(obj.__class__, binary_file)
713 if source is not None:
717 if source is not None:
714 out['source'] = source.rstrip()
718 out['source'] = source.rstrip()
715 except Exception:
719 except Exception:
716 pass
720 pass
717
721
718 if ds and source is None:
722 if ds and source is None:
719 out['docstring'] = ds
723 out['docstring'] = ds
720
724
721
725
722 # Constructor docstring for classes
726 # Constructor docstring for classes
723 if inspect.isclass(obj):
727 if inspect.isclass(obj):
724 out['isclass'] = True
728 out['isclass'] = True
725 # reconstruct the function definition and print it:
729 # reconstruct the function definition and print it:
726 try:
730 try:
727 obj_init = obj.__init__
731 obj_init = obj.__init__
728 except AttributeError:
732 except AttributeError:
729 init_def = init_ds = None
733 init_def = init_ds = None
730 else:
734 else:
731 init_def = self._getdef(obj_init,oname)
735 init_def = self._getdef(obj_init,oname)
732 init_ds = getdoc(obj_init)
736 init_ds = getdoc(obj_init)
733 # Skip Python's auto-generated docstrings
737 # Skip Python's auto-generated docstrings
734 if init_ds == _object_init_docstring:
738 if init_ds == _object_init_docstring:
735 init_ds = None
739 init_ds = None
736
740
737 if init_def or init_ds:
741 if init_def or init_ds:
738 if init_def:
742 if init_def:
739 out['init_definition'] = self.format(init_def)
743 out['init_definition'] = self.format(init_def)
740 if init_ds:
744 if init_ds:
741 out['init_docstring'] = init_ds
745 out['init_docstring'] = init_ds
742
746
743 # and class docstring for instances:
747 # and class docstring for instances:
744 else:
748 else:
745 # reconstruct the function definition and print it:
749 # reconstruct the function definition and print it:
746 defln = self._getdef(obj, oname)
750 defln = self._getdef(obj, oname)
747 if defln:
751 if defln:
748 out['definition'] = self.format(defln)
752 out['definition'] = self.format(defln)
749
753
750 # First, check whether the instance docstring is identical to the
754 # First, check whether the instance docstring is identical to the
751 # class one, and print it separately if they don't coincide. In
755 # class one, and print it separately if they don't coincide. In
752 # most cases they will, but it's nice to print all the info for
756 # most cases they will, but it's nice to print all the info for
753 # objects which use instance-customized docstrings.
757 # objects which use instance-customized docstrings.
754 if ds:
758 if ds:
755 try:
759 try:
756 cls = getattr(obj,'__class__')
760 cls = getattr(obj,'__class__')
757 except:
761 except:
758 class_ds = None
762 class_ds = None
759 else:
763 else:
760 class_ds = getdoc(cls)
764 class_ds = getdoc(cls)
761 # Skip Python's auto-generated docstrings
765 # Skip Python's auto-generated docstrings
762 if class_ds in _builtin_type_docstrings:
766 if class_ds in _builtin_type_docstrings:
763 class_ds = None
767 class_ds = None
764 if class_ds and ds != class_ds:
768 if class_ds and ds != class_ds:
765 out['class_docstring'] = class_ds
769 out['class_docstring'] = class_ds
766
770
767 # Next, try to show constructor docstrings
771 # Next, try to show constructor docstrings
768 try:
772 try:
769 init_ds = getdoc(obj.__init__)
773 init_ds = getdoc(obj.__init__)
770 # Skip Python's auto-generated docstrings
774 # Skip Python's auto-generated docstrings
771 if init_ds == _object_init_docstring:
775 if init_ds == _object_init_docstring:
772 init_ds = None
776 init_ds = None
773 except AttributeError:
777 except AttributeError:
774 init_ds = None
778 init_ds = None
775 if init_ds:
779 if init_ds:
776 out['init_docstring'] = init_ds
780 out['init_docstring'] = init_ds
777
781
778 # Call form docstring for callable instances
782 # Call form docstring for callable instances
779 if safe_hasattr(obj, '__call__') and not is_simple_callable(obj):
783 if safe_hasattr(obj, '__call__') and not is_simple_callable(obj):
780 call_def = self._getdef(obj.__call__, oname)
784 call_def = self._getdef(obj.__call__, oname)
781 if call_def:
785 if call_def:
782 call_def = self.format(call_def)
786 call_def = self.format(call_def)
783 # it may never be the case that call def and definition differ,
787 # it may never be the case that call def and definition differ,
784 # but don't include the same signature twice
788 # but don't include the same signature twice
785 if call_def != out.get('definition'):
789 if call_def != out.get('definition'):
786 out['call_def'] = call_def
790 out['call_def'] = call_def
787 call_ds = getdoc(obj.__call__)
791 call_ds = getdoc(obj.__call__)
788 # Skip Python's auto-generated docstrings
792 # Skip Python's auto-generated docstrings
789 if call_ds == _func_call_docstring:
793 if call_ds == _func_call_docstring:
790 call_ds = None
794 call_ds = None
791 if call_ds:
795 if call_ds:
792 out['call_docstring'] = call_ds
796 out['call_docstring'] = call_ds
793
797
794 # Compute the object's argspec as a callable. The key is to decide
798 # Compute the object's argspec as a callable. The key is to decide
795 # whether to pull it from the object itself, from its __init__ or
799 # whether to pull it from the object itself, from its __init__ or
796 # from its __call__ method.
800 # from its __call__ method.
797
801
798 if inspect.isclass(obj):
802 if inspect.isclass(obj):
799 # Old-style classes need not have an __init__
803 # Old-style classes need not have an __init__
800 callable_obj = getattr(obj, "__init__", None)
804 callable_obj = getattr(obj, "__init__", None)
801 elif callable(obj):
805 elif callable(obj):
802 callable_obj = obj
806 callable_obj = obj
803 else:
807 else:
804 callable_obj = None
808 callable_obj = None
805
809
806 if callable_obj:
810 if callable_obj:
807 try:
811 try:
808 argspec = getargspec(callable_obj)
812 argspec = getargspec(callable_obj)
809 except (TypeError, AttributeError):
813 except (TypeError, AttributeError):
810 # For extensions/builtins we can't retrieve the argspec
814 # For extensions/builtins we can't retrieve the argspec
811 pass
815 pass
812 else:
816 else:
813 # named tuples' _asdict() method returns an OrderedDict, but we
817 # named tuples' _asdict() method returns an OrderedDict, but we
814 # we want a normal
818 # we want a normal
815 out['argspec'] = argspec_dict = dict(argspec._asdict())
819 out['argspec'] = argspec_dict = dict(argspec._asdict())
816 # We called this varkw before argspec became a named tuple.
820 # We called this varkw before argspec became a named tuple.
817 # With getfullargspec it's also called varkw.
821 # With getfullargspec it's also called varkw.
818 if 'varkw' not in argspec_dict:
822 if 'varkw' not in argspec_dict:
819 argspec_dict['varkw'] = argspec_dict.pop('keywords')
823 argspec_dict['varkw'] = argspec_dict.pop('keywords')
820
824
821 return object_info(**out)
825 return object_info(**out)
822
826
823
827
824 def psearch(self,pattern,ns_table,ns_search=[],
828 def psearch(self,pattern,ns_table,ns_search=[],
825 ignore_case=False,show_all=False):
829 ignore_case=False,show_all=False):
826 """Search namespaces with wildcards for objects.
830 """Search namespaces with wildcards for objects.
827
831
828 Arguments:
832 Arguments:
829
833
830 - pattern: string containing shell-like wildcards to use in namespace
834 - pattern: string containing shell-like wildcards to use in namespace
831 searches and optionally a type specification to narrow the search to
835 searches and optionally a type specification to narrow the search to
832 objects of that type.
836 objects of that type.
833
837
834 - ns_table: dict of name->namespaces for search.
838 - ns_table: dict of name->namespaces for search.
835
839
836 Optional arguments:
840 Optional arguments:
837
841
838 - ns_search: list of namespace names to include in search.
842 - ns_search: list of namespace names to include in search.
839
843
840 - ignore_case(False): make the search case-insensitive.
844 - ignore_case(False): make the search case-insensitive.
841
845
842 - show_all(False): show all names, including those starting with
846 - show_all(False): show all names, including those starting with
843 underscores.
847 underscores.
844 """
848 """
845 #print 'ps pattern:<%r>' % pattern # dbg
849 #print 'ps pattern:<%r>' % pattern # dbg
846
850
847 # defaults
851 # defaults
848 type_pattern = 'all'
852 type_pattern = 'all'
849 filter = ''
853 filter = ''
850
854
851 cmds = pattern.split()
855 cmds = pattern.split()
852 len_cmds = len(cmds)
856 len_cmds = len(cmds)
853 if len_cmds == 1:
857 if len_cmds == 1:
854 # Only filter pattern given
858 # Only filter pattern given
855 filter = cmds[0]
859 filter = cmds[0]
856 elif len_cmds == 2:
860 elif len_cmds == 2:
857 # Both filter and type specified
861 # Both filter and type specified
858 filter,type_pattern = cmds
862 filter,type_pattern = cmds
859 else:
863 else:
860 raise ValueError('invalid argument string for psearch: <%s>' %
864 raise ValueError('invalid argument string for psearch: <%s>' %
861 pattern)
865 pattern)
862
866
863 # filter search namespaces
867 # filter search namespaces
864 for name in ns_search:
868 for name in ns_search:
865 if name not in ns_table:
869 if name not in ns_table:
866 raise ValueError('invalid namespace <%s>. Valid names: %s' %
870 raise ValueError('invalid namespace <%s>. Valid names: %s' %
867 (name,ns_table.keys()))
871 (name,ns_table.keys()))
868
872
869 #print 'type_pattern:',type_pattern # dbg
873 #print 'type_pattern:',type_pattern # dbg
870 search_result, namespaces_seen = set(), set()
874 search_result, namespaces_seen = set(), set()
871 for ns_name in ns_search:
875 for ns_name in ns_search:
872 ns = ns_table[ns_name]
876 ns = ns_table[ns_name]
873 # Normally, locals and globals are the same, so we just check one.
877 # Normally, locals and globals are the same, so we just check one.
874 if id(ns) in namespaces_seen:
878 if id(ns) in namespaces_seen:
875 continue
879 continue
876 namespaces_seen.add(id(ns))
880 namespaces_seen.add(id(ns))
877 tmp_res = list_namespace(ns, type_pattern, filter,
881 tmp_res = list_namespace(ns, type_pattern, filter,
878 ignore_case=ignore_case, show_all=show_all)
882 ignore_case=ignore_case, show_all=show_all)
879 search_result.update(tmp_res)
883 search_result.update(tmp_res)
880
884
881 page.page('\n'.join(sorted(search_result)))
885 page.page('\n'.join(sorted(search_result)))
@@ -1,353 +1,360
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Paging capabilities for IPython.core
3 Paging capabilities for IPython.core
4
4
5 Authors:
5 Authors:
6
6
7 * Brian Granger
7 * Brian Granger
8 * Fernando Perez
8 * Fernando Perez
9
9
10 Notes
10 Notes
11 -----
11 -----
12
12
13 For now this uses ipapi, so it can't be in IPython.utils. If we can get
13 For now this uses ipapi, so it can't be in IPython.utils. If we can get
14 rid of that dependency, we could move it there.
14 rid of that dependency, we could move it there.
15 -----
15 -----
16 """
16 """
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Copyright (C) 2008-2011 The IPython Development Team
19 # Copyright (C) 2008-2011 The IPython Development Team
20 #
20 #
21 # Distributed under the terms of the BSD License. The full license is in
21 # Distributed under the terms of the BSD License. The full license is in
22 # the file COPYING, distributed as part of this software.
22 # the file COPYING, distributed as part of this software.
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24
24
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26 # Imports
26 # Imports
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28 from __future__ import print_function
28 from __future__ import print_function
29
29
30 import os
30 import os
31 import re
31 import re
32 import sys
32 import sys
33 import tempfile
33 import tempfile
34
34
35 from io import UnsupportedOperation
35 from io import UnsupportedOperation
36
36
37 from IPython import get_ipython
37 from IPython import get_ipython
38 from IPython.core.error import TryNext
38 from IPython.core.error import TryNext
39 from IPython.utils.data import chop
39 from IPython.utils.data import chop
40 from IPython.utils import io
40 from IPython.utils import io
41 from IPython.utils.process import system
41 from IPython.utils.process import system
42 from IPython.utils.terminal import get_terminal_size
42 from IPython.utils.terminal import get_terminal_size
43 from IPython.utils import py3compat
43 from IPython.utils import py3compat
44
44
45
45
46 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
47 # Classes and functions
47 # Classes and functions
48 #-----------------------------------------------------------------------------
48 #-----------------------------------------------------------------------------
49
49
50 esc_re = re.compile(r"(\x1b[^m]+m)")
50 esc_re = re.compile(r"(\x1b[^m]+m)")
51
51
52 def page_dumb(strng, start=0, screen_lines=25):
52 def page_dumb(strng, start=0, screen_lines=25):
53 """Very dumb 'pager' in Python, for when nothing else works.
53 """Very dumb 'pager' in Python, for when nothing else works.
54
54
55 Only moves forward, same interface as page(), except for pager_cmd and
55 Only moves forward, same interface as page(), except for pager_cmd and
56 mode."""
56 mode."""
57
57
58 out_ln = strng.splitlines()[start:]
58 out_ln = strng.splitlines()[start:]
59 screens = chop(out_ln,screen_lines-1)
59 screens = chop(out_ln,screen_lines-1)
60 if len(screens) == 1:
60 if len(screens) == 1:
61 print(os.linesep.join(screens[0]), file=io.stdout)
61 print(os.linesep.join(screens[0]), file=io.stdout)
62 else:
62 else:
63 last_escape = ""
63 last_escape = ""
64 for scr in screens[0:-1]:
64 for scr in screens[0:-1]:
65 hunk = os.linesep.join(scr)
65 hunk = os.linesep.join(scr)
66 print(last_escape + hunk, file=io.stdout)
66 print(last_escape + hunk, file=io.stdout)
67 if not page_more():
67 if not page_more():
68 return
68 return
69 esc_list = esc_re.findall(hunk)
69 esc_list = esc_re.findall(hunk)
70 if len(esc_list) > 0:
70 if len(esc_list) > 0:
71 last_escape = esc_list[-1]
71 last_escape = esc_list[-1]
72 print(last_escape + os.linesep.join(screens[-1]), file=io.stdout)
72 print(last_escape + os.linesep.join(screens[-1]), file=io.stdout)
73
73
74 def _detect_screen_size(screen_lines_def):
74 def _detect_screen_size(screen_lines_def):
75 """Attempt to work out the number of lines on the screen.
75 """Attempt to work out the number of lines on the screen.
76
76
77 This is called by page(). It can raise an error (e.g. when run in the
77 This is called by page(). It can raise an error (e.g. when run in the
78 test suite), so it's separated out so it can easily be called in a try block.
78 test suite), so it's separated out so it can easily be called in a try block.
79 """
79 """
80 TERM = os.environ.get('TERM',None)
80 TERM = os.environ.get('TERM',None)
81 if not((TERM=='xterm' or TERM=='xterm-color') and sys.platform != 'sunos5'):
81 if not((TERM=='xterm' or TERM=='xterm-color') and sys.platform != 'sunos5'):
82 # curses causes problems on many terminals other than xterm, and
82 # curses causes problems on many terminals other than xterm, and
83 # some termios calls lock up on Sun OS5.
83 # some termios calls lock up on Sun OS5.
84 return screen_lines_def
84 return screen_lines_def
85
85
86 try:
86 try:
87 import termios
87 import termios
88 import curses
88 import curses
89 except ImportError:
89 except ImportError:
90 return screen_lines_def
90 return screen_lines_def
91
91
92 # There is a bug in curses, where *sometimes* it fails to properly
92 # There is a bug in curses, where *sometimes* it fails to properly
93 # initialize, and then after the endwin() call is made, the
93 # initialize, and then after the endwin() call is made, the
94 # terminal is left in an unusable state. Rather than trying to
94 # terminal is left in an unusable state. Rather than trying to
95 # check everytime for this (by requesting and comparing termios
95 # check everytime for this (by requesting and comparing termios
96 # flags each time), we just save the initial terminal state and
96 # flags each time), we just save the initial terminal state and
97 # unconditionally reset it every time. It's cheaper than making
97 # unconditionally reset it every time. It's cheaper than making
98 # the checks.
98 # the checks.
99 term_flags = termios.tcgetattr(sys.stdout)
99 term_flags = termios.tcgetattr(sys.stdout)
100
100
101 # Curses modifies the stdout buffer size by default, which messes
101 # Curses modifies the stdout buffer size by default, which messes
102 # up Python's normal stdout buffering. This would manifest itself
102 # up Python's normal stdout buffering. This would manifest itself
103 # to IPython users as delayed printing on stdout after having used
103 # to IPython users as delayed printing on stdout after having used
104 # the pager.
104 # the pager.
105 #
105 #
106 # We can prevent this by manually setting the NCURSES_NO_SETBUF
106 # We can prevent this by manually setting the NCURSES_NO_SETBUF
107 # environment variable. For more details, see:
107 # environment variable. For more details, see:
108 # http://bugs.python.org/issue10144
108 # http://bugs.python.org/issue10144
109 NCURSES_NO_SETBUF = os.environ.get('NCURSES_NO_SETBUF', None)
109 NCURSES_NO_SETBUF = os.environ.get('NCURSES_NO_SETBUF', None)
110 os.environ['NCURSES_NO_SETBUF'] = ''
110 os.environ['NCURSES_NO_SETBUF'] = ''
111
111
112 # Proceed with curses initialization
112 # Proceed with curses initialization
113 try:
113 try:
114 scr = curses.initscr()
114 scr = curses.initscr()
115 except AttributeError:
115 except AttributeError:
116 # Curses on Solaris may not be complete, so we can't use it there
116 # Curses on Solaris may not be complete, so we can't use it there
117 return screen_lines_def
117 return screen_lines_def
118
118
119 screen_lines_real,screen_cols = scr.getmaxyx()
119 screen_lines_real,screen_cols = scr.getmaxyx()
120 curses.endwin()
120 curses.endwin()
121
121
122 # Restore environment
122 # Restore environment
123 if NCURSES_NO_SETBUF is None:
123 if NCURSES_NO_SETBUF is None:
124 del os.environ['NCURSES_NO_SETBUF']
124 del os.environ['NCURSES_NO_SETBUF']
125 else:
125 else:
126 os.environ['NCURSES_NO_SETBUF'] = NCURSES_NO_SETBUF
126 os.environ['NCURSES_NO_SETBUF'] = NCURSES_NO_SETBUF
127
127
128 # Restore terminal state in case endwin() didn't.
128 # Restore terminal state in case endwin() didn't.
129 termios.tcsetattr(sys.stdout,termios.TCSANOW,term_flags)
129 termios.tcsetattr(sys.stdout,termios.TCSANOW,term_flags)
130 # Now we have what we needed: the screen size in rows/columns
130 # Now we have what we needed: the screen size in rows/columns
131 return screen_lines_real
131 return screen_lines_real
132 #print '***Screen size:',screen_lines_real,'lines x',\
132 #print '***Screen size:',screen_lines_real,'lines x',\
133 #screen_cols,'columns.' # dbg
133 #screen_cols,'columns.' # dbg
134
134
135 def page(strng, start=0, screen_lines=0, pager_cmd=None):
135 def page(strng, start=0, screen_lines=0, pager_cmd=None):
136 """Print a string, piping through a pager after a certain length.
136 """Display a string, piping through a pager after a certain length.
137
138 strng can be a mime-bundle dict, supplying multiple representations,
139 keyed by mime-type.
137
140
138 The screen_lines parameter specifies the number of *usable* lines of your
141 The screen_lines parameter specifies the number of *usable* lines of your
139 terminal screen (total lines minus lines you need to reserve to show other
142 terminal screen (total lines minus lines you need to reserve to show other
140 information).
143 information).
141
144
142 If you set screen_lines to a number <=0, page() will try to auto-determine
145 If you set screen_lines to a number <=0, page() will try to auto-determine
143 your screen size and will only use up to (screen_size+screen_lines) for
146 your screen size and will only use up to (screen_size+screen_lines) for
144 printing, paging after that. That is, if you want auto-detection but need
147 printing, paging after that. That is, if you want auto-detection but need
145 to reserve the bottom 3 lines of the screen, use screen_lines = -3, and for
148 to reserve the bottom 3 lines of the screen, use screen_lines = -3, and for
146 auto-detection without any lines reserved simply use screen_lines = 0.
149 auto-detection without any lines reserved simply use screen_lines = 0.
147
150
148 If a string won't fit in the allowed lines, it is sent through the
151 If a string won't fit in the allowed lines, it is sent through the
149 specified pager command. If none given, look for PAGER in the environment,
152 specified pager command. If none given, look for PAGER in the environment,
150 and ultimately default to less.
153 and ultimately default to less.
151
154
152 If no system pager works, the string is sent through a 'dumb pager'
155 If no system pager works, the string is sent through a 'dumb pager'
153 written in python, very simplistic.
156 written in python, very simplistic.
154 """
157 """
158
159 # for compatibility with mime-bundle form:
160 if isinstance(strng, dict):
161 strng = strng['text/plain']
155
162
156 # Some routines may auto-compute start offsets incorrectly and pass a
163 # Some routines may auto-compute start offsets incorrectly and pass a
157 # negative value. Offset to 0 for robustness.
164 # negative value. Offset to 0 for robustness.
158 start = max(0, start)
165 start = max(0, start)
159
166
160 # first, try the hook
167 # first, try the hook
161 ip = get_ipython()
168 ip = get_ipython()
162 if ip:
169 if ip:
163 try:
170 try:
164 ip.hooks.show_in_pager(strng)
171 ip.hooks.show_in_pager(strng)
165 return
172 return
166 except TryNext:
173 except TryNext:
167 pass
174 pass
168
175
169 # Ugly kludge, but calling curses.initscr() flat out crashes in emacs
176 # Ugly kludge, but calling curses.initscr() flat out crashes in emacs
170 TERM = os.environ.get('TERM','dumb')
177 TERM = os.environ.get('TERM','dumb')
171 if TERM in ['dumb','emacs'] and os.name != 'nt':
178 if TERM in ['dumb','emacs'] and os.name != 'nt':
172 print(strng)
179 print(strng)
173 return
180 return
174 # chop off the topmost part of the string we don't want to see
181 # chop off the topmost part of the string we don't want to see
175 str_lines = strng.splitlines()[start:]
182 str_lines = strng.splitlines()[start:]
176 str_toprint = os.linesep.join(str_lines)
183 str_toprint = os.linesep.join(str_lines)
177 num_newlines = len(str_lines)
184 num_newlines = len(str_lines)
178 len_str = len(str_toprint)
185 len_str = len(str_toprint)
179
186
180 # Dumb heuristics to guesstimate number of on-screen lines the string
187 # Dumb heuristics to guesstimate number of on-screen lines the string
181 # takes. Very basic, but good enough for docstrings in reasonable
188 # takes. Very basic, but good enough for docstrings in reasonable
182 # terminals. If someone later feels like refining it, it's not hard.
189 # terminals. If someone later feels like refining it, it's not hard.
183 numlines = max(num_newlines,int(len_str/80)+1)
190 numlines = max(num_newlines,int(len_str/80)+1)
184
191
185 screen_lines_def = get_terminal_size()[1]
192 screen_lines_def = get_terminal_size()[1]
186
193
187 # auto-determine screen size
194 # auto-determine screen size
188 if screen_lines <= 0:
195 if screen_lines <= 0:
189 try:
196 try:
190 screen_lines += _detect_screen_size(screen_lines_def)
197 screen_lines += _detect_screen_size(screen_lines_def)
191 except (TypeError, UnsupportedOperation):
198 except (TypeError, UnsupportedOperation):
192 print(str_toprint, file=io.stdout)
199 print(str_toprint, file=io.stdout)
193 return
200 return
194
201
195 #print 'numlines',numlines,'screenlines',screen_lines # dbg
202 #print 'numlines',numlines,'screenlines',screen_lines # dbg
196 if numlines <= screen_lines :
203 if numlines <= screen_lines :
197 #print '*** normal print' # dbg
204 #print '*** normal print' # dbg
198 print(str_toprint, file=io.stdout)
205 print(str_toprint, file=io.stdout)
199 else:
206 else:
200 # Try to open pager and default to internal one if that fails.
207 # Try to open pager and default to internal one if that fails.
201 # All failure modes are tagged as 'retval=1', to match the return
208 # All failure modes are tagged as 'retval=1', to match the return
202 # value of a failed system command. If any intermediate attempt
209 # value of a failed system command. If any intermediate attempt
203 # sets retval to 1, at the end we resort to our own page_dumb() pager.
210 # sets retval to 1, at the end we resort to our own page_dumb() pager.
204 pager_cmd = get_pager_cmd(pager_cmd)
211 pager_cmd = get_pager_cmd(pager_cmd)
205 pager_cmd += ' ' + get_pager_start(pager_cmd,start)
212 pager_cmd += ' ' + get_pager_start(pager_cmd,start)
206 if os.name == 'nt':
213 if os.name == 'nt':
207 if pager_cmd.startswith('type'):
214 if pager_cmd.startswith('type'):
208 # The default WinXP 'type' command is failing on complex strings.
215 # The default WinXP 'type' command is failing on complex strings.
209 retval = 1
216 retval = 1
210 else:
217 else:
211 fd, tmpname = tempfile.mkstemp('.txt')
218 fd, tmpname = tempfile.mkstemp('.txt')
212 try:
219 try:
213 os.close(fd)
220 os.close(fd)
214 with open(tmpname, 'wt') as tmpfile:
221 with open(tmpname, 'wt') as tmpfile:
215 tmpfile.write(strng)
222 tmpfile.write(strng)
216 cmd = "%s < %s" % (pager_cmd, tmpname)
223 cmd = "%s < %s" % (pager_cmd, tmpname)
217 # tmpfile needs to be closed for windows
224 # tmpfile needs to be closed for windows
218 if os.system(cmd):
225 if os.system(cmd):
219 retval = 1
226 retval = 1
220 else:
227 else:
221 retval = None
228 retval = None
222 finally:
229 finally:
223 os.remove(tmpname)
230 os.remove(tmpname)
224 else:
231 else:
225 try:
232 try:
226 retval = None
233 retval = None
227 # if I use popen4, things hang. No idea why.
234 # if I use popen4, things hang. No idea why.
228 #pager,shell_out = os.popen4(pager_cmd)
235 #pager,shell_out = os.popen4(pager_cmd)
229 pager = os.popen(pager_cmd, 'w')
236 pager = os.popen(pager_cmd, 'w')
230 try:
237 try:
231 pager_encoding = pager.encoding or sys.stdout.encoding
238 pager_encoding = pager.encoding or sys.stdout.encoding
232 pager.write(py3compat.cast_bytes_py2(
239 pager.write(py3compat.cast_bytes_py2(
233 strng, encoding=pager_encoding))
240 strng, encoding=pager_encoding))
234 finally:
241 finally:
235 retval = pager.close()
242 retval = pager.close()
236 except IOError as msg: # broken pipe when user quits
243 except IOError as msg: # broken pipe when user quits
237 if msg.args == (32, 'Broken pipe'):
244 if msg.args == (32, 'Broken pipe'):
238 retval = None
245 retval = None
239 else:
246 else:
240 retval = 1
247 retval = 1
241 except OSError:
248 except OSError:
242 # Other strange problems, sometimes seen in Win2k/cygwin
249 # Other strange problems, sometimes seen in Win2k/cygwin
243 retval = 1
250 retval = 1
244 if retval is not None:
251 if retval is not None:
245 page_dumb(strng,screen_lines=screen_lines)
252 page_dumb(strng,screen_lines=screen_lines)
246
253
247
254
248 def page_file(fname, start=0, pager_cmd=None):
255 def page_file(fname, start=0, pager_cmd=None):
249 """Page a file, using an optional pager command and starting line.
256 """Page a file, using an optional pager command and starting line.
250 """
257 """
251
258
252 pager_cmd = get_pager_cmd(pager_cmd)
259 pager_cmd = get_pager_cmd(pager_cmd)
253 pager_cmd += ' ' + get_pager_start(pager_cmd,start)
260 pager_cmd += ' ' + get_pager_start(pager_cmd,start)
254
261
255 try:
262 try:
256 if os.environ['TERM'] in ['emacs','dumb']:
263 if os.environ['TERM'] in ['emacs','dumb']:
257 raise EnvironmentError
264 raise EnvironmentError
258 system(pager_cmd + ' ' + fname)
265 system(pager_cmd + ' ' + fname)
259 except:
266 except:
260 try:
267 try:
261 if start > 0:
268 if start > 0:
262 start -= 1
269 start -= 1
263 page(open(fname).read(),start)
270 page(open(fname).read(),start)
264 except:
271 except:
265 print('Unable to show file',repr(fname))
272 print('Unable to show file',repr(fname))
266
273
267
274
268 def get_pager_cmd(pager_cmd=None):
275 def get_pager_cmd(pager_cmd=None):
269 """Return a pager command.
276 """Return a pager command.
270
277
271 Makes some attempts at finding an OS-correct one.
278 Makes some attempts at finding an OS-correct one.
272 """
279 """
273 if os.name == 'posix':
280 if os.name == 'posix':
274 default_pager_cmd = 'less -r' # -r for color control sequences
281 default_pager_cmd = 'less -r' # -r for color control sequences
275 elif os.name in ['nt','dos']:
282 elif os.name in ['nt','dos']:
276 default_pager_cmd = 'type'
283 default_pager_cmd = 'type'
277
284
278 if pager_cmd is None:
285 if pager_cmd is None:
279 try:
286 try:
280 pager_cmd = os.environ['PAGER']
287 pager_cmd = os.environ['PAGER']
281 except:
288 except:
282 pager_cmd = default_pager_cmd
289 pager_cmd = default_pager_cmd
283 return pager_cmd
290 return pager_cmd
284
291
285
292
286 def get_pager_start(pager, start):
293 def get_pager_start(pager, start):
287 """Return the string for paging files with an offset.
294 """Return the string for paging files with an offset.
288
295
289 This is the '+N' argument which less and more (under Unix) accept.
296 This is the '+N' argument which less and more (under Unix) accept.
290 """
297 """
291
298
292 if pager in ['less','more']:
299 if pager in ['less','more']:
293 if start:
300 if start:
294 start_string = '+' + str(start)
301 start_string = '+' + str(start)
295 else:
302 else:
296 start_string = ''
303 start_string = ''
297 else:
304 else:
298 start_string = ''
305 start_string = ''
299 return start_string
306 return start_string
300
307
301
308
302 # (X)emacs on win32 doesn't like to be bypassed with msvcrt.getch()
309 # (X)emacs on win32 doesn't like to be bypassed with msvcrt.getch()
303 if os.name == 'nt' and os.environ.get('TERM','dumb') != 'emacs':
310 if os.name == 'nt' and os.environ.get('TERM','dumb') != 'emacs':
304 import msvcrt
311 import msvcrt
305 def page_more():
312 def page_more():
306 """ Smart pausing between pages
313 """ Smart pausing between pages
307
314
308 @return: True if need print more lines, False if quit
315 @return: True if need print more lines, False if quit
309 """
316 """
310 io.stdout.write('---Return to continue, q to quit--- ')
317 io.stdout.write('---Return to continue, q to quit--- ')
311 ans = msvcrt.getwch()
318 ans = msvcrt.getwch()
312 if ans in ("q", "Q"):
319 if ans in ("q", "Q"):
313 result = False
320 result = False
314 else:
321 else:
315 result = True
322 result = True
316 io.stdout.write("\b"*37 + " "*37 + "\b"*37)
323 io.stdout.write("\b"*37 + " "*37 + "\b"*37)
317 return result
324 return result
318 else:
325 else:
319 def page_more():
326 def page_more():
320 ans = py3compat.input('---Return to continue, q to quit--- ')
327 ans = py3compat.input('---Return to continue, q to quit--- ')
321 if ans.lower().startswith('q'):
328 if ans.lower().startswith('q'):
322 return False
329 return False
323 else:
330 else:
324 return True
331 return True
325
332
326
333
327 def snip_print(str,width = 75,print_full = 0,header = ''):
334 def snip_print(str,width = 75,print_full = 0,header = ''):
328 """Print a string snipping the midsection to fit in width.
335 """Print a string snipping the midsection to fit in width.
329
336
330 print_full: mode control:
337 print_full: mode control:
331
338
332 - 0: only snip long strings
339 - 0: only snip long strings
333 - 1: send to page() directly.
340 - 1: send to page() directly.
334 - 2: snip long strings and ask for full length viewing with page()
341 - 2: snip long strings and ask for full length viewing with page()
335
342
336 Return 1 if snipping was necessary, 0 otherwise."""
343 Return 1 if snipping was necessary, 0 otherwise."""
337
344
338 if print_full == 1:
345 if print_full == 1:
339 page(header+str)
346 page(header+str)
340 return 0
347 return 0
341
348
342 print(header, end=' ')
349 print(header, end=' ')
343 if len(str) < width:
350 if len(str) < width:
344 print(str)
351 print(str)
345 snip = 0
352 snip = 0
346 else:
353 else:
347 whalf = int((width -5)/2)
354 whalf = int((width -5)/2)
348 print(str[:whalf] + ' <...> ' + str[-whalf:])
355 print(str[:whalf] + ' <...> ' + str[-whalf:])
349 snip = 1
356 snip = 1
350 if snip and print_full == 2:
357 if snip and print_full == 2:
351 if py3compat.input(header+' Snipped. View (y/n)? [N]').lower() == 'y':
358 if py3compat.input(header+' Snipped. View (y/n)? [N]').lower() == 'y':
352 page(str)
359 page(str)
353 return snip
360 return snip
@@ -1,96 +1,50
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """A payload based version of page."""
3 A payload based version of page.
4
3
5 Authors:
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
6
6
7 * Brian Granger
7 from IPython.core.getipython import get_ipython
8 * Fernando Perez
9 """
10
11 #-----------------------------------------------------------------------------
12 # Copyright (C) 2008-2011 The IPython Development Team
13 #
14 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
16 #-----------------------------------------------------------------------------
17
18 #-----------------------------------------------------------------------------
19 # Imports
20 #-----------------------------------------------------------------------------
21
22 # Third-party
23 try:
24 from docutils.core import publish_string
25 except ImportError:
26 # html paging won't be available, but we don't raise any errors. It's a
27 # purely optional feature.
28 pass
29
30 # Our own
31 from IPython.core.interactiveshell import InteractiveShell
32
8
33 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
34 # Classes and functions
10 # Classes and functions
35 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
36
12
37 def page(strng, start=0, screen_lines=0, pager_cmd=None,
13 def page(strng, start=0, screen_lines=0, pager_cmd=None):
38 html=None, auto_html=False):
39 """Print a string, piping through a pager.
14 """Print a string, piping through a pager.
40
15
41 This version ignores the screen_lines and pager_cmd arguments and uses
16 This version ignores the screen_lines and pager_cmd arguments and uses
42 IPython's payload system instead.
17 IPython's payload system instead.
43
18
44 Parameters
19 Parameters
45 ----------
20 ----------
46 strng : str
21 strng : str or mime-dict
47 Text to page.
22 Text to page, or a mime-type keyed dict of already formatted data.
48
23
49 start : int
24 start : int
50 Starting line at which to place the display.
25 Starting line at which to place the display.
51
52 html : str, optional
53 If given, an html string to send as well.
54
55 auto_html : bool, optional
56 If true, the input string is assumed to be valid reStructuredText and is
57 converted to HTML with docutils. Note that if docutils is not found,
58 this option is silently ignored.
59
60 Notes
61 -----
62
63 Only one of the ``html`` and ``auto_html`` options can be given, not
64 both.
65 """
26 """
66
27
67 # Some routines may auto-compute start offsets incorrectly and pass a
28 # Some routines may auto-compute start offsets incorrectly and pass a
68 # negative value. Offset to 0 for robustness.
29 # negative value. Offset to 0 for robustness.
69 start = max(0, start)
30 start = max(0, start)
70 shell = InteractiveShell.instance()
31 shell = get_ipython()
71
32
72 if auto_html:
33 if isinstance(strng, dict):
73 try:
34 data = strng
74 # These defaults ensure user configuration variables for docutils
35 else:
75 # are not loaded, only our config is used here.
36 data = {'text/plain' : strng}
76 defaults = {'file_insertion_enabled': 0,
77 'raw_enabled': 0,
78 '_disable_config': 1}
79 html = publish_string(strng, writer_name='html',
80 settings_overrides=defaults)
81 except:
82 pass
83
84 payload = dict(
37 payload = dict(
85 source='page',
38 source='page',
39 data=data,
86 text=strng,
40 text=strng,
87 html=html,
41 start=start,
88 start_line_number=start
42 screen_lines=screen_lines,
89 )
43 )
90 shell.payload_manager.write_payload(payload)
44 shell.payload_manager.write_payload(payload)
91
45
92
46
93 def install_payload_page():
47 def install_payload_page():
94 """Install this version of page as IPython.core.page.page."""
48 """Install this version of page as IPython.core.page.page."""
95 from IPython.core import page as corepage
49 from IPython.core import page as corepage
96 corepage.page = page
50 corepage.page = page
@@ -1,149 +1,150
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Release data for the IPython project."""
2 """Release data for the IPython project."""
3
3
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Copyright (c) 2008, IPython Development Team.
5 # Copyright (c) 2008, IPython Development Team.
6 # Copyright (c) 2001, Fernando Perez <fernando.perez@colorado.edu>
6 # Copyright (c) 2001, Fernando Perez <fernando.perez@colorado.edu>
7 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
7 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
8 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
8 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
9 #
9 #
10 # Distributed under the terms of the Modified BSD License.
10 # Distributed under the terms of the Modified BSD License.
11 #
11 #
12 # The full license is in the file COPYING.txt, distributed with this software.
12 # The full license is in the file COPYING.txt, distributed with this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 # Name of the package for release purposes. This is the name which labels
15 # Name of the package for release purposes. This is the name which labels
16 # the tarballs and RPMs made by distutils, so it's best to lowercase it.
16 # the tarballs and RPMs made by distutils, so it's best to lowercase it.
17 name = 'ipython'
17 name = 'ipython'
18
18
19 # IPython version information. An empty _version_extra corresponds to a full
19 # IPython version information. An empty _version_extra corresponds to a full
20 # release. 'dev' as a _version_extra string means this is a development
20 # release. 'dev' as a _version_extra string means this is a development
21 # version
21 # version
22 _version_major = 3
22 _version_major = 3
23 _version_minor = 0
23 _version_minor = 0
24 _version_patch = 0
24 _version_patch = 0
25 _version_extra = 'dev'
25 _version_extra = 'dev'
26 # _version_extra = 'rc1'
26 # _version_extra = 'rc1'
27 # _version_extra = '' # Uncomment this for full releases
27 # _version_extra = '' # Uncomment this for full releases
28
28
29 # release.codename is deprecated in 2.0, will be removed in 3.0
29 # release.codename is deprecated in 2.0, will be removed in 3.0
30 codename = ''
30 codename = ''
31
31
32 # Construct full version string from these.
32 # Construct full version string from these.
33 _ver = [_version_major, _version_minor, _version_patch]
33 _ver = [_version_major, _version_minor, _version_patch]
34
34
35 __version__ = '.'.join(map(str, _ver))
35 __version__ = '.'.join(map(str, _ver))
36 if _version_extra:
36 if _version_extra:
37 __version__ = __version__ + '-' + _version_extra
37 __version__ = __version__ + '-' + _version_extra
38
38
39 version = __version__ # backwards compatibility name
39 version = __version__ # backwards compatibility name
40 version_info = (_version_major, _version_minor, _version_patch, _version_extra)
40 version_info = (_version_major, _version_minor, _version_patch, _version_extra)
41
41
42 # Change this when incrementing the kernel protocol version
42 # Change this when incrementing the kernel protocol version
43 kernel_protocol_version_info = (4, 1)
43 kernel_protocol_version_info = (5, 0)
44 kernel_protocol_version = "%i.%i" % kernel_protocol_version_info
44
45
45 description = "IPython: Productive Interactive Computing"
46 description = "IPython: Productive Interactive Computing"
46
47
47 long_description = \
48 long_description = \
48 """
49 """
49 IPython provides a rich toolkit to help you make the most out of using Python
50 IPython provides a rich toolkit to help you make the most out of using Python
50 interactively. Its main components are:
51 interactively. Its main components are:
51
52
52 * Powerful interactive Python shells (terminal- and Qt-based).
53 * Powerful interactive Python shells (terminal- and Qt-based).
53 * A web-based interactive notebook environment with all shell features plus
54 * A web-based interactive notebook environment with all shell features plus
54 support for embedded figures, animations and rich media.
55 support for embedded figures, animations and rich media.
55 * Support for interactive data visualization and use of GUI toolkits.
56 * Support for interactive data visualization and use of GUI toolkits.
56 * Flexible, embeddable interpreters to load into your own projects.
57 * Flexible, embeddable interpreters to load into your own projects.
57 * A high-performance library for high level and interactive parallel computing
58 * A high-performance library for high level and interactive parallel computing
58 that works in multicore systems, clusters, supercomputing and cloud scenarios.
59 that works in multicore systems, clusters, supercomputing and cloud scenarios.
59
60
60 The enhanced interactive Python shells have the following main features:
61 The enhanced interactive Python shells have the following main features:
61
62
62 * Comprehensive object introspection.
63 * Comprehensive object introspection.
63
64
64 * Input history, persistent across sessions.
65 * Input history, persistent across sessions.
65
66
66 * Caching of output results during a session with automatically generated
67 * Caching of output results during a session with automatically generated
67 references.
68 references.
68
69
69 * Extensible tab completion, with support by default for completion of python
70 * Extensible tab completion, with support by default for completion of python
70 variables and keywords, filenames and function keywords.
71 variables and keywords, filenames and function keywords.
71
72
72 * Extensible system of 'magic' commands for controlling the environment and
73 * Extensible system of 'magic' commands for controlling the environment and
73 performing many tasks related either to IPython or the operating system.
74 performing many tasks related either to IPython or the operating system.
74
75
75 * A rich configuration system with easy switching between different setups
76 * A rich configuration system with easy switching between different setups
76 (simpler than changing $PYTHONSTARTUP environment variables every time).
77 (simpler than changing $PYTHONSTARTUP environment variables every time).
77
78
78 * Session logging and reloading.
79 * Session logging and reloading.
79
80
80 * Extensible syntax processing for special purpose situations.
81 * Extensible syntax processing for special purpose situations.
81
82
82 * Access to the system shell with user-extensible alias system.
83 * Access to the system shell with user-extensible alias system.
83
84
84 * Easily embeddable in other Python programs and GUIs.
85 * Easily embeddable in other Python programs and GUIs.
85
86
86 * Integrated access to the pdb debugger and the Python profiler.
87 * Integrated access to the pdb debugger and the Python profiler.
87
88
88 The parallel computing architecture has the following main features:
89 The parallel computing architecture has the following main features:
89
90
90 * Quickly parallelize Python code from an interactive Python/IPython session.
91 * Quickly parallelize Python code from an interactive Python/IPython session.
91
92
92 * A flexible and dynamic process model that be deployed on anything from
93 * A flexible and dynamic process model that be deployed on anything from
93 multicore workstations to supercomputers.
94 multicore workstations to supercomputers.
94
95
95 * An architecture that supports many different styles of parallelism, from
96 * An architecture that supports many different styles of parallelism, from
96 message passing to task farming.
97 message passing to task farming.
97
98
98 * Both blocking and fully asynchronous interfaces.
99 * Both blocking and fully asynchronous interfaces.
99
100
100 * High level APIs that enable many things to be parallelized in a few lines
101 * High level APIs that enable many things to be parallelized in a few lines
101 of code.
102 of code.
102
103
103 * Share live parallel jobs with other users securely.
104 * Share live parallel jobs with other users securely.
104
105
105 * Dynamically load balanced task farming system.
106 * Dynamically load balanced task farming system.
106
107
107 * Robust error handling in parallel code.
108 * Robust error handling in parallel code.
108
109
109 The latest development version is always available from IPython's `GitHub
110 The latest development version is always available from IPython's `GitHub
110 site <http://github.com/ipython>`_.
111 site <http://github.com/ipython>`_.
111 """
112 """
112
113
113 license = 'BSD'
114 license = 'BSD'
114
115
115 authors = {'Fernando' : ('Fernando Perez','fperez.net@gmail.com'),
116 authors = {'Fernando' : ('Fernando Perez','fperez.net@gmail.com'),
116 'Janko' : ('Janko Hauser','jhauser@zscout.de'),
117 'Janko' : ('Janko Hauser','jhauser@zscout.de'),
117 'Nathan' : ('Nathaniel Gray','n8gray@caltech.edu'),
118 'Nathan' : ('Nathaniel Gray','n8gray@caltech.edu'),
118 'Ville' : ('Ville Vainio','vivainio@gmail.com'),
119 'Ville' : ('Ville Vainio','vivainio@gmail.com'),
119 'Brian' : ('Brian E Granger', 'ellisonbg@gmail.com'),
120 'Brian' : ('Brian E Granger', 'ellisonbg@gmail.com'),
120 'Min' : ('Min Ragan-Kelley', 'benjaminrk@gmail.com'),
121 'Min' : ('Min Ragan-Kelley', 'benjaminrk@gmail.com'),
121 'Thomas' : ('Thomas A. Kluyver', 'takowl@gmail.com'),
122 'Thomas' : ('Thomas A. Kluyver', 'takowl@gmail.com'),
122 'Jorgen' : ('Jorgen Stenarson', 'jorgen.stenarson@bostream.nu'),
123 'Jorgen' : ('Jorgen Stenarson', 'jorgen.stenarson@bostream.nu'),
123 'Matthias' : ('Matthias Bussonnier', 'bussonniermatthias@gmail.com'),
124 'Matthias' : ('Matthias Bussonnier', 'bussonniermatthias@gmail.com'),
124 }
125 }
125
126
126 author = 'The IPython Development Team'
127 author = 'The IPython Development Team'
127
128
128 author_email = 'ipython-dev@scipy.org'
129 author_email = 'ipython-dev@scipy.org'
129
130
130 url = 'http://ipython.org'
131 url = 'http://ipython.org'
131
132
132 download_url = 'https://github.com/ipython/ipython/downloads'
133 download_url = 'https://github.com/ipython/ipython/downloads'
133
134
134 platforms = ['Linux','Mac OSX','Windows XP/Vista/7/8']
135 platforms = ['Linux','Mac OSX','Windows XP/Vista/7/8']
135
136
136 keywords = ['Interactive','Interpreter','Shell','Parallel','Distributed',
137 keywords = ['Interactive','Interpreter','Shell','Parallel','Distributed',
137 'Web-based computing', 'Qt console', 'Embedding']
138 'Web-based computing', 'Qt console', 'Embedding']
138
139
139 classifiers = [
140 classifiers = [
140 'Intended Audience :: Developers',
141 'Intended Audience :: Developers',
141 'Intended Audience :: Science/Research',
142 'Intended Audience :: Science/Research',
142 'License :: OSI Approved :: BSD License',
143 'License :: OSI Approved :: BSD License',
143 'Programming Language :: Python',
144 'Programming Language :: Python',
144 'Programming Language :: Python :: 2',
145 'Programming Language :: Python :: 2',
145 'Programming Language :: Python :: 2.7',
146 'Programming Language :: Python :: 2.7',
146 'Programming Language :: Python :: 3',
147 'Programming Language :: Python :: 3',
147 'Topic :: System :: Distributed Computing',
148 'Topic :: System :: Distributed Computing',
148 'Topic :: System :: Shells'
149 'Topic :: System :: Shells'
149 ]
150 ]
@@ -1,734 +1,721
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for the key interactiveshell module.
2 """Tests for the key interactiveshell module.
3
3
4 Historically the main classes in interactiveshell have been under-tested. This
4 Historically the main classes in interactiveshell have been under-tested. This
5 module should grow as many single-method tests as possible to trap many of the
5 module should grow as many single-method tests as possible to trap many of the
6 recurring bugs we seem to encounter with high-level interaction.
6 recurring bugs we seem to encounter with high-level interaction.
7
8 Authors
9 -------
10 * Fernando Perez
11 """
7 """
12 #-----------------------------------------------------------------------------
13 # Copyright (C) 2011 The IPython Development Team
14 #
15 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
17 #-----------------------------------------------------------------------------
18
8
19 #-----------------------------------------------------------------------------
9 # Copyright (c) IPython Development Team.
20 # Imports
10 # Distributed under the terms of the Modified BSD License.
21 #-----------------------------------------------------------------------------
11
22 # stdlib
23 import ast
12 import ast
24 import os
13 import os
25 import signal
14 import signal
26 import shutil
15 import shutil
27 import sys
16 import sys
28 import tempfile
17 import tempfile
29 import unittest
18 import unittest
30 try:
19 try:
31 from unittest import mock
20 from unittest import mock
32 except ImportError:
21 except ImportError:
33 import mock
22 import mock
34 from os.path import join
23 from os.path import join
35
24
36 # third-party
37 import nose.tools as nt
25 import nose.tools as nt
38
26
39 # Our own
40 from IPython.core.inputtransformer import InputTransformer
27 from IPython.core.inputtransformer import InputTransformer
41 from IPython.testing.decorators import skipif, skip_win32, onlyif_unicode_paths
28 from IPython.testing.decorators import skipif, skip_win32, onlyif_unicode_paths
42 from IPython.testing import tools as tt
29 from IPython.testing import tools as tt
43 from IPython.utils import io
30 from IPython.utils import io
44 from IPython.utils import py3compat
31 from IPython.utils import py3compat
45 from IPython.utils.py3compat import unicode_type, PY3
32 from IPython.utils.py3compat import unicode_type, PY3
46
33
47 if PY3:
34 if PY3:
48 from io import StringIO
35 from io import StringIO
49 else:
36 else:
50 from StringIO import StringIO
37 from StringIO import StringIO
51
38
52 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
53 # Globals
40 # Globals
54 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
55 # This is used by every single test, no point repeating it ad nauseam
42 # This is used by every single test, no point repeating it ad nauseam
56 ip = get_ipython()
43 ip = get_ipython()
57
44
58 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
59 # Tests
46 # Tests
60 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
61
48
62 class InteractiveShellTestCase(unittest.TestCase):
49 class InteractiveShellTestCase(unittest.TestCase):
63 def test_naked_string_cells(self):
50 def test_naked_string_cells(self):
64 """Test that cells with only naked strings are fully executed"""
51 """Test that cells with only naked strings are fully executed"""
65 # First, single-line inputs
52 # First, single-line inputs
66 ip.run_cell('"a"\n')
53 ip.run_cell('"a"\n')
67 self.assertEqual(ip.user_ns['_'], 'a')
54 self.assertEqual(ip.user_ns['_'], 'a')
68 # And also multi-line cells
55 # And also multi-line cells
69 ip.run_cell('"""a\nb"""\n')
56 ip.run_cell('"""a\nb"""\n')
70 self.assertEqual(ip.user_ns['_'], 'a\nb')
57 self.assertEqual(ip.user_ns['_'], 'a\nb')
71
58
72 def test_run_empty_cell(self):
59 def test_run_empty_cell(self):
73 """Just make sure we don't get a horrible error with a blank
60 """Just make sure we don't get a horrible error with a blank
74 cell of input. Yes, I did overlook that."""
61 cell of input. Yes, I did overlook that."""
75 old_xc = ip.execution_count
62 old_xc = ip.execution_count
76 ip.run_cell('')
63 ip.run_cell('')
77 self.assertEqual(ip.execution_count, old_xc)
64 self.assertEqual(ip.execution_count, old_xc)
78
65
79 def test_run_cell_multiline(self):
66 def test_run_cell_multiline(self):
80 """Multi-block, multi-line cells must execute correctly.
67 """Multi-block, multi-line cells must execute correctly.
81 """
68 """
82 src = '\n'.join(["x=1",
69 src = '\n'.join(["x=1",
83 "y=2",
70 "y=2",
84 "if 1:",
71 "if 1:",
85 " x += 1",
72 " x += 1",
86 " y += 1",])
73 " y += 1",])
87 ip.run_cell(src)
74 ip.run_cell(src)
88 self.assertEqual(ip.user_ns['x'], 2)
75 self.assertEqual(ip.user_ns['x'], 2)
89 self.assertEqual(ip.user_ns['y'], 3)
76 self.assertEqual(ip.user_ns['y'], 3)
90
77
91 def test_multiline_string_cells(self):
78 def test_multiline_string_cells(self):
92 "Code sprinkled with multiline strings should execute (GH-306)"
79 "Code sprinkled with multiline strings should execute (GH-306)"
93 ip.run_cell('tmp=0')
80 ip.run_cell('tmp=0')
94 self.assertEqual(ip.user_ns['tmp'], 0)
81 self.assertEqual(ip.user_ns['tmp'], 0)
95 ip.run_cell('tmp=1;"""a\nb"""\n')
82 ip.run_cell('tmp=1;"""a\nb"""\n')
96 self.assertEqual(ip.user_ns['tmp'], 1)
83 self.assertEqual(ip.user_ns['tmp'], 1)
97
84
98 def test_dont_cache_with_semicolon(self):
85 def test_dont_cache_with_semicolon(self):
99 "Ending a line with semicolon should not cache the returned object (GH-307)"
86 "Ending a line with semicolon should not cache the returned object (GH-307)"
100 oldlen = len(ip.user_ns['Out'])
87 oldlen = len(ip.user_ns['Out'])
101 for cell in ['1;', '1;1;']:
88 for cell in ['1;', '1;1;']:
102 ip.run_cell(cell, store_history=True)
89 ip.run_cell(cell, store_history=True)
103 newlen = len(ip.user_ns['Out'])
90 newlen = len(ip.user_ns['Out'])
104 self.assertEqual(oldlen, newlen)
91 self.assertEqual(oldlen, newlen)
105 i = 0
92 i = 0
106 #also test the default caching behavior
93 #also test the default caching behavior
107 for cell in ['1', '1;1']:
94 for cell in ['1', '1;1']:
108 ip.run_cell(cell, store_history=True)
95 ip.run_cell(cell, store_history=True)
109 newlen = len(ip.user_ns['Out'])
96 newlen = len(ip.user_ns['Out'])
110 i += 1
97 i += 1
111 self.assertEqual(oldlen+i, newlen)
98 self.assertEqual(oldlen+i, newlen)
112
99
113 def test_In_variable(self):
100 def test_In_variable(self):
114 "Verify that In variable grows with user input (GH-284)"
101 "Verify that In variable grows with user input (GH-284)"
115 oldlen = len(ip.user_ns['In'])
102 oldlen = len(ip.user_ns['In'])
116 ip.run_cell('1;', store_history=True)
103 ip.run_cell('1;', store_history=True)
117 newlen = len(ip.user_ns['In'])
104 newlen = len(ip.user_ns['In'])
118 self.assertEqual(oldlen+1, newlen)
105 self.assertEqual(oldlen+1, newlen)
119 self.assertEqual(ip.user_ns['In'][-1],'1;')
106 self.assertEqual(ip.user_ns['In'][-1],'1;')
120
107
121 def test_magic_names_in_string(self):
108 def test_magic_names_in_string(self):
122 ip.run_cell('a = """\n%exit\n"""')
109 ip.run_cell('a = """\n%exit\n"""')
123 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
110 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
124
111
125 def test_trailing_newline(self):
112 def test_trailing_newline(self):
126 """test that running !(command) does not raise a SyntaxError"""
113 """test that running !(command) does not raise a SyntaxError"""
127 ip.run_cell('!(true)\n', False)
114 ip.run_cell('!(true)\n', False)
128 ip.run_cell('!(true)\n\n\n', False)
115 ip.run_cell('!(true)\n\n\n', False)
129
116
130 def test_gh_597(self):
117 def test_gh_597(self):
131 """Pretty-printing lists of objects with non-ascii reprs may cause
118 """Pretty-printing lists of objects with non-ascii reprs may cause
132 problems."""
119 problems."""
133 class Spam(object):
120 class Spam(object):
134 def __repr__(self):
121 def __repr__(self):
135 return "\xe9"*50
122 return "\xe9"*50
136 import IPython.core.formatters
123 import IPython.core.formatters
137 f = IPython.core.formatters.PlainTextFormatter()
124 f = IPython.core.formatters.PlainTextFormatter()
138 f([Spam(),Spam()])
125 f([Spam(),Spam()])
139
126
140
127
141 def test_future_flags(self):
128 def test_future_flags(self):
142 """Check that future flags are used for parsing code (gh-777)"""
129 """Check that future flags are used for parsing code (gh-777)"""
143 ip.run_cell('from __future__ import print_function')
130 ip.run_cell('from __future__ import print_function')
144 try:
131 try:
145 ip.run_cell('prfunc_return_val = print(1,2, sep=" ")')
132 ip.run_cell('prfunc_return_val = print(1,2, sep=" ")')
146 assert 'prfunc_return_val' in ip.user_ns
133 assert 'prfunc_return_val' in ip.user_ns
147 finally:
134 finally:
148 # Reset compiler flags so we don't mess up other tests.
135 # Reset compiler flags so we don't mess up other tests.
149 ip.compile.reset_compiler_flags()
136 ip.compile.reset_compiler_flags()
150
137
151 def test_future_unicode(self):
138 def test_future_unicode(self):
152 """Check that unicode_literals is imported from __future__ (gh #786)"""
139 """Check that unicode_literals is imported from __future__ (gh #786)"""
153 try:
140 try:
154 ip.run_cell(u'byte_str = "a"')
141 ip.run_cell(u'byte_str = "a"')
155 assert isinstance(ip.user_ns['byte_str'], str) # string literals are byte strings by default
142 assert isinstance(ip.user_ns['byte_str'], str) # string literals are byte strings by default
156 ip.run_cell('from __future__ import unicode_literals')
143 ip.run_cell('from __future__ import unicode_literals')
157 ip.run_cell(u'unicode_str = "a"')
144 ip.run_cell(u'unicode_str = "a"')
158 assert isinstance(ip.user_ns['unicode_str'], unicode_type) # strings literals are now unicode
145 assert isinstance(ip.user_ns['unicode_str'], unicode_type) # strings literals are now unicode
159 finally:
146 finally:
160 # Reset compiler flags so we don't mess up other tests.
147 # Reset compiler flags so we don't mess up other tests.
161 ip.compile.reset_compiler_flags()
148 ip.compile.reset_compiler_flags()
162
149
163 def test_can_pickle(self):
150 def test_can_pickle(self):
164 "Can we pickle objects defined interactively (GH-29)"
151 "Can we pickle objects defined interactively (GH-29)"
165 ip = get_ipython()
152 ip = get_ipython()
166 ip.reset()
153 ip.reset()
167 ip.run_cell(("class Mylist(list):\n"
154 ip.run_cell(("class Mylist(list):\n"
168 " def __init__(self,x=[]):\n"
155 " def __init__(self,x=[]):\n"
169 " list.__init__(self,x)"))
156 " list.__init__(self,x)"))
170 ip.run_cell("w=Mylist([1,2,3])")
157 ip.run_cell("w=Mylist([1,2,3])")
171
158
172 from pickle import dumps
159 from pickle import dumps
173
160
174 # We need to swap in our main module - this is only necessary
161 # We need to swap in our main module - this is only necessary
175 # inside the test framework, because IPython puts the interactive module
162 # inside the test framework, because IPython puts the interactive module
176 # in place (but the test framework undoes this).
163 # in place (but the test framework undoes this).
177 _main = sys.modules['__main__']
164 _main = sys.modules['__main__']
178 sys.modules['__main__'] = ip.user_module
165 sys.modules['__main__'] = ip.user_module
179 try:
166 try:
180 res = dumps(ip.user_ns["w"])
167 res = dumps(ip.user_ns["w"])
181 finally:
168 finally:
182 sys.modules['__main__'] = _main
169 sys.modules['__main__'] = _main
183 self.assertTrue(isinstance(res, bytes))
170 self.assertTrue(isinstance(res, bytes))
184
171
185 def test_global_ns(self):
172 def test_global_ns(self):
186 "Code in functions must be able to access variables outside them."
173 "Code in functions must be able to access variables outside them."
187 ip = get_ipython()
174 ip = get_ipython()
188 ip.run_cell("a = 10")
175 ip.run_cell("a = 10")
189 ip.run_cell(("def f(x):\n"
176 ip.run_cell(("def f(x):\n"
190 " return x + a"))
177 " return x + a"))
191 ip.run_cell("b = f(12)")
178 ip.run_cell("b = f(12)")
192 self.assertEqual(ip.user_ns["b"], 22)
179 self.assertEqual(ip.user_ns["b"], 22)
193
180
194 def test_bad_custom_tb(self):
181 def test_bad_custom_tb(self):
195 """Check that InteractiveShell is protected from bad custom exception handlers"""
182 """Check that InteractiveShell is protected from bad custom exception handlers"""
196 from IPython.utils import io
183 from IPython.utils import io
197 save_stderr = io.stderr
184 save_stderr = io.stderr
198 try:
185 try:
199 # capture stderr
186 # capture stderr
200 io.stderr = StringIO()
187 io.stderr = StringIO()
201 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
188 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
202 self.assertEqual(ip.custom_exceptions, (IOError,))
189 self.assertEqual(ip.custom_exceptions, (IOError,))
203 ip.run_cell(u'raise IOError("foo")')
190 ip.run_cell(u'raise IOError("foo")')
204 self.assertEqual(ip.custom_exceptions, ())
191 self.assertEqual(ip.custom_exceptions, ())
205 self.assertTrue("Custom TB Handler failed" in io.stderr.getvalue())
192 self.assertTrue("Custom TB Handler failed" in io.stderr.getvalue())
206 finally:
193 finally:
207 io.stderr = save_stderr
194 io.stderr = save_stderr
208
195
209 def test_bad_custom_tb_return(self):
196 def test_bad_custom_tb_return(self):
210 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
197 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
211 from IPython.utils import io
198 from IPython.utils import io
212 save_stderr = io.stderr
199 save_stderr = io.stderr
213 try:
200 try:
214 # capture stderr
201 # capture stderr
215 io.stderr = StringIO()
202 io.stderr = StringIO()
216 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
203 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
217 self.assertEqual(ip.custom_exceptions, (NameError,))
204 self.assertEqual(ip.custom_exceptions, (NameError,))
218 ip.run_cell(u'a=abracadabra')
205 ip.run_cell(u'a=abracadabra')
219 self.assertEqual(ip.custom_exceptions, ())
206 self.assertEqual(ip.custom_exceptions, ())
220 self.assertTrue("Custom TB Handler failed" in io.stderr.getvalue())
207 self.assertTrue("Custom TB Handler failed" in io.stderr.getvalue())
221 finally:
208 finally:
222 io.stderr = save_stderr
209 io.stderr = save_stderr
223
210
224 def test_drop_by_id(self):
211 def test_drop_by_id(self):
225 myvars = {"a":object(), "b":object(), "c": object()}
212 myvars = {"a":object(), "b":object(), "c": object()}
226 ip.push(myvars, interactive=False)
213 ip.push(myvars, interactive=False)
227 for name in myvars:
214 for name in myvars:
228 assert name in ip.user_ns, name
215 assert name in ip.user_ns, name
229 assert name in ip.user_ns_hidden, name
216 assert name in ip.user_ns_hidden, name
230 ip.user_ns['b'] = 12
217 ip.user_ns['b'] = 12
231 ip.drop_by_id(myvars)
218 ip.drop_by_id(myvars)
232 for name in ["a", "c"]:
219 for name in ["a", "c"]:
233 assert name not in ip.user_ns, name
220 assert name not in ip.user_ns, name
234 assert name not in ip.user_ns_hidden, name
221 assert name not in ip.user_ns_hidden, name
235 assert ip.user_ns['b'] == 12
222 assert ip.user_ns['b'] == 12
236 ip.reset()
223 ip.reset()
237
224
238 def test_var_expand(self):
225 def test_var_expand(self):
239 ip.user_ns['f'] = u'Ca\xf1o'
226 ip.user_ns['f'] = u'Ca\xf1o'
240 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
227 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
241 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
228 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
242 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
229 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
243 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
230 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
244
231
245 ip.user_ns['f'] = b'Ca\xc3\xb1o'
232 ip.user_ns['f'] = b'Ca\xc3\xb1o'
246 # This should not raise any exception:
233 # This should not raise any exception:
247 ip.var_expand(u'echo $f')
234 ip.var_expand(u'echo $f')
248
235
249 def test_var_expand_local(self):
236 def test_var_expand_local(self):
250 """Test local variable expansion in !system and %magic calls"""
237 """Test local variable expansion in !system and %magic calls"""
251 # !system
238 # !system
252 ip.run_cell('def test():\n'
239 ip.run_cell('def test():\n'
253 ' lvar = "ttt"\n'
240 ' lvar = "ttt"\n'
254 ' ret = !echo {lvar}\n'
241 ' ret = !echo {lvar}\n'
255 ' return ret[0]\n')
242 ' return ret[0]\n')
256 res = ip.user_ns['test']()
243 res = ip.user_ns['test']()
257 nt.assert_in('ttt', res)
244 nt.assert_in('ttt', res)
258
245
259 # %magic
246 # %magic
260 ip.run_cell('def makemacro():\n'
247 ip.run_cell('def makemacro():\n'
261 ' macroname = "macro_var_expand_locals"\n'
248 ' macroname = "macro_var_expand_locals"\n'
262 ' %macro {macroname} codestr\n')
249 ' %macro {macroname} codestr\n')
263 ip.user_ns['codestr'] = "str(12)"
250 ip.user_ns['codestr'] = "str(12)"
264 ip.run_cell('makemacro()')
251 ip.run_cell('makemacro()')
265 nt.assert_in('macro_var_expand_locals', ip.user_ns)
252 nt.assert_in('macro_var_expand_locals', ip.user_ns)
266
253
267 def test_var_expand_self(self):
254 def test_var_expand_self(self):
268 """Test variable expansion with the name 'self', which was failing.
255 """Test variable expansion with the name 'self', which was failing.
269
256
270 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
257 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
271 """
258 """
272 ip.run_cell('class cTest:\n'
259 ip.run_cell('class cTest:\n'
273 ' classvar="see me"\n'
260 ' classvar="see me"\n'
274 ' def test(self):\n'
261 ' def test(self):\n'
275 ' res = !echo Variable: {self.classvar}\n'
262 ' res = !echo Variable: {self.classvar}\n'
276 ' return res[0]\n')
263 ' return res[0]\n')
277 nt.assert_in('see me', ip.user_ns['cTest']().test())
264 nt.assert_in('see me', ip.user_ns['cTest']().test())
278
265
279 def test_bad_var_expand(self):
266 def test_bad_var_expand(self):
280 """var_expand on invalid formats shouldn't raise"""
267 """var_expand on invalid formats shouldn't raise"""
281 # SyntaxError
268 # SyntaxError
282 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
269 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
283 # NameError
270 # NameError
284 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
271 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
285 # ZeroDivisionError
272 # ZeroDivisionError
286 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
273 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
287
274
288 def test_silent_postexec(self):
275 def test_silent_postexec(self):
289 """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks"""
276 """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks"""
290 pre_explicit = mock.Mock()
277 pre_explicit = mock.Mock()
291 pre_always = mock.Mock()
278 pre_always = mock.Mock()
292 post_explicit = mock.Mock()
279 post_explicit = mock.Mock()
293 post_always = mock.Mock()
280 post_always = mock.Mock()
294
281
295 ip.events.register('pre_run_cell', pre_explicit)
282 ip.events.register('pre_run_cell', pre_explicit)
296 ip.events.register('pre_execute', pre_always)
283 ip.events.register('pre_execute', pre_always)
297 ip.events.register('post_run_cell', post_explicit)
284 ip.events.register('post_run_cell', post_explicit)
298 ip.events.register('post_execute', post_always)
285 ip.events.register('post_execute', post_always)
299
286
300 try:
287 try:
301 ip.run_cell("1", silent=True)
288 ip.run_cell("1", silent=True)
302 assert pre_always.called
289 assert pre_always.called
303 assert not pre_explicit.called
290 assert not pre_explicit.called
304 assert post_always.called
291 assert post_always.called
305 assert not post_explicit.called
292 assert not post_explicit.called
306 # double-check that non-silent exec did what we expected
293 # double-check that non-silent exec did what we expected
307 # silent to avoid
294 # silent to avoid
308 ip.run_cell("1")
295 ip.run_cell("1")
309 assert pre_explicit.called
296 assert pre_explicit.called
310 assert post_explicit.called
297 assert post_explicit.called
311 finally:
298 finally:
312 # remove post-exec
299 # remove post-exec
313 ip.events.reset_all()
300 ip.events.reset_all()
314
301
315 def test_silent_noadvance(self):
302 def test_silent_noadvance(self):
316 """run_cell(silent=True) doesn't advance execution_count"""
303 """run_cell(silent=True) doesn't advance execution_count"""
317 ec = ip.execution_count
304 ec = ip.execution_count
318 # silent should force store_history=False
305 # silent should force store_history=False
319 ip.run_cell("1", store_history=True, silent=True)
306 ip.run_cell("1", store_history=True, silent=True)
320
307
321 self.assertEqual(ec, ip.execution_count)
308 self.assertEqual(ec, ip.execution_count)
322 # double-check that non-silent exec did what we expected
309 # double-check that non-silent exec did what we expected
323 # silent to avoid
310 # silent to avoid
324 ip.run_cell("1", store_history=True)
311 ip.run_cell("1", store_history=True)
325 self.assertEqual(ec+1, ip.execution_count)
312 self.assertEqual(ec+1, ip.execution_count)
326
313
327 def test_silent_nodisplayhook(self):
314 def test_silent_nodisplayhook(self):
328 """run_cell(silent=True) doesn't trigger displayhook"""
315 """run_cell(silent=True) doesn't trigger displayhook"""
329 d = dict(called=False)
316 d = dict(called=False)
330
317
331 trap = ip.display_trap
318 trap = ip.display_trap
332 save_hook = trap.hook
319 save_hook = trap.hook
333
320
334 def failing_hook(*args, **kwargs):
321 def failing_hook(*args, **kwargs):
335 d['called'] = True
322 d['called'] = True
336
323
337 try:
324 try:
338 trap.hook = failing_hook
325 trap.hook = failing_hook
339 ip.run_cell("1", silent=True)
326 ip.run_cell("1", silent=True)
340 self.assertFalse(d['called'])
327 self.assertFalse(d['called'])
341 # double-check that non-silent exec did what we expected
328 # double-check that non-silent exec did what we expected
342 # silent to avoid
329 # silent to avoid
343 ip.run_cell("1")
330 ip.run_cell("1")
344 self.assertTrue(d['called'])
331 self.assertTrue(d['called'])
345 finally:
332 finally:
346 trap.hook = save_hook
333 trap.hook = save_hook
347
334
348 @skipif(sys.version_info[0] >= 3, "softspace removed in py3")
335 @skipif(sys.version_info[0] >= 3, "softspace removed in py3")
349 def test_print_softspace(self):
336 def test_print_softspace(self):
350 """Verify that softspace is handled correctly when executing multiple
337 """Verify that softspace is handled correctly when executing multiple
351 statements.
338 statements.
352
339
353 In [1]: print 1; print 2
340 In [1]: print 1; print 2
354 1
341 1
355 2
342 2
356
343
357 In [2]: print 1,; print 2
344 In [2]: print 1,; print 2
358 1 2
345 1 2
359 """
346 """
360
347
361 def test_ofind_line_magic(self):
348 def test_ofind_line_magic(self):
362 from IPython.core.magic import register_line_magic
349 from IPython.core.magic import register_line_magic
363
350
364 @register_line_magic
351 @register_line_magic
365 def lmagic(line):
352 def lmagic(line):
366 "A line magic"
353 "A line magic"
367
354
368 # Get info on line magic
355 # Get info on line magic
369 lfind = ip._ofind('lmagic')
356 lfind = ip._ofind('lmagic')
370 info = dict(found=True, isalias=False, ismagic=True,
357 info = dict(found=True, isalias=False, ismagic=True,
371 namespace = 'IPython internal', obj= lmagic.__wrapped__,
358 namespace = 'IPython internal', obj= lmagic.__wrapped__,
372 parent = None)
359 parent = None)
373 nt.assert_equal(lfind, info)
360 nt.assert_equal(lfind, info)
374
361
375 def test_ofind_cell_magic(self):
362 def test_ofind_cell_magic(self):
376 from IPython.core.magic import register_cell_magic
363 from IPython.core.magic import register_cell_magic
377
364
378 @register_cell_magic
365 @register_cell_magic
379 def cmagic(line, cell):
366 def cmagic(line, cell):
380 "A cell magic"
367 "A cell magic"
381
368
382 # Get info on cell magic
369 # Get info on cell magic
383 find = ip._ofind('cmagic')
370 find = ip._ofind('cmagic')
384 info = dict(found=True, isalias=False, ismagic=True,
371 info = dict(found=True, isalias=False, ismagic=True,
385 namespace = 'IPython internal', obj= cmagic.__wrapped__,
372 namespace = 'IPython internal', obj= cmagic.__wrapped__,
386 parent = None)
373 parent = None)
387 nt.assert_equal(find, info)
374 nt.assert_equal(find, info)
388
375
389 def test_custom_exception(self):
376 def test_custom_exception(self):
390 called = []
377 called = []
391 def my_handler(shell, etype, value, tb, tb_offset=None):
378 def my_handler(shell, etype, value, tb, tb_offset=None):
392 called.append(etype)
379 called.append(etype)
393 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
380 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
394
381
395 ip.set_custom_exc((ValueError,), my_handler)
382 ip.set_custom_exc((ValueError,), my_handler)
396 try:
383 try:
397 ip.run_cell("raise ValueError('test')")
384 ip.run_cell("raise ValueError('test')")
398 # Check that this was called, and only once.
385 # Check that this was called, and only once.
399 self.assertEqual(called, [ValueError])
386 self.assertEqual(called, [ValueError])
400 finally:
387 finally:
401 # Reset the custom exception hook
388 # Reset the custom exception hook
402 ip.set_custom_exc((), None)
389 ip.set_custom_exc((), None)
403
390
404 @skipif(sys.version_info[0] >= 3, "no differences with __future__ in py3")
391 @skipif(sys.version_info[0] >= 3, "no differences with __future__ in py3")
405 def test_future_environment(self):
392 def test_future_environment(self):
406 "Can we run code with & without the shell's __future__ imports?"
393 "Can we run code with & without the shell's __future__ imports?"
407 ip.run_cell("from __future__ import division")
394 ip.run_cell("from __future__ import division")
408 ip.run_cell("a = 1/2", shell_futures=True)
395 ip.run_cell("a = 1/2", shell_futures=True)
409 self.assertEqual(ip.user_ns['a'], 0.5)
396 self.assertEqual(ip.user_ns['a'], 0.5)
410 ip.run_cell("b = 1/2", shell_futures=False)
397 ip.run_cell("b = 1/2", shell_futures=False)
411 self.assertEqual(ip.user_ns['b'], 0)
398 self.assertEqual(ip.user_ns['b'], 0)
412
399
413 ip.compile.reset_compiler_flags()
400 ip.compile.reset_compiler_flags()
414 # This shouldn't leak to the shell's compiler
401 # This shouldn't leak to the shell's compiler
415 ip.run_cell("from __future__ import division \nc=1/2", shell_futures=False)
402 ip.run_cell("from __future__ import division \nc=1/2", shell_futures=False)
416 self.assertEqual(ip.user_ns['c'], 0.5)
403 self.assertEqual(ip.user_ns['c'], 0.5)
417 ip.run_cell("d = 1/2", shell_futures=True)
404 ip.run_cell("d = 1/2", shell_futures=True)
418 self.assertEqual(ip.user_ns['d'], 0)
405 self.assertEqual(ip.user_ns['d'], 0)
419
406
420
407
421 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
408 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
422
409
423 @onlyif_unicode_paths
410 @onlyif_unicode_paths
424 def setUp(self):
411 def setUp(self):
425 self.BASETESTDIR = tempfile.mkdtemp()
412 self.BASETESTDIR = tempfile.mkdtemp()
426 self.TESTDIR = join(self.BASETESTDIR, u"åäö")
413 self.TESTDIR = join(self.BASETESTDIR, u"åäö")
427 os.mkdir(self.TESTDIR)
414 os.mkdir(self.TESTDIR)
428 with open(join(self.TESTDIR, u"åäötestscript.py"), "w") as sfile:
415 with open(join(self.TESTDIR, u"åäötestscript.py"), "w") as sfile:
429 sfile.write("pass\n")
416 sfile.write("pass\n")
430 self.oldpath = py3compat.getcwd()
417 self.oldpath = py3compat.getcwd()
431 os.chdir(self.TESTDIR)
418 os.chdir(self.TESTDIR)
432 self.fname = u"åäötestscript.py"
419 self.fname = u"åäötestscript.py"
433
420
434 def tearDown(self):
421 def tearDown(self):
435 os.chdir(self.oldpath)
422 os.chdir(self.oldpath)
436 shutil.rmtree(self.BASETESTDIR)
423 shutil.rmtree(self.BASETESTDIR)
437
424
438 @onlyif_unicode_paths
425 @onlyif_unicode_paths
439 def test_1(self):
426 def test_1(self):
440 """Test safe_execfile with non-ascii path
427 """Test safe_execfile with non-ascii path
441 """
428 """
442 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
429 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
443
430
444 class ExitCodeChecks(tt.TempFileMixin):
431 class ExitCodeChecks(tt.TempFileMixin):
445 def test_exit_code_ok(self):
432 def test_exit_code_ok(self):
446 self.system('exit 0')
433 self.system('exit 0')
447 self.assertEqual(ip.user_ns['_exit_code'], 0)
434 self.assertEqual(ip.user_ns['_exit_code'], 0)
448
435
449 def test_exit_code_error(self):
436 def test_exit_code_error(self):
450 self.system('exit 1')
437 self.system('exit 1')
451 self.assertEqual(ip.user_ns['_exit_code'], 1)
438 self.assertEqual(ip.user_ns['_exit_code'], 1)
452
439
453 @skipif(not hasattr(signal, 'SIGALRM'))
440 @skipif(not hasattr(signal, 'SIGALRM'))
454 def test_exit_code_signal(self):
441 def test_exit_code_signal(self):
455 self.mktmp("import signal, time\n"
442 self.mktmp("import signal, time\n"
456 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
443 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
457 "time.sleep(1)\n")
444 "time.sleep(1)\n")
458 self.system("%s %s" % (sys.executable, self.fname))
445 self.system("%s %s" % (sys.executable, self.fname))
459 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
446 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
460
447
461 class TestSystemRaw(unittest.TestCase, ExitCodeChecks):
448 class TestSystemRaw(unittest.TestCase, ExitCodeChecks):
462 system = ip.system_raw
449 system = ip.system_raw
463
450
464 @onlyif_unicode_paths
451 @onlyif_unicode_paths
465 def test_1(self):
452 def test_1(self):
466 """Test system_raw with non-ascii cmd
453 """Test system_raw with non-ascii cmd
467 """
454 """
468 cmd = u'''python -c "'åäö'" '''
455 cmd = u'''python -c "'åäö'" '''
469 ip.system_raw(cmd)
456 ip.system_raw(cmd)
470
457
471 # TODO: Exit codes are currently ignored on Windows.
458 # TODO: Exit codes are currently ignored on Windows.
472 class TestSystemPipedExitCode(unittest.TestCase, ExitCodeChecks):
459 class TestSystemPipedExitCode(unittest.TestCase, ExitCodeChecks):
473 system = ip.system_piped
460 system = ip.system_piped
474
461
475 @skip_win32
462 @skip_win32
476 def test_exit_code_ok(self):
463 def test_exit_code_ok(self):
477 ExitCodeChecks.test_exit_code_ok(self)
464 ExitCodeChecks.test_exit_code_ok(self)
478
465
479 @skip_win32
466 @skip_win32
480 def test_exit_code_error(self):
467 def test_exit_code_error(self):
481 ExitCodeChecks.test_exit_code_error(self)
468 ExitCodeChecks.test_exit_code_error(self)
482
469
483 @skip_win32
470 @skip_win32
484 def test_exit_code_signal(self):
471 def test_exit_code_signal(self):
485 ExitCodeChecks.test_exit_code_signal(self)
472 ExitCodeChecks.test_exit_code_signal(self)
486
473
487 class TestModules(unittest.TestCase, tt.TempFileMixin):
474 class TestModules(unittest.TestCase, tt.TempFileMixin):
488 def test_extraneous_loads(self):
475 def test_extraneous_loads(self):
489 """Test we're not loading modules on startup that we shouldn't.
476 """Test we're not loading modules on startup that we shouldn't.
490 """
477 """
491 self.mktmp("import sys\n"
478 self.mktmp("import sys\n"
492 "print('numpy' in sys.modules)\n"
479 "print('numpy' in sys.modules)\n"
493 "print('IPython.parallel' in sys.modules)\n"
480 "print('IPython.parallel' in sys.modules)\n"
494 "print('IPython.kernel.zmq' in sys.modules)\n"
481 "print('IPython.kernel.zmq' in sys.modules)\n"
495 )
482 )
496 out = "False\nFalse\nFalse\n"
483 out = "False\nFalse\nFalse\n"
497 tt.ipexec_validate(self.fname, out)
484 tt.ipexec_validate(self.fname, out)
498
485
499 class Negator(ast.NodeTransformer):
486 class Negator(ast.NodeTransformer):
500 """Negates all number literals in an AST."""
487 """Negates all number literals in an AST."""
501 def visit_Num(self, node):
488 def visit_Num(self, node):
502 node.n = -node.n
489 node.n = -node.n
503 return node
490 return node
504
491
505 class TestAstTransform(unittest.TestCase):
492 class TestAstTransform(unittest.TestCase):
506 def setUp(self):
493 def setUp(self):
507 self.negator = Negator()
494 self.negator = Negator()
508 ip.ast_transformers.append(self.negator)
495 ip.ast_transformers.append(self.negator)
509
496
510 def tearDown(self):
497 def tearDown(self):
511 ip.ast_transformers.remove(self.negator)
498 ip.ast_transformers.remove(self.negator)
512
499
513 def test_run_cell(self):
500 def test_run_cell(self):
514 with tt.AssertPrints('-34'):
501 with tt.AssertPrints('-34'):
515 ip.run_cell('print (12 + 22)')
502 ip.run_cell('print (12 + 22)')
516
503
517 # A named reference to a number shouldn't be transformed.
504 # A named reference to a number shouldn't be transformed.
518 ip.user_ns['n'] = 55
505 ip.user_ns['n'] = 55
519 with tt.AssertNotPrints('-55'):
506 with tt.AssertNotPrints('-55'):
520 ip.run_cell('print (n)')
507 ip.run_cell('print (n)')
521
508
522 def test_timeit(self):
509 def test_timeit(self):
523 called = set()
510 called = set()
524 def f(x):
511 def f(x):
525 called.add(x)
512 called.add(x)
526 ip.push({'f':f})
513 ip.push({'f':f})
527
514
528 with tt.AssertPrints("best of "):
515 with tt.AssertPrints("best of "):
529 ip.run_line_magic("timeit", "-n1 f(1)")
516 ip.run_line_magic("timeit", "-n1 f(1)")
530 self.assertEqual(called, set([-1]))
517 self.assertEqual(called, set([-1]))
531 called.clear()
518 called.clear()
532
519
533 with tt.AssertPrints("best of "):
520 with tt.AssertPrints("best of "):
534 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
521 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
535 self.assertEqual(called, set([-2, -3]))
522 self.assertEqual(called, set([-2, -3]))
536
523
537 def test_time(self):
524 def test_time(self):
538 called = []
525 called = []
539 def f(x):
526 def f(x):
540 called.append(x)
527 called.append(x)
541 ip.push({'f':f})
528 ip.push({'f':f})
542
529
543 # Test with an expression
530 # Test with an expression
544 with tt.AssertPrints("Wall time: "):
531 with tt.AssertPrints("Wall time: "):
545 ip.run_line_magic("time", "f(5+9)")
532 ip.run_line_magic("time", "f(5+9)")
546 self.assertEqual(called, [-14])
533 self.assertEqual(called, [-14])
547 called[:] = []
534 called[:] = []
548
535
549 # Test with a statement (different code path)
536 # Test with a statement (different code path)
550 with tt.AssertPrints("Wall time: "):
537 with tt.AssertPrints("Wall time: "):
551 ip.run_line_magic("time", "a = f(-3 + -2)")
538 ip.run_line_magic("time", "a = f(-3 + -2)")
552 self.assertEqual(called, [5])
539 self.assertEqual(called, [5])
553
540
554 def test_macro(self):
541 def test_macro(self):
555 ip.push({'a':10})
542 ip.push({'a':10})
556 # The AST transformation makes this do a+=-1
543 # The AST transformation makes this do a+=-1
557 ip.define_macro("amacro", "a+=1\nprint(a)")
544 ip.define_macro("amacro", "a+=1\nprint(a)")
558
545
559 with tt.AssertPrints("9"):
546 with tt.AssertPrints("9"):
560 ip.run_cell("amacro")
547 ip.run_cell("amacro")
561 with tt.AssertPrints("8"):
548 with tt.AssertPrints("8"):
562 ip.run_cell("amacro")
549 ip.run_cell("amacro")
563
550
564 class IntegerWrapper(ast.NodeTransformer):
551 class IntegerWrapper(ast.NodeTransformer):
565 """Wraps all integers in a call to Integer()"""
552 """Wraps all integers in a call to Integer()"""
566 def visit_Num(self, node):
553 def visit_Num(self, node):
567 if isinstance(node.n, int):
554 if isinstance(node.n, int):
568 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
555 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
569 args=[node], keywords=[])
556 args=[node], keywords=[])
570 return node
557 return node
571
558
572 class TestAstTransform2(unittest.TestCase):
559 class TestAstTransform2(unittest.TestCase):
573 def setUp(self):
560 def setUp(self):
574 self.intwrapper = IntegerWrapper()
561 self.intwrapper = IntegerWrapper()
575 ip.ast_transformers.append(self.intwrapper)
562 ip.ast_transformers.append(self.intwrapper)
576
563
577 self.calls = []
564 self.calls = []
578 def Integer(*args):
565 def Integer(*args):
579 self.calls.append(args)
566 self.calls.append(args)
580 return args
567 return args
581 ip.push({"Integer": Integer})
568 ip.push({"Integer": Integer})
582
569
583 def tearDown(self):
570 def tearDown(self):
584 ip.ast_transformers.remove(self.intwrapper)
571 ip.ast_transformers.remove(self.intwrapper)
585 del ip.user_ns['Integer']
572 del ip.user_ns['Integer']
586
573
587 def test_run_cell(self):
574 def test_run_cell(self):
588 ip.run_cell("n = 2")
575 ip.run_cell("n = 2")
589 self.assertEqual(self.calls, [(2,)])
576 self.assertEqual(self.calls, [(2,)])
590
577
591 # This shouldn't throw an error
578 # This shouldn't throw an error
592 ip.run_cell("o = 2.0")
579 ip.run_cell("o = 2.0")
593 self.assertEqual(ip.user_ns['o'], 2.0)
580 self.assertEqual(ip.user_ns['o'], 2.0)
594
581
595 def test_timeit(self):
582 def test_timeit(self):
596 called = set()
583 called = set()
597 def f(x):
584 def f(x):
598 called.add(x)
585 called.add(x)
599 ip.push({'f':f})
586 ip.push({'f':f})
600
587
601 with tt.AssertPrints("best of "):
588 with tt.AssertPrints("best of "):
602 ip.run_line_magic("timeit", "-n1 f(1)")
589 ip.run_line_magic("timeit", "-n1 f(1)")
603 self.assertEqual(called, set([(1,)]))
590 self.assertEqual(called, set([(1,)]))
604 called.clear()
591 called.clear()
605
592
606 with tt.AssertPrints("best of "):
593 with tt.AssertPrints("best of "):
607 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
594 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
608 self.assertEqual(called, set([(2,), (3,)]))
595 self.assertEqual(called, set([(2,), (3,)]))
609
596
610 class ErrorTransformer(ast.NodeTransformer):
597 class ErrorTransformer(ast.NodeTransformer):
611 """Throws an error when it sees a number."""
598 """Throws an error when it sees a number."""
612 def visit_Num(self):
599 def visit_Num(self):
613 raise ValueError("test")
600 raise ValueError("test")
614
601
615 class TestAstTransformError(unittest.TestCase):
602 class TestAstTransformError(unittest.TestCase):
616 def test_unregistering(self):
603 def test_unregistering(self):
617 err_transformer = ErrorTransformer()
604 err_transformer = ErrorTransformer()
618 ip.ast_transformers.append(err_transformer)
605 ip.ast_transformers.append(err_transformer)
619
606
620 with tt.AssertPrints("unregister", channel='stderr'):
607 with tt.AssertPrints("unregister", channel='stderr'):
621 ip.run_cell("1 + 2")
608 ip.run_cell("1 + 2")
622
609
623 # This should have been removed.
610 # This should have been removed.
624 nt.assert_not_in(err_transformer, ip.ast_transformers)
611 nt.assert_not_in(err_transformer, ip.ast_transformers)
625
612
626 def test__IPYTHON__():
613 def test__IPYTHON__():
627 # This shouldn't raise a NameError, that's all
614 # This shouldn't raise a NameError, that's all
628 __IPYTHON__
615 __IPYTHON__
629
616
630
617
631 class DummyRepr(object):
618 class DummyRepr(object):
632 def __repr__(self):
619 def __repr__(self):
633 return "DummyRepr"
620 return "DummyRepr"
634
621
635 def _repr_html_(self):
622 def _repr_html_(self):
636 return "<b>dummy</b>"
623 return "<b>dummy</b>"
637
624
638 def _repr_javascript_(self):
625 def _repr_javascript_(self):
639 return "console.log('hi');", {'key': 'value'}
626 return "console.log('hi');", {'key': 'value'}
640
627
641
628
642 def test_user_variables():
629 def test_user_variables():
643 # enable all formatters
630 # enable all formatters
644 ip.display_formatter.active_types = ip.display_formatter.format_types
631 ip.display_formatter.active_types = ip.display_formatter.format_types
645
632
646 ip.user_ns['dummy'] = d = DummyRepr()
633 ip.user_ns['dummy'] = d = DummyRepr()
647 keys = set(['dummy', 'doesnotexist'])
634 keys = set(['dummy', 'doesnotexist'])
648 r = ip.user_variables(keys)
635 r = ip.user_expressions({ key:key for key in keys})
649
636
650 nt.assert_equal(keys, set(r.keys()))
637 nt.assert_equal(keys, set(r.keys()))
651 dummy = r['dummy']
638 dummy = r['dummy']
652 nt.assert_equal(set(['status', 'data', 'metadata']), set(dummy.keys()))
639 nt.assert_equal(set(['status', 'data', 'metadata']), set(dummy.keys()))
653 nt.assert_equal(dummy['status'], 'ok')
640 nt.assert_equal(dummy['status'], 'ok')
654 data = dummy['data']
641 data = dummy['data']
655 metadata = dummy['metadata']
642 metadata = dummy['metadata']
656 nt.assert_equal(data.get('text/html'), d._repr_html_())
643 nt.assert_equal(data.get('text/html'), d._repr_html_())
657 js, jsmd = d._repr_javascript_()
644 js, jsmd = d._repr_javascript_()
658 nt.assert_equal(data.get('application/javascript'), js)
645 nt.assert_equal(data.get('application/javascript'), js)
659 nt.assert_equal(metadata.get('application/javascript'), jsmd)
646 nt.assert_equal(metadata.get('application/javascript'), jsmd)
660
647
661 dne = r['doesnotexist']
648 dne = r['doesnotexist']
662 nt.assert_equal(dne['status'], 'error')
649 nt.assert_equal(dne['status'], 'error')
663 nt.assert_equal(dne['ename'], 'KeyError')
650 nt.assert_equal(dne['ename'], 'NameError')
664
651
665 # back to text only
652 # back to text only
666 ip.display_formatter.active_types = ['text/plain']
653 ip.display_formatter.active_types = ['text/plain']
667
654
668 def test_user_expression():
655 def test_user_expression():
669 # enable all formatters
656 # enable all formatters
670 ip.display_formatter.active_types = ip.display_formatter.format_types
657 ip.display_formatter.active_types = ip.display_formatter.format_types
671 query = {
658 query = {
672 'a' : '1 + 2',
659 'a' : '1 + 2',
673 'b' : '1/0',
660 'b' : '1/0',
674 }
661 }
675 r = ip.user_expressions(query)
662 r = ip.user_expressions(query)
676 import pprint
663 import pprint
677 pprint.pprint(r)
664 pprint.pprint(r)
678 nt.assert_equal(set(r.keys()), set(query.keys()))
665 nt.assert_equal(set(r.keys()), set(query.keys()))
679 a = r['a']
666 a = r['a']
680 nt.assert_equal(set(['status', 'data', 'metadata']), set(a.keys()))
667 nt.assert_equal(set(['status', 'data', 'metadata']), set(a.keys()))
681 nt.assert_equal(a['status'], 'ok')
668 nt.assert_equal(a['status'], 'ok')
682 data = a['data']
669 data = a['data']
683 metadata = a['metadata']
670 metadata = a['metadata']
684 nt.assert_equal(data.get('text/plain'), '3')
671 nt.assert_equal(data.get('text/plain'), '3')
685
672
686 b = r['b']
673 b = r['b']
687 nt.assert_equal(b['status'], 'error')
674 nt.assert_equal(b['status'], 'error')
688 nt.assert_equal(b['ename'], 'ZeroDivisionError')
675 nt.assert_equal(b['ename'], 'ZeroDivisionError')
689
676
690 # back to text only
677 # back to text only
691 ip.display_formatter.active_types = ['text/plain']
678 ip.display_formatter.active_types = ['text/plain']
692
679
693
680
694
681
695
682
696
683
697 class TestSyntaxErrorTransformer(unittest.TestCase):
684 class TestSyntaxErrorTransformer(unittest.TestCase):
698 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
685 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
699
686
700 class SyntaxErrorTransformer(InputTransformer):
687 class SyntaxErrorTransformer(InputTransformer):
701
688
702 def push(self, line):
689 def push(self, line):
703 pos = line.find('syntaxerror')
690 pos = line.find('syntaxerror')
704 if pos >= 0:
691 if pos >= 0:
705 e = SyntaxError('input contains "syntaxerror"')
692 e = SyntaxError('input contains "syntaxerror"')
706 e.text = line
693 e.text = line
707 e.offset = pos + 1
694 e.offset = pos + 1
708 raise e
695 raise e
709 return line
696 return line
710
697
711 def reset(self):
698 def reset(self):
712 pass
699 pass
713
700
714 def setUp(self):
701 def setUp(self):
715 self.transformer = TestSyntaxErrorTransformer.SyntaxErrorTransformer()
702 self.transformer = TestSyntaxErrorTransformer.SyntaxErrorTransformer()
716 ip.input_splitter.python_line_transforms.append(self.transformer)
703 ip.input_splitter.python_line_transforms.append(self.transformer)
717 ip.input_transformer_manager.python_line_transforms.append(self.transformer)
704 ip.input_transformer_manager.python_line_transforms.append(self.transformer)
718
705
719 def tearDown(self):
706 def tearDown(self):
720 ip.input_splitter.python_line_transforms.remove(self.transformer)
707 ip.input_splitter.python_line_transforms.remove(self.transformer)
721 ip.input_transformer_manager.python_line_transforms.remove(self.transformer)
708 ip.input_transformer_manager.python_line_transforms.remove(self.transformer)
722
709
723 def test_syntaxerror_input_transformer(self):
710 def test_syntaxerror_input_transformer(self):
724 with tt.AssertPrints('1234'):
711 with tt.AssertPrints('1234'):
725 ip.run_cell('1234')
712 ip.run_cell('1234')
726 with tt.AssertPrints('SyntaxError: invalid syntax'):
713 with tt.AssertPrints('SyntaxError: invalid syntax'):
727 ip.run_cell('1 2 3') # plain python syntax error
714 ip.run_cell('1 2 3') # plain python syntax error
728 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
715 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
729 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
716 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
730 with tt.AssertPrints('3456'):
717 with tt.AssertPrints('3456'):
731 ip.run_cell('3456')
718 ip.run_cell('3456')
732
719
733
720
734
721
@@ -1,526 +1,557
1 //----------------------------------------------------------------------------
1 // Copyright (c) IPython Development Team.
2 // Copyright (C) 2008-2012 The IPython Development Team
2 // Distributed under the terms of the Modified BSD License.
3 //
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
7
3
8 //============================================================================
4 //============================================================================
9 // Utilities
5 // Utilities
10 //============================================================================
6 //============================================================================
7
11 IPython.namespace('IPython.utils');
8 IPython.namespace('IPython.utils');
12
9
13 IPython.utils = (function (IPython) {
10 IPython.utils = (function (IPython) {
14 "use strict";
11 "use strict";
15
12
16 IPython.load_extensions = function () {
13 IPython.load_extensions = function () {
17 // load one or more IPython notebook extensions with requirejs
14 // load one or more IPython notebook extensions with requirejs
18
15
19 var extensions = [];
16 var extensions = [];
20 var extension_names = arguments;
17 var extension_names = arguments;
21 for (var i = 0; i < extension_names.length; i++) {
18 for (var i = 0; i < extension_names.length; i++) {
22 extensions.push("nbextensions/" + arguments[i]);
19 extensions.push("nbextensions/" + arguments[i]);
23 }
20 }
24
21
25 require(extensions,
22 require(extensions,
26 function () {
23 function () {
27 for (var i = 0; i < arguments.length; i++) {
24 for (var i = 0; i < arguments.length; i++) {
28 var ext = arguments[i];
25 var ext = arguments[i];
29 var ext_name = extension_names[i];
26 var ext_name = extension_names[i];
30 // success callback
27 // success callback
31 console.log("Loaded extension: " + ext_name);
28 console.log("Loaded extension: " + ext_name);
32 if (ext && ext.load_ipython_extension !== undefined) {
29 if (ext && ext.load_ipython_extension !== undefined) {
33 ext.load_ipython_extension();
30 ext.load_ipython_extension();
34 }
31 }
35 }
32 }
36 },
33 },
37 function (err) {
34 function (err) {
38 // failure callback
35 // failure callback
39 console.log("Failed to load extension(s):", err.requireModules, err);
36 console.log("Failed to load extension(s):", err.requireModules, err);
40 }
37 }
41 );
38 );
42 };
39 };
43
40
44 //============================================================================
41 //============================================================================
45 // Cross-browser RegEx Split
42 // Cross-browser RegEx Split
46 //============================================================================
43 //============================================================================
47
44
48 // This code has been MODIFIED from the code licensed below to not replace the
45 // This code has been MODIFIED from the code licensed below to not replace the
49 // default browser split. The license is reproduced here.
46 // default browser split. The license is reproduced here.
50
47
51 // see http://blog.stevenlevithan.com/archives/cross-browser-split for more info:
48 // see http://blog.stevenlevithan.com/archives/cross-browser-split for more info:
52 /*!
49 /*!
53 * Cross-Browser Split 1.1.1
50 * Cross-Browser Split 1.1.1
54 * Copyright 2007-2012 Steven Levithan <stevenlevithan.com>
51 * Copyright 2007-2012 Steven Levithan <stevenlevithan.com>
55 * Available under the MIT License
52 * Available under the MIT License
56 * ECMAScript compliant, uniform cross-browser split method
53 * ECMAScript compliant, uniform cross-browser split method
57 */
54 */
58
55
59 /**
56 /**
60 * Splits a string into an array of strings using a regex or string
57 * Splits a string into an array of strings using a regex or string
61 * separator. Matches of the separator are not included in the result array.
58 * separator. Matches of the separator are not included in the result array.
62 * However, if `separator` is a regex that contains capturing groups,
59 * However, if `separator` is a regex that contains capturing groups,
63 * backreferences are spliced into the result each time `separator` is
60 * backreferences are spliced into the result each time `separator` is
64 * matched. Fixes browser bugs compared to the native
61 * matched. Fixes browser bugs compared to the native
65 * `String.prototype.split` and can be used reliably cross-browser.
62 * `String.prototype.split` and can be used reliably cross-browser.
66 * @param {String} str String to split.
63 * @param {String} str String to split.
67 * @param {RegExp|String} separator Regex or string to use for separating
64 * @param {RegExp|String} separator Regex or string to use for separating
68 * the string.
65 * the string.
69 * @param {Number} [limit] Maximum number of items to include in the result
66 * @param {Number} [limit] Maximum number of items to include in the result
70 * array.
67 * array.
71 * @returns {Array} Array of substrings.
68 * @returns {Array} Array of substrings.
72 * @example
69 * @example
73 *
70 *
74 * // Basic use
71 * // Basic use
75 * regex_split('a b c d', ' ');
72 * regex_split('a b c d', ' ');
76 * // -> ['a', 'b', 'c', 'd']
73 * // -> ['a', 'b', 'c', 'd']
77 *
74 *
78 * // With limit
75 * // With limit
79 * regex_split('a b c d', ' ', 2);
76 * regex_split('a b c d', ' ', 2);
80 * // -> ['a', 'b']
77 * // -> ['a', 'b']
81 *
78 *
82 * // Backreferences in result array
79 * // Backreferences in result array
83 * regex_split('..word1 word2..', /([a-z]+)(\d+)/i);
80 * regex_split('..word1 word2..', /([a-z]+)(\d+)/i);
84 * // -> ['..', 'word', '1', ' ', 'word', '2', '..']
81 * // -> ['..', 'word', '1', ' ', 'word', '2', '..']
85 */
82 */
86 var regex_split = function (str, separator, limit) {
83 var regex_split = function (str, separator, limit) {
87 // If `separator` is not a regex, use `split`
84 // If `separator` is not a regex, use `split`
88 if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
85 if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
89 return split.call(str, separator, limit);
86 return split.call(str, separator, limit);
90 }
87 }
91 var output = [],
88 var output = [],
92 flags = (separator.ignoreCase ? "i" : "") +
89 flags = (separator.ignoreCase ? "i" : "") +
93 (separator.multiline ? "m" : "") +
90 (separator.multiline ? "m" : "") +
94 (separator.extended ? "x" : "") + // Proposed for ES6
91 (separator.extended ? "x" : "") + // Proposed for ES6
95 (separator.sticky ? "y" : ""), // Firefox 3+
92 (separator.sticky ? "y" : ""), // Firefox 3+
96 lastLastIndex = 0,
93 lastLastIndex = 0,
97 // Make `global` and avoid `lastIndex` issues by working with a copy
94 // Make `global` and avoid `lastIndex` issues by working with a copy
98 separator = new RegExp(separator.source, flags + "g"),
95 separator = new RegExp(separator.source, flags + "g"),
99 separator2, match, lastIndex, lastLength;
96 separator2, match, lastIndex, lastLength;
100 str += ""; // Type-convert
97 str += ""; // Type-convert
101
98
102 var compliantExecNpcg = typeof(/()??/.exec("")[1]) === "undefined";
99 var compliantExecNpcg = typeof(/()??/.exec("")[1]) === "undefined";
103 if (!compliantExecNpcg) {
100 if (!compliantExecNpcg) {
104 // Doesn't need flags gy, but they don't hurt
101 // Doesn't need flags gy, but they don't hurt
105 separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
102 separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
106 }
103 }
107 /* Values for `limit`, per the spec:
104 /* Values for `limit`, per the spec:
108 * If undefined: 4294967295 // Math.pow(2, 32) - 1
105 * If undefined: 4294967295 // Math.pow(2, 32) - 1
109 * If 0, Infinity, or NaN: 0
106 * If 0, Infinity, or NaN: 0
110 * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
107 * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
111 * If negative number: 4294967296 - Math.floor(Math.abs(limit))
108 * If negative number: 4294967296 - Math.floor(Math.abs(limit))
112 * If other: Type-convert, then use the above rules
109 * If other: Type-convert, then use the above rules
113 */
110 */
114 limit = typeof(limit) === "undefined" ?
111 limit = typeof(limit) === "undefined" ?
115 -1 >>> 0 : // Math.pow(2, 32) - 1
112 -1 >>> 0 : // Math.pow(2, 32) - 1
116 limit >>> 0; // ToUint32(limit)
113 limit >>> 0; // ToUint32(limit)
117 while (match = separator.exec(str)) {
114 while (match = separator.exec(str)) {
118 // `separator.lastIndex` is not reliable cross-browser
115 // `separator.lastIndex` is not reliable cross-browser
119 lastIndex = match.index + match[0].length;
116 lastIndex = match.index + match[0].length;
120 if (lastIndex > lastLastIndex) {
117 if (lastIndex > lastLastIndex) {
121 output.push(str.slice(lastLastIndex, match.index));
118 output.push(str.slice(lastLastIndex, match.index));
122 // Fix browsers whose `exec` methods don't consistently return `undefined` for
119 // Fix browsers whose `exec` methods don't consistently return `undefined` for
123 // nonparticipating capturing groups
120 // nonparticipating capturing groups
124 if (!compliantExecNpcg && match.length > 1) {
121 if (!compliantExecNpcg && match.length > 1) {
125 match[0].replace(separator2, function () {
122 match[0].replace(separator2, function () {
126 for (var i = 1; i < arguments.length - 2; i++) {
123 for (var i = 1; i < arguments.length - 2; i++) {
127 if (typeof(arguments[i]) === "undefined") {
124 if (typeof(arguments[i]) === "undefined") {
128 match[i] = undefined;
125 match[i] = undefined;
129 }
126 }
130 }
127 }
131 });
128 });
132 }
129 }
133 if (match.length > 1 && match.index < str.length) {
130 if (match.length > 1 && match.index < str.length) {
134 Array.prototype.push.apply(output, match.slice(1));
131 Array.prototype.push.apply(output, match.slice(1));
135 }
132 }
136 lastLength = match[0].length;
133 lastLength = match[0].length;
137 lastLastIndex = lastIndex;
134 lastLastIndex = lastIndex;
138 if (output.length >= limit) {
135 if (output.length >= limit) {
139 break;
136 break;
140 }
137 }
141 }
138 }
142 if (separator.lastIndex === match.index) {
139 if (separator.lastIndex === match.index) {
143 separator.lastIndex++; // Avoid an infinite loop
140 separator.lastIndex++; // Avoid an infinite loop
144 }
141 }
145 }
142 }
146 if (lastLastIndex === str.length) {
143 if (lastLastIndex === str.length) {
147 if (lastLength || !separator.test("")) {
144 if (lastLength || !separator.test("")) {
148 output.push("");
145 output.push("");
149 }
146 }
150 } else {
147 } else {
151 output.push(str.slice(lastLastIndex));
148 output.push(str.slice(lastLastIndex));
152 }
149 }
153 return output.length > limit ? output.slice(0, limit) : output;
150 return output.length > limit ? output.slice(0, limit) : output;
154 };
151 };
155
152
156 //============================================================================
153 //============================================================================
157 // End contributed Cross-browser RegEx Split
154 // End contributed Cross-browser RegEx Split
158 //============================================================================
155 //============================================================================
159
156
160
157
161 var uuid = function () {
158 var uuid = function () {
162 // http://www.ietf.org/rfc/rfc4122.txt
159 // http://www.ietf.org/rfc/rfc4122.txt
163 var s = [];
160 var s = [];
164 var hexDigits = "0123456789ABCDEF";
161 var hexDigits = "0123456789ABCDEF";
165 for (var i = 0; i < 32; i++) {
162 for (var i = 0; i < 32; i++) {
166 s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
163 s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
167 }
164 }
168 s[12] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
165 s[12] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
169 s[16] = hexDigits.substr((s[16] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
166 s[16] = hexDigits.substr((s[16] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
170
167
171 var uuid = s.join("");
168 var uuid = s.join("");
172 return uuid;
169 return uuid;
173 };
170 };
174
171
175
172
176 //Fix raw text to parse correctly in crazy XML
173 //Fix raw text to parse correctly in crazy XML
177 function xmlencode(string) {
174 function xmlencode(string) {
178 return string.replace(/\&/g,'&'+'amp;')
175 return string.replace(/\&/g,'&'+'amp;')
179 .replace(/</g,'&'+'lt;')
176 .replace(/</g,'&'+'lt;')
180 .replace(/>/g,'&'+'gt;')
177 .replace(/>/g,'&'+'gt;')
181 .replace(/\'/g,'&'+'apos;')
178 .replace(/\'/g,'&'+'apos;')
182 .replace(/\"/g,'&'+'quot;')
179 .replace(/\"/g,'&'+'quot;')
183 .replace(/`/g,'&'+'#96;');
180 .replace(/`/g,'&'+'#96;');
184 }
181 }
185
182
186
183
187 //Map from terminal commands to CSS classes
184 //Map from terminal commands to CSS classes
188 var ansi_colormap = {
185 var ansi_colormap = {
189 "01":"ansibold",
186 "01":"ansibold",
190
187
191 "30":"ansiblack",
188 "30":"ansiblack",
192 "31":"ansired",
189 "31":"ansired",
193 "32":"ansigreen",
190 "32":"ansigreen",
194 "33":"ansiyellow",
191 "33":"ansiyellow",
195 "34":"ansiblue",
192 "34":"ansiblue",
196 "35":"ansipurple",
193 "35":"ansipurple",
197 "36":"ansicyan",
194 "36":"ansicyan",
198 "37":"ansigray",
195 "37":"ansigray",
199
196
200 "40":"ansibgblack",
197 "40":"ansibgblack",
201 "41":"ansibgred",
198 "41":"ansibgred",
202 "42":"ansibggreen",
199 "42":"ansibggreen",
203 "43":"ansibgyellow",
200 "43":"ansibgyellow",
204 "44":"ansibgblue",
201 "44":"ansibgblue",
205 "45":"ansibgpurple",
202 "45":"ansibgpurple",
206 "46":"ansibgcyan",
203 "46":"ansibgcyan",
207 "47":"ansibggray"
204 "47":"ansibggray"
208 };
205 };
209
206
210 function _process_numbers(attrs, numbers) {
207 function _process_numbers(attrs, numbers) {
211 // process ansi escapes
208 // process ansi escapes
212 var n = numbers.shift();
209 var n = numbers.shift();
213 if (ansi_colormap[n]) {
210 if (ansi_colormap[n]) {
214 if ( ! attrs["class"] ) {
211 if ( ! attrs["class"] ) {
215 attrs["class"] = ansi_colormap[n];
212 attrs["class"] = ansi_colormap[n];
216 } else {
213 } else {
217 attrs["class"] += " " + ansi_colormap[n];
214 attrs["class"] += " " + ansi_colormap[n];
218 }
215 }
219 } else if (n == "38" || n == "48") {
216 } else if (n == "38" || n == "48") {
220 // VT100 256 color or 24 bit RGB
217 // VT100 256 color or 24 bit RGB
221 if (numbers.length < 2) {
218 if (numbers.length < 2) {
222 console.log("Not enough fields for VT100 color", numbers);
219 console.log("Not enough fields for VT100 color", numbers);
223 return;
220 return;
224 }
221 }
225
222
226 var index_or_rgb = numbers.shift();
223 var index_or_rgb = numbers.shift();
227 var r,g,b;
224 var r,g,b;
228 if (index_or_rgb == "5") {
225 if (index_or_rgb == "5") {
229 // 256 color
226 // 256 color
230 var idx = parseInt(numbers.shift());
227 var idx = parseInt(numbers.shift());
231 if (idx < 16) {
228 if (idx < 16) {
232 // indexed ANSI
229 // indexed ANSI
233 // ignore bright / non-bright distinction
230 // ignore bright / non-bright distinction
234 idx = idx % 8;
231 idx = idx % 8;
235 var ansiclass = ansi_colormap[n[0] + (idx % 8).toString()];
232 var ansiclass = ansi_colormap[n[0] + (idx % 8).toString()];
236 if ( ! attrs["class"] ) {
233 if ( ! attrs["class"] ) {
237 attrs["class"] = ansiclass;
234 attrs["class"] = ansiclass;
238 } else {
235 } else {
239 attrs["class"] += " " + ansiclass;
236 attrs["class"] += " " + ansiclass;
240 }
237 }
241 return;
238 return;
242 } else if (idx < 232) {
239 } else if (idx < 232) {
243 // 216 color 6x6x6 RGB
240 // 216 color 6x6x6 RGB
244 idx = idx - 16;
241 idx = idx - 16;
245 b = idx % 6;
242 b = idx % 6;
246 g = Math.floor(idx / 6) % 6;
243 g = Math.floor(idx / 6) % 6;
247 r = Math.floor(idx / 36) % 6;
244 r = Math.floor(idx / 36) % 6;
248 // convert to rgb
245 // convert to rgb
249 r = (r * 51);
246 r = (r * 51);
250 g = (g * 51);
247 g = (g * 51);
251 b = (b * 51);
248 b = (b * 51);
252 } else {
249 } else {
253 // grayscale
250 // grayscale
254 idx = idx - 231;
251 idx = idx - 231;
255 // it's 1-24 and should *not* include black or white,
252 // it's 1-24 and should *not* include black or white,
256 // so a 26 point scale
253 // so a 26 point scale
257 r = g = b = Math.floor(idx * 256 / 26);
254 r = g = b = Math.floor(idx * 256 / 26);
258 }
255 }
259 } else if (index_or_rgb == "2") {
256 } else if (index_or_rgb == "2") {
260 // Simple 24 bit RGB
257 // Simple 24 bit RGB
261 if (numbers.length > 3) {
258 if (numbers.length > 3) {
262 console.log("Not enough fields for RGB", numbers);
259 console.log("Not enough fields for RGB", numbers);
263 return;
260 return;
264 }
261 }
265 r = numbers.shift();
262 r = numbers.shift();
266 g = numbers.shift();
263 g = numbers.shift();
267 b = numbers.shift();
264 b = numbers.shift();
268 } else {
265 } else {
269 console.log("unrecognized control", numbers);
266 console.log("unrecognized control", numbers);
270 return;
267 return;
271 }
268 }
272 if (r !== undefined) {
269 if (r !== undefined) {
273 // apply the rgb color
270 // apply the rgb color
274 var line;
271 var line;
275 if (n == "38") {
272 if (n == "38") {
276 line = "color: ";
273 line = "color: ";
277 } else {
274 } else {
278 line = "background-color: ";
275 line = "background-color: ";
279 }
276 }
280 line = line + "rgb(" + r + "," + g + "," + b + ");"
277 line = line + "rgb(" + r + "," + g + "," + b + ");"
281 if ( !attrs["style"] ) {
278 if ( !attrs["style"] ) {
282 attrs["style"] = line;
279 attrs["style"] = line;
283 } else {
280 } else {
284 attrs["style"] += " " + line;
281 attrs["style"] += " " + line;
285 }
282 }
286 }
283 }
287 }
284 }
288 }
285 }
289
286
290 function ansispan(str) {
287 function ansispan(str) {
291 // ansispan function adapted from github.com/mmalecki/ansispan (MIT License)
288 // ansispan function adapted from github.com/mmalecki/ansispan (MIT License)
292 // regular ansi escapes (using the table above)
289 // regular ansi escapes (using the table above)
293 return str.replace(/\033\[(0?[01]|22|39)?([;\d]+)?m/g, function(match, prefix, pattern) {
290 return str.replace(/\033\[(0?[01]|22|39)?([;\d]+)?m/g, function(match, prefix, pattern) {
294 if (!pattern) {
291 if (!pattern) {
295 // [(01|22|39|)m close spans
292 // [(01|22|39|)m close spans
296 return "</span>";
293 return "</span>";
297 }
294 }
298 // consume sequence of color escapes
295 // consume sequence of color escapes
299 var numbers = pattern.match(/\d+/g);
296 var numbers = pattern.match(/\d+/g);
300 var attrs = {};
297 var attrs = {};
301 while (numbers.length > 0) {
298 while (numbers.length > 0) {
302 _process_numbers(attrs, numbers);
299 _process_numbers(attrs, numbers);
303 }
300 }
304
301
305 var span = "<span ";
302 var span = "<span ";
306 for (var attr in attrs) {
303 for (var attr in attrs) {
307 var value = attrs[attr];
304 var value = attrs[attr];
308 span = span + " " + attr + '="' + attrs[attr] + '"';
305 span = span + " " + attr + '="' + attrs[attr] + '"';
309 }
306 }
310 return span + ">";
307 return span + ">";
311 });
308 });
312 };
309 };
313
310
314 // Transform ANSI color escape codes into HTML <span> tags with css
311 // Transform ANSI color escape codes into HTML <span> tags with css
315 // classes listed in the above ansi_colormap object. The actual color used
312 // classes listed in the above ansi_colormap object. The actual color used
316 // are set in the css file.
313 // are set in the css file.
317 function fixConsole(txt) {
314 function fixConsole(txt) {
318 txt = xmlencode(txt);
315 txt = xmlencode(txt);
319 var re = /\033\[([\dA-Fa-f;]*?)m/;
316 var re = /\033\[([\dA-Fa-f;]*?)m/;
320 var opened = false;
317 var opened = false;
321 var cmds = [];
318 var cmds = [];
322 var opener = "";
319 var opener = "";
323 var closer = "";
320 var closer = "";
324
321
325 // Strip all ANSI codes that are not color related. Matches
322 // Strip all ANSI codes that are not color related. Matches
326 // all ANSI codes that do not end with "m".
323 // all ANSI codes that do not end with "m".
327 var ignored_re = /(?=(\033\[[\d;=]*[a-ln-zA-Z]{1}))\1(?!m)/g;
324 var ignored_re = /(?=(\033\[[\d;=]*[a-ln-zA-Z]{1}))\1(?!m)/g;
328 txt = txt.replace(ignored_re, "");
325 txt = txt.replace(ignored_re, "");
329
326
330 // color ansi codes
327 // color ansi codes
331 txt = ansispan(txt);
328 txt = ansispan(txt);
332 return txt;
329 return txt;
333 }
330 }
334
331
335 // Remove chunks that should be overridden by the effect of
332 // Remove chunks that should be overridden by the effect of
336 // carriage return characters
333 // carriage return characters
337 function fixCarriageReturn(txt) {
334 function fixCarriageReturn(txt) {
338 var tmp = txt;
335 var tmp = txt;
339 do {
336 do {
340 txt = tmp;
337 txt = tmp;
341 tmp = txt.replace(/\r+\n/gm, '\n'); // \r followed by \n --> newline
338 tmp = txt.replace(/\r+\n/gm, '\n'); // \r followed by \n --> newline
342 tmp = tmp.replace(/^.*\r+/gm, ''); // Other \r --> clear line
339 tmp = tmp.replace(/^.*\r+/gm, ''); // Other \r --> clear line
343 } while (tmp.length < txt.length);
340 } while (tmp.length < txt.length);
344 return txt;
341 return txt;
345 }
342 }
346
343
347 // Locate any URLs and convert them to a anchor tag
344 // Locate any URLs and convert them to a anchor tag
348 function autoLinkUrls(txt) {
345 function autoLinkUrls(txt) {
349 return txt.replace(/(^|\s)(https?|ftp)(:[^'">\s]+)/gi,
346 return txt.replace(/(^|\s)(https?|ftp)(:[^'">\s]+)/gi,
350 "$1<a target=\"_blank\" href=\"$2$3\">$2$3</a>");
347 "$1<a target=\"_blank\" href=\"$2$3\">$2$3</a>");
351 }
348 }
352
349
353 var points_to_pixels = function (points) {
350 var points_to_pixels = function (points) {
354 // A reasonably good way of converting between points and pixels.
351 // A reasonably good way of converting between points and pixels.
355 var test = $('<div style="display: none; width: 10000pt; padding:0; border:0;"></div>');
352 var test = $('<div style="display: none; width: 10000pt; padding:0; border:0;"></div>');
356 $(body).append(test);
353 $(body).append(test);
357 var pixel_per_point = test.width()/10000;
354 var pixel_per_point = test.width()/10000;
358 test.remove();
355 test.remove();
359 return Math.floor(points*pixel_per_point);
356 return Math.floor(points*pixel_per_point);
360 };
357 };
361
358
362 var always_new = function (constructor) {
359 var always_new = function (constructor) {
363 // wrapper around contructor to avoid requiring `var a = new constructor()`
360 // wrapper around contructor to avoid requiring `var a = new constructor()`
364 // useful for passing constructors as callbacks,
361 // useful for passing constructors as callbacks,
365 // not for programmer laziness.
362 // not for programmer laziness.
366 // from http://programmers.stackexchange.com/questions/118798
363 // from http://programmers.stackexchange.com/questions/118798
367 return function () {
364 return function () {
368 var obj = Object.create(constructor.prototype);
365 var obj = Object.create(constructor.prototype);
369 constructor.apply(obj, arguments);
366 constructor.apply(obj, arguments);
370 return obj;
367 return obj;
371 };
368 };
372 };
369 };
373
370
374 var url_path_join = function () {
371 var url_path_join = function () {
375 // join a sequence of url components with '/'
372 // join a sequence of url components with '/'
376 var url = '';
373 var url = '';
377 for (var i = 0; i < arguments.length; i++) {
374 for (var i = 0; i < arguments.length; i++) {
378 if (arguments[i] === '') {
375 if (arguments[i] === '') {
379 continue;
376 continue;
380 }
377 }
381 if (url.length > 0 && url[url.length-1] != '/') {
378 if (url.length > 0 && url[url.length-1] != '/') {
382 url = url + '/' + arguments[i];
379 url = url + '/' + arguments[i];
383 } else {
380 } else {
384 url = url + arguments[i];
381 url = url + arguments[i];
385 }
382 }
386 }
383 }
387 url = url.replace(/\/\/+/, '/');
384 url = url.replace(/\/\/+/, '/');
388 return url;
385 return url;
389 };
386 };
390
387
391 var parse_url = function (url) {
388 var parse_url = function (url) {
392 // an `a` element with an href allows attr-access to the parsed segments of a URL
389 // an `a` element with an href allows attr-access to the parsed segments of a URL
393 // a = parse_url("http://localhost:8888/path/name#hash")
390 // a = parse_url("http://localhost:8888/path/name#hash")
394 // a.protocol = "http:"
391 // a.protocol = "http:"
395 // a.host = "localhost:8888"
392 // a.host = "localhost:8888"
396 // a.hostname = "localhost"
393 // a.hostname = "localhost"
397 // a.port = 8888
394 // a.port = 8888
398 // a.pathname = "/path/name"
395 // a.pathname = "/path/name"
399 // a.hash = "#hash"
396 // a.hash = "#hash"
400 var a = document.createElement("a");
397 var a = document.createElement("a");
401 a.href = url;
398 a.href = url;
402 return a;
399 return a;
403 };
400 };
404
401
405 var encode_uri_components = function (uri) {
402 var encode_uri_components = function (uri) {
406 // encode just the components of a multi-segment uri,
403 // encode just the components of a multi-segment uri,
407 // leaving '/' separators
404 // leaving '/' separators
408 return uri.split('/').map(encodeURIComponent).join('/');
405 return uri.split('/').map(encodeURIComponent).join('/');
409 };
406 };
410
407
411 var url_join_encode = function () {
408 var url_join_encode = function () {
412 // join a sequence of url components with '/',
409 // join a sequence of url components with '/',
413 // encoding each component with encodeURIComponent
410 // encoding each component with encodeURIComponent
414 return encode_uri_components(url_path_join.apply(null, arguments));
411 return encode_uri_components(url_path_join.apply(null, arguments));
415 };
412 };
416
413
417
414
418 var splitext = function (filename) {
415 var splitext = function (filename) {
419 // mimic Python os.path.splitext
416 // mimic Python os.path.splitext
420 // Returns ['base', '.ext']
417 // Returns ['base', '.ext']
421 var idx = filename.lastIndexOf('.');
418 var idx = filename.lastIndexOf('.');
422 if (idx > 0) {
419 if (idx > 0) {
423 return [filename.slice(0, idx), filename.slice(idx)];
420 return [filename.slice(0, idx), filename.slice(idx)];
424 } else {
421 } else {
425 return [filename, ''];
422 return [filename, ''];
426 }
423 }
427 };
424 };
428
425
429
426
430 var escape_html = function (text) {
427 var escape_html = function (text) {
431 // escape text to HTML
428 // escape text to HTML
432 return $("<div/>").text(text).html();
429 return $("<div/>").text(text).html();
433 }
430 };
434
431
435
432
436 var get_body_data = function(key) {
433 var get_body_data = function(key) {
437 // get a url-encoded item from body.data and decode it
434 // get a url-encoded item from body.data and decode it
438 // we should never have any encoded URLs anywhere else in code
435 // we should never have any encoded URLs anywhere else in code
439 // until we are building an actual request
436 // until we are building an actual request
440 return decodeURIComponent($('body').data(key));
437 return decodeURIComponent($('body').data(key));
441 };
438 };
442
439
443
440 var to_absolute_cursor_pos = function (cm, cursor) {
441 // get the absolute cursor position from CodeMirror's col, ch
442 if (!cursor) {
443 cursor = cm.getCursor();
444 }
445 var cursor_pos = cursor.ch;
446 for (var i = 0; i < cursor.line; i++) {
447 cursor_pos += cm.getLine(i).length + 1;
448 }
449 return cursor_pos;
450 };
451
452 var from_absolute_cursor_pos = function (cm, cursor_pos) {
453 // turn absolute cursor postion into CodeMirror col, ch cursor
454 var i, line;
455 var offset = 0;
456 for (i = 0, line=cm.getLine(i); line !== undefined; i++, line=cm.getLine(i)) {
457 if (offset + line.length < cursor_pos) {
458 offset += line.length + 1;
459 } else {
460 return {
461 line : i,
462 ch : cursor_pos - offset,
463 };
464 }
465 }
466 // reached end, return endpoint
467 return {
468 ch : line.length - 1,
469 line : i - 1,
470 };
471 };
472
444 // http://stackoverflow.com/questions/2400935/browser-detection-in-javascript
473 // http://stackoverflow.com/questions/2400935/browser-detection-in-javascript
445 var browser = (function() {
474 var browser = (function() {
446 if (typeof navigator === 'undefined') {
475 if (typeof navigator === 'undefined') {
447 // navigator undefined in node
476 // navigator undefined in node
448 return 'None';
477 return 'None';
449 }
478 }
450 var N= navigator.appName, ua= navigator.userAgent, tem;
479 var N= navigator.appName, ua= navigator.userAgent, tem;
451 var M= ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
480 var M= ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
452 if (M && (tem= ua.match(/version\/([\.\d]+)/i))!= null) M[2]= tem[1];
481 if (M && (tem= ua.match(/version\/([\.\d]+)/i)) !== null) M[2]= tem[1];
453 M= M? [M[1], M[2]]: [N, navigator.appVersion,'-?'];
482 M= M? [M[1], M[2]]: [N, navigator.appVersion,'-?'];
454 return M;
483 return M;
455 })();
484 })();
456
485
457 // http://stackoverflow.com/questions/11219582/how-to-detect-my-browser-version-and-operating-system-using-javascript
486 // http://stackoverflow.com/questions/11219582/how-to-detect-my-browser-version-and-operating-system-using-javascript
458 var platform = (function () {
487 var platform = (function () {
459 if (typeof navigator === 'undefined') {
488 if (typeof navigator === 'undefined') {
460 // navigator undefined in node
489 // navigator undefined in node
461 return 'None';
490 return 'None';
462 }
491 }
463 var OSName="None";
492 var OSName="None";
464 if (navigator.appVersion.indexOf("Win")!=-1) OSName="Windows";
493 if (navigator.appVersion.indexOf("Win")!=-1) OSName="Windows";
465 if (navigator.appVersion.indexOf("Mac")!=-1) OSName="MacOS";
494 if (navigator.appVersion.indexOf("Mac")!=-1) OSName="MacOS";
466 if (navigator.appVersion.indexOf("X11")!=-1) OSName="UNIX";
495 if (navigator.appVersion.indexOf("X11")!=-1) OSName="UNIX";
467 if (navigator.appVersion.indexOf("Linux")!=-1) OSName="Linux";
496 if (navigator.appVersion.indexOf("Linux")!=-1) OSName="Linux";
468 return OSName
497 return OSName;
469 })();
498 })();
470
499
471 var is_or_has = function (a, b) {
500 var is_or_has = function (a, b) {
472 // Is b a child of a or a itself?
501 // Is b a child of a or a itself?
473 return a.has(b).length !==0 || a.is(b);
502 return a.has(b).length !==0 || a.is(b);
474 }
503 };
475
504
476 var is_focused = function (e) {
505 var is_focused = function (e) {
477 // Is element e, or one of its children focused?
506 // Is element e, or one of its children focused?
478 e = $(e);
507 e = $(e);
479 var target = $(document.activeElement);
508 var target = $(document.activeElement);
480 if (target.length > 0) {
509 if (target.length > 0) {
481 if (is_or_has(e, target)) {
510 if (is_or_has(e, target)) {
482 return true;
511 return true;
483 } else {
512 } else {
484 return false;
513 return false;
485 }
514 }
486 } else {
515 } else {
487 return false;
516 return false;
488 }
517 }
489 }
518 };
490
519
491 var log_ajax_error = function (jqXHR, status, error) {
520 var log_ajax_error = function (jqXHR, status, error) {
492 // log ajax failures with informative messages
521 // log ajax failures with informative messages
493 var msg = "API request failed (" + jqXHR.status + "): ";
522 var msg = "API request failed (" + jqXHR.status + "): ";
494 console.log(jqXHR);
523 console.log(jqXHR);
495 if (jqXHR.responseJSON && jqXHR.responseJSON.message) {
524 if (jqXHR.responseJSON && jqXHR.responseJSON.message) {
496 msg += jqXHR.responseJSON.message;
525 msg += jqXHR.responseJSON.message;
497 } else {
526 } else {
498 msg += jqXHR.statusText;
527 msg += jqXHR.statusText;
499 }
528 }
500 console.log(msg);
529 console.log(msg);
501 };
530 };
502
531
503 return {
532 return {
504 regex_split : regex_split,
533 regex_split : regex_split,
505 uuid : uuid,
534 uuid : uuid,
506 fixConsole : fixConsole,
535 fixConsole : fixConsole,
507 fixCarriageReturn : fixCarriageReturn,
536 fixCarriageReturn : fixCarriageReturn,
508 autoLinkUrls : autoLinkUrls,
537 autoLinkUrls : autoLinkUrls,
509 points_to_pixels : points_to_pixels,
538 points_to_pixels : points_to_pixels,
510 get_body_data : get_body_data,
539 get_body_data : get_body_data,
511 parse_url : parse_url,
540 parse_url : parse_url,
512 url_path_join : url_path_join,
541 url_path_join : url_path_join,
513 url_join_encode : url_join_encode,
542 url_join_encode : url_join_encode,
514 encode_uri_components : encode_uri_components,
543 encode_uri_components : encode_uri_components,
515 splitext : splitext,
544 splitext : splitext,
516 escape_html : escape_html,
545 escape_html : escape_html,
517 always_new : always_new,
546 always_new : always_new,
547 to_absolute_cursor_pos : to_absolute_cursor_pos,
548 from_absolute_cursor_pos : from_absolute_cursor_pos,
518 browser : browser,
549 browser : browser,
519 platform: platform,
550 platform: platform,
520 is_or_has : is_or_has,
551 is_or_has : is_or_has,
521 is_focused : is_focused,
552 is_focused : is_focused,
522 log_ajax_error : log_ajax_error,
553 log_ajax_error : log_ajax_error,
523 };
554 };
524
555
525 }(IPython));
556 }(IPython));
526
557
@@ -1,370 +1,376
1 // function completer.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
3
4 // Completer
2 //
5 //
3 // completer should be a class that takes an cell instance
6 // Completer is be a class that takes a cell instance.
7
4 var IPython = (function (IPython) {
8 var IPython = (function (IPython) {
5 // that will prevent us from misspelling
9 // that will prevent us from misspelling
6 "use strict";
10 "use strict";
7
11
8 // easier key mapping
12 // easier key mapping
9 var keycodes = IPython.keyboard.keycodes;
13 var keycodes = IPython.keyboard.keycodes;
14 var utils = IPython.utils;
10
15
11 function prepend_n_prc(str, n) {
16 var prepend_n_prc = function(str, n) {
12 for( var i =0 ; i< n ; i++){
17 for( var i =0 ; i< n ; i++){
13 str = '%'+str ;
18 str = '%'+str ;
14 }
19 }
15 return str;
20 return str;
16 };
21 };
17
22
18 function _existing_completion(item, completion_array){
23 var _existing_completion = function(item, completion_array){
19 for( var i=0; i < completion_array.length; i++) {
24 for( var i=0; i < completion_array.length; i++) {
20 if (completion_array[i].trim().substr(-item.length) == item) {
25 if (completion_array[i].trim().substr(-item.length) == item) {
21 return true;
26 return true;
22 }
27 }
23 }
28 }
24 return false;
29 return false;
25 };
30 };
26
31
27 // what is the common start of all completions
32 // what is the common start of all completions
28 function shared_start(B, drop_prct) {
33 function shared_start(B, drop_prct) {
29 if (B.length == 1) {
34 if (B.length == 1) {
30 return B[0];
35 return B[0];
31 }
36 }
32 var A = [];
37 var A = [];
33 var common;
38 var common;
34 var min_lead_prct = 10;
39 var min_lead_prct = 10;
35 for (var i = 0; i < B.length; i++) {
40 for (var i = 0; i < B.length; i++) {
36 var str = B[i].str;
41 var str = B[i].str;
37 var localmin = 0;
42 var localmin = 0;
38 if(drop_prct === true){
43 if(drop_prct === true){
39 while ( str.substr(0, 1) == '%') {
44 while ( str.substr(0, 1) == '%') {
40 localmin = localmin+1;
45 localmin = localmin+1;
41 str = str.substring(1);
46 str = str.substring(1);
42 }
47 }
43 }
48 }
44 min_lead_prct = Math.min(min_lead_prct, localmin);
49 min_lead_prct = Math.min(min_lead_prct, localmin);
45 A.push(str);
50 A.push(str);
46 }
51 }
47
52
48 if (A.length > 1) {
53 if (A.length > 1) {
49 var tem1, tem2, s;
54 var tem1, tem2, s;
50 A = A.slice(0).sort();
55 A = A.slice(0).sort();
51 tem1 = A[0];
56 tem1 = A[0];
52 s = tem1.length;
57 s = tem1.length;
53 tem2 = A.pop();
58 tem2 = A.pop();
54 while (s && tem2.indexOf(tem1) == -1) {
59 while (s && tem2.indexOf(tem1) == -1) {
55 tem1 = tem1.substring(0, --s);
60 tem1 = tem1.substring(0, --s);
56 }
61 }
57 if (tem1 === "" || tem2.indexOf(tem1) !== 0) {
62 if (tem1 === "" || tem2.indexOf(tem1) !== 0) {
58 return {
63 return {
59 str:prepend_n_prc('', min_lead_prct),
64 str:prepend_n_prc('', min_lead_prct),
60 type: "computed",
65 type: "computed",
61 from: B[0].from,
66 from: B[0].from,
62 to: B[0].to
67 to: B[0].to
63 };
68 };
64 }
69 }
65 return {
70 return {
66 str: prepend_n_prc(tem1, min_lead_prct),
71 str: prepend_n_prc(tem1, min_lead_prct),
67 type: "computed",
72 type: "computed",
68 from: B[0].from,
73 from: B[0].from,
69 to: B[0].to
74 to: B[0].to
70 };
75 };
71 }
76 }
72 return null;
77 return null;
73 }
78 }
74
79
75
80
76 var Completer = function (cell) {
81 var Completer = function (cell) {
77 this.cell = cell;
82 this.cell = cell;
78 this.editor = cell.code_mirror;
83 this.editor = cell.code_mirror;
79 var that = this;
84 var that = this;
80 $([IPython.events]).on('status_busy.Kernel', function () {
85 $([IPython.events]).on('status_busy.Kernel', function () {
81 that.skip_kernel_completion = true;
86 that.skip_kernel_completion = true;
82 });
87 });
83 $([IPython.events]).on('status_idle.Kernel', function () {
88 $([IPython.events]).on('status_idle.Kernel', function () {
84 that.skip_kernel_completion = false;
89 that.skip_kernel_completion = false;
85 });
90 });
86 };
91 };
87
92
88 Completer.prototype.startCompletion = function () {
93 Completer.prototype.startCompletion = function () {
89 // call for a 'first' completion, that will set the editor and do some
94 // call for a 'first' completion, that will set the editor and do some
90 // special behavior like autopicking if only one completion available.
95 // special behavior like autopicking if only one completion available.
91 if (this.editor.somethingSelected()) return;
96 if (this.editor.somethingSelected()) return;
92 this.done = false;
97 this.done = false;
93 // use to get focus back on opera
98 // use to get focus back on opera
94 this.carry_on_completion(true);
99 this.carry_on_completion(true);
95 };
100 };
96
101
97
102
98 // easy access for julia to monkeypatch
103 // easy access for julia to monkeypatch
99 //
104 //
100 Completer.reinvoke_re = /[%0-9a-z._/\\:~-]/i;
105 Completer.reinvoke_re = /[%0-9a-z._/\\:~-]/i;
101
106
102 Completer.prototype.reinvoke= function(pre_cursor, block, cursor){
107 Completer.prototype.reinvoke= function(pre_cursor, block, cursor){
103 return Completer.reinvoke_re.test(pre_cursor);
108 return Completer.reinvoke_re.test(pre_cursor);
104 };
109 };
105
110
106 /**
111 /**
107 *
112 *
108 * pass true as parameter if this is the first invocation of the completer
113 * pass true as parameter if this is the first invocation of the completer
109 * this will prevent the completer to dissmiss itself if it is not on a
114 * this will prevent the completer to dissmiss itself if it is not on a
110 * word boundary like pressing tab after a space, and make it autopick the
115 * word boundary like pressing tab after a space, and make it autopick the
111 * only choice if there is only one which prevent from popping the UI. as
116 * only choice if there is only one which prevent from popping the UI. as
112 * well as fast-forwarding the typing if all completion have a common
117 * well as fast-forwarding the typing if all completion have a common
113 * shared start
118 * shared start
114 **/
119 **/
115 Completer.prototype.carry_on_completion = function (first_invocation) {
120 Completer.prototype.carry_on_completion = function (first_invocation) {
116 // Pass true as parameter if you want the completer to autopick when
121 // Pass true as parameter if you want the completer to autopick when
117 // only one completion. This function is automatically reinvoked at
122 // only one completion. This function is automatically reinvoked at
118 // each keystroke with first_invocation = false
123 // each keystroke with first_invocation = false
119 var cur = this.editor.getCursor();
124 var cur = this.editor.getCursor();
120 var line = this.editor.getLine(cur.line);
125 var line = this.editor.getLine(cur.line);
121 var pre_cursor = this.editor.getRange({
126 var pre_cursor = this.editor.getRange({
122 line: cur.line,
127 line: cur.line,
123 ch: cur.ch - 1
128 ch: cur.ch - 1
124 }, cur);
129 }, cur);
125
130
126 // we need to check that we are still on a word boundary
131 // we need to check that we are still on a word boundary
127 // because while typing the completer is still reinvoking itself
132 // because while typing the completer is still reinvoking itself
128 // so dismiss if we are on a "bad" caracter
133 // so dismiss if we are on a "bad" caracter
129 if (!this.reinvoke(pre_cursor) && !first_invocation) {
134 if (!this.reinvoke(pre_cursor) && !first_invocation) {
130 this.close();
135 this.close();
131 return;
136 return;
132 }
137 }
133
138
134 this.autopick = false;
139 this.autopick = false;
135 if (first_invocation) {
140 if (first_invocation) {
136 this.autopick = true;
141 this.autopick = true;
137 }
142 }
138
143
139 // We want a single cursor position.
144 // We want a single cursor position.
140 if (this.editor.somethingSelected()) {
145 if (this.editor.somethingSelected()) {
141 return;
146 return;
142 }
147 }
143
148
144 // one kernel completion came back, finish_completing will be called with the results
149 // one kernel completion came back, finish_completing will be called with the results
145 // we fork here and directly call finish completing if kernel is busy
150 // we fork here and directly call finish completing if kernel is busy
151 var cursor_pos = utils.to_absolute_cursor_pos(this.editor, cur);
146 if (this.skip_kernel_completion) {
152 if (this.skip_kernel_completion) {
147 this.finish_completing({
153 this.finish_completing({ content: {
148 'matches': [],
154 matches: [],
149 matched_text: ""
155 cursor_start: cursor_pos,
150 });
156 cursor_end: cursor_pos,
157 }});
151 } else {
158 } else {
152 this.cell.kernel.complete(line, cur.ch, $.proxy(this.finish_completing, this));
159 this.cell.kernel.complete(this.editor.getValue(), cursor_pos,
160 $.proxy(this.finish_completing, this)
161 );
153 }
162 }
154 };
163 };
155
164
156 Completer.prototype.finish_completing = function (msg) {
165 Completer.prototype.finish_completing = function (msg) {
157 // let's build a function that wrap all that stuff into what is needed
166 // let's build a function that wrap all that stuff into what is needed
158 // for the new completer:
167 // for the new completer:
159 var content = msg.content;
168 var content = msg.content;
160 var matched_text = content.matched_text;
169 var start = content.cursor_start;
170 var end = content.cursor_end;
161 var matches = content.matches;
171 var matches = content.matches;
162
172
163 var cur = this.editor.getCursor();
173 var cur = this.editor.getCursor();
164 var results = CodeMirror.contextHint(this.editor);
174 var results = CodeMirror.contextHint(this.editor);
165 var filtered_results = [];
175 var filtered_results = [];
166 //remove results from context completion
176 //remove results from context completion
167 //that are already in kernel completion
177 //that are already in kernel completion
168 for (var i=0; i < results.length; i++) {
178 var i;
179 for (i=0; i < results.length; i++) {
169 if (!_existing_completion(results[i].str, matches)) {
180 if (!_existing_completion(results[i].str, matches)) {
170 filtered_results.push(results[i]);
181 filtered_results.push(results[i]);
171 }
182 }
172 }
183 }
173
184
174 // append the introspection result, in order, at at the beginning of
185 // append the introspection result, in order, at at the beginning of
175 // the table and compute the replacement range from current cursor
186 // the table and compute the replacement range from current cursor
176 // positon and matched_text length.
187 // positon and matched_text length.
177 for (var i = matches.length - 1; i >= 0; --i) {
188 for (i = matches.length - 1; i >= 0; --i) {
178 filtered_results.unshift({
189 filtered_results.unshift({
179 str: matches[i],
190 str: matches[i],
180 type: "introspection",
191 type: "introspection",
181 from: {
192 from: utils.from_absolute_cursor_pos(this.editor, start),
182 line: cur.line,
193 to: utils.from_absolute_cursor_pos(this.editor, end)
183 ch: cur.ch - matched_text.length
184 },
185 to: {
186 line: cur.line,
187 ch: cur.ch
188 }
189 });
194 });
190 }
195 }
191
196
192 // one the 2 sources results have been merge, deal with it
197 // one the 2 sources results have been merge, deal with it
193 this.raw_result = filtered_results;
198 this.raw_result = filtered_results;
194
199
195 // if empty result return
200 // if empty result return
196 if (!this.raw_result || !this.raw_result.length) return;
201 if (!this.raw_result || !this.raw_result.length) return;
197
202
198 // When there is only one completion, use it directly.
203 // When there is only one completion, use it directly.
199 if (this.autopick && this.raw_result.length == 1) {
204 if (this.autopick && this.raw_result.length == 1) {
200 this.insert(this.raw_result[0]);
205 this.insert(this.raw_result[0]);
201 return;
206 return;
202 }
207 }
203
208
204 if (this.raw_result.length == 1) {
209 if (this.raw_result.length == 1) {
205 // test if first and only completion totally matches
210 // test if first and only completion totally matches
206 // what is typed, in this case dismiss
211 // what is typed, in this case dismiss
207 var str = this.raw_result[0].str;
212 var str = this.raw_result[0].str;
208 var pre_cursor = this.editor.getRange({
213 var pre_cursor = this.editor.getRange({
209 line: cur.line,
214 line: cur.line,
210 ch: cur.ch - str.length
215 ch: cur.ch - str.length
211 }, cur);
216 }, cur);
212 if (pre_cursor == str) {
217 if (pre_cursor == str) {
213 this.close();
218 this.close();
214 return;
219 return;
215 }
220 }
216 }
221 }
217
222
218 if (!this.visible) {
223 if (!this.visible) {
219 this.complete = $('<div/>').addClass('completions');
224 this.complete = $('<div/>').addClass('completions');
220 this.complete.attr('id', 'complete');
225 this.complete.attr('id', 'complete');
221
226
222 // Currently webkit doesn't use the size attr correctly. See:
227 // Currently webkit doesn't use the size attr correctly. See:
223 // https://code.google.com/p/chromium/issues/detail?id=4579
228 // https://code.google.com/p/chromium/issues/detail?id=4579
224 this.sel = $('<select/>')
229 this.sel = $('<select/>')
225 .attr('tabindex', -1)
230 .attr('tabindex', -1)
226 .attr('multiple', 'true');
231 .attr('multiple', 'true');
227 this.complete.append(this.sel);
232 this.complete.append(this.sel);
228 this.visible = true;
233 this.visible = true;
229 $('body').append(this.complete);
234 $('body').append(this.complete);
230
235
231 //build the container
236 //build the container
232 var that = this;
237 var that = this;
233 this.sel.dblclick(function () {
238 this.sel.dblclick(function () {
234 that.pick();
239 that.pick();
235 });
240 });
236 this.sel.focus(function () {
241 this.sel.focus(function () {
237 that.editor.focus();
242 that.editor.focus();
238 });
243 });
239 this._handle_keydown = function (cm, event) {
244 this._handle_keydown = function (cm, event) {
240 that.keydown(event);
245 that.keydown(event);
241 };
246 };
242 this.editor.on('keydown', this._handle_keydown);
247 this.editor.on('keydown', this._handle_keydown);
243 this._handle_keypress = function (cm, event) {
248 this._handle_keypress = function (cm, event) {
244 that.keypress(event);
249 that.keypress(event);
245 };
250 };
246 this.editor.on('keypress', this._handle_keypress);
251 this.editor.on('keypress', this._handle_keypress);
247 }
252 }
248 this.sel.attr('size', Math.min(10, this.raw_result.length));
253 this.sel.attr('size', Math.min(10, this.raw_result.length));
249
254
250 // After everything is on the page, compute the postion.
255 // After everything is on the page, compute the postion.
251 // We put it above the code if it is too close to the bottom of the page.
256 // We put it above the code if it is too close to the bottom of the page.
252 cur.ch = cur.ch-matched_text.length;
257 var pos = this.editor.cursorCoords(
253 var pos = this.editor.cursorCoords(cur);
258 utils.from_absolute_cursor_pos(this.editor, start)
259 );
254 var left = pos.left-3;
260 var left = pos.left-3;
255 var top;
261 var top;
256 var cheight = this.complete.height();
262 var cheight = this.complete.height();
257 var wheight = $(window).height();
263 var wheight = $(window).height();
258 if (pos.bottom+cheight+5 > wheight) {
264 if (pos.bottom+cheight+5 > wheight) {
259 top = pos.top-cheight-4;
265 top = pos.top-cheight-4;
260 } else {
266 } else {
261 top = pos.bottom+1;
267 top = pos.bottom+1;
262 }
268 }
263 this.complete.css('left', left + 'px');
269 this.complete.css('left', left + 'px');
264 this.complete.css('top', top + 'px');
270 this.complete.css('top', top + 'px');
265
271
266 // Clear and fill the list.
272 // Clear and fill the list.
267 this.sel.text('');
273 this.sel.text('');
268 this.build_gui_list(this.raw_result);
274 this.build_gui_list(this.raw_result);
269 return true;
275 return true;
270 };
276 };
271
277
272 Completer.prototype.insert = function (completion) {
278 Completer.prototype.insert = function (completion) {
273 this.editor.replaceRange(completion.str, completion.from, completion.to);
279 this.editor.replaceRange(completion.str, completion.from, completion.to);
274 };
280 };
275
281
276 Completer.prototype.build_gui_list = function (completions) {
282 Completer.prototype.build_gui_list = function (completions) {
277 for (var i = 0; i < completions.length; ++i) {
283 for (var i = 0; i < completions.length; ++i) {
278 var opt = $('<option/>').text(completions[i].str).addClass(completions[i].type);
284 var opt = $('<option/>').text(completions[i].str).addClass(completions[i].type);
279 this.sel.append(opt);
285 this.sel.append(opt);
280 }
286 }
281 this.sel.children().first().attr('selected', 'true');
287 this.sel.children().first().attr('selected', 'true');
282 this.sel.scrollTop(0);
288 this.sel.scrollTop(0);
283 };
289 };
284
290
285 Completer.prototype.close = function () {
291 Completer.prototype.close = function () {
286 this.done = true;
292 this.done = true;
287 $('#complete').remove();
293 $('#complete').remove();
288 this.editor.off('keydown', this._handle_keydown);
294 this.editor.off('keydown', this._handle_keydown);
289 this.editor.off('keypress', this._handle_keypress);
295 this.editor.off('keypress', this._handle_keypress);
290 this.visible = false;
296 this.visible = false;
291 };
297 };
292
298
293 Completer.prototype.pick = function () {
299 Completer.prototype.pick = function () {
294 this.insert(this.raw_result[this.sel[0].selectedIndex]);
300 this.insert(this.raw_result[this.sel[0].selectedIndex]);
295 this.close();
301 this.close();
296 };
302 };
297
303
298 Completer.prototype.keydown = function (event) {
304 Completer.prototype.keydown = function (event) {
299 var code = event.keyCode;
305 var code = event.keyCode;
300 var that = this;
306 var that = this;
301
307
302 // Enter
308 // Enter
303 if (code == keycodes.enter) {
309 if (code == keycodes.enter) {
304 CodeMirror.e_stop(event);
310 CodeMirror.e_stop(event);
305 this.pick();
311 this.pick();
306 // Escape or backspace
312 // Escape or backspace
307 } else if (code == keycodes.esc || code == keycodes.backspace) {
313 } else if (code == keycodes.esc || code == keycodes.backspace) {
308 CodeMirror.e_stop(event);
314 CodeMirror.e_stop(event);
309 this.close();
315 this.close();
310 } else if (code == keycodes.tab) {
316 } else if (code == keycodes.tab) {
311 //all the fastforwarding operation,
317 //all the fastforwarding operation,
312 //Check that shared start is not null which can append with prefixed completion
318 //Check that shared start is not null which can append with prefixed completion
313 // like %pylab , pylab have no shred start, and ff will result in py<tab><tab>
319 // like %pylab , pylab have no shred start, and ff will result in py<tab><tab>
314 // to erase py
320 // to erase py
315 var sh = shared_start(this.raw_result, true);
321 var sh = shared_start(this.raw_result, true);
316 if (sh) {
322 if (sh) {
317 this.insert(sh);
323 this.insert(sh);
318 }
324 }
319 this.close();
325 this.close();
320 //reinvoke self
326 //reinvoke self
321 setTimeout(function () {
327 setTimeout(function () {
322 that.carry_on_completion();
328 that.carry_on_completion();
323 }, 50);
329 }, 50);
324 } else if (code == keycodes.up || code == keycodes.down) {
330 } else if (code == keycodes.up || code == keycodes.down) {
325 // need to do that to be able to move the arrow
331 // need to do that to be able to move the arrow
326 // when on the first or last line ofo a code cell
332 // when on the first or last line ofo a code cell
327 CodeMirror.e_stop(event);
333 CodeMirror.e_stop(event);
328
334
329 var options = this.sel.find('option');
335 var options = this.sel.find('option');
330 var index = this.sel[0].selectedIndex;
336 var index = this.sel[0].selectedIndex;
331 if (code == keycodes.up) {
337 if (code == keycodes.up) {
332 index--;
338 index--;
333 }
339 }
334 if (code == keycodes.down) {
340 if (code == keycodes.down) {
335 index++;
341 index++;
336 }
342 }
337 index = Math.min(Math.max(index, 0), options.length-1);
343 index = Math.min(Math.max(index, 0), options.length-1);
338 this.sel[0].selectedIndex = index;
344 this.sel[0].selectedIndex = index;
339 } else if (code == keycodes.left || code == keycodes.right) {
345 } else if (code == keycodes.left || code == keycodes.right) {
340 this.close();
346 this.close();
341 }
347 }
342 };
348 };
343
349
344 Completer.prototype.keypress = function (event) {
350 Completer.prototype.keypress = function (event) {
345 // FIXME: This is a band-aid.
351 // FIXME: This is a band-aid.
346 // on keypress, trigger insertion of a single character.
352 // on keypress, trigger insertion of a single character.
347 // This simulates the old behavior of completion as you type,
353 // This simulates the old behavior of completion as you type,
348 // before events were disconnected and CodeMirror stopped
354 // before events were disconnected and CodeMirror stopped
349 // receiving events while the completer is focused.
355 // receiving events while the completer is focused.
350
356
351 var that = this;
357 var that = this;
352 var code = event.keyCode;
358 var code = event.keyCode;
353
359
354 // don't handle keypress if it's not a character (arrows on FF)
360 // don't handle keypress if it's not a character (arrows on FF)
355 // or ENTER/TAB
361 // or ENTER/TAB
356 if (event.charCode === 0 ||
362 if (event.charCode === 0 ||
357 code == keycodes.tab ||
363 code == keycodes.tab ||
358 code == keycodes.enter
364 code == keycodes.enter
359 ) return;
365 ) return;
360
366
361 this.close();
367 this.close();
362 this.editor.focus();
368 this.editor.focus();
363 setTimeout(function () {
369 setTimeout(function () {
364 that.carry_on_completion();
370 that.carry_on_completion();
365 }, 50);
371 }, 50);
366 };
372 };
367 IPython.Completer = Completer;
373 IPython.Completer = Completer;
368
374
369 return IPython;
375 return IPython;
370 }(IPython));
376 }(IPython));
@@ -1,963 +1,999
1 //----------------------------------------------------------------------------
1 // Copyright (c) IPython Development Team.
2 // Copyright (C) 2008 The IPython Development Team
2 // Distributed under the terms of the Modified BSD License.
3 //
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
7
3
8 //============================================================================
4 //============================================================================
9 // OutputArea
5 // OutputArea
10 //============================================================================
6 //============================================================================
11
7
12 /**
8 /**
13 * @module IPython
9 * @module IPython
14 * @namespace IPython
10 * @namespace IPython
15 * @submodule OutputArea
11 * @submodule OutputArea
16 */
12 */
17 var IPython = (function (IPython) {
13 var IPython = (function (IPython) {
18 "use strict";
14 "use strict";
19
15
20 var utils = IPython.utils;
16 var utils = IPython.utils;
21
17
22 /**
18 /**
23 * @class OutputArea
19 * @class OutputArea
24 *
20 *
25 * @constructor
21 * @constructor
26 */
22 */
27
23
28 var OutputArea = function (selector, prompt_area) {
24 var OutputArea = function (selector, prompt_area) {
29 this.selector = selector;
25 this.selector = selector;
30 this.wrapper = $(selector);
26 this.wrapper = $(selector);
31 this.outputs = [];
27 this.outputs = [];
32 this.collapsed = false;
28 this.collapsed = false;
33 this.scrolled = false;
29 this.scrolled = false;
34 this.trusted = true;
30 this.trusted = true;
35 this.clear_queued = null;
31 this.clear_queued = null;
36 if (prompt_area === undefined) {
32 if (prompt_area === undefined) {
37 this.prompt_area = true;
33 this.prompt_area = true;
38 } else {
34 } else {
39 this.prompt_area = prompt_area;
35 this.prompt_area = prompt_area;
40 }
36 }
41 this.create_elements();
37 this.create_elements();
42 this.style();
38 this.style();
43 this.bind_events();
39 this.bind_events();
44 };
40 };
45
41
46
42
47 /**
43 /**
48 * Class prototypes
44 * Class prototypes
49 **/
45 **/
50
46
51 OutputArea.prototype.create_elements = function () {
47 OutputArea.prototype.create_elements = function () {
52 this.element = $("<div/>");
48 this.element = $("<div/>");
53 this.collapse_button = $("<div/>");
49 this.collapse_button = $("<div/>");
54 this.prompt_overlay = $("<div/>");
50 this.prompt_overlay = $("<div/>");
55 this.wrapper.append(this.prompt_overlay);
51 this.wrapper.append(this.prompt_overlay);
56 this.wrapper.append(this.element);
52 this.wrapper.append(this.element);
57 this.wrapper.append(this.collapse_button);
53 this.wrapper.append(this.collapse_button);
58 };
54 };
59
55
60
56
61 OutputArea.prototype.style = function () {
57 OutputArea.prototype.style = function () {
62 this.collapse_button.hide();
58 this.collapse_button.hide();
63 this.prompt_overlay.hide();
59 this.prompt_overlay.hide();
64
60
65 this.wrapper.addClass('output_wrapper');
61 this.wrapper.addClass('output_wrapper');
66 this.element.addClass('output');
62 this.element.addClass('output');
67
63
68 this.collapse_button.addClass("btn output_collapsed");
64 this.collapse_button.addClass("btn output_collapsed");
69 this.collapse_button.attr('title', 'click to expand output');
65 this.collapse_button.attr('title', 'click to expand output');
70 this.collapse_button.text('. . .');
66 this.collapse_button.text('. . .');
71
67
72 this.prompt_overlay.addClass('out_prompt_overlay prompt');
68 this.prompt_overlay.addClass('out_prompt_overlay prompt');
73 this.prompt_overlay.attr('title', 'click to expand output; double click to hide output');
69 this.prompt_overlay.attr('title', 'click to expand output; double click to hide output');
74
70
75 this.collapse();
71 this.collapse();
76 };
72 };
77
73
78 /**
74 /**
79 * Should the OutputArea scroll?
75 * Should the OutputArea scroll?
80 * Returns whether the height (in lines) exceeds a threshold.
76 * Returns whether the height (in lines) exceeds a threshold.
81 *
77 *
82 * @private
78 * @private
83 * @method _should_scroll
79 * @method _should_scroll
84 * @param [lines=100]{Integer}
80 * @param [lines=100]{Integer}
85 * @return {Bool}
81 * @return {Bool}
86 *
82 *
87 */
83 */
88 OutputArea.prototype._should_scroll = function (lines) {
84 OutputArea.prototype._should_scroll = function (lines) {
89 if (lines <=0 ){ return }
85 if (lines <=0 ){ return }
90 if (!lines) {
86 if (!lines) {
91 lines = 100;
87 lines = 100;
92 }
88 }
93 // line-height from http://stackoverflow.com/questions/1185151
89 // line-height from http://stackoverflow.com/questions/1185151
94 var fontSize = this.element.css('font-size');
90 var fontSize = this.element.css('font-size');
95 var lineHeight = Math.floor(parseInt(fontSize.replace('px','')) * 1.5);
91 var lineHeight = Math.floor(parseInt(fontSize.replace('px','')) * 1.5);
96
92
97 return (this.element.height() > lines * lineHeight);
93 return (this.element.height() > lines * lineHeight);
98 };
94 };
99
95
100
96
101 OutputArea.prototype.bind_events = function () {
97 OutputArea.prototype.bind_events = function () {
102 var that = this;
98 var that = this;
103 this.prompt_overlay.dblclick(function () { that.toggle_output(); });
99 this.prompt_overlay.dblclick(function () { that.toggle_output(); });
104 this.prompt_overlay.click(function () { that.toggle_scroll(); });
100 this.prompt_overlay.click(function () { that.toggle_scroll(); });
105
101
106 this.element.resize(function () {
102 this.element.resize(function () {
107 // FIXME: Firefox on Linux misbehaves, so automatic scrolling is disabled
103 // FIXME: Firefox on Linux misbehaves, so automatic scrolling is disabled
108 if ( IPython.utils.browser[0] === "Firefox" ) {
104 if ( IPython.utils.browser[0] === "Firefox" ) {
109 return;
105 return;
110 }
106 }
111 // maybe scroll output,
107 // maybe scroll output,
112 // if it's grown large enough and hasn't already been scrolled.
108 // if it's grown large enough and hasn't already been scrolled.
113 if ( !that.scrolled && that._should_scroll(OutputArea.auto_scroll_threshold)) {
109 if ( !that.scrolled && that._should_scroll(OutputArea.auto_scroll_threshold)) {
114 that.scroll_area();
110 that.scroll_area();
115 }
111 }
116 });
112 });
117 this.collapse_button.click(function () {
113 this.collapse_button.click(function () {
118 that.expand();
114 that.expand();
119 });
115 });
120 };
116 };
121
117
122
118
123 OutputArea.prototype.collapse = function () {
119 OutputArea.prototype.collapse = function () {
124 if (!this.collapsed) {
120 if (!this.collapsed) {
125 this.element.hide();
121 this.element.hide();
126 this.prompt_overlay.hide();
122 this.prompt_overlay.hide();
127 if (this.element.html()){
123 if (this.element.html()){
128 this.collapse_button.show();
124 this.collapse_button.show();
129 }
125 }
130 this.collapsed = true;
126 this.collapsed = true;
131 }
127 }
132 };
128 };
133
129
134
130
135 OutputArea.prototype.expand = function () {
131 OutputArea.prototype.expand = function () {
136 if (this.collapsed) {
132 if (this.collapsed) {
137 this.collapse_button.hide();
133 this.collapse_button.hide();
138 this.element.show();
134 this.element.show();
139 this.prompt_overlay.show();
135 this.prompt_overlay.show();
140 this.collapsed = false;
136 this.collapsed = false;
141 }
137 }
142 };
138 };
143
139
144
140
145 OutputArea.prototype.toggle_output = function () {
141 OutputArea.prototype.toggle_output = function () {
146 if (this.collapsed) {
142 if (this.collapsed) {
147 this.expand();
143 this.expand();
148 } else {
144 } else {
149 this.collapse();
145 this.collapse();
150 }
146 }
151 };
147 };
152
148
153
149
154 OutputArea.prototype.scroll_area = function () {
150 OutputArea.prototype.scroll_area = function () {
155 this.element.addClass('output_scroll');
151 this.element.addClass('output_scroll');
156 this.prompt_overlay.attr('title', 'click to unscroll output; double click to hide');
152 this.prompt_overlay.attr('title', 'click to unscroll output; double click to hide');
157 this.scrolled = true;
153 this.scrolled = true;
158 };
154 };
159
155
160
156
161 OutputArea.prototype.unscroll_area = function () {
157 OutputArea.prototype.unscroll_area = function () {
162 this.element.removeClass('output_scroll');
158 this.element.removeClass('output_scroll');
163 this.prompt_overlay.attr('title', 'click to scroll output; double click to hide');
159 this.prompt_overlay.attr('title', 'click to scroll output; double click to hide');
164 this.scrolled = false;
160 this.scrolled = false;
165 };
161 };
166
162
167 /**
163 /**
168 *
164 *
169 * Scroll OutputArea if height supperior than a threshold (in lines).
165 * Scroll OutputArea if height supperior than a threshold (in lines).
170 *
166 *
171 * Threshold is a maximum number of lines. If unspecified, defaults to
167 * Threshold is a maximum number of lines. If unspecified, defaults to
172 * OutputArea.minimum_scroll_threshold.
168 * OutputArea.minimum_scroll_threshold.
173 *
169 *
174 * Negative threshold will prevent the OutputArea from ever scrolling.
170 * Negative threshold will prevent the OutputArea from ever scrolling.
175 *
171 *
176 * @method scroll_if_long
172 * @method scroll_if_long
177 *
173 *
178 * @param [lines=20]{Number} Default to 20 if not set,
174 * @param [lines=20]{Number} Default to 20 if not set,
179 * behavior undefined for value of `0`.
175 * behavior undefined for value of `0`.
180 *
176 *
181 **/
177 **/
182 OutputArea.prototype.scroll_if_long = function (lines) {
178 OutputArea.prototype.scroll_if_long = function (lines) {
183 var n = lines | OutputArea.minimum_scroll_threshold;
179 var n = lines | OutputArea.minimum_scroll_threshold;
184 if(n <= 0){
180 if(n <= 0){
185 return
181 return
186 }
182 }
187
183
188 if (this._should_scroll(n)) {
184 if (this._should_scroll(n)) {
189 // only allow scrolling long-enough output
185 // only allow scrolling long-enough output
190 this.scroll_area();
186 this.scroll_area();
191 }
187 }
192 };
188 };
193
189
194
190
195 OutputArea.prototype.toggle_scroll = function () {
191 OutputArea.prototype.toggle_scroll = function () {
196 if (this.scrolled) {
192 if (this.scrolled) {
197 this.unscroll_area();
193 this.unscroll_area();
198 } else {
194 } else {
199 // only allow scrolling long-enough output
195 // only allow scrolling long-enough output
200 this.scroll_if_long();
196 this.scroll_if_long();
201 }
197 }
202 };
198 };
203
199
204
200
205 // typeset with MathJax if MathJax is available
201 // typeset with MathJax if MathJax is available
206 OutputArea.prototype.typeset = function () {
202 OutputArea.prototype.typeset = function () {
207 if (window.MathJax){
203 if (window.MathJax){
208 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
204 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
209 }
205 }
210 };
206 };
211
207
212
208
213 OutputArea.prototype.handle_output = function (msg) {
209 OutputArea.prototype.handle_output = function (msg) {
214 var json = {};
210 var json = {};
215 var msg_type = json.output_type = msg.header.msg_type;
211 var msg_type = json.output_type = msg.header.msg_type;
216 var content = msg.content;
212 var content = msg.content;
217 if (msg_type === "stream") {
213 if (msg_type === "stream") {
218 json.text = content.data;
214 json.text = content.data;
219 json.stream = content.name;
215 json.stream = content.name;
220 } else if (msg_type === "display_data") {
216 } else if (msg_type === "display_data") {
221 json = content.data;
217 json = content.data;
222 json.output_type = msg_type;
218 json.output_type = msg_type;
223 json.metadata = content.metadata;
219 json.metadata = content.metadata;
224 } else if (msg_type === "pyout") {
220 } else if (msg_type === "execute_result") {
225 json = content.data;
221 json = content.data;
226 json.output_type = msg_type;
222 json.output_type = msg_type;
227 json.metadata = content.metadata;
223 json.metadata = content.metadata;
228 json.prompt_number = content.execution_count;
224 json.prompt_number = content.execution_count;
229 } else if (msg_type === "pyerr") {
225 } else if (msg_type === "error") {
230 json.ename = content.ename;
226 json.ename = content.ename;
231 json.evalue = content.evalue;
227 json.evalue = content.evalue;
232 json.traceback = content.traceback;
228 json.traceback = content.traceback;
229 } else {
230 console.log("unhandled output message", msg);
231 return;
233 }
232 }
234 this.append_output(json);
233 this.append_output(json);
235 };
234 };
236
235
237
236
238 OutputArea.prototype.rename_keys = function (data, key_map) {
237 OutputArea.prototype.rename_keys = function (data, key_map) {
239 var remapped = {};
238 var remapped = {};
240 for (var key in data) {
239 for (var key in data) {
241 var new_key = key_map[key] || key;
240 var new_key = key_map[key] || key;
242 remapped[new_key] = data[key];
241 remapped[new_key] = data[key];
243 }
242 }
244 return remapped;
243 return remapped;
245 };
244 };
246
245
247
246
248 OutputArea.output_types = [
247 OutputArea.output_types = [
249 'application/javascript',
248 'application/javascript',
250 'text/html',
249 'text/html',
251 'text/markdown',
250 'text/markdown',
252 'text/latex',
251 'text/latex',
253 'image/svg+xml',
252 'image/svg+xml',
254 'image/png',
253 'image/png',
255 'image/jpeg',
254 'image/jpeg',
256 'application/pdf',
255 'application/pdf',
257 'text/plain'
256 'text/plain'
258 ];
257 ];
259
258
260 OutputArea.prototype.validate_output = function (json) {
259 OutputArea.prototype.validate_output = function (json) {
261 // scrub invalid outputs
260 // scrub invalid outputs
262 // TODO: right now everything is a string, but JSON really shouldn't be.
261 // TODO: right now everything is a string, but JSON really shouldn't be.
263 // nbformat 4 will fix that.
262 // nbformat 4 will fix that.
264 $.map(OutputArea.output_types, function(key){
263 $.map(OutputArea.output_types, function(key){
265 if (json[key] !== undefined && typeof json[key] !== 'string') {
264 if (json[key] !== undefined && typeof json[key] !== 'string') {
266 console.log("Invalid type for " + key, json[key]);
265 console.log("Invalid type for " + key, json[key]);
267 delete json[key];
266 delete json[key];
268 }
267 }
269 });
268 });
270 return json;
269 return json;
271 };
270 };
272
271
273 OutputArea.prototype.append_output = function (json) {
272 OutputArea.prototype.append_output = function (json) {
274 this.expand();
273 this.expand();
275
274
276 // validate output data types
275 // validate output data types
277 json = this.validate_output(json);
276 json = this.validate_output(json);
278
277
279 // Clear the output if clear is queued.
278 // Clear the output if clear is queued.
280 var needs_height_reset = false;
279 var needs_height_reset = false;
281 if (this.clear_queued) {
280 if (this.clear_queued) {
282 this.clear_output(false);
281 this.clear_output(false);
283 needs_height_reset = true;
282 needs_height_reset = true;
284 }
283 }
285
284
286 if (json.output_type === 'pyout') {
285 if (json.output_type === 'execute_result') {
287 this.append_pyout(json);
286 this.append_execute_result(json);
288 } else if (json.output_type === 'pyerr') {
287 } else if (json.output_type === 'error') {
289 this.append_pyerr(json);
288 this.append_error(json);
290 } else if (json.output_type === 'stream') {
289 } else if (json.output_type === 'stream') {
291 this.append_stream(json);
290 this.append_stream(json);
292 }
291 }
293
292
294 // We must release the animation fixed height in a callback since Gecko
293 // We must release the animation fixed height in a callback since Gecko
295 // (FireFox) doesn't render the image immediately as the data is
294 // (FireFox) doesn't render the image immediately as the data is
296 // available.
295 // available.
297 var that = this;
296 var that = this;
298 var handle_appended = function ($el) {
297 var handle_appended = function ($el) {
299 // Only reset the height to automatic if the height is currently
298 // Only reset the height to automatic if the height is currently
300 // fixed (done by wait=True flag on clear_output).
299 // fixed (done by wait=True flag on clear_output).
301 if (needs_height_reset) {
300 if (needs_height_reset) {
302 that.element.height('');
301 that.element.height('');
303 }
302 }
304 that.element.trigger('resize');
303 that.element.trigger('resize');
305 };
304 };
306 if (json.output_type === 'display_data') {
305 if (json.output_type === 'display_data') {
307 this.append_display_data(json, handle_appended);
306 this.append_display_data(json, handle_appended);
308 } else {
307 } else {
309 handle_appended();
308 handle_appended();
310 }
309 }
311
310
312 this.outputs.push(json);
311 this.outputs.push(json);
313 };
312 };
314
313
315
314
316 OutputArea.prototype.create_output_area = function () {
315 OutputArea.prototype.create_output_area = function () {
317 var oa = $("<div/>").addClass("output_area");
316 var oa = $("<div/>").addClass("output_area");
318 if (this.prompt_area) {
317 if (this.prompt_area) {
319 oa.append($('<div/>').addClass('prompt'));
318 oa.append($('<div/>').addClass('prompt'));
320 }
319 }
321 return oa;
320 return oa;
322 };
321 };
323
322
324
323
325 function _get_metadata_key(metadata, key, mime) {
324 function _get_metadata_key(metadata, key, mime) {
326 var mime_md = metadata[mime];
325 var mime_md = metadata[mime];
327 // mime-specific higher priority
326 // mime-specific higher priority
328 if (mime_md && mime_md[key] !== undefined) {
327 if (mime_md && mime_md[key] !== undefined) {
329 return mime_md[key];
328 return mime_md[key];
330 }
329 }
331 // fallback on global
330 // fallback on global
332 return metadata[key];
331 return metadata[key];
333 }
332 }
334
333
335 OutputArea.prototype.create_output_subarea = function(md, classes, mime) {
334 OutputArea.prototype.create_output_subarea = function(md, classes, mime) {
336 var subarea = $('<div/>').addClass('output_subarea').addClass(classes);
335 var subarea = $('<div/>').addClass('output_subarea').addClass(classes);
337 if (_get_metadata_key(md, 'isolated', mime)) {
336 if (_get_metadata_key(md, 'isolated', mime)) {
338 // Create an iframe to isolate the subarea from the rest of the
337 // Create an iframe to isolate the subarea from the rest of the
339 // document
338 // document
340 var iframe = $('<iframe/>').addClass('box-flex1');
339 var iframe = $('<iframe/>').addClass('box-flex1');
341 iframe.css({'height':1, 'width':'100%', 'display':'block'});
340 iframe.css({'height':1, 'width':'100%', 'display':'block'});
342 iframe.attr('frameborder', 0);
341 iframe.attr('frameborder', 0);
343 iframe.attr('scrolling', 'auto');
342 iframe.attr('scrolling', 'auto');
344
343
345 // Once the iframe is loaded, the subarea is dynamically inserted
344 // Once the iframe is loaded, the subarea is dynamically inserted
346 iframe.on('load', function() {
345 iframe.on('load', function() {
347 // Workaround needed by Firefox, to properly render svg inside
346 // Workaround needed by Firefox, to properly render svg inside
348 // iframes, see http://stackoverflow.com/questions/10177190/
347 // iframes, see http://stackoverflow.com/questions/10177190/
349 // svg-dynamically-added-to-iframe-does-not-render-correctly
348 // svg-dynamically-added-to-iframe-does-not-render-correctly
350 this.contentDocument.open();
349 this.contentDocument.open();
351
350
352 // Insert the subarea into the iframe
351 // Insert the subarea into the iframe
353 // We must directly write the html. When using Jquery's append
352 // We must directly write the html. When using Jquery's append
354 // method, javascript is evaluated in the parent document and
353 // method, javascript is evaluated in the parent document and
355 // not in the iframe document. At this point, subarea doesn't
354 // not in the iframe document. At this point, subarea doesn't
356 // contain any user content.
355 // contain any user content.
357 this.contentDocument.write(subarea.html());
356 this.contentDocument.write(subarea.html());
358
357
359 this.contentDocument.close();
358 this.contentDocument.close();
360
359
361 var body = this.contentDocument.body;
360 var body = this.contentDocument.body;
362 // Adjust the iframe height automatically
361 // Adjust the iframe height automatically
363 iframe.height(body.scrollHeight + 'px');
362 iframe.height(body.scrollHeight + 'px');
364 });
363 });
365
364
366 // Elements should be appended to the inner subarea and not to the
365 // Elements should be appended to the inner subarea and not to the
367 // iframe
366 // iframe
368 iframe.append = function(that) {
367 iframe.append = function(that) {
369 subarea.append(that);
368 subarea.append(that);
370 };
369 };
371
370
372 return iframe;
371 return iframe;
373 } else {
372 } else {
374 return subarea;
373 return subarea;
375 }
374 }
376 }
375 }
377
376
378
377
379 OutputArea.prototype._append_javascript_error = function (err, element) {
378 OutputArea.prototype._append_javascript_error = function (err, element) {
380 // display a message when a javascript error occurs in display output
379 // display a message when a javascript error occurs in display output
381 var msg = "Javascript error adding output!"
380 var msg = "Javascript error adding output!"
382 if ( element === undefined ) return;
381 if ( element === undefined ) return;
383 element
382 element
384 .append($('<div/>').text(msg).addClass('js-error'))
383 .append($('<div/>').text(msg).addClass('js-error'))
385 .append($('<div/>').text(err.toString()).addClass('js-error'))
384 .append($('<div/>').text(err.toString()).addClass('js-error'))
386 .append($('<div/>').text('See your browser Javascript console for more details.').addClass('js-error'));
385 .append($('<div/>').text('See your browser Javascript console for more details.').addClass('js-error'));
387 };
386 };
388
387
389 OutputArea.prototype._safe_append = function (toinsert) {
388 OutputArea.prototype._safe_append = function (toinsert) {
390 // safely append an item to the document
389 // safely append an item to the document
391 // this is an object created by user code,
390 // this is an object created by user code,
392 // and may have errors, which should not be raised
391 // and may have errors, which should not be raised
393 // under any circumstances.
392 // under any circumstances.
394 try {
393 try {
395 this.element.append(toinsert);
394 this.element.append(toinsert);
396 } catch(err) {
395 } catch(err) {
397 console.log(err);
396 console.log(err);
398 // Create an actual output_area and output_subarea, which creates
397 // Create an actual output_area and output_subarea, which creates
399 // the prompt area and the proper indentation.
398 // the prompt area and the proper indentation.
400 var toinsert = this.create_output_area();
399 var toinsert = this.create_output_area();
401 var subarea = $('<div/>').addClass('output_subarea');
400 var subarea = $('<div/>').addClass('output_subarea');
402 toinsert.append(subarea);
401 toinsert.append(subarea);
403 this._append_javascript_error(err, subarea);
402 this._append_javascript_error(err, subarea);
404 this.element.append(toinsert);
403 this.element.append(toinsert);
405 }
404 }
406 };
405 };
407
406
408
407
409 OutputArea.prototype.append_pyout = function (json) {
408 OutputArea.prototype.append_execute_result = function (json) {
410 var n = json.prompt_number || ' ';
409 var n = json.prompt_number || ' ';
411 var toinsert = this.create_output_area();
410 var toinsert = this.create_output_area();
412 if (this.prompt_area) {
411 if (this.prompt_area) {
413 toinsert.find('div.prompt').addClass('output_prompt').text('Out[' + n + ']:');
412 toinsert.find('div.prompt').addClass('output_prompt').text('Out[' + n + ']:');
414 }
413 }
415 var inserted = this.append_mime_type(json, toinsert);
414 var inserted = this.append_mime_type(json, toinsert);
416 if (inserted) {
415 if (inserted) {
417 inserted.addClass('output_pyout');
416 inserted.addClass('output_result');
418 }
417 }
419 this._safe_append(toinsert);
418 this._safe_append(toinsert);
420 // If we just output latex, typeset it.
419 // If we just output latex, typeset it.
421 if ((json['text/latex'] !== undefined) ||
420 if ((json['text/latex'] !== undefined) ||
422 (json['text/html'] !== undefined) ||
421 (json['text/html'] !== undefined) ||
423 (json['text/markdown'] !== undefined)) {
422 (json['text/markdown'] !== undefined)) {
424 this.typeset();
423 this.typeset();
425 }
424 }
426 };
425 };
427
426
428
427
429 OutputArea.prototype.append_pyerr = function (json) {
428 OutputArea.prototype.append_error = function (json) {
430 var tb = json.traceback;
429 var tb = json.traceback;
431 if (tb !== undefined && tb.length > 0) {
430 if (tb !== undefined && tb.length > 0) {
432 var s = '';
431 var s = '';
433 var len = tb.length;
432 var len = tb.length;
434 for (var i=0; i<len; i++) {
433 for (var i=0; i<len; i++) {
435 s = s + tb[i] + '\n';
434 s = s + tb[i] + '\n';
436 }
435 }
437 s = s + '\n';
436 s = s + '\n';
438 var toinsert = this.create_output_area();
437 var toinsert = this.create_output_area();
439 var append_text = OutputArea.append_map['text/plain'];
438 var append_text = OutputArea.append_map['text/plain'];
440 if (append_text) {
439 if (append_text) {
441 append_text.apply(this, [s, {}, toinsert]).addClass('output_pyerr');
440 append_text.apply(this, [s, {}, toinsert]).addClass('output_error');
442 }
441 }
443 this._safe_append(toinsert);
442 this._safe_append(toinsert);
444 }
443 }
445 };
444 };
446
445
447
446
448 OutputArea.prototype.append_stream = function (json) {
447 OutputArea.prototype.append_stream = function (json) {
449 // temporary fix: if stream undefined (json file written prior to this patch),
448 // temporary fix: if stream undefined (json file written prior to this patch),
450 // default to most likely stdout:
449 // default to most likely stdout:
451 if (json.stream === undefined){
450 if (json.stream === undefined){
452 json.stream = 'stdout';
451 json.stream = 'stdout';
453 }
452 }
454 var text = json.text;
453 var text = json.text;
455 var subclass = "output_"+json.stream;
454 var subclass = "output_"+json.stream;
456 if (this.outputs.length > 0){
455 if (this.outputs.length > 0){
457 // have at least one output to consider
456 // have at least one output to consider
458 var last = this.outputs[this.outputs.length-1];
457 var last = this.outputs[this.outputs.length-1];
459 if (last.output_type == 'stream' && json.stream == last.stream){
458 if (last.output_type == 'stream' && json.stream == last.stream){
460 // latest output was in the same stream,
459 // latest output was in the same stream,
461 // so append directly into its pre tag
460 // so append directly into its pre tag
462 // escape ANSI & HTML specials:
461 // escape ANSI & HTML specials:
463 var pre = this.element.find('div.'+subclass).last().find('pre');
462 var pre = this.element.find('div.'+subclass).last().find('pre');
464 var html = utils.fixCarriageReturn(
463 var html = utils.fixCarriageReturn(
465 pre.html() + utils.fixConsole(text));
464 pre.html() + utils.fixConsole(text));
466 // The only user content injected with this HTML call is
465 // The only user content injected with this HTML call is
467 // escaped by the fixConsole() method.
466 // escaped by the fixConsole() method.
468 pre.html(html);
467 pre.html(html);
469 return;
468 return;
470 }
469 }
471 }
470 }
472
471
473 if (!text.replace("\r", "")) {
472 if (!text.replace("\r", "")) {
474 // text is nothing (empty string, \r, etc.)
473 // text is nothing (empty string, \r, etc.)
475 // so don't append any elements, which might add undesirable space
474 // so don't append any elements, which might add undesirable space
476 return;
475 return;
477 }
476 }
478
477
479 // If we got here, attach a new div
478 // If we got here, attach a new div
480 var toinsert = this.create_output_area();
479 var toinsert = this.create_output_area();
481 var append_text = OutputArea.append_map['text/plain'];
480 var append_text = OutputArea.append_map['text/plain'];
482 if (append_text) {
481 if (append_text) {
483 append_text.apply(this, [text, {}, toinsert]).addClass("output_stream " + subclass);
482 append_text.apply(this, [text, {}, toinsert]).addClass("output_stream " + subclass);
484 }
483 }
485 this._safe_append(toinsert);
484 this._safe_append(toinsert);
486 };
485 };
487
486
488
487
489 OutputArea.prototype.append_display_data = function (json, handle_inserted) {
488 OutputArea.prototype.append_display_data = function (json, handle_inserted) {
490 var toinsert = this.create_output_area();
489 var toinsert = this.create_output_area();
491 if (this.append_mime_type(json, toinsert, handle_inserted)) {
490 if (this.append_mime_type(json, toinsert, handle_inserted)) {
492 this._safe_append(toinsert);
491 this._safe_append(toinsert);
493 // If we just output latex, typeset it.
492 // If we just output latex, typeset it.
494 if ((json['text/latex'] !== undefined) ||
493 if ((json['text/latex'] !== undefined) ||
495 (json['text/html'] !== undefined) ||
494 (json['text/html'] !== undefined) ||
496 (json['text/markdown'] !== undefined)) {
495 (json['text/markdown'] !== undefined)) {
497 this.typeset();
496 this.typeset();
498 }
497 }
499 }
498 }
500 };
499 };
501
500
502
501
503 OutputArea.safe_outputs = {
502 OutputArea.safe_outputs = {
504 'text/plain' : true,
503 'text/plain' : true,
505 'text/latex' : true,
504 'text/latex' : true,
506 'image/png' : true,
505 'image/png' : true,
507 'image/jpeg' : true
506 'image/jpeg' : true
508 };
507 };
509
508
510 OutputArea.prototype.append_mime_type = function (json, element, handle_inserted) {
509 OutputArea.prototype.append_mime_type = function (json, element, handle_inserted) {
511 for (var i=0; i < OutputArea.display_order.length; i++) {
510 for (var i=0; i < OutputArea.display_order.length; i++) {
512 var type = OutputArea.display_order[i];
511 var type = OutputArea.display_order[i];
513 var append = OutputArea.append_map[type];
512 var append = OutputArea.append_map[type];
514 if ((json[type] !== undefined) && append) {
513 if ((json[type] !== undefined) && append) {
515 var value = json[type];
514 var value = json[type];
516 if (!this.trusted && !OutputArea.safe_outputs[type]) {
515 if (!this.trusted && !OutputArea.safe_outputs[type]) {
517 // not trusted, sanitize HTML
516 // not trusted, sanitize HTML
518 if (type==='text/html' || type==='text/svg') {
517 if (type==='text/html' || type==='text/svg') {
519 value = IPython.security.sanitize_html(value);
518 value = IPython.security.sanitize_html(value);
520 } else {
519 } else {
521 // don't display if we don't know how to sanitize it
520 // don't display if we don't know how to sanitize it
522 console.log("Ignoring untrusted " + type + " output.");
521 console.log("Ignoring untrusted " + type + " output.");
523 continue;
522 continue;
524 }
523 }
525 }
524 }
526 var md = json.metadata || {};
525 var md = json.metadata || {};
527 var toinsert = append.apply(this, [value, md, element, handle_inserted]);
526 var toinsert = append.apply(this, [value, md, element, handle_inserted]);
528 // Since only the png and jpeg mime types call the inserted
527 // Since only the png and jpeg mime types call the inserted
529 // callback, if the mime type is something other we must call the
528 // callback, if the mime type is something other we must call the
530 // inserted callback only when the element is actually inserted
529 // inserted callback only when the element is actually inserted
531 // into the DOM. Use a timeout of 0 to do this.
530 // into the DOM. Use a timeout of 0 to do this.
532 if (['image/png', 'image/jpeg'].indexOf(type) < 0 && handle_inserted !== undefined) {
531 if (['image/png', 'image/jpeg'].indexOf(type) < 0 && handle_inserted !== undefined) {
533 setTimeout(handle_inserted, 0);
532 setTimeout(handle_inserted, 0);
534 }
533 }
535 $([IPython.events]).trigger('output_appended.OutputArea', [type, value, md, toinsert]);
534 $([IPython.events]).trigger('output_appended.OutputArea', [type, value, md, toinsert]);
536 return toinsert;
535 return toinsert;
537 }
536 }
538 }
537 }
539 return null;
538 return null;
540 };
539 };
541
540
542
541
543 var append_html = function (html, md, element) {
542 var append_html = function (html, md, element) {
544 var type = 'text/html';
543 var type = 'text/html';
545 var toinsert = this.create_output_subarea(md, "output_html rendered_html", type);
544 var toinsert = this.create_output_subarea(md, "output_html rendered_html", type);
546 IPython.keyboard_manager.register_events(toinsert);
545 IPython.keyboard_manager.register_events(toinsert);
547 toinsert.append(html);
546 toinsert.append(html);
548 element.append(toinsert);
547 element.append(toinsert);
549 return toinsert;
548 return toinsert;
550 };
549 };
551
550
552
551
553 var append_markdown = function(markdown, md, element) {
552 var append_markdown = function(markdown, md, element) {
554 var type = 'text/markdown';
553 var type = 'text/markdown';
555 var toinsert = this.create_output_subarea(md, "output_markdown", type);
554 var toinsert = this.create_output_subarea(md, "output_markdown", type);
556 var text_and_math = IPython.mathjaxutils.remove_math(markdown);
555 var text_and_math = IPython.mathjaxutils.remove_math(markdown);
557 var text = text_and_math[0];
556 var text = text_and_math[0];
558 var math = text_and_math[1];
557 var math = text_and_math[1];
559 var html = marked.parser(marked.lexer(text));
558 var html = marked.parser(marked.lexer(text));
560 html = IPython.mathjaxutils.replace_math(html, math);
559 html = IPython.mathjaxutils.replace_math(html, math);
561 toinsert.append(html);
560 toinsert.append(html);
562 element.append(toinsert);
561 element.append(toinsert);
563 return toinsert;
562 return toinsert;
564 };
563 };
565
564
566
565
567 var append_javascript = function (js, md, element) {
566 var append_javascript = function (js, md, element) {
568 // We just eval the JS code, element appears in the local scope.
567 // We just eval the JS code, element appears in the local scope.
569 var type = 'application/javascript';
568 var type = 'application/javascript';
570 var toinsert = this.create_output_subarea(md, "output_javascript", type);
569 var toinsert = this.create_output_subarea(md, "output_javascript", type);
571 IPython.keyboard_manager.register_events(toinsert);
570 IPython.keyboard_manager.register_events(toinsert);
572 element.append(toinsert);
571 element.append(toinsert);
573 // FIXME TODO : remove `container element for 3.0`
572 // FIXME TODO : remove `container element for 3.0`
574 //backward compat, js should be eval'ed in a context where `container` is defined.
573 //backward compat, js should be eval'ed in a context where `container` is defined.
575 var container = element;
574 var container = element;
576 container.show = function(){console.log('Warning "container.show()" is deprecated.')};
575 container.show = function(){console.log('Warning "container.show()" is deprecated.')};
577 // end backward compat
576 // end backward compat
578
577
579 // Fix for ipython/issues/5293, make sure `element` is the area which
578 // Fix for ipython/issues/5293, make sure `element` is the area which
580 // output can be inserted into at the time of JS execution.
579 // output can be inserted into at the time of JS execution.
581 element = toinsert;
580 element = toinsert;
582 try {
581 try {
583 eval(js);
582 eval(js);
584 } catch(err) {
583 } catch(err) {
585 console.log(err);
584 console.log(err);
586 this._append_javascript_error(err, toinsert);
585 this._append_javascript_error(err, toinsert);
587 }
586 }
588 return toinsert;
587 return toinsert;
589 };
588 };
590
589
591
590
592 var append_text = function (data, md, element) {
591 var append_text = function (data, md, element) {
593 var type = 'text/plain';
592 var type = 'text/plain';
594 var toinsert = this.create_output_subarea(md, "output_text", type);
593 var toinsert = this.create_output_subarea(md, "output_text", type);
595 // escape ANSI & HTML specials in plaintext:
594 // escape ANSI & HTML specials in plaintext:
596 data = utils.fixConsole(data);
595 data = utils.fixConsole(data);
597 data = utils.fixCarriageReturn(data);
596 data = utils.fixCarriageReturn(data);
598 data = utils.autoLinkUrls(data);
597 data = utils.autoLinkUrls(data);
599 // The only user content injected with this HTML call is
598 // The only user content injected with this HTML call is
600 // escaped by the fixConsole() method.
599 // escaped by the fixConsole() method.
601 toinsert.append($("<pre/>").html(data));
600 toinsert.append($("<pre/>").html(data));
602 element.append(toinsert);
601 element.append(toinsert);
603 return toinsert;
602 return toinsert;
604 };
603 };
605
604
606
605
607 var append_svg = function (svg_html, md, element) {
606 var append_svg = function (svg_html, md, element) {
608 var type = 'image/svg+xml';
607 var type = 'image/svg+xml';
609 var toinsert = this.create_output_subarea(md, "output_svg", type);
608 var toinsert = this.create_output_subarea(md, "output_svg", type);
610
609
611 // Get the svg element from within the HTML.
610 // Get the svg element from within the HTML.
612 var svg = $('<div />').html(svg_html).find('svg');
611 var svg = $('<div />').html(svg_html).find('svg');
613 var svg_area = $('<div />');
612 var svg_area = $('<div />');
614 var width = svg.attr('width');
613 var width = svg.attr('width');
615 var height = svg.attr('height');
614 var height = svg.attr('height');
616 svg
615 svg
617 .width('100%')
616 .width('100%')
618 .height('100%');
617 .height('100%');
619 svg_area
618 svg_area
620 .width(width)
619 .width(width)
621 .height(height);
620 .height(height);
622
621
623 // The jQuery resize handlers don't seem to work on the svg element.
622 // The jQuery resize handlers don't seem to work on the svg element.
624 // When the svg renders completely, measure it's size and set the parent
623 // When the svg renders completely, measure it's size and set the parent
625 // div to that size. Then set the svg to 100% the size of the parent
624 // div to that size. Then set the svg to 100% the size of the parent
626 // div and make the parent div resizable.
625 // div and make the parent div resizable.
627 this._dblclick_to_reset_size(svg_area, true, false);
626 this._dblclick_to_reset_size(svg_area, true, false);
628
627
629 svg_area.append(svg);
628 svg_area.append(svg);
630 toinsert.append(svg_area);
629 toinsert.append(svg_area);
631 element.append(toinsert);
630 element.append(toinsert);
632
631
633 return toinsert;
632 return toinsert;
634 };
633 };
635
634
636 OutputArea.prototype._dblclick_to_reset_size = function (img, immediately, resize_parent) {
635 OutputArea.prototype._dblclick_to_reset_size = function (img, immediately, resize_parent) {
637 // Add a resize handler to an element
636 // Add a resize handler to an element
638 //
637 //
639 // img: jQuery element
638 // img: jQuery element
640 // immediately: bool=False
639 // immediately: bool=False
641 // Wait for the element to load before creating the handle.
640 // Wait for the element to load before creating the handle.
642 // resize_parent: bool=True
641 // resize_parent: bool=True
643 // Should the parent of the element be resized when the element is
642 // Should the parent of the element be resized when the element is
644 // reset (by double click).
643 // reset (by double click).
645 var callback = function (){
644 var callback = function (){
646 var h0 = img.height();
645 var h0 = img.height();
647 var w0 = img.width();
646 var w0 = img.width();
648 if (!(h0 && w0)) {
647 if (!(h0 && w0)) {
649 // zero size, don't make it resizable
648 // zero size, don't make it resizable
650 return;
649 return;
651 }
650 }
652 img.resizable({
651 img.resizable({
653 aspectRatio: true,
652 aspectRatio: true,
654 autoHide: true
653 autoHide: true
655 });
654 });
656 img.dblclick(function () {
655 img.dblclick(function () {
657 // resize wrapper & image together for some reason:
656 // resize wrapper & image together for some reason:
658 img.height(h0);
657 img.height(h0);
659 img.width(w0);
658 img.width(w0);
660 if (resize_parent === undefined || resize_parent) {
659 if (resize_parent === undefined || resize_parent) {
661 img.parent().height(h0);
660 img.parent().height(h0);
662 img.parent().width(w0);
661 img.parent().width(w0);
663 }
662 }
664 });
663 });
665 };
664 };
666
665
667 if (immediately) {
666 if (immediately) {
668 callback();
667 callback();
669 } else {
668 } else {
670 img.on("load", callback);
669 img.on("load", callback);
671 }
670 }
672 };
671 };
673
672
674 var set_width_height = function (img, md, mime) {
673 var set_width_height = function (img, md, mime) {
675 // set width and height of an img element from metadata
674 // set width and height of an img element from metadata
676 var height = _get_metadata_key(md, 'height', mime);
675 var height = _get_metadata_key(md, 'height', mime);
677 if (height !== undefined) img.attr('height', height);
676 if (height !== undefined) img.attr('height', height);
678 var width = _get_metadata_key(md, 'width', mime);
677 var width = _get_metadata_key(md, 'width', mime);
679 if (width !== undefined) img.attr('width', width);
678 if (width !== undefined) img.attr('width', width);
680 };
679 };
681
680
682 var append_png = function (png, md, element, handle_inserted) {
681 var append_png = function (png, md, element, handle_inserted) {
683 var type = 'image/png';
682 var type = 'image/png';
684 var toinsert = this.create_output_subarea(md, "output_png", type);
683 var toinsert = this.create_output_subarea(md, "output_png", type);
685 var img = $("<img/>");
684 var img = $("<img/>");
686 if (handle_inserted !== undefined) {
685 if (handle_inserted !== undefined) {
687 img.on('load', function(){
686 img.on('load', function(){
688 handle_inserted(img);
687 handle_inserted(img);
689 });
688 });
690 }
689 }
691 img[0].src = 'data:image/png;base64,'+ png;
690 img[0].src = 'data:image/png;base64,'+ png;
692 set_width_height(img, md, 'image/png');
691 set_width_height(img, md, 'image/png');
693 this._dblclick_to_reset_size(img);
692 this._dblclick_to_reset_size(img);
694 toinsert.append(img);
693 toinsert.append(img);
695 element.append(toinsert);
694 element.append(toinsert);
696 return toinsert;
695 return toinsert;
697 };
696 };
698
697
699
698
700 var append_jpeg = function (jpeg, md, element, handle_inserted) {
699 var append_jpeg = function (jpeg, md, element, handle_inserted) {
701 var type = 'image/jpeg';
700 var type = 'image/jpeg';
702 var toinsert = this.create_output_subarea(md, "output_jpeg", type);
701 var toinsert = this.create_output_subarea(md, "output_jpeg", type);
703 var img = $("<img/>");
702 var img = $("<img/>");
704 if (handle_inserted !== undefined) {
703 if (handle_inserted !== undefined) {
705 img.on('load', function(){
704 img.on('load', function(){
706 handle_inserted(img);
705 handle_inserted(img);
707 });
706 });
708 }
707 }
709 img[0].src = 'data:image/jpeg;base64,'+ jpeg;
708 img[0].src = 'data:image/jpeg;base64,'+ jpeg;
710 set_width_height(img, md, 'image/jpeg');
709 set_width_height(img, md, 'image/jpeg');
711 this._dblclick_to_reset_size(img);
710 this._dblclick_to_reset_size(img);
712 toinsert.append(img);
711 toinsert.append(img);
713 element.append(toinsert);
712 element.append(toinsert);
714 return toinsert;
713 return toinsert;
715 };
714 };
716
715
717
716
718 var append_pdf = function (pdf, md, element) {
717 var append_pdf = function (pdf, md, element) {
719 var type = 'application/pdf';
718 var type = 'application/pdf';
720 var toinsert = this.create_output_subarea(md, "output_pdf", type);
719 var toinsert = this.create_output_subarea(md, "output_pdf", type);
721 var a = $('<a/>').attr('href', 'data:application/pdf;base64,'+pdf);
720 var a = $('<a/>').attr('href', 'data:application/pdf;base64,'+pdf);
722 a.attr('target', '_blank');
721 a.attr('target', '_blank');
723 a.text('View PDF')
722 a.text('View PDF')
724 toinsert.append(a);
723 toinsert.append(a);
725 element.append(toinsert);
724 element.append(toinsert);
726 return toinsert;
725 return toinsert;
727 }
726 }
728
727
729 var append_latex = function (latex, md, element) {
728 var append_latex = function (latex, md, element) {
730 // This method cannot do the typesetting because the latex first has to
729 // This method cannot do the typesetting because the latex first has to
731 // be on the page.
730 // be on the page.
732 var type = 'text/latex';
731 var type = 'text/latex';
733 var toinsert = this.create_output_subarea(md, "output_latex", type);
732 var toinsert = this.create_output_subarea(md, "output_latex", type);
734 toinsert.append(latex);
733 toinsert.append(latex);
735 element.append(toinsert);
734 element.append(toinsert);
736 return toinsert;
735 return toinsert;
737 };
736 };
738
737
739
738
740 OutputArea.prototype.append_raw_input = function (msg) {
739 OutputArea.prototype.append_raw_input = function (msg) {
741 var that = this;
740 var that = this;
742 this.expand();
741 this.expand();
743 var content = msg.content;
742 var content = msg.content;
744 var area = this.create_output_area();
743 var area = this.create_output_area();
745
744
746 // disable any other raw_inputs, if they are left around
745 // disable any other raw_inputs, if they are left around
747 $("div.output_subarea.raw_input_container").remove();
746 $("div.output_subarea.raw_input_container").remove();
748
747
748 var input_type = content.password ? 'password' : 'text';
749
749 area.append(
750 area.append(
750 $("<div/>")
751 $("<div/>")
751 .addClass("box-flex1 output_subarea raw_input_container")
752 .addClass("box-flex1 output_subarea raw_input_container")
752 .append(
753 .append(
753 $("<span/>")
754 $("<span/>")
754 .addClass("raw_input_prompt")
755 .addClass("raw_input_prompt")
755 .text(content.prompt)
756 .text(content.prompt)
756 )
757 )
757 .append(
758 .append(
758 $("<input/>")
759 $("<input/>")
759 .addClass("raw_input")
760 .addClass("raw_input")
760 .attr('type', 'text')
761 .attr('type', input_type)
761 .attr("size", 47)
762 .attr("size", 47)
762 .keydown(function (event, ui) {
763 .keydown(function (event, ui) {
763 // make sure we submit on enter,
764 // make sure we submit on enter,
764 // and don't re-execute the *cell* on shift-enter
765 // and don't re-execute the *cell* on shift-enter
765 if (event.which === IPython.keyboard.keycodes.enter) {
766 if (event.which === IPython.keyboard.keycodes.enter) {
766 that._submit_raw_input();
767 that._submit_raw_input();
767 return false;
768 return false;
768 }
769 }
769 })
770 })
770 )
771 )
771 );
772 );
772
773
773 this.element.append(area);
774 this.element.append(area);
774 var raw_input = area.find('input.raw_input');
775 var raw_input = area.find('input.raw_input');
775 // Register events that enable/disable the keyboard manager while raw
776 // Register events that enable/disable the keyboard manager while raw
776 // input is focused.
777 // input is focused.
777 IPython.keyboard_manager.register_events(raw_input);
778 IPython.keyboard_manager.register_events(raw_input);
778 // Note, the following line used to read raw_input.focus().focus().
779 // Note, the following line used to read raw_input.focus().focus().
779 // This seemed to be needed otherwise only the cell would be focused.
780 // This seemed to be needed otherwise only the cell would be focused.
780 // But with the modal UI, this seems to work fine with one call to focus().
781 // But with the modal UI, this seems to work fine with one call to focus().
781 raw_input.focus();
782 raw_input.focus();
782 }
783 }
783
784
784 OutputArea.prototype._submit_raw_input = function (evt) {
785 OutputArea.prototype._submit_raw_input = function (evt) {
785 var container = this.element.find("div.raw_input_container");
786 var container = this.element.find("div.raw_input_container");
786 var theprompt = container.find("span.raw_input_prompt");
787 var theprompt = container.find("span.raw_input_prompt");
787 var theinput = container.find("input.raw_input");
788 var theinput = container.find("input.raw_input");
788 var value = theinput.val();
789 var value = theinput.val();
790 var echo = value;
791 // don't echo if it's a password
792 if (theinput.attr('type') == 'password') {
793 echo = '········';
794 }
789 var content = {
795 var content = {
790 output_type : 'stream',
796 output_type : 'stream',
791 name : 'stdout',
797 name : 'stdout',
792 text : theprompt.text() + value + '\n'
798 text : theprompt.text() + echo + '\n'
793 }
799 }
794 // remove form container
800 // remove form container
795 container.parent().remove();
801 container.parent().remove();
796 // replace with plaintext version in stdout
802 // replace with plaintext version in stdout
797 this.append_output(content, false);
803 this.append_output(content, false);
798 $([IPython.events]).trigger('send_input_reply.Kernel', value);
804 $([IPython.events]).trigger('send_input_reply.Kernel', value);
799 }
805 }
800
806
801
807
802 OutputArea.prototype.handle_clear_output = function (msg) {
808 OutputArea.prototype.handle_clear_output = function (msg) {
803 // msg spec v4 had stdout, stderr, display keys
809 // msg spec v4 had stdout, stderr, display keys
804 // v4.1 replaced these with just wait
810 // v4.1 replaced these with just wait
805 // The default behavior is the same (stdout=stderr=display=True, wait=False),
811 // The default behavior is the same (stdout=stderr=display=True, wait=False),
806 // so v4 messages will still be properly handled,
812 // so v4 messages will still be properly handled,
807 // except for the rarely used clearing less than all output.
813 // except for the rarely used clearing less than all output.
808 this.clear_output(msg.content.wait || false);
814 this.clear_output(msg.content.wait || false);
809 };
815 };
810
816
811
817
812 OutputArea.prototype.clear_output = function(wait) {
818 OutputArea.prototype.clear_output = function(wait) {
813 if (wait) {
819 if (wait) {
814
820
815 // If a clear is queued, clear before adding another to the queue.
821 // If a clear is queued, clear before adding another to the queue.
816 if (this.clear_queued) {
822 if (this.clear_queued) {
817 this.clear_output(false);
823 this.clear_output(false);
818 };
824 };
819
825
820 this.clear_queued = true;
826 this.clear_queued = true;
821 } else {
827 } else {
822
828
823 // Fix the output div's height if the clear_output is waiting for
829 // Fix the output div's height if the clear_output is waiting for
824 // new output (it is being used in an animation).
830 // new output (it is being used in an animation).
825 if (this.clear_queued) {
831 if (this.clear_queued) {
826 var height = this.element.height();
832 var height = this.element.height();
827 this.element.height(height);
833 this.element.height(height);
828 this.clear_queued = false;
834 this.clear_queued = false;
829 }
835 }
830
836
831 // Clear all
837 // Clear all
832 // Remove load event handlers from img tags because we don't want
838 // Remove load event handlers from img tags because we don't want
833 // them to fire if the image is never added to the page.
839 // them to fire if the image is never added to the page.
834 this.element.find('img').off('load');
840 this.element.find('img').off('load');
835 this.element.html("");
841 this.element.html("");
836 this.outputs = [];
842 this.outputs = [];
837 this.trusted = true;
843 this.trusted = true;
838 this.unscroll_area();
844 this.unscroll_area();
839 return;
845 return;
840 };
846 };
841 };
847 };
842
848
843
849
844 // JSON serialization
850 // JSON serialization
845
851
846 OutputArea.prototype.fromJSON = function (outputs) {
852 OutputArea.prototype.fromJSON = function (outputs) {
847 var len = outputs.length;
853 var len = outputs.length;
848 var data;
854 var data;
849
855
850 for (var i=0; i<len; i++) {
856 for (var i=0; i<len; i++) {
851 data = outputs[i];
857 data = outputs[i];
852 var msg_type = data.output_type;
858 var msg_type = data.output_type;
853 if (msg_type === "display_data" || msg_type === "pyout") {
859 if (msg_type == "pyout") {
860 // pyout message has been renamed to execute_result,
861 // but the nbformat has not been updated,
862 // so transform back to pyout for json.
863 msg_type = data.output_type = "execute_result";
864 } else if (msg_type == "pyerr") {
865 // pyerr message has been renamed to error,
866 // but the nbformat has not been updated,
867 // so transform back to pyerr for json.
868 msg_type = data.output_type = "error";
869 }
870 if (msg_type === "display_data" || msg_type === "execute_result") {
854 // convert short keys to mime keys
871 // convert short keys to mime keys
855 // TODO: remove mapping of short keys when we update to nbformat 4
872 // TODO: remove mapping of short keys when we update to nbformat 4
856 data = this.rename_keys(data, OutputArea.mime_map_r);
873 data = this.rename_keys(data, OutputArea.mime_map_r);
857 data.metadata = this.rename_keys(data.metadata, OutputArea.mime_map_r);
874 data.metadata = this.rename_keys(data.metadata, OutputArea.mime_map_r);
875 // msg spec JSON is an object, nbformat v3 JSON is a JSON string
876 if (data["application/json"] !== undefined && typeof data["application/json"] === 'string') {
877 data["application/json"] = JSON.parse(data["application/json"]);
878 }
858 }
879 }
859
880
860 this.append_output(data);
881 this.append_output(data);
861 }
882 }
862 };
883 };
863
884
864
885
865 OutputArea.prototype.toJSON = function () {
886 OutputArea.prototype.toJSON = function () {
866 var outputs = [];
887 var outputs = [];
867 var len = this.outputs.length;
888 var len = this.outputs.length;
868 var data;
889 var data;
869 for (var i=0; i<len; i++) {
890 for (var i=0; i<len; i++) {
870 data = this.outputs[i];
891 data = this.outputs[i];
871 var msg_type = data.output_type;
892 var msg_type = data.output_type;
872 if (msg_type === "display_data" || msg_type === "pyout") {
893 if (msg_type === "display_data" || msg_type === "execute_result") {
873 // convert mime keys to short keys
894 // convert mime keys to short keys
874 data = this.rename_keys(data, OutputArea.mime_map);
895 data = this.rename_keys(data, OutputArea.mime_map);
875 data.metadata = this.rename_keys(data.metadata, OutputArea.mime_map);
896 data.metadata = this.rename_keys(data.metadata, OutputArea.mime_map);
897 // msg spec JSON is an object, nbformat v3 JSON is a JSON string
898 if (data.json !== undefined && typeof data.json !== 'string') {
899 data.json = JSON.stringify(data.json);
900 }
901 }
902 if (msg_type == "execute_result") {
903 // pyout message has been renamed to execute_result,
904 // but the nbformat has not been updated,
905 // so transform back to pyout for json.
906 data.output_type = "pyout";
907 } else if (msg_type == "error") {
908 // pyerr message has been renamed to error,
909 // but the nbformat has not been updated,
910 // so transform back to pyerr for json.
911 data.output_type = "pyerr";
876 }
912 }
877 outputs[i] = data;
913 outputs[i] = data;
878 }
914 }
879 return outputs;
915 return outputs;
880 };
916 };
881
917
882 /**
918 /**
883 * Class properties
919 * Class properties
884 **/
920 **/
885
921
886 /**
922 /**
887 * Threshold to trigger autoscroll when the OutputArea is resized,
923 * Threshold to trigger autoscroll when the OutputArea is resized,
888 * typically when new outputs are added.
924 * typically when new outputs are added.
889 *
925 *
890 * Behavior is undefined if autoscroll is lower than minimum_scroll_threshold,
926 * Behavior is undefined if autoscroll is lower than minimum_scroll_threshold,
891 * unless it is < 0, in which case autoscroll will never be triggered
927 * unless it is < 0, in which case autoscroll will never be triggered
892 *
928 *
893 * @property auto_scroll_threshold
929 * @property auto_scroll_threshold
894 * @type Number
930 * @type Number
895 * @default 100
931 * @default 100
896 *
932 *
897 **/
933 **/
898 OutputArea.auto_scroll_threshold = 100;
934 OutputArea.auto_scroll_threshold = 100;
899
935
900 /**
936 /**
901 * Lower limit (in lines) for OutputArea to be made scrollable. OutputAreas
937 * Lower limit (in lines) for OutputArea to be made scrollable. OutputAreas
902 * shorter than this are never scrolled.
938 * shorter than this are never scrolled.
903 *
939 *
904 * @property minimum_scroll_threshold
940 * @property minimum_scroll_threshold
905 * @type Number
941 * @type Number
906 * @default 20
942 * @default 20
907 *
943 *
908 **/
944 **/
909 OutputArea.minimum_scroll_threshold = 20;
945 OutputArea.minimum_scroll_threshold = 20;
910
946
911
947
912
948
913 OutputArea.mime_map = {
949 OutputArea.mime_map = {
914 "text/plain" : "text",
950 "text/plain" : "text",
915 "text/html" : "html",
951 "text/html" : "html",
916 "image/svg+xml" : "svg",
952 "image/svg+xml" : "svg",
917 "image/png" : "png",
953 "image/png" : "png",
918 "image/jpeg" : "jpeg",
954 "image/jpeg" : "jpeg",
919 "text/latex" : "latex",
955 "text/latex" : "latex",
920 "application/json" : "json",
956 "application/json" : "json",
921 "application/javascript" : "javascript",
957 "application/javascript" : "javascript",
922 };
958 };
923
959
924 OutputArea.mime_map_r = {
960 OutputArea.mime_map_r = {
925 "text" : "text/plain",
961 "text" : "text/plain",
926 "html" : "text/html",
962 "html" : "text/html",
927 "svg" : "image/svg+xml",
963 "svg" : "image/svg+xml",
928 "png" : "image/png",
964 "png" : "image/png",
929 "jpeg" : "image/jpeg",
965 "jpeg" : "image/jpeg",
930 "latex" : "text/latex",
966 "latex" : "text/latex",
931 "json" : "application/json",
967 "json" : "application/json",
932 "javascript" : "application/javascript",
968 "javascript" : "application/javascript",
933 };
969 };
934
970
935 OutputArea.display_order = [
971 OutputArea.display_order = [
936 'application/javascript',
972 'application/javascript',
937 'text/html',
973 'text/html',
938 'text/markdown',
974 'text/markdown',
939 'text/latex',
975 'text/latex',
940 'image/svg+xml',
976 'image/svg+xml',
941 'image/png',
977 'image/png',
942 'image/jpeg',
978 'image/jpeg',
943 'application/pdf',
979 'application/pdf',
944 'text/plain'
980 'text/plain'
945 ];
981 ];
946
982
947 OutputArea.append_map = {
983 OutputArea.append_map = {
948 "text/plain" : append_text,
984 "text/plain" : append_text,
949 "text/html" : append_html,
985 "text/html" : append_html,
950 "text/markdown": append_markdown,
986 "text/markdown": append_markdown,
951 "image/svg+xml" : append_svg,
987 "image/svg+xml" : append_svg,
952 "image/png" : append_png,
988 "image/png" : append_png,
953 "image/jpeg" : append_jpeg,
989 "image/jpeg" : append_jpeg,
954 "text/latex" : append_latex,
990 "text/latex" : append_latex,
955 "application/javascript" : append_javascript,
991 "application/javascript" : append_javascript,
956 "application/pdf" : append_pdf
992 "application/pdf" : append_pdf
957 };
993 };
958
994
959 IPython.OutputArea = OutputArea;
995 IPython.OutputArea = OutputArea;
960
996
961 return IPython;
997 return IPython;
962
998
963 }(IPython));
999 }(IPython));
@@ -1,178 +1,180
1 //----------------------------------------------------------------------------
1 // Copyright (c) IPython Development Team.
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Distributed under the terms of the Modified BSD License.
3 //
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
7
3
8 //============================================================================
4 //============================================================================
9 // Pager
5 // Pager
10 //============================================================================
6 //============================================================================
11
7
12 var IPython = (function (IPython) {
8 var IPython = (function (IPython) {
13 "use strict";
9 "use strict";
14
10
15 var utils = IPython.utils;
11 var utils = IPython.utils;
16
12
17 var Pager = function (pager_selector, pager_splitter_selector) {
13 var Pager = function (pager_selector, pager_splitter_selector) {
18 this.pager_element = $(pager_selector);
14 this.pager_element = $(pager_selector);
19 this.pager_button_area = $('#pager_button_area');
15 this.pager_button_area = $('#pager_button_area');
20 var that = this;
16 var that = this;
21 this.percentage_height = 0.40;
17 this.percentage_height = 0.40;
22 this.pager_splitter_element = $(pager_splitter_selector)
18 this.pager_splitter_element = $(pager_splitter_selector)
23 .draggable({
19 .draggable({
24 containment: 'window',
20 containment: 'window',
25 axis:'y',
21 axis:'y',
26 helper: null ,
22 helper: null ,
27 drag: function(event, ui) {
23 drag: function(event, ui) {
28 // recalculate the amount of space the pager should take
24 // recalculate the amount of space the pager should take
29 var pheight = ($(document.body).height()-event.clientY-4);
25 var pheight = ($(document.body).height()-event.clientY-4);
30 var downprct = pheight/IPython.layout_manager.app_height();
26 var downprct = pheight/IPython.layout_manager.app_height();
31 downprct = Math.min(0.9, downprct);
27 downprct = Math.min(0.9, downprct);
32 if (downprct < 0.1) {
28 if (downprct < 0.1) {
33 that.percentage_height = 0.1;
29 that.percentage_height = 0.1;
34 that.collapse({'duration':0});
30 that.collapse({'duration':0});
35 } else if (downprct > 0.2) {
31 } else if (downprct > 0.2) {
36 that.percentage_height = downprct;
32 that.percentage_height = downprct;
37 that.expand({'duration':0});
33 that.expand({'duration':0});
38 }
34 }
39 IPython.layout_manager.do_resize();
35 IPython.layout_manager.do_resize();
40 }
36 }
41 });
37 });
42 this.expanded = false;
38 this.expanded = false;
43 this.style();
39 this.style();
44 this.create_button_area();
40 this.create_button_area();
45 this.bind_events();
41 this.bind_events();
46 };
42 };
47
43
48 Pager.prototype.create_button_area = function(){
44 Pager.prototype.create_button_area = function(){
49 var that = this;
45 var that = this;
50 this.pager_button_area.append(
46 this.pager_button_area.append(
51 $('<a>').attr('role', "button")
47 $('<a>').attr('role', "button")
52 .attr('title',"Open the pager in an external window")
48 .attr('title',"Open the pager in an external window")
53 .addClass('ui-button')
49 .addClass('ui-button')
54 .click(function(){that.detach()})
50 .click(function(){that.detach()})
55 .attr('style','position: absolute; right: 20px;')
51 .attr('style','position: absolute; right: 20px;')
56 .append(
52 .append(
57 $('<span>').addClass("ui-icon ui-icon-extlink")
53 $('<span>').addClass("ui-icon ui-icon-extlink")
58 )
54 )
59 )
55 )
60 this.pager_button_area.append(
56 this.pager_button_area.append(
61 $('<a>').attr('role', "button")
57 $('<a>').attr('role', "button")
62 .attr('title',"Close the pager")
58 .attr('title',"Close the pager")
63 .addClass('ui-button')
59 .addClass('ui-button')
64 .click(function(){that.collapse()})
60 .click(function(){that.collapse()})
65 .attr('style','position: absolute; right: 5px;')
61 .attr('style','position: absolute; right: 5px;')
66 .append(
62 .append(
67 $('<span>').addClass("ui-icon ui-icon-close")
63 $('<span>').addClass("ui-icon ui-icon-close")
68 )
64 )
69 )
65 )
70 };
66 };
71
67
72 Pager.prototype.style = function () {
68 Pager.prototype.style = function () {
73 this.pager_splitter_element.addClass('border-box-sizing ui-widget ui-state-default');
69 this.pager_splitter_element.addClass('border-box-sizing ui-widget ui-state-default');
74 this.pager_element.addClass('border-box-sizing');
70 this.pager_element.addClass('border-box-sizing');
75 this.pager_element.find(".container").addClass('border-box-sizing');
71 this.pager_element.find(".container").addClass('border-box-sizing');
76 this.pager_splitter_element.attr('title', 'Click to Show/Hide pager area, drag to Resize');
72 this.pager_splitter_element.attr('title', 'Click to Show/Hide pager area, drag to Resize');
77 };
73 };
78
74
79
75
80 Pager.prototype.bind_events = function () {
76 Pager.prototype.bind_events = function () {
81 var that = this;
77 var that = this;
82
78
83 this.pager_element.bind('collapse_pager', function (event, extrap) {
79 this.pager_element.bind('collapse_pager', function (event, extrap) {
84 var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
80 var time = 'fast';
81 if (extrap && extrap.duration) {
82 time = extrap.duration;
83 }
85 that.pager_element.hide(time);
84 that.pager_element.hide(time);
86 });
85 });
87
86
88 this.pager_element.bind('expand_pager', function (event, extrap) {
87 this.pager_element.bind('expand_pager', function (event, extrap) {
89 var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
88 var time = 'fast';
89 if (extrap && extrap.duration) {
90 time = extrap.duration;
91 }
90 that.pager_element.show(time);
92 that.pager_element.show(time);
91 });
93 });
92
94
93 this.pager_splitter_element.hover(
95 this.pager_splitter_element.hover(
94 function () {
96 function () {
95 that.pager_splitter_element.addClass('ui-state-hover');
97 that.pager_splitter_element.addClass('ui-state-hover');
96 },
98 },
97 function () {
99 function () {
98 that.pager_splitter_element.removeClass('ui-state-hover');
100 that.pager_splitter_element.removeClass('ui-state-hover');
99 }
101 }
100 );
102 );
101
103
102 this.pager_splitter_element.click(function () {
104 this.pager_splitter_element.click(function () {
103 that.toggle();
105 that.toggle();
104 });
106 });
105
107
106 $([IPython.events]).on('open_with_text.Pager', function (event, data) {
108 $([IPython.events]).on('open_with_text.Pager', function (event, payload) {
107 if (data.text.trim() !== '') {
109 // FIXME: support other mime types
110 if (payload.data['text/plain'] && payload.data['text/plain'] !== "") {
108 that.clear();
111 that.clear();
109 that.expand();
112 that.expand();
110 that.append_text(data.text);
113 that.append_text(payload.data['text/plain']);
111 };
114 }
112 });
115 });
113 };
116 };
114
117
115
118
116 Pager.prototype.collapse = function (extrap) {
119 Pager.prototype.collapse = function (extrap) {
117 if (this.expanded === true) {
120 if (this.expanded === true) {
118 this.expanded = false;
121 this.expanded = false;
119 this.pager_element.add($('div#notebook')).trigger('collapse_pager', extrap);
122 this.pager_element.add($('div#notebook')).trigger('collapse_pager', extrap);
120 };
123 }
121 };
124 };
122
125
123
126
124 Pager.prototype.expand = function (extrap) {
127 Pager.prototype.expand = function (extrap) {
125 if (this.expanded !== true) {
128 if (this.expanded !== true) {
126 this.expanded = true;
129 this.expanded = true;
127 this.pager_element.add($('div#notebook')).trigger('expand_pager', extrap);
130 this.pager_element.add($('div#notebook')).trigger('expand_pager', extrap);
128 };
131 }
129 };
132 };
130
133
131
134
132 Pager.prototype.toggle = function () {
135 Pager.prototype.toggle = function () {
133 if (this.expanded === true) {
136 if (this.expanded === true) {
134 this.collapse();
137 this.collapse();
135 } else {
138 } else {
136 this.expand();
139 this.expand();
137 };
140 }
138 };
141 };
139
142
140
143
141 Pager.prototype.clear = function (text) {
144 Pager.prototype.clear = function (text) {
142 this.pager_element.find(".container").empty();
145 this.pager_element.find(".container").empty();
143 };
146 };
144
147
145 Pager.prototype.detach = function(){
148 Pager.prototype.detach = function(){
146 var w = window.open("","_blank");
149 var w = window.open("","_blank");
147 $(w.document.head)
150 $(w.document.head)
148 .append(
151 .append(
149 $('<link>')
152 $('<link>')
150 .attr('rel',"stylesheet")
153 .attr('rel',"stylesheet")
151 .attr('href',"/static/css/notebook.css")
154 .attr('href',"/static/css/notebook.css")
152 .attr('type',"text/css")
155 .attr('type',"text/css")
153 )
156 )
154 .append(
157 .append(
155 $('<title>').text("IPython Pager")
158 $('<title>').text("IPython Pager")
156 );
159 );
157 var pager_body = $(w.document.body);
160 var pager_body = $(w.document.body);
158 pager_body.css('overflow','scroll');
161 pager_body.css('overflow','scroll');
159
162
160 pager_body.append(this.pager_element.clone().children());
163 pager_body.append(this.pager_element.clone().children());
161 w.document.close();
164 w.document.close();
162 this.collapse();
165 this.collapse();
163
166 };
164 }
165
167
166 Pager.prototype.append_text = function (text) {
168 Pager.prototype.append_text = function (text) {
167 // The only user content injected with this HTML call is escaped by
169 // The only user content injected with this HTML call is escaped by
168 // the fixConsole() method.
170 // the fixConsole() method.
169 this.pager_element.find(".container").append($('<pre/>').html(utils.fixCarriageReturn(utils.fixConsole(text))));
171 this.pager_element.find(".container").append($('<pre/>').html(utils.fixCarriageReturn(utils.fixConsole(text))));
170 };
172 };
171
173
172
174
173 IPython.Pager = Pager;
175 IPython.Pager = Pager;
174
176
175 return IPython;
177 return IPython;
176
178
177 }(IPython));
179 }(IPython));
178
180
@@ -1,387 +1,345
1 //----------------------------------------------------------------------------
1 // Copyright (c) IPython Development Team.
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Distributed under the terms of the Modified BSD License.
3 //
3
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
7 //============================================================================
4 //============================================================================
8 // Tooltip
5 // Tooltip
9 //============================================================================
6 //============================================================================
10 //
7 //
11 // you can set the autocall time by setting `IPython.tooltip.time_before_tooltip` in ms
8 // you can set the autocall time by setting `IPython.tooltip.time_before_tooltip` in ms
12 //
9 //
13 // you can configure the differents action of pressing shift-tab several times in a row by
10 // you can configure the differents action of pressing shift-tab several times in a row by
14 // setting/appending different fonction in the array
11 // setting/appending different fonction in the array
15 // IPython.tooltip.tabs_functions
12 // IPython.tooltip.tabs_functions
16 //
13 //
17 // eg :
14 // eg :
18 // IPython.tooltip.tabs_functions[4] = function (){console.log('this is the action of the 4th tab pressing')}
15 // IPython.tooltip.tabs_functions[4] = function (){console.log('this is the action of the 4th tab pressing')}
19 //
16 //
20 var IPython = (function (IPython) {
17 var IPython = (function (IPython) {
21 "use strict";
18 "use strict";
22
19
23 var utils = IPython.utils;
20 var utils = IPython.utils;
24
21
25 // tooltip constructor
22 // tooltip constructor
26 var Tooltip = function () {
23 var Tooltip = function () {
27 var that = this;
24 var that = this;
28 this.time_before_tooltip = 1200;
25 this.time_before_tooltip = 1200;
29
26
30 // handle to html
27 // handle to html
31 this.tooltip = $('#tooltip');
28 this.tooltip = $('#tooltip');
32 this._hidden = true;
29 this._hidden = true;
33
30
34 // variable for consecutive call
31 // variable for consecutive call
35 this._old_cell = null;
32 this._old_cell = null;
36 this._old_request = null;
33 this._old_request = null;
37 this._consecutive_counter = 0;
34 this._consecutive_counter = 0;
38
35
39 // 'sticky ?'
36 // 'sticky ?'
40 this._sticky = false;
37 this._sticky = false;
41
38
42 // display tooltip if the docstring is empty?
39 // display tooltip if the docstring is empty?
43 this._hide_if_no_docstring = false;
40 this._hide_if_no_docstring = false;
44
41
45 // contain the button in the upper right corner
42 // contain the button in the upper right corner
46 this.buttons = $('<div/>').addClass('tooltipbuttons');
43 this.buttons = $('<div/>').addClass('tooltipbuttons');
47
44
48 // will contain the docstring
45 // will contain the docstring
49 this.text = $('<div/>').addClass('tooltiptext').addClass('smalltooltip');
46 this.text = $('<div/>').addClass('tooltiptext').addClass('smalltooltip');
50
47
51 // build the buttons menu on the upper right
48 // build the buttons menu on the upper right
52 // expand the tooltip to see more
49 // expand the tooltip to see more
53 var expandlink = $('<a/>').attr('href', "#").addClass("ui-corner-all") //rounded corner
50 var expandlink = $('<a/>').attr('href', "#").addClass("ui-corner-all") //rounded corner
54 .attr('role', "button").attr('id', 'expanbutton').attr('title', 'Grow the tooltip vertically (press shift-tab twice)').click(function () {
51 .attr('role', "button").attr('id', 'expanbutton').attr('title', 'Grow the tooltip vertically (press shift-tab twice)').click(function () {
55 that.expand();
52 that.expand();
56 }).append(
53 }).append(
57 $('<span/>').text('Expand').addClass('ui-icon').addClass('ui-icon-plus'));
54 $('<span/>').text('Expand').addClass('ui-icon').addClass('ui-icon-plus'));
58
55
59 // open in pager
56 // open in pager
60 var morelink = $('<a/>').attr('href', "#").attr('role', "button").addClass('ui-button').attr('title', 'show the current docstring in pager (press shift-tab 4 times)');
57 var morelink = $('<a/>').attr('href', "#").attr('role', "button").addClass('ui-button').attr('title', 'show the current docstring in pager (press shift-tab 4 times)');
61 var morespan = $('<span/>').text('Open in Pager').addClass('ui-icon').addClass('ui-icon-arrowstop-l-n');
58 var morespan = $('<span/>').text('Open in Pager').addClass('ui-icon').addClass('ui-icon-arrowstop-l-n');
62 morelink.append(morespan);
59 morelink.append(morespan);
63 morelink.click(function () {
60 morelink.click(function () {
64 that.showInPager(that._old_cell);
61 that.showInPager(that._old_cell);
65 });
62 });
66
63
67 // close the tooltip
64 // close the tooltip
68 var closelink = $('<a/>').attr('href', "#").attr('role', "button").addClass('ui-button');
65 var closelink = $('<a/>').attr('href', "#").attr('role', "button").addClass('ui-button');
69 var closespan = $('<span/>').text('Close').addClass('ui-icon').addClass('ui-icon-close');
66 var closespan = $('<span/>').text('Close').addClass('ui-icon').addClass('ui-icon-close');
70 closelink.append(closespan);
67 closelink.append(closespan);
71 closelink.click(function () {
68 closelink.click(function () {
72 that.remove_and_cancel_tooltip(true);
69 that.remove_and_cancel_tooltip(true);
73 });
70 });
74
71
75 this._clocklink = $('<a/>').attr('href', "#");
72 this._clocklink = $('<a/>').attr('href', "#");
76 this._clocklink.attr('role', "button");
73 this._clocklink.attr('role', "button");
77 this._clocklink.addClass('ui-button');
74 this._clocklink.addClass('ui-button');
78 this._clocklink.attr('title', 'Tootip is not dismissed while typing for 10 seconds');
75 this._clocklink.attr('title', 'Tootip is not dismissed while typing for 10 seconds');
79 var clockspan = $('<span/>').text('Close');
76 var clockspan = $('<span/>').text('Close');
80 clockspan.addClass('ui-icon');
77 clockspan.addClass('ui-icon');
81 clockspan.addClass('ui-icon-clock');
78 clockspan.addClass('ui-icon-clock');
82 this._clocklink.append(clockspan);
79 this._clocklink.append(clockspan);
83 this._clocklink.click(function () {
80 this._clocklink.click(function () {
84 that.cancel_stick();
81 that.cancel_stick();
85 });
82 });
86
83
87
84
88
85
89
86
90 //construct the tooltip
87 //construct the tooltip
91 // add in the reverse order you want them to appear
88 // add in the reverse order you want them to appear
92 this.buttons.append(closelink);
89 this.buttons.append(closelink);
93 this.buttons.append(expandlink);
90 this.buttons.append(expandlink);
94 this.buttons.append(morelink);
91 this.buttons.append(morelink);
95 this.buttons.append(this._clocklink);
92 this.buttons.append(this._clocklink);
96 this._clocklink.hide();
93 this._clocklink.hide();
97
94
98
95
99 // we need a phony element to make the small arrow
96 // we need a phony element to make the small arrow
100 // of the tooltip in css
97 // of the tooltip in css
101 // we will move the arrow later
98 // we will move the arrow later
102 this.arrow = $('<div/>').addClass('pretooltiparrow');
99 this.arrow = $('<div/>').addClass('pretooltiparrow');
103 this.tooltip.append(this.buttons);
100 this.tooltip.append(this.buttons);
104 this.tooltip.append(this.arrow);
101 this.tooltip.append(this.arrow);
105 this.tooltip.append(this.text);
102 this.tooltip.append(this.text);
106
103
107 // function that will be called if you press tab 1, 2, 3... times in a row
104 // function that will be called if you press tab 1, 2, 3... times in a row
108 this.tabs_functions = [function (cell, text) {
105 this.tabs_functions = [function (cell, text, cursor) {
109 that._request_tooltip(cell, text);
106 that._request_tooltip(cell, text, cursor);
110 }, function () {
107 }, function () {
111 that.expand();
108 that.expand();
112 }, function () {
109 }, function () {
113 that.stick();
110 that.stick();
114 }, function (cell) {
111 }, function (cell) {
115 that.cancel_stick();
112 that.cancel_stick();
116 that.showInPager(cell);
113 that.showInPager(cell);
117 }];
114 }];
118 // call after all the tabs function above have bee call to clean their effects
115 // call after all the tabs function above have bee call to clean their effects
119 // if necessary
116 // if necessary
120 this.reset_tabs_function = function (cell, text) {
117 this.reset_tabs_function = function (cell, text) {
121 this._old_cell = (cell) ? cell : null;
118 this._old_cell = (cell) ? cell : null;
122 this._old_request = (text) ? text : null;
119 this._old_request = (text) ? text : null;
123 this._consecutive_counter = 0;
120 this._consecutive_counter = 0;
124 };
121 };
125 };
122 };
126
123
127 Tooltip.prototype.is_visible = function () {
124 Tooltip.prototype.is_visible = function () {
128 return !this._hidden;
125 return !this._hidden;
129 };
126 };
130
127
131 Tooltip.prototype.showInPager = function (cell) {
128 Tooltip.prototype.showInPager = function (cell) {
132 // reexecute last call in pager by appending ? to show back in pager
129 // reexecute last call in pager by appending ? to show back in pager
133 var that = this;
130 var that = this;
134 var callbacks = {'shell' : {
131 var payload = {};
135 'payload' : {
132 payload.text = that._reply.content.data['text/plain'];
136 'page' : $.proxy(cell._open_with_pager, cell)
133
137 }
134 $([IPython.events]).trigger('open_with_text.Pager', payload);
138 }
139 };
140 cell.kernel.execute(that.name + '?', callbacks, {'silent': false, 'store_history': true});
141 this.remove_and_cancel_tooltip();
135 this.remove_and_cancel_tooltip();
142 };
136 };
143
137
144 // grow the tooltip verticaly
138 // grow the tooltip verticaly
145 Tooltip.prototype.expand = function () {
139 Tooltip.prototype.expand = function () {
146 this.text.removeClass('smalltooltip');
140 this.text.removeClass('smalltooltip');
147 this.text.addClass('bigtooltip');
141 this.text.addClass('bigtooltip');
148 $('#expanbutton').hide('slow');
142 $('#expanbutton').hide('slow');
149 };
143 };
150
144
151 // deal with all the logic of hiding the tooltip
145 // deal with all the logic of hiding the tooltip
152 // and reset it's status
146 // and reset it's status
153 Tooltip.prototype._hide = function () {
147 Tooltip.prototype._hide = function () {
154 this._hidden = true;
148 this._hidden = true;
155 this.tooltip.fadeOut('fast');
149 this.tooltip.fadeOut('fast');
156 $('#expanbutton').show('slow');
150 $('#expanbutton').show('slow');
157 this.text.removeClass('bigtooltip');
151 this.text.removeClass('bigtooltip');
158 this.text.addClass('smalltooltip');
152 this.text.addClass('smalltooltip');
159 // keep scroll top to be sure to always see the first line
153 // keep scroll top to be sure to always see the first line
160 this.text.scrollTop(0);
154 this.text.scrollTop(0);
161 this.code_mirror = null;
155 this.code_mirror = null;
162 };
156 };
163
157
164 // return true on successfully removing a visible tooltip; otherwise return
158 // return true on successfully removing a visible tooltip; otherwise return
165 // false.
159 // false.
166 Tooltip.prototype.remove_and_cancel_tooltip = function (force) {
160 Tooltip.prototype.remove_and_cancel_tooltip = function (force) {
167 // note that we don't handle closing directly inside the calltip
161 // note that we don't handle closing directly inside the calltip
168 // as in the completer, because it is not focusable, so won't
162 // as in the completer, because it is not focusable, so won't
169 // get the event.
163 // get the event.
170 this.cancel_pending();
164 this.cancel_pending();
171 if (!this._hidden) {
165 if (!this._hidden) {
172 if (force || !this._sticky) {
166 if (force || !this._sticky) {
173 this.cancel_stick();
167 this.cancel_stick();
174 this._hide();
168 this._hide();
175 }
169 }
176 this.reset_tabs_function();
170 this.reset_tabs_function();
177 return true;
171 return true;
178 } else {
172 } else {
179 return false;
173 return false;
180 }
174 }
181 };
175 };
182
176
183 // cancel autocall done after '(' for example.
177 // cancel autocall done after '(' for example.
184 Tooltip.prototype.cancel_pending = function () {
178 Tooltip.prototype.cancel_pending = function () {
185 if (this._tooltip_timeout !== null) {
179 if (this._tooltip_timeout !== null) {
186 clearTimeout(this._tooltip_timeout);
180 clearTimeout(this._tooltip_timeout);
187 this._tooltip_timeout = null;
181 this._tooltip_timeout = null;
188 }
182 }
189 };
183 };
190
184
191 // will trigger tooltip after timeout
185 // will trigger tooltip after timeout
192 Tooltip.prototype.pending = function (cell, hide_if_no_docstring) {
186 Tooltip.prototype.pending = function (cell, hide_if_no_docstring) {
193 var that = this;
187 var that = this;
194 this._tooltip_timeout = setTimeout(function () {
188 this._tooltip_timeout = setTimeout(function () {
195 that.request(cell, hide_if_no_docstring);
189 that.request(cell, hide_if_no_docstring);
196 }, that.time_before_tooltip);
190 }, that.time_before_tooltip);
197 };
191 };
198
192
199 // easy access for julia monkey patching.
193 // easy access for julia monkey patching.
200 Tooltip.last_token_re = /[a-z_][0-9a-z._]*$/gi;
194 Tooltip.last_token_re = /[a-z_][0-9a-z._]*$/gi;
201
195
202 Tooltip.prototype.extract_oir_token = function(line){
196 Tooltip.prototype.extract_oir_token = function(line){
203 // use internally just to make the request to the kernel
197 // use internally just to make the request to the kernel
204 // Feel free to shorten this logic if you are better
198 // Feel free to shorten this logic if you are better
205 // than me in regEx
199 // than me in regEx
206 // basicaly you shoul be able to get xxx.xxx.xxx from
200 // basicaly you shoul be able to get xxx.xxx.xxx from
207 // something(range(10), kwarg=smth) ; xxx.xxx.xxx( firstarg, rand(234,23), kwarg1=2,
201 // something(range(10), kwarg=smth) ; xxx.xxx.xxx( firstarg, rand(234,23), kwarg1=2,
208 // remove everything between matchin bracket (need to iterate)
202 // remove everything between matchin bracket (need to iterate)
209 var matchBracket = /\([^\(\)]+\)/g;
203 var matchBracket = /\([^\(\)]+\)/g;
210 var endBracket = /\([^\(]*$/g;
204 var endBracket = /\([^\(]*$/g;
211 var oldline = line;
205 var oldline = line;
212
206
213 line = line.replace(matchBracket, "");
207 line = line.replace(matchBracket, "");
214 while (oldline != line) {
208 while (oldline != line) {
215 oldline = line;
209 oldline = line;
216 line = line.replace(matchBracket, "");
210 line = line.replace(matchBracket, "");
217 }
211 }
218 // remove everything after last open bracket
212 // remove everything after last open bracket
219 line = line.replace(endBracket, "");
213 line = line.replace(endBracket, "");
220 // reset the regex object
214 // reset the regex object
221 Tooltip.last_token_re.lastIndex = 0;
215 Tooltip.last_token_re.lastIndex = 0;
222 return Tooltip.last_token_re.exec(line);
216 return Tooltip.last_token_re.exec(line);
223 };
217 };
224
218
225 Tooltip.prototype._request_tooltip = function (cell, line) {
219 Tooltip.prototype._request_tooltip = function (cell, text, cursor_pos) {
226 var callbacks = $.proxy(this._show, this);
220 var callbacks = $.proxy(this._show, this);
227 var oir_token = this.extract_oir_token(line);
221 var msg_id = cell.kernel.inspect(text, cursor_pos, callbacks);
228 var msg_id = cell.kernel.object_info(oir_token, callbacks);
229 };
222 };
230
223
231 // make an imediate completion request
224 // make an imediate completion request
232 Tooltip.prototype.request = function (cell, hide_if_no_docstring) {
225 Tooltip.prototype.request = function (cell, hide_if_no_docstring) {
233 // request(codecell)
226 // request(codecell)
234 // Deal with extracting the text from the cell and counting
227 // Deal with extracting the text from the cell and counting
235 // call in a row
228 // call in a row
236 this.cancel_pending();
229 this.cancel_pending();
237 var editor = cell.code_mirror;
230 var editor = cell.code_mirror;
238 var cursor = editor.getCursor();
231 var cursor = editor.getCursor();
239 var text = editor.getRange({
232 var cursor_pos = utils.to_absolute_cursor_pos(editor, cursor);
240 line: cursor.line,
233 var text = cell.get_text();
241 ch: 0
242 }, cursor).trim();
243
234
244 this._hide_if_no_docstring = hide_if_no_docstring;
235 this._hide_if_no_docstring = hide_if_no_docstring;
245
236
246 if(editor.somethingSelected()){
237 if(editor.somethingSelected()){
247 text = editor.getSelection();
238 text = editor.getSelection();
248 }
239 }
249
240
250 // need a permanent handel to code_mirror for future auto recall
241 // need a permanent handel to code_mirror for future auto recall
251 this.code_mirror = editor;
242 this.code_mirror = editor;
252
243
253 // now we treat the different number of keypress
244 // now we treat the different number of keypress
254 // first if same cell, same text, increment counter by 1
245 // first if same cell, same text, increment counter by 1
255 if (this._old_cell == cell && this._old_request == text && this._hidden === false) {
246 if (this._old_cell == cell && this._old_request == text && this._hidden === false) {
256 this._consecutive_counter++;
247 this._consecutive_counter++;
257 } else {
248 } else {
258 // else reset
249 // else reset
259 this.cancel_stick();
250 this.cancel_stick();
260 this.reset_tabs_function (cell, text);
251 this.reset_tabs_function (cell, text);
261 }
252 }
262
253
263 // don't do anything if line beggin with '(' or is empty
254 this.tabs_functions[this._consecutive_counter](cell, text, cursor_pos);
264 if (text === "" || text === "(") {
265 return;
266 }
267
268 this.tabs_functions[this._consecutive_counter](cell, text);
269
255
270 // then if we are at the end of list function, reset
256 // then if we are at the end of list function, reset
271 if (this._consecutive_counter == this.tabs_functions.length) {
257 if (this._consecutive_counter == this.tabs_functions.length) {
272 this.reset_tabs_function (cell, text);
258 this.reset_tabs_function (cell, text, cursor);
273 }
259 }
274
260
275 return;
261 return;
276 };
262 };
277
263
278 // cancel the option of having the tooltip to stick
264 // cancel the option of having the tooltip to stick
279 Tooltip.prototype.cancel_stick = function () {
265 Tooltip.prototype.cancel_stick = function () {
280 clearTimeout(this._stick_timeout);
266 clearTimeout(this._stick_timeout);
281 this._stick_timeout = null;
267 this._stick_timeout = null;
282 this._clocklink.hide('slow');
268 this._clocklink.hide('slow');
283 this._sticky = false;
269 this._sticky = false;
284 };
270 };
285
271
286 // put the tooltip in a sicky state for 10 seconds
272 // put the tooltip in a sicky state for 10 seconds
287 // it won't be removed by remove_and_cancell() unless you called with
273 // it won't be removed by remove_and_cancell() unless you called with
288 // the first parameter set to true.
274 // the first parameter set to true.
289 // remove_and_cancell_tooltip(true)
275 // remove_and_cancell_tooltip(true)
290 Tooltip.prototype.stick = function (time) {
276 Tooltip.prototype.stick = function (time) {
291 time = (time !== undefined) ? time : 10;
277 time = (time !== undefined) ? time : 10;
292 var that = this;
278 var that = this;
293 this._sticky = true;
279 this._sticky = true;
294 this._clocklink.show('slow');
280 this._clocklink.show('slow');
295 this._stick_timeout = setTimeout(function () {
281 this._stick_timeout = setTimeout(function () {
296 that._sticky = false;
282 that._sticky = false;
297 that._clocklink.hide('slow');
283 that._clocklink.hide('slow');
298 }, time * 1000);
284 }, time * 1000);
299 };
285 };
300
286
301 // should be called with the kernel reply to actually show the tooltip
287 // should be called with the kernel reply to actually show the tooltip
302 Tooltip.prototype._show = function (reply) {
288 Tooltip.prototype._show = function (reply) {
303 // move the bubble if it is not hidden
289 // move the bubble if it is not hidden
304 // otherwise fade it
290 // otherwise fade it
291 this._reply = reply;
305 var content = reply.content;
292 var content = reply.content;
306 if (!content.found) {
293 if (!content.found) {
307 // object not found, nothing to show
294 // object not found, nothing to show
308 return;
295 return;
309 }
296 }
310 this.name = content.name;
297 this.name = content.name;
311
298
312 // do some math to have the tooltip arrow on more or less on left or right
299 // do some math to have the tooltip arrow on more or less on left or right
313 // width of the editor
300 // width of the editor
314 var w = $(this.code_mirror.getScrollerElement()).width();
301 var w = $(this.code_mirror.getScrollerElement()).width();
315 // ofset of the editor
302 // ofset of the editor
316 var o = $(this.code_mirror.getScrollerElement()).offset();
303 var o = $(this.code_mirror.getScrollerElement()).offset();
317
304
318 // whatever anchor/head order but arrow at mid x selection
305 // whatever anchor/head order but arrow at mid x selection
319 var anchor = this.code_mirror.cursorCoords(false);
306 var anchor = this.code_mirror.cursorCoords(false);
320 var head = this.code_mirror.cursorCoords(true);
307 var head = this.code_mirror.cursorCoords(true);
321 var xinit = (head.left+anchor.left)/2;
308 var xinit = (head.left+anchor.left)/2;
322 var xinter = o.left + (xinit - o.left) / w * (w - 450);
309 var xinter = o.left + (xinit - o.left) / w * (w - 450);
323 var posarrowleft = xinit - xinter;
310 var posarrowleft = xinit - xinter;
324
311
325 if (this._hidden === false) {
312 if (this._hidden === false) {
326 this.tooltip.animate({
313 this.tooltip.animate({
327 'left': xinter - 30 + 'px',
314 'left': xinter - 30 + 'px',
328 'top': (head.bottom + 10) + 'px'
315 'top': (head.bottom + 10) + 'px'
329 });
316 });
330 } else {
317 } else {
331 this.tooltip.css({
318 this.tooltip.css({
332 'left': xinter - 30 + 'px'
319 'left': xinter - 30 + 'px'
333 });
320 });
334 this.tooltip.css({
321 this.tooltip.css({
335 'top': (head.bottom + 10) + 'px'
322 'top': (head.bottom + 10) + 'px'
336 });
323 });
337 }
324 }
338 this.arrow.animate({
325 this.arrow.animate({
339 'left': posarrowleft + 'px'
326 'left': posarrowleft + 'px'
340 });
327 });
341
328
342 // build docstring
343 var defstring = content.call_def;
344 if (!defstring) {
345 defstring = content.init_definition;
346 }
347 if (!defstring) {
348 defstring = content.definition;
349 }
350
351 var docstring = content.call_docstring;
352 if (!docstring) {
353 docstring = content.init_docstring;
354 }
355 if (!docstring) {
356 docstring = content.docstring;
357 }
358
359 if (!docstring) {
360 // For reals this time, no docstring
361 if (this._hide_if_no_docstring) {
362 return;
363 } else {
364 docstring = "<empty docstring>";
365 }
366 }
367
368 this._hidden = false;
329 this._hidden = false;
369 this.tooltip.fadeIn('fast');
330 this.tooltip.fadeIn('fast');
370 this.text.children().remove();
331 this.text.children().remove();
371
332
333 // This should support rich data types, but only text/plain for now
372 // Any HTML within the docstring is escaped by the fixConsole() method.
334 // Any HTML within the docstring is escaped by the fixConsole() method.
373 var pre = $('<pre/>').html(utils.fixConsole(docstring));
335 var pre = $('<pre/>').html(utils.fixConsole(content.data['text/plain']));
374 if (defstring) {
375 var defstring_html = $('<pre/>').html(utils.fixConsole(defstring));
376 this.text.append(defstring_html);
377 }
378 this.text.append(pre);
336 this.text.append(pre);
379 // keep scroll top to be sure to always see the first line
337 // keep scroll top to be sure to always see the first line
380 this.text.scrollTop(0);
338 this.text.scrollTop(0);
381 };
339 };
382
340
383 IPython.Tooltip = Tooltip;
341 IPython.Tooltip = Tooltip;
384
342
385 return IPython;
343 return IPython;
386
344
387 }(IPython));
345 }(IPython));
@@ -1,174 +1,174
1 div.output_wrapper {
1 div.output_wrapper {
2 /* this position must be relative to enable descendents to be absolute within it */
2 /* this position must be relative to enable descendents to be absolute within it */
3 position: relative;
3 position: relative;
4 .vbox()
4 .vbox()
5 }
5 }
6
6
7 /* class for the output area when it should be height-limited */
7 /* class for the output area when it should be height-limited */
8 div.output_scroll {
8 div.output_scroll {
9 /* ideally, this would be max-height, but FF barfs all over that */
9 /* ideally, this would be max-height, but FF barfs all over that */
10 height: 24em;
10 height: 24em;
11 /* FF needs this *and the wrapper* to specify full width, or it will shrinkwrap */
11 /* FF needs this *and the wrapper* to specify full width, or it will shrinkwrap */
12 width: 100%;
12 width: 100%;
13
13
14 overflow: auto;
14 overflow: auto;
15 .corner-all;
15 .corner-all;
16 .box-shadow(inset 0 2px 8px rgba(0, 0, 0, .8));
16 .box-shadow(inset 0 2px 8px rgba(0, 0, 0, .8));
17 display: block;
17 display: block;
18 }
18 }
19
19
20 /* output div while it is collapsed */
20 /* output div while it is collapsed */
21 div.output_collapsed {
21 div.output_collapsed {
22 margin: 0px;
22 margin: 0px;
23 padding: 0px;
23 padding: 0px;
24 .vbox();
24 .vbox();
25 }
25 }
26
26
27 div.out_prompt_overlay {
27 div.out_prompt_overlay {
28 height: 100%;
28 height: 100%;
29 padding: 0px @code_padding;
29 padding: 0px @code_padding;
30 position: absolute;
30 position: absolute;
31 .corner-all;
31 .corner-all;
32 }
32 }
33
33
34 div.out_prompt_overlay:hover {
34 div.out_prompt_overlay:hover {
35 /* use inner shadow to get border that is computed the same on WebKit/FF */
35 /* use inner shadow to get border that is computed the same on WebKit/FF */
36 .box-shadow(inset 0 0 1px #000);
36 .box-shadow(inset 0 0 1px #000);
37 background: rgba(240, 240, 240, 0.5);
37 background: rgba(240, 240, 240, 0.5);
38 }
38 }
39
39
40 div.output_prompt {
40 div.output_prompt {
41 color: darkred;
41 color: darkred;
42 }
42 }
43
43
44 /* This class is the outer container of all output sections. */
44 /* This class is the outer container of all output sections. */
45 div.output_area {
45 div.output_area {
46 padding: 0px;
46 padding: 0px;
47 page-break-inside: avoid;
47 page-break-inside: avoid;
48 .hbox();
48 .hbox();
49
49
50 .MathJax_Display {
50 .MathJax_Display {
51 // Inside a CodeCell, elements are left justified
51 // Inside a CodeCell, elements are left justified
52 text-align: left !important;
52 text-align: left !important;
53 }
53 }
54
54
55 .rendered_html {
55 .rendered_html {
56 // Inside a CodeCell, elements are left justified
56 // Inside a CodeCell, elements are left justified
57 table {
57 table {
58 margin-left: 0;
58 margin-left: 0;
59 margin-right: 0;
59 margin-right: 0;
60 }
60 }
61
61
62 img {
62 img {
63 margin-left: 0;
63 margin-left: 0;
64 margin-right: 0;
64 margin-right: 0;
65 }
65 }
66 }
66 }
67 }
67 }
68
68
69 /* This is needed to protect the pre formating from global settings such
69 /* This is needed to protect the pre formating from global settings such
70 as that of bootstrap */
70 as that of bootstrap */
71 .output {
71 .output {
72 .vbox();
72 .vbox();
73 }
73 }
74
74
75 @media (max-width: 480px) {
75 @media (max-width: 480px) {
76 // move prompts above output on small screens
76 // move prompts above output on small screens
77 div.output_area {
77 div.output_area {
78 .vbox();
78 .vbox();
79 }
79 }
80 }
80 }
81
81
82 div.output_area pre {
82 div.output_area pre {
83 margin: 0;
83 margin: 0;
84 padding: 0;
84 padding: 0;
85 border: 0;
85 border: 0;
86 vertical-align: baseline;
86 vertical-align: baseline;
87 color: black;
87 color: black;
88 background-color: transparent;
88 background-color: transparent;
89 .border-radius(0);
89 .border-radius(0);
90 }
90 }
91
91
92 /* This class is for the output subarea inside the output_area and after
92 /* This class is for the output subarea inside the output_area and after
93 the prompt div. */
93 the prompt div. */
94 div.output_subarea {
94 div.output_subarea {
95 padding: @code_padding @code_padding 0.0em @code_padding;
95 padding: @code_padding @code_padding 0.0em @code_padding;
96 .box-flex1();
96 .box-flex1();
97 }
97 }
98
98
99 /* The rest of the output_* classes are for special styling of the different
99 /* The rest of the output_* classes are for special styling of the different
100 output types */
100 output types */
101
101
102 /* all text output has this class: */
102 /* all text output has this class: */
103 div.output_text {
103 div.output_text {
104 text-align: left;
104 text-align: left;
105 color: @textColor;
105 color: @textColor;
106 /* This has to match that of the the CodeMirror class line-height below */
106 /* This has to match that of the the CodeMirror class line-height below */
107 line-height: @code_line_height;
107 line-height: @code_line_height;
108 }
108 }
109
109
110 /* stdout/stderr are 'text' as well as 'stream', but pyout/pyerr are *not* streams */
110 /* stdout/stderr are 'text' as well as 'stream', but execute_result/error are *not* streams */
111 div.output_stream {
111 div.output_stream {
112 }
112 }
113
113
114 div.output_stdout {
114 div.output_stdout {
115 }
115 }
116
116
117 div.output_stderr {
117 div.output_stderr {
118 background: #fdd; /* very light red background for stderr */
118 background: #fdd; /* very light red background for stderr */
119 }
119 }
120
120
121 div.output_latex {
121 div.output_latex {
122 text-align: left;
122 text-align: left;
123 }
123 }
124
124
125 div.output_html {
125 div.output_html {
126 }
126 }
127
127
128 div.output_png {
128 div.output_png {
129 }
129 }
130
130
131 div.output_jpeg {
131 div.output_jpeg {
132 }
132 }
133
133
134 /* Empty output_javascript divs should have no height */
134 /* Empty output_javascript divs should have no height */
135 div.output_javascript:empty {
135 div.output_javascript:empty {
136 padding: 0;
136 padding: 0;
137 }
137 }
138
138
139 .js-error {
139 .js-error {
140 color: darkred;
140 color: darkred;
141 }
141 }
142
142
143 /* raw_input styles */
143 /* raw_input styles */
144
144
145 div.raw_input_container {
145 div.raw_input_container {
146 font-family: @monoFontFamily;
146 font-family: @monoFontFamily;
147 // for some reason, em padding doesn't compute the same for raw_input
147 // for some reason, em padding doesn't compute the same for raw_input
148 // that is not the first input, but px does
148 // that is not the first input, but px does
149 padding-top: 5px;
149 padding-top: 5px;
150 }
150 }
151
151
152 span.raw_input_prompt {
152 span.raw_input_prompt {
153 /* nothing needed here */
153 /* nothing needed here */
154 }
154 }
155
155
156 input.raw_input {
156 input.raw_input {
157 font-family: inherit;
157 font-family: inherit;
158 font-size: inherit;
158 font-size: inherit;
159 color: inherit;
159 color: inherit;
160 width: auto;
160 width: auto;
161 /* make sure input baseline aligns with prompt */
161 /* make sure input baseline aligns with prompt */
162 vertical-align: baseline;
162 vertical-align: baseline;
163 /* padding + margin = 0.5em between prompt and cursor */
163 /* padding + margin = 0.5em between prompt and cursor */
164 padding: 0em 0.25em;
164 padding: 0em 0.25em;
165 margin: 0em 0.25em;
165 margin: 0em 0.25em;
166 }
166 }
167
167
168 input.raw_input:focus {
168 input.raw_input:focus {
169 box-shadow: none;
169 box-shadow: none;
170 }
170 }
171
171
172 p.p-space {
172 p.p-space {
173 margin-bottom: 10px;
173 margin-bottom: 10px;
174 }
174 }
@@ -1,624 +1,619
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 //============================================================================
4 //============================================================================
5 // Kernel
5 // Kernel
6 //============================================================================
6 //============================================================================
7
7
8 /**
8 /**
9 * @module IPython
9 * @module IPython
10 * @namespace IPython
10 * @namespace IPython
11 * @submodule Kernel
11 * @submodule Kernel
12 */
12 */
13
13
14 var IPython = (function (IPython) {
14 var IPython = (function (IPython) {
15 "use strict";
15 "use strict";
16
16
17 var utils = IPython.utils;
17 var utils = IPython.utils;
18
18
19 // Initialization and connection.
19 // Initialization and connection.
20 /**
20 /**
21 * A Kernel Class to communicate with the Python kernel
21 * A Kernel Class to communicate with the Python kernel
22 * @Class Kernel
22 * @Class Kernel
23 */
23 */
24 var Kernel = function (kernel_service_url) {
24 var Kernel = function (kernel_service_url) {
25 this.kernel_id = null;
25 this.kernel_id = null;
26 this.shell_channel = null;
26 this.shell_channel = null;
27 this.iopub_channel = null;
27 this.iopub_channel = null;
28 this.stdin_channel = null;
28 this.stdin_channel = null;
29 this.kernel_service_url = kernel_service_url;
29 this.kernel_service_url = kernel_service_url;
30 this.running = false;
30 this.running = false;
31 this.username = "username";
31 this.username = "username";
32 this.session_id = utils.uuid();
32 this.session_id = utils.uuid();
33 this._msg_callbacks = {};
33 this._msg_callbacks = {};
34 this.post = $.post;
34 this.post = $.post;
35
35
36 if (typeof(WebSocket) !== 'undefined') {
36 if (typeof(WebSocket) !== 'undefined') {
37 this.WebSocket = WebSocket;
37 this.WebSocket = WebSocket;
38 } else if (typeof(MozWebSocket) !== 'undefined') {
38 } else if (typeof(MozWebSocket) !== 'undefined') {
39 this.WebSocket = MozWebSocket;
39 this.WebSocket = MozWebSocket;
40 } else {
40 } else {
41 alert('Your browser does not have WebSocket support, please try Chrome, Safari or Firefox ≥ 6. Firefox 4 and 5 are also supported by you have to enable WebSockets in about:config.');
41 alert('Your browser does not have WebSocket support, please try Chrome, Safari or Firefox ≥ 6. Firefox 4 and 5 are also supported by you have to enable WebSockets in about:config.');
42 }
42 }
43
43
44 this.bind_events();
44 this.bind_events();
45 this.init_iopub_handlers();
45 this.init_iopub_handlers();
46 this.comm_manager = new IPython.CommManager(this);
46 this.comm_manager = new IPython.CommManager(this);
47 this.widget_manager = new IPython.WidgetManager(this.comm_manager);
47 this.widget_manager = new IPython.WidgetManager(this.comm_manager);
48
48
49 this.last_msg_id = null;
49 this.last_msg_id = null;
50 this.last_msg_callbacks = {};
50 this.last_msg_callbacks = {};
51 };
51 };
52
52
53
53
54 Kernel.prototype._get_msg = function (msg_type, content, metadata) {
54 Kernel.prototype._get_msg = function (msg_type, content, metadata) {
55 var msg = {
55 var msg = {
56 header : {
56 header : {
57 msg_id : utils.uuid(),
57 msg_id : utils.uuid(),
58 username : this.username,
58 username : this.username,
59 session : this.session_id,
59 session : this.session_id,
60 msg_type : msg_type
60 msg_type : msg_type,
61 version : "5.0"
61 },
62 },
62 metadata : metadata || {},
63 metadata : metadata || {},
63 content : content,
64 content : content,
64 parent_header : {}
65 parent_header : {}
65 };
66 };
66 return msg;
67 return msg;
67 };
68 };
68
69
69 Kernel.prototype.bind_events = function () {
70 Kernel.prototype.bind_events = function () {
70 var that = this;
71 var that = this;
71 $([IPython.events]).on('send_input_reply.Kernel', function(evt, data) {
72 $([IPython.events]).on('send_input_reply.Kernel', function(evt, data) {
72 that.send_input_reply(data);
73 that.send_input_reply(data);
73 });
74 });
74 };
75 };
75
76
76 // Initialize the iopub handlers
77 // Initialize the iopub handlers
77
78
78 Kernel.prototype.init_iopub_handlers = function () {
79 Kernel.prototype.init_iopub_handlers = function () {
79 var output_types = ['stream', 'display_data', 'pyout', 'pyerr'];
80 var output_msg_types = ['stream', 'display_data', 'execute_result', 'error'];
80 this._iopub_handlers = {};
81 this._iopub_handlers = {};
81 this.register_iopub_handler('status', $.proxy(this._handle_status_message, this));
82 this.register_iopub_handler('status', $.proxy(this._handle_status_message, this));
82 this.register_iopub_handler('clear_output', $.proxy(this._handle_clear_output, this));
83 this.register_iopub_handler('clear_output', $.proxy(this._handle_clear_output, this));
83
84
84 for (var i=0; i < output_types.length; i++) {
85 for (var i=0; i < output_msg_types.length; i++) {
85 this.register_iopub_handler(output_types[i], $.proxy(this._handle_output_message, this));
86 this.register_iopub_handler(output_msg_types[i], $.proxy(this._handle_output_message, this));
86 }
87 }
87 };
88 };
88
89
89 /**
90 /**
90 * Start the Python kernel
91 * Start the Python kernel
91 * @method start
92 * @method start
92 */
93 */
93 Kernel.prototype.start = function (params) {
94 Kernel.prototype.start = function (params) {
94 params = params || {};
95 params = params || {};
95 if (!this.running) {
96 if (!this.running) {
96 var qs = $.param(params);
97 var qs = $.param(params);
97 this.post(utils.url_join_encode(this.kernel_service_url) + '?' + qs,
98 this.post(utils.url_join_encode(this.kernel_service_url) + '?' + qs,
98 $.proxy(this._kernel_started, this),
99 $.proxy(this._kernel_started, this),
99 'json'
100 'json'
100 );
101 );
101 }
102 }
102 };
103 };
103
104
104 /**
105 /**
105 * Restart the python kernel.
106 * Restart the python kernel.
106 *
107 *
107 * Emit a 'status_restarting.Kernel' event with
108 * Emit a 'status_restarting.Kernel' event with
108 * the current object as parameter
109 * the current object as parameter
109 *
110 *
110 * @method restart
111 * @method restart
111 */
112 */
112 Kernel.prototype.restart = function () {
113 Kernel.prototype.restart = function () {
113 $([IPython.events]).trigger('status_restarting.Kernel', {kernel: this});
114 $([IPython.events]).trigger('status_restarting.Kernel', {kernel: this});
114 if (this.running) {
115 if (this.running) {
115 this.stop_channels();
116 this.stop_channels();
116 this.post(utils.url_join_encode(this.kernel_url, "restart"),
117 this.post(utils.url_join_encode(this.kernel_url, "restart"),
117 $.proxy(this._kernel_started, this),
118 $.proxy(this._kernel_started, this),
118 'json'
119 'json'
119 );
120 );
120 }
121 }
121 };
122 };
122
123
123
124
124 Kernel.prototype._kernel_started = function (json) {
125 Kernel.prototype._kernel_started = function (json) {
125 console.log("Kernel started: ", json.id);
126 console.log("Kernel started: ", json.id);
126 this.running = true;
127 this.running = true;
127 this.kernel_id = json.id;
128 this.kernel_id = json.id;
128 // trailing 's' in https will become wss for secure web sockets
129 // trailing 's' in https will become wss for secure web sockets
129 this.ws_host = location.protocol.replace('http', 'ws') + "//" + location.host;
130 this.ws_host = location.protocol.replace('http', 'ws') + "//" + location.host;
130 this.kernel_url = utils.url_path_join(this.kernel_service_url, this.kernel_id);
131 this.kernel_url = utils.url_path_join(this.kernel_service_url, this.kernel_id);
131 this.start_channels();
132 this.start_channels();
132 };
133 };
133
134
134
135
135 Kernel.prototype._websocket_closed = function(ws_url, early) {
136 Kernel.prototype._websocket_closed = function(ws_url, early) {
136 this.stop_channels();
137 this.stop_channels();
137 $([IPython.events]).trigger('websocket_closed.Kernel',
138 $([IPython.events]).trigger('websocket_closed.Kernel',
138 {ws_url: ws_url, kernel: this, early: early}
139 {ws_url: ws_url, kernel: this, early: early}
139 );
140 );
140 };
141 };
141
142
142 /**
143 /**
143 * Start the `shell`and `iopub` channels.
144 * Start the `shell`and `iopub` channels.
144 * Will stop and restart them if they already exist.
145 * Will stop and restart them if they already exist.
145 *
146 *
146 * @method start_channels
147 * @method start_channels
147 */
148 */
148 Kernel.prototype.start_channels = function () {
149 Kernel.prototype.start_channels = function () {
149 var that = this;
150 var that = this;
150 this.stop_channels();
151 this.stop_channels();
151 var ws_host_url = this.ws_host + this.kernel_url;
152 var ws_host_url = this.ws_host + this.kernel_url;
152 console.log("Starting WebSockets:", ws_host_url);
153 console.log("Starting WebSockets:", ws_host_url);
153 this.shell_channel = new this.WebSocket(
154 this.shell_channel = new this.WebSocket(
154 this.ws_host + utils.url_join_encode(this.kernel_url, "shell")
155 this.ws_host + utils.url_join_encode(this.kernel_url, "shell")
155 );
156 );
156 this.stdin_channel = new this.WebSocket(
157 this.stdin_channel = new this.WebSocket(
157 this.ws_host + utils.url_join_encode(this.kernel_url, "stdin")
158 this.ws_host + utils.url_join_encode(this.kernel_url, "stdin")
158 );
159 );
159 this.iopub_channel = new this.WebSocket(
160 this.iopub_channel = new this.WebSocket(
160 this.ws_host + utils.url_join_encode(this.kernel_url, "iopub")
161 this.ws_host + utils.url_join_encode(this.kernel_url, "iopub")
161 );
162 );
162
163
163 var already_called_onclose = false; // only alert once
164 var already_called_onclose = false; // only alert once
164 var ws_closed_early = function(evt){
165 var ws_closed_early = function(evt){
165 if (already_called_onclose){
166 if (already_called_onclose){
166 return;
167 return;
167 }
168 }
168 already_called_onclose = true;
169 already_called_onclose = true;
169 if ( ! evt.wasClean ){
170 if ( ! evt.wasClean ){
170 that._websocket_closed(ws_host_url, true);
171 that._websocket_closed(ws_host_url, true);
171 }
172 }
172 };
173 };
173 var ws_closed_late = function(evt){
174 var ws_closed_late = function(evt){
174 if (already_called_onclose){
175 if (already_called_onclose){
175 return;
176 return;
176 }
177 }
177 already_called_onclose = true;
178 already_called_onclose = true;
178 if ( ! evt.wasClean ){
179 if ( ! evt.wasClean ){
179 that._websocket_closed(ws_host_url, false);
180 that._websocket_closed(ws_host_url, false);
180 }
181 }
181 };
182 };
182 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
183 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
183 for (var i=0; i < channels.length; i++) {
184 for (var i=0; i < channels.length; i++) {
184 channels[i].onopen = $.proxy(this._ws_opened, this);
185 channels[i].onopen = $.proxy(this._ws_opened, this);
185 channels[i].onclose = ws_closed_early;
186 channels[i].onclose = ws_closed_early;
186 }
187 }
187 // switch from early-close to late-close message after 1s
188 // switch from early-close to late-close message after 1s
188 setTimeout(function() {
189 setTimeout(function() {
189 for (var i=0; i < channels.length; i++) {
190 for (var i=0; i < channels.length; i++) {
190 if (channels[i] !== null) {
191 if (channels[i] !== null) {
191 channels[i].onclose = ws_closed_late;
192 channels[i].onclose = ws_closed_late;
192 }
193 }
193 }
194 }
194 }, 1000);
195 }, 1000);
195 this.shell_channel.onmessage = $.proxy(this._handle_shell_reply, this);
196 this.shell_channel.onmessage = $.proxy(this._handle_shell_reply, this);
196 this.iopub_channel.onmessage = $.proxy(this._handle_iopub_message, this);
197 this.iopub_channel.onmessage = $.proxy(this._handle_iopub_message, this);
197 this.stdin_channel.onmessage = $.proxy(this._handle_input_request, this);
198 this.stdin_channel.onmessage = $.proxy(this._handle_input_request, this);
198 };
199 };
199
200
200 /**
201 /**
201 * Handle a websocket entering the open state
202 * Handle a websocket entering the open state
202 * sends session and cookie authentication info as first message.
203 * sends session and cookie authentication info as first message.
203 * Once all sockets are open, signal the Kernel.status_started event.
204 * Once all sockets are open, signal the Kernel.status_started event.
204 * @method _ws_opened
205 * @method _ws_opened
205 */
206 */
206 Kernel.prototype._ws_opened = function (evt) {
207 Kernel.prototype._ws_opened = function (evt) {
207 // send the session id so the Session object Python-side
208 // send the session id so the Session object Python-side
208 // has the same identity
209 // has the same identity
209 evt.target.send(this.session_id + ':' + document.cookie);
210 evt.target.send(this.session_id + ':' + document.cookie);
210
211
211 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
212 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
212 for (var i=0; i < channels.length; i++) {
213 for (var i=0; i < channels.length; i++) {
213 // if any channel is not ready, don't trigger event.
214 // if any channel is not ready, don't trigger event.
214 if ( !channels[i].readyState ) return;
215 if ( !channels[i].readyState ) return;
215 }
216 }
216 // all events ready, trigger started event.
217 // all events ready, trigger started event.
217 $([IPython.events]).trigger('status_started.Kernel', {kernel: this});
218 $([IPython.events]).trigger('status_started.Kernel', {kernel: this});
218 };
219 };
219
220
220 /**
221 /**
221 * Stop the websocket channels.
222 * Stop the websocket channels.
222 * @method stop_channels
223 * @method stop_channels
223 */
224 */
224 Kernel.prototype.stop_channels = function () {
225 Kernel.prototype.stop_channels = function () {
225 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
226 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
226 for (var i=0; i < channels.length; i++) {
227 for (var i=0; i < channels.length; i++) {
227 if ( channels[i] !== null ) {
228 if ( channels[i] !== null ) {
228 channels[i].onclose = null;
229 channels[i].onclose = null;
229 channels[i].close();
230 channels[i].close();
230 }
231 }
231 }
232 }
232 this.shell_channel = this.iopub_channel = this.stdin_channel = null;
233 this.shell_channel = this.iopub_channel = this.stdin_channel = null;
233 };
234 };
234
235
235 // Main public methods.
236 // Main public methods.
236
237
237 // send a message on the Kernel's shell channel
238 // send a message on the Kernel's shell channel
238 Kernel.prototype.send_shell_message = function (msg_type, content, callbacks, metadata) {
239 Kernel.prototype.send_shell_message = function (msg_type, content, callbacks, metadata) {
239 var msg = this._get_msg(msg_type, content, metadata);
240 var msg = this._get_msg(msg_type, content, metadata);
240 this.shell_channel.send(JSON.stringify(msg));
241 this.shell_channel.send(JSON.stringify(msg));
241 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
242 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
242 return msg.header.msg_id;
243 return msg.header.msg_id;
243 };
244 };
244
245
245 /**
246 /**
246 * Get kernel info
247 * Get kernel info
247 *
248 *
248 * @param callback {function}
249 * @param callback {function}
249 * @method object_info
250 * @method kernel_info
250 *
251 *
251 * When calling this method, pass a callback function that expects one argument.
252 * When calling this method, pass a callback function that expects one argument.
252 * The callback will be passed the complete `kernel_info_reply` message documented
253 * The callback will be passed the complete `kernel_info_reply` message documented
253 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#kernel-info)
254 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#kernel-info)
254 */
255 */
255 Kernel.prototype.kernel_info = function (callback) {
256 Kernel.prototype.kernel_info = function (callback) {
256 var callbacks;
257 var callbacks;
257 if (callback) {
258 if (callback) {
258 callbacks = { shell : { reply : callback } };
259 callbacks = { shell : { reply : callback } };
259 }
260 }
260 return this.send_shell_message("kernel_info_request", {}, callbacks);
261 return this.send_shell_message("kernel_info_request", {}, callbacks);
261 };
262 };
262
263
263 /**
264 /**
264 * Get info on an object
265 * Get info on an object
265 *
266 *
266 * @param objname {string}
267 * @param code {string}
268 * @param cursor_pos {integer}
267 * @param callback {function}
269 * @param callback {function}
268 * @method object_info
270 * @method inspect
269 *
271 *
270 * When calling this method, pass a callback function that expects one argument.
272 * When calling this method, pass a callback function that expects one argument.
271 * The callback will be passed the complete `object_info_reply` message documented
273 * The callback will be passed the complete `inspect_reply` message documented
272 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#object-information)
274 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#object-information)
273 */
275 */
274 Kernel.prototype.object_info = function (objname, callback) {
276 Kernel.prototype.inspect = function (code, cursor_pos, callback) {
275 var callbacks;
277 var callbacks;
276 if (callback) {
278 if (callback) {
277 callbacks = { shell : { reply : callback } };
279 callbacks = { shell : { reply : callback } };
278 }
280 }
279
281
280 if (typeof(objname) !== null && objname !== null) {
282 var content = {
281 var content = {
283 code : code,
282 oname : objname.toString(),
284 cursor_pos : cursor_pos,
283 detail_level : 0,
285 detail_level : 0,
284 };
286 };
285 return this.send_shell_message("object_info_request", content, callbacks);
287 return this.send_shell_message("inspect_request", content, callbacks);
286 }
287 return;
288 };
288 };
289
289
290 /**
290 /**
291 * Execute given code into kernel, and pass result to callback.
291 * Execute given code into kernel, and pass result to callback.
292 *
292 *
293 * @async
293 * @async
294 * @method execute
294 * @method execute
295 * @param {string} code
295 * @param {string} code
296 * @param [callbacks] {Object} With the following keys (all optional)
296 * @param [callbacks] {Object} With the following keys (all optional)
297 * @param callbacks.shell.reply {function}
297 * @param callbacks.shell.reply {function}
298 * @param callbacks.shell.payload.[payload_name] {function}
298 * @param callbacks.shell.payload.[payload_name] {function}
299 * @param callbacks.iopub.output {function}
299 * @param callbacks.iopub.output {function}
300 * @param callbacks.iopub.clear_output {function}
300 * @param callbacks.iopub.clear_output {function}
301 * @param callbacks.input {function}
301 * @param callbacks.input {function}
302 * @param {object} [options]
302 * @param {object} [options]
303 * @param [options.silent=false] {Boolean}
303 * @param [options.silent=false] {Boolean}
304 * @param [options.user_expressions=empty_dict] {Dict}
304 * @param [options.user_expressions=empty_dict] {Dict}
305 * @param [options.user_variables=empty_list] {List od Strings}
306 * @param [options.allow_stdin=false] {Boolean} true|false
305 * @param [options.allow_stdin=false] {Boolean} true|false
307 *
306 *
308 * @example
307 * @example
309 *
308 *
310 * The options object should contain the options for the execute call. Its default
309 * The options object should contain the options for the execute call. Its default
311 * values are:
310 * values are:
312 *
311 *
313 * options = {
312 * options = {
314 * silent : true,
313 * silent : true,
315 * user_variables : [],
316 * user_expressions : {},
314 * user_expressions : {},
317 * allow_stdin : false
315 * allow_stdin : false
318 * }
316 * }
319 *
317 *
320 * When calling this method pass a callbacks structure of the form:
318 * When calling this method pass a callbacks structure of the form:
321 *
319 *
322 * callbacks = {
320 * callbacks = {
323 * shell : {
321 * shell : {
324 * reply : execute_reply_callback,
322 * reply : execute_reply_callback,
325 * payload : {
323 * payload : {
326 * set_next_input : set_next_input_callback,
324 * set_next_input : set_next_input_callback,
327 * }
325 * }
328 * },
326 * },
329 * iopub : {
327 * iopub : {
330 * output : output_callback,
328 * output : output_callback,
331 * clear_output : clear_output_callback,
329 * clear_output : clear_output_callback,
332 * },
330 * },
333 * input : raw_input_callback
331 * input : raw_input_callback
334 * }
332 * }
335 *
333 *
336 * Each callback will be passed the entire message as a single arugment.
334 * Each callback will be passed the entire message as a single arugment.
337 * Payload handlers will be passed the corresponding payload and the execute_reply message.
335 * Payload handlers will be passed the corresponding payload and the execute_reply message.
338 */
336 */
339 Kernel.prototype.execute = function (code, callbacks, options) {
337 Kernel.prototype.execute = function (code, callbacks, options) {
340
338
341 var content = {
339 var content = {
342 code : code,
340 code : code,
343 silent : true,
341 silent : true,
344 store_history : false,
342 store_history : false,
345 user_variables : [],
346 user_expressions : {},
343 user_expressions : {},
347 allow_stdin : false
344 allow_stdin : false
348 };
345 };
349 callbacks = callbacks || {};
346 callbacks = callbacks || {};
350 if (callbacks.input !== undefined) {
347 if (callbacks.input !== undefined) {
351 content.allow_stdin = true;
348 content.allow_stdin = true;
352 }
349 }
353 $.extend(true, content, options);
350 $.extend(true, content, options);
354 $([IPython.events]).trigger('execution_request.Kernel', {kernel: this, content:content});
351 $([IPython.events]).trigger('execution_request.Kernel', {kernel: this, content:content});
355 return this.send_shell_message("execute_request", content, callbacks);
352 return this.send_shell_message("execute_request", content, callbacks);
356 };
353 };
357
354
358 /**
355 /**
359 * When calling this method, pass a function to be called with the `complete_reply` message
356 * When calling this method, pass a function to be called with the `complete_reply` message
360 * as its only argument when it arrives.
357 * as its only argument when it arrives.
361 *
358 *
362 * `complete_reply` is documented
359 * `complete_reply` is documented
363 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#complete)
360 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#complete)
364 *
361 *
365 * @method complete
362 * @method complete
366 * @param line {integer}
363 * @param code {string}
367 * @param cursor_pos {integer}
364 * @param cursor_pos {integer}
368 * @param callback {function}
365 * @param callback {function}
369 *
366 *
370 */
367 */
371 Kernel.prototype.complete = function (line, cursor_pos, callback) {
368 Kernel.prototype.complete = function (code, cursor_pos, callback) {
372 var callbacks;
369 var callbacks;
373 if (callback) {
370 if (callback) {
374 callbacks = { shell : { reply : callback } };
371 callbacks = { shell : { reply : callback } };
375 }
372 }
376 var content = {
373 var content = {
377 text : '',
374 code : code,
378 line : line,
375 cursor_pos : cursor_pos,
379 block : null,
380 cursor_pos : cursor_pos
381 };
376 };
382 return this.send_shell_message("complete_request", content, callbacks);
377 return this.send_shell_message("complete_request", content, callbacks);
383 };
378 };
384
379
385
380
386 Kernel.prototype.interrupt = function () {
381 Kernel.prototype.interrupt = function () {
387 if (this.running) {
382 if (this.running) {
388 $([IPython.events]).trigger('status_interrupting.Kernel', {kernel: this});
383 $([IPython.events]).trigger('status_interrupting.Kernel', {kernel: this});
389 this.post(utils.url_join_encode(this.kernel_url, "interrupt"));
384 this.post(utils.url_join_encode(this.kernel_url, "interrupt"));
390 }
385 }
391 };
386 };
392
387
393
388
394 Kernel.prototype.kill = function () {
389 Kernel.prototype.kill = function () {
395 if (this.running) {
390 if (this.running) {
396 this.running = false;
391 this.running = false;
397 var settings = {
392 var settings = {
398 cache : false,
393 cache : false,
399 type : "DELETE",
394 type : "DELETE",
400 error : utils.log_ajax_error,
395 error : utils.log_ajax_error,
401 };
396 };
402 $.ajax(utils.url_join_encode(this.kernel_url), settings);
397 $.ajax(utils.url_join_encode(this.kernel_url), settings);
403 }
398 }
404 };
399 };
405
400
406 Kernel.prototype.send_input_reply = function (input) {
401 Kernel.prototype.send_input_reply = function (input) {
407 var content = {
402 var content = {
408 value : input,
403 value : input,
409 };
404 };
410 $([IPython.events]).trigger('input_reply.Kernel', {kernel: this, content:content});
405 $([IPython.events]).trigger('input_reply.Kernel', {kernel: this, content:content});
411 var msg = this._get_msg("input_reply", content);
406 var msg = this._get_msg("input_reply", content);
412 this.stdin_channel.send(JSON.stringify(msg));
407 this.stdin_channel.send(JSON.stringify(msg));
413 return msg.header.msg_id;
408 return msg.header.msg_id;
414 };
409 };
415
410
416
411
417 // Reply handlers
412 // Reply handlers
418
413
419 Kernel.prototype.register_iopub_handler = function (msg_type, callback) {
414 Kernel.prototype.register_iopub_handler = function (msg_type, callback) {
420 this._iopub_handlers[msg_type] = callback;
415 this._iopub_handlers[msg_type] = callback;
421 };
416 };
422
417
423 Kernel.prototype.get_iopub_handler = function (msg_type) {
418 Kernel.prototype.get_iopub_handler = function (msg_type) {
424 // get iopub handler for a specific message type
419 // get iopub handler for a specific message type
425 return this._iopub_handlers[msg_type];
420 return this._iopub_handlers[msg_type];
426 };
421 };
427
422
428
423
429 Kernel.prototype.get_callbacks_for_msg = function (msg_id) {
424 Kernel.prototype.get_callbacks_for_msg = function (msg_id) {
430 // get callbacks for a specific message
425 // get callbacks for a specific message
431 if (msg_id == this.last_msg_id) {
426 if (msg_id == this.last_msg_id) {
432 return this.last_msg_callbacks;
427 return this.last_msg_callbacks;
433 } else {
428 } else {
434 return this._msg_callbacks[msg_id];
429 return this._msg_callbacks[msg_id];
435 }
430 }
436 };
431 };
437
432
438
433
439 Kernel.prototype.clear_callbacks_for_msg = function (msg_id) {
434 Kernel.prototype.clear_callbacks_for_msg = function (msg_id) {
440 if (this._msg_callbacks[msg_id] !== undefined ) {
435 if (this._msg_callbacks[msg_id] !== undefined ) {
441 delete this._msg_callbacks[msg_id];
436 delete this._msg_callbacks[msg_id];
442 }
437 }
443 };
438 };
444
439
445 Kernel.prototype._finish_shell = function (msg_id) {
440 Kernel.prototype._finish_shell = function (msg_id) {
446 var callbacks = this._msg_callbacks[msg_id];
441 var callbacks = this._msg_callbacks[msg_id];
447 if (callbacks !== undefined) {
442 if (callbacks !== undefined) {
448 callbacks.shell_done = true;
443 callbacks.shell_done = true;
449 if (callbacks.iopub_done) {
444 if (callbacks.iopub_done) {
450 this.clear_callbacks_for_msg(msg_id);
445 this.clear_callbacks_for_msg(msg_id);
451 }
446 }
452 }
447 }
453 };
448 };
454
449
455 Kernel.prototype._finish_iopub = function (msg_id) {
450 Kernel.prototype._finish_iopub = function (msg_id) {
456 var callbacks = this._msg_callbacks[msg_id];
451 var callbacks = this._msg_callbacks[msg_id];
457 if (callbacks !== undefined) {
452 if (callbacks !== undefined) {
458 callbacks.iopub_done = true;
453 callbacks.iopub_done = true;
459 if (callbacks.shell_done) {
454 if (callbacks.shell_done) {
460 this.clear_callbacks_for_msg(msg_id);
455 this.clear_callbacks_for_msg(msg_id);
461 }
456 }
462 }
457 }
463 };
458 };
464
459
465 /* Set callbacks for a particular message.
460 /* Set callbacks for a particular message.
466 * Callbacks should be a struct of the following form:
461 * Callbacks should be a struct of the following form:
467 * shell : {
462 * shell : {
468 *
463 *
469 * }
464 * }
470
465
471 */
466 */
472 Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) {
467 Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) {
473 this.last_msg_id = msg_id;
468 this.last_msg_id = msg_id;
474 if (callbacks) {
469 if (callbacks) {
475 // shallow-copy mapping, because we will modify it at the top level
470 // shallow-copy mapping, because we will modify it at the top level
476 var cbcopy = this._msg_callbacks[msg_id] = this.last_msg_callbacks = {};
471 var cbcopy = this._msg_callbacks[msg_id] = this.last_msg_callbacks = {};
477 cbcopy.shell = callbacks.shell;
472 cbcopy.shell = callbacks.shell;
478 cbcopy.iopub = callbacks.iopub;
473 cbcopy.iopub = callbacks.iopub;
479 cbcopy.input = callbacks.input;
474 cbcopy.input = callbacks.input;
480 cbcopy.shell_done = (!callbacks.shell);
475 cbcopy.shell_done = (!callbacks.shell);
481 cbcopy.iopub_done = (!callbacks.iopub);
476 cbcopy.iopub_done = (!callbacks.iopub);
482 } else {
477 } else {
483 this.last_msg_callbacks = {};
478 this.last_msg_callbacks = {};
484 }
479 }
485 };
480 };
486
481
487
482
488 Kernel.prototype._handle_shell_reply = function (e) {
483 Kernel.prototype._handle_shell_reply = function (e) {
489 var reply = $.parseJSON(e.data);
484 var reply = $.parseJSON(e.data);
490 $([IPython.events]).trigger('shell_reply.Kernel', {kernel: this, reply:reply});
485 $([IPython.events]).trigger('shell_reply.Kernel', {kernel: this, reply:reply});
491 var content = reply.content;
486 var content = reply.content;
492 var metadata = reply.metadata;
487 var metadata = reply.metadata;
493 var parent_id = reply.parent_header.msg_id;
488 var parent_id = reply.parent_header.msg_id;
494 var callbacks = this.get_callbacks_for_msg(parent_id);
489 var callbacks = this.get_callbacks_for_msg(parent_id);
495 if (!callbacks || !callbacks.shell) {
490 if (!callbacks || !callbacks.shell) {
496 return;
491 return;
497 }
492 }
498 var shell_callbacks = callbacks.shell;
493 var shell_callbacks = callbacks.shell;
499
494
500 // signal that shell callbacks are done
495 // signal that shell callbacks are done
501 this._finish_shell(parent_id);
496 this._finish_shell(parent_id);
502
497
503 if (shell_callbacks.reply !== undefined) {
498 if (shell_callbacks.reply !== undefined) {
504 shell_callbacks.reply(reply);
499 shell_callbacks.reply(reply);
505 }
500 }
506 if (content.payload && shell_callbacks.payload) {
501 if (content.payload && shell_callbacks.payload) {
507 this._handle_payloads(content.payload, shell_callbacks.payload, reply);
502 this._handle_payloads(content.payload, shell_callbacks.payload, reply);
508 }
503 }
509 };
504 };
510
505
511
506
512 Kernel.prototype._handle_payloads = function (payloads, payload_callbacks, msg) {
507 Kernel.prototype._handle_payloads = function (payloads, payload_callbacks, msg) {
513 var l = payloads.length;
508 var l = payloads.length;
514 // Payloads are handled by triggering events because we don't want the Kernel
509 // Payloads are handled by triggering events because we don't want the Kernel
515 // to depend on the Notebook or Pager classes.
510 // to depend on the Notebook or Pager classes.
516 for (var i=0; i<l; i++) {
511 for (var i=0; i<l; i++) {
517 var payload = payloads[i];
512 var payload = payloads[i];
518 var callback = payload_callbacks[payload.source];
513 var callback = payload_callbacks[payload.source];
519 if (callback) {
514 if (callback) {
520 callback(payload, msg);
515 callback(payload, msg);
521 }
516 }
522 }
517 }
523 };
518 };
524
519
525 Kernel.prototype._handle_status_message = function (msg) {
520 Kernel.prototype._handle_status_message = function (msg) {
526 var execution_state = msg.content.execution_state;
521 var execution_state = msg.content.execution_state;
527 var parent_id = msg.parent_header.msg_id;
522 var parent_id = msg.parent_header.msg_id;
528
523
529 // dispatch status msg callbacks, if any
524 // dispatch status msg callbacks, if any
530 var callbacks = this.get_callbacks_for_msg(parent_id);
525 var callbacks = this.get_callbacks_for_msg(parent_id);
531 if (callbacks && callbacks.iopub && callbacks.iopub.status) {
526 if (callbacks && callbacks.iopub && callbacks.iopub.status) {
532 try {
527 try {
533 callbacks.iopub.status(msg);
528 callbacks.iopub.status(msg);
534 } catch (e) {
529 } catch (e) {
535 console.log("Exception in status msg handler", e, e.stack);
530 console.log("Exception in status msg handler", e, e.stack);
536 }
531 }
537 }
532 }
538
533
539 if (execution_state === 'busy') {
534 if (execution_state === 'busy') {
540 $([IPython.events]).trigger('status_busy.Kernel', {kernel: this});
535 $([IPython.events]).trigger('status_busy.Kernel', {kernel: this});
541 } else if (execution_state === 'idle') {
536 } else if (execution_state === 'idle') {
542 // signal that iopub callbacks are (probably) done
537 // signal that iopub callbacks are (probably) done
543 // async output may still arrive,
538 // async output may still arrive,
544 // but only for the most recent request
539 // but only for the most recent request
545 this._finish_iopub(parent_id);
540 this._finish_iopub(parent_id);
546
541
547 // trigger status_idle event
542 // trigger status_idle event
548 $([IPython.events]).trigger('status_idle.Kernel', {kernel: this});
543 $([IPython.events]).trigger('status_idle.Kernel', {kernel: this});
549 } else if (execution_state === 'restarting') {
544 } else if (execution_state === 'restarting') {
550 // autorestarting is distinct from restarting,
545 // autorestarting is distinct from restarting,
551 // in that it means the kernel died and the server is restarting it.
546 // in that it means the kernel died and the server is restarting it.
552 // status_restarting sets the notification widget,
547 // status_restarting sets the notification widget,
553 // autorestart shows the more prominent dialog.
548 // autorestart shows the more prominent dialog.
554 $([IPython.events]).trigger('status_autorestarting.Kernel', {kernel: this});
549 $([IPython.events]).trigger('status_autorestarting.Kernel', {kernel: this});
555 $([IPython.events]).trigger('status_restarting.Kernel', {kernel: this});
550 $([IPython.events]).trigger('status_restarting.Kernel', {kernel: this});
556 } else if (execution_state === 'dead') {
551 } else if (execution_state === 'dead') {
557 this.stop_channels();
552 this.stop_channels();
558 $([IPython.events]).trigger('status_dead.Kernel', {kernel: this});
553 $([IPython.events]).trigger('status_dead.Kernel', {kernel: this});
559 }
554 }
560 };
555 };
561
556
562
557
563 // handle clear_output message
558 // handle clear_output message
564 Kernel.prototype._handle_clear_output = function (msg) {
559 Kernel.prototype._handle_clear_output = function (msg) {
565 var callbacks = this.get_callbacks_for_msg(msg.parent_header.msg_id);
560 var callbacks = this.get_callbacks_for_msg(msg.parent_header.msg_id);
566 if (!callbacks || !callbacks.iopub) {
561 if (!callbacks || !callbacks.iopub) {
567 return;
562 return;
568 }
563 }
569 var callback = callbacks.iopub.clear_output;
564 var callback = callbacks.iopub.clear_output;
570 if (callback) {
565 if (callback) {
571 callback(msg);
566 callback(msg);
572 }
567 }
573 };
568 };
574
569
575
570
576 // handle an output message (pyout, display_data, etc.)
571 // handle an output message (execute_result, display_data, etc.)
577 Kernel.prototype._handle_output_message = function (msg) {
572 Kernel.prototype._handle_output_message = function (msg) {
578 var callbacks = this.get_callbacks_for_msg(msg.parent_header.msg_id);
573 var callbacks = this.get_callbacks_for_msg(msg.parent_header.msg_id);
579 if (!callbacks || !callbacks.iopub) {
574 if (!callbacks || !callbacks.iopub) {
580 return;
575 return;
581 }
576 }
582 var callback = callbacks.iopub.output;
577 var callback = callbacks.iopub.output;
583 if (callback) {
578 if (callback) {
584 callback(msg);
579 callback(msg);
585 }
580 }
586 };
581 };
587
582
588 // dispatch IOPub messages to respective handlers.
583 // dispatch IOPub messages to respective handlers.
589 // each message type should have a handler.
584 // each message type should have a handler.
590 Kernel.prototype._handle_iopub_message = function (e) {
585 Kernel.prototype._handle_iopub_message = function (e) {
591 var msg = $.parseJSON(e.data);
586 var msg = $.parseJSON(e.data);
592
587
593 var handler = this.get_iopub_handler(msg.header.msg_type);
588 var handler = this.get_iopub_handler(msg.header.msg_type);
594 if (handler !== undefined) {
589 if (handler !== undefined) {
595 handler(msg);
590 handler(msg);
596 }
591 }
597 };
592 };
598
593
599
594
600 Kernel.prototype._handle_input_request = function (e) {
595 Kernel.prototype._handle_input_request = function (e) {
601 var request = $.parseJSON(e.data);
596 var request = $.parseJSON(e.data);
602 var header = request.header;
597 var header = request.header;
603 var content = request.content;
598 var content = request.content;
604 var metadata = request.metadata;
599 var metadata = request.metadata;
605 var msg_type = header.msg_type;
600 var msg_type = header.msg_type;
606 if (msg_type !== 'input_request') {
601 if (msg_type !== 'input_request') {
607 console.log("Invalid input request!", request);
602 console.log("Invalid input request!", request);
608 return;
603 return;
609 }
604 }
610 var callbacks = this.get_callbacks_for_msg(request.parent_header.msg_id);
605 var callbacks = this.get_callbacks_for_msg(request.parent_header.msg_id);
611 if (callbacks) {
606 if (callbacks) {
612 if (callbacks.input) {
607 if (callbacks.input) {
613 callbacks.input(request);
608 callbacks.input(request);
614 }
609 }
615 }
610 }
616 };
611 };
617
612
618
613
619 IPython.Kernel = Kernel;
614 IPython.Kernel = Kernel;
620
615
621 return IPython;
616 return IPython;
622
617
623 }(IPython));
618 }(IPython));
624
619
@@ -1,64 +1,63
1 //
1 //
2 // Test display of images
2 // Test display of images
3 //
3 //
4 // The effect of shape metadata is validated,
4 // The effect of shape metadata is validated,
5 // using Image(retina=True)
5 // using Image(retina=True)
6 //
6 //
7
7
8
8
9 // 2x2 black square in b64 jpeg and png
9 // 2x2 black square in b64 jpeg and png
10 b64_image_data = {
10 b64_image_data = {
11 "image/png" : "b'iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAC0lEQVR4nGNgQAYAAA4AAamRc7EA\\nAAAASUVORK5CYII='",
11 "image/png" : "b'iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAC0lEQVR4nGNgQAYAAA4AAamRc7EA\\nAAAASUVORK5CYII='",
12 "image/jpeg" : "b'/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0a\\nHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIy\\nMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAACAAIDASIA\\nAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQA\\nAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3\\nODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWm\\np6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEA\\nAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSEx\\nBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElK\\nU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3\\nuLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD5/ooo\\noA//2Q=='"
12 "image/jpeg" : "b'/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0a\\nHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIy\\nMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAACAAIDASIA\\nAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQA\\nAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3\\nODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWm\\np6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEA\\nAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSEx\\nBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElK\\nU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3\\nuLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD5/ooo\\noA//2Q=='"
13 }
13 }
14
14
15
15
16 casper.notebook_test(function () {
16 casper.notebook_test(function () {
17 // this.printLog();
18 this.test_img_shape = function(fmt, retina) {
17 this.test_img_shape = function(fmt, retina) {
19 this.thenEvaluate(function (b64data, retina) {
18 this.thenEvaluate(function (b64data, retina) {
20 IPython.notebook.get_cell(0).clear_output();
19 IPython.notebook.insert_cell_at_index(0, "code");
21 var cell = IPython.notebook.get_cell(0);
20 var cell = IPython.notebook.get_cell(0);
22 cell.set_text([
21 cell.set_text([
23 "import base64",
22 "import base64",
24 "from IPython.display import display, Image",
23 "from IPython.display import display, Image",
25 "data = base64.decodestring(" + b64data + ")",
24 "data = base64.decodestring(" + b64data + ")",
26 "retina = bool(" + retina + ")",
25 "retina = bool(" + retina + ")",
27 "display(Image(data, retina=retina))"
26 "display(Image(data, retina=retina))"
28 ].join("\n"));
27 ].join("\n"));
29 cell.execute();
28 cell.execute();
30 }, {b64data : b64_image_data[fmt], retina : retina ? 1:0 });
29 }, {b64data : b64_image_data[fmt], retina : retina ? 1:0 });
31
30
32 this.wait_for_output(0);
31 this.wait_for_output(0);
33
32
34 this.then(function() {
33 this.then(function() {
35 var img = this.evaluate(function() {
34 var img = this.evaluate(function() {
36 // get a summary of the image that was just displayed
35 // get a summary of the image that was just displayed
37 var cell = IPython.notebook.get_cell(0);
36 var cell = IPython.notebook.get_cell(0);
38 var img = $(cell.output_area.element.find("img")[0]);
37 var img = $(cell.output_area.element.find("img")[0]);
39 return {
38 return {
40 src : img.attr("src"),
39 src : img.attr("src"),
41 width : img.width(),
40 width : img.width(),
42 height : img.height(),
41 height : img.height(),
43 width_attr : img.attr("width"),
42 width_attr : img.attr("width"),
44 height_attr : img.attr("height")
43 height_attr : img.attr("height")
45 };
44 };
46 });
45 });
47 var prefix = "Image('" + fmt + "', retina=" + retina + ") ";
46 var prefix = "Image('" + fmt + "', retina=" + retina + ") ";
48 this.test.assertType(img, "object", prefix + "img was displayed");
47 this.test.assertType(img, "object", prefix + "img was displayed");
49 this.test.assertEquals(img.src.split(',')[0], "data:" + fmt + ";base64",
48 this.test.assertEquals(img.src.split(',')[0], "data:" + fmt + ";base64",
50 prefix + "data-uri prefix"
49 prefix + "data-uri prefix"
51 );
50 );
52 var sz = retina ? 1 : 2;
51 var sz = retina ? 1 : 2;
53 var sz_attr = retina ? "1" : undefined;
52 var sz_attr = retina ? "1" : undefined;
54 this.test.assertEquals(img.height, sz, prefix + "measured height");
53 this.test.assertEquals(img.height, sz, prefix + "measured height");
55 this.test.assertEquals(img.width, sz, prefix + "measured width");
54 this.test.assertEquals(img.width, sz, prefix + "measured width");
56 this.test.assertEquals(img.height_attr, sz_attr, prefix + "height attr");
55 this.test.assertEquals(img.height_attr, sz_attr, prefix + "height attr");
57 this.test.assertEquals(img.width_attr, sz_attr, prefix + "width attr");
56 this.test.assertEquals(img.width_attr, sz_attr, prefix + "width attr");
58 });
57 });
59 };
58 };
60 this.test_img_shape("image/png", false);
59 this.test_img_shape("image/png", false);
61 this.test_img_shape("image/png", true);
60 this.test_img_shape("image/png", true);
62 this.test_img_shape("image/jpeg", false);
61 this.test_img_shape("image/jpeg", false);
63 this.test_img_shape("image/jpeg", true);
62 this.test_img_shape("image/jpeg", true);
64 });
63 });
@@ -1,245 +1,245
1 // Test opening a rich notebook, saving it, and reopening it again.
1 // Test opening a rich notebook, saving it, and reopening it again.
2 //
2 //
3 //toJSON fromJSON toJSON and do a string comparison
3 //toJSON fromJSON toJSON and do a string comparison
4
4
5
5
6 // this is just a copy of OutputArea.mime_mape_r in IPython/html/static/notebook/js/outputarea.js
6 // this is just a copy of OutputArea.mime_mape_r in IPython/html/static/notebook/js/outputarea.js
7 mime = {
7 mime = {
8 "text" : "text/plain",
8 "text" : "text/plain",
9 "html" : "text/html",
9 "html" : "text/html",
10 "svg" : "image/svg+xml",
10 "svg" : "image/svg+xml",
11 "png" : "image/png",
11 "png" : "image/png",
12 "jpeg" : "image/jpeg",
12 "jpeg" : "image/jpeg",
13 "latex" : "text/latex",
13 "latex" : "text/latex",
14 "json" : "application/json",
14 "json" : "application/json",
15 "javascript" : "application/javascript",
15 "javascript" : "application/javascript",
16 };
16 };
17
17
18 var black_dot_jpeg="u\"\"\"/9j/4AAQSkZJRgABAQEASABIAAD/2wBDACodICUgGiolIiUvLSoyP2lEPzo6P4FcYUxpmYagnpaG\nk5GovfLNqLPltZGT0v/V5fr/////o8v///////L/////2wBDAS0vLz83P3xERHz/rpOu////////\n////////////////////////////////////////////////////////////wgARCAABAAEDAREA\nAhEBAxEB/8QAFAABAAAAAAAAAAAAAAAAAAAABP/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEA\nAhADEAAAARn/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/9oACAEBAAEFAn//xAAUEQEAAAAAAAAAAAAA\nAAAAAAAA/9oACAEDAQE/AX//xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAECAQE/AX//xAAUEAEA\nAAAAAAAAAAAAAAAAAAAA/9oACAEBAAY/An//xAAUEAEAAAAAAAAAAAAAAAAAAAAA/9oACAEBAAE/\nIX//2gAMAwEAAgADAAAAEB//xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAEDAQE/EH//xAAUEQEA\nAAAAAAAAAAAAAAAAAAAA/9oACAECAQE/EH//xAAUEAEAAAAAAAAAAAAAAAAAAAAA/9oACAEBAAE/\nEH//2Q==\"\"\"";
18 var black_dot_jpeg="u\"\"\"/9j/4AAQSkZJRgABAQEASABIAAD/2wBDACodICUgGiolIiUvLSoyP2lEPzo6P4FcYUxpmYagnpaG\nk5GovfLNqLPltZGT0v/V5fr/////o8v///////L/////2wBDAS0vLz83P3xERHz/rpOu////////\n////////////////////////////////////////////////////////////wgARCAABAAEDAREA\nAhEBAxEB/8QAFAABAAAAAAAAAAAAAAAAAAAABP/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEA\nAhADEAAAARn/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/9oACAEBAAEFAn//xAAUEQEAAAAAAAAAAAAA\nAAAAAAAA/9oACAEDAQE/AX//xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAECAQE/AX//xAAUEAEA\nAAAAAAAAAAAAAAAAAAAA/9oACAEBAAY/An//xAAUEAEAAAAAAAAAAAAAAAAAAAAA/9oACAEBAAE/\nIX//2gAMAwEAAgADAAAAEB//xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAEDAQE/EH//xAAUEQEA\nAAAAAAAAAAAAAAAAAAAA/9oACAECAQE/EH//xAAUEAEAAAAAAAAAAAAAAAAAAAAA/9oACAEBAAE/\nEH//2Q==\"\"\"";
19 var black_dot_png = 'u\"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAWJLR0QA\\niAUdSAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB94BCRQnOqNu0b4AAAAKSURBVAjXY2AA\\nAAACAAHiIbwzAAAAAElFTkSuQmCC\"';
19 var black_dot_png = 'u\"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAWJLR0QA\\niAUdSAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB94BCRQnOqNu0b4AAAAKSURBVAjXY2AA\\nAAACAAHiIbwzAAAAAElFTkSuQmCC\"';
20 var svg = "\"<svg width='1cm' height='1cm' viewBox='0 0 1000 500'><defs><style>rect {fill:red;}; </style></defs><rect id='r1' x='200' y='100' width='600' height='300' /></svg>\"";
20 var svg = "\"<svg width='1cm' height='1cm' viewBox='0 0 1000 500'><defs><style>rect {fill:red;}; </style></defs><rect id='r1' x='200' y='100' width='600' height='300' /></svg>\"";
21
21
22 // helper function to ensure that the short_name is found in the toJSON
22 // helper function to ensure that the short_name is found in the toJSON
23 // represetnation, while the original in-memory cell retains its long mimetype
23 // represetnation, while the original in-memory cell retains its long mimetype
24 // name, and that fromJSON also gets its long mimetype name
24 // name, and that fromJSON also gets its long mimetype name
25 function assert_has(short_name, json, result, result2) {
25 function assert_has(short_name, json, result, result2) {
26 long_name = mime[short_name];
26 long_name = mime[short_name];
27 this.test.assertTrue(json[0].hasOwnProperty(short_name),
27 this.test.assertTrue(json[0].hasOwnProperty(short_name),
28 'toJSON() representation uses ' + short_name);
28 'toJSON() representation uses ' + short_name);
29 this.test.assertTrue(result.hasOwnProperty(long_name),
29 this.test.assertTrue(result.hasOwnProperty(long_name),
30 'toJSON() original embedded JSON keeps ' + long_name);
30 'toJSON() original embedded JSON keeps ' + long_name);
31 this.test.assertTrue(result2.hasOwnProperty(long_name),
31 this.test.assertTrue(result2.hasOwnProperty(long_name),
32 'fromJSON() embedded ' + short_name + ' gets mime key ' + long_name);
32 'fromJSON() embedded ' + short_name + ' gets mime key ' + long_name);
33 }
33 }
34
34
35 // helper function for checkout that the first two cells have a particular
35 // helper function for checkout that the first two cells have a particular
36 // output_type (either 'pyout' or 'display_data'), and checks the to/fromJSON
36 // output_type (either 'execute_result' or 'display_data'), and checks the to/fromJSON
37 // for a set of mimetype keys, using their short names ('javascript', 'text',
37 // for a set of mimetype keys, using their short names ('javascript', 'text',
38 // 'png', etc).
38 // 'png', etc).
39 function check_output_area(output_type, keys) {
39 function check_output_area(output_type, keys) {
40 this.wait_for_output(0);
40 this.wait_for_output(0);
41 json = this.evaluate(function() {
41 json = this.evaluate(function() {
42 var json = IPython.notebook.get_cell(0).output_area.toJSON();
42 var json = IPython.notebook.get_cell(0).output_area.toJSON();
43 // appended cell will initially be empty, let's add some output
43 // appended cell will initially be empty, let's add some output
44 IPython.notebook.get_cell(1).output_area.fromJSON(json);
44 IPython.notebook.get_cell(1).output_area.fromJSON(json);
45 return json;
45 return json;
46 });
46 });
47 // The evaluate call above happens asynchronously: wait for cell[1] to have output
47 // The evaluate call above happens asynchronously: wait for cell[1] to have output
48 this.wait_for_output(1);
48 this.wait_for_output(1);
49 var result = this.get_output_cell(0);
49 var result = this.get_output_cell(0);
50 var result2 = this.get_output_cell(1);
50 var result2 = this.get_output_cell(1);
51 this.test.assertEquals(result.output_type, output_type,
51 this.test.assertEquals(result.output_type, output_type,
52 'testing ' + output_type + ' for ' + keys.join(' and '));
52 'testing ' + output_type + ' for ' + keys.join(' and '));
53
53
54 for (var idx in keys) {
54 for (var idx in keys) {
55 assert_has.apply(this, [keys[idx], json, result, result2]);
55 assert_has.apply(this, [keys[idx], json, result, result2]);
56 }
56 }
57 }
57 }
58
58
59
59
60 // helper function to clear the first two cells, set the text of and execute
60 // helper function to clear the first two cells, set the text of and execute
61 // the first one
61 // the first one
62 function clear_and_execute(that, code) {
62 function clear_and_execute(that, code) {
63 that.evaluate(function() {
63 that.evaluate(function() {
64 IPython.notebook.get_cell(0).clear_output();
64 IPython.notebook.get_cell(0).clear_output();
65 IPython.notebook.get_cell(1).clear_output();
65 IPython.notebook.get_cell(1).clear_output();
66 });
66 });
67 that.then(function () {
67 that.then(function () {
68 that.set_cell_text(0, code);
68 that.set_cell_text(0, code);
69 that.execute_cell(0);
69 that.execute_cell(0);
70 that.wait_for_idle();
70 that.wait_for_idle();
71 });
71 });
72 };
72 };
73
73
74 casper.notebook_test(function () {
74 casper.notebook_test(function () {
75 this.evaluate(function () {
75 this.evaluate(function () {
76 var cell = IPython.notebook.get_cell(0);
76 var cell = IPython.notebook.get_cell(0);
77 // "we have to make messes to find out who we are"
77 // "we have to make messes to find out who we are"
78 cell.set_text([
78 cell.set_text([
79 "%%javascript",
79 "%%javascript",
80 "IPython.notebook.insert_cell_below('code')"
80 "IPython.notebook.insert_cell_below('code')"
81 ].join('\n')
81 ].join('\n')
82 );
82 );
83 });
83 });
84
84
85 this.execute_cell_then(0, function () {
85 this.execute_cell_then(0, function () {
86 var result = this.get_output_cell(0);
86 var result = this.get_output_cell(0);
87 var num_cells = this.get_cells_length();
87 var num_cells = this.get_cells_length();
88 this.test.assertEquals(num_cells, 2, '%%javascript magic works');
88 this.test.assertEquals(num_cells, 2, '%%javascript magic works');
89 this.test.assertTrue(result.hasOwnProperty('application/javascript'),
89 this.test.assertTrue(result.hasOwnProperty('application/javascript'),
90 'testing JS embedded with mime key');
90 'testing JS embedded with mime key');
91 });
91 });
92
92
93 //this.thenEvaluate(function() { IPython.notebook.save_notebook(); });
93 //this.thenEvaluate(function() { IPython.notebook.save_notebook(); });
94 this.then(function () {
94 this.then(function () {
95 clear_and_execute(this, [
95 clear_and_execute(this, [
96 "%%javascript",
96 "%%javascript",
97 "var a=5;"
97 "var a=5;"
98 ].join('\n'));
98 ].join('\n'));
99 });
99 });
100
100
101
101
102 this.then(function () {
102 this.then(function () {
103 check_output_area.apply(this, ['display_data', ['javascript']]);
103 check_output_area.apply(this, ['display_data', ['javascript']]);
104
104
105 });
105 });
106
106
107 this.then(function() {
107 this.then(function() {
108 clear_and_execute(this, '%lsmagic');
108 clear_and_execute(this, '%lsmagic');
109 });
109 });
110
110
111 this.then(function () {
111 this.then(function () {
112 check_output_area.apply(this, ['pyout', ['text', 'json']]);
112 check_output_area.apply(this, ['execute_result', ['text', 'json']]);
113 });
113 });
114
114
115 this.then(function() {
115 this.then(function() {
116 clear_and_execute(this,
116 clear_and_execute(this,
117 "x = %lsmagic\nfrom IPython.display import display; display(x)");
117 "x = %lsmagic\nfrom IPython.display import display; display(x)");
118 });
118 });
119
119
120 this.then(function ( ) {
120 this.then(function ( ) {
121 check_output_area.apply(this, ['display_data', ['text', 'json']]);
121 check_output_area.apply(this, ['display_data', ['text', 'json']]);
122 });
122 });
123
123
124 this.then(function() {
124 this.then(function() {
125 clear_and_execute(this,
125 clear_and_execute(this,
126 "from IPython.display import Latex; Latex('$X^2$')");
126 "from IPython.display import Latex; Latex('$X^2$')");
127 });
127 });
128
128
129 this.then(function ( ) {
129 this.then(function ( ) {
130 check_output_area.apply(this, ['pyout', ['text', 'latex']]);
130 check_output_area.apply(this, ['execute_result', ['text', 'latex']]);
131 });
131 });
132
132
133 this.then(function() {
133 this.then(function() {
134 clear_and_execute(this,
134 clear_and_execute(this,
135 "from IPython.display import Latex, display; display(Latex('$X^2$'))");
135 "from IPython.display import Latex, display; display(Latex('$X^2$'))");
136 });
136 });
137
137
138 this.then(function ( ) {
138 this.then(function ( ) {
139 check_output_area.apply(this, ['display_data', ['text', 'latex']]);
139 check_output_area.apply(this, ['display_data', ['text', 'latex']]);
140 });
140 });
141
141
142 this.then(function() {
142 this.then(function() {
143 clear_and_execute(this,
143 clear_and_execute(this,
144 "from IPython.display import HTML; HTML('<b>it works!</b>')");
144 "from IPython.display import HTML; HTML('<b>it works!</b>')");
145 });
145 });
146
146
147 this.then(function ( ) {
147 this.then(function ( ) {
148 check_output_area.apply(this, ['pyout', ['text', 'html']]);
148 check_output_area.apply(this, ['execute_result', ['text', 'html']]);
149 });
149 });
150
150
151 this.then(function() {
151 this.then(function() {
152 clear_and_execute(this,
152 clear_and_execute(this,
153 "from IPython.display import HTML, display; display(HTML('<b>it works!</b>'))");
153 "from IPython.display import HTML, display; display(HTML('<b>it works!</b>'))");
154 });
154 });
155
155
156 this.then(function ( ) {
156 this.then(function ( ) {
157 check_output_area.apply(this, ['display_data', ['text', 'html']]);
157 check_output_area.apply(this, ['display_data', ['text', 'html']]);
158 });
158 });
159
159
160
160
161 this.then(function() {
161 this.then(function() {
162 clear_and_execute(this,
162 clear_and_execute(this,
163 "from IPython.display import Image; Image(" + black_dot_png + ")");
163 "from IPython.display import Image; Image(" + black_dot_png + ")");
164 });
164 });
165 this.thenEvaluate(function() { IPython.notebook.save_notebook(); });
165 this.thenEvaluate(function() { IPython.notebook.save_notebook(); });
166
166
167 this.then(function ( ) {
167 this.then(function ( ) {
168 check_output_area.apply(this, ['pyout', ['text', 'png']]);
168 check_output_area.apply(this, ['execute_result', ['text', 'png']]);
169 });
169 });
170
170
171 this.then(function() {
171 this.then(function() {
172 clear_and_execute(this,
172 clear_and_execute(this,
173 "from IPython.display import Image, display; display(Image(" + black_dot_png + "))");
173 "from IPython.display import Image, display; display(Image(" + black_dot_png + "))");
174 });
174 });
175
175
176 this.then(function ( ) {
176 this.then(function ( ) {
177 check_output_area.apply(this, ['display_data', ['text', 'png']]);
177 check_output_area.apply(this, ['display_data', ['text', 'png']]);
178 });
178 });
179
179
180
180
181 this.then(function() {
181 this.then(function() {
182 clear_and_execute(this,
182 clear_and_execute(this,
183 "from IPython.display import Image; Image(" + black_dot_jpeg + ", format='jpeg')");
183 "from IPython.display import Image; Image(" + black_dot_jpeg + ", format='jpeg')");
184 });
184 });
185
185
186 this.then(function ( ) {
186 this.then(function ( ) {
187 check_output_area.apply(this, ['pyout', ['text', 'jpeg']]);
187 check_output_area.apply(this, ['execute_result', ['text', 'jpeg']]);
188 });
188 });
189
189
190 this.then(function() {
190 this.then(function() {
191 clear_and_execute(this,
191 clear_and_execute(this,
192 "from IPython.display import Image, display; display(Image(" + black_dot_jpeg + ", format='jpeg'))");
192 "from IPython.display import Image, display; display(Image(" + black_dot_jpeg + ", format='jpeg'))");
193 });
193 });
194
194
195 this.then(function ( ) {
195 this.then(function ( ) {
196 check_output_area.apply(this, ['display_data', ['text', 'jpeg']]);
196 check_output_area.apply(this, ['display_data', ['text', 'jpeg']]);
197 });
197 });
198
198
199 this.then(function() {
199 this.then(function() {
200 clear_and_execute(this,
200 clear_and_execute(this,
201 "from IPython.core.display import SVG; SVG(" + svg + ")");
201 "from IPython.core.display import SVG; SVG(" + svg + ")");
202 });
202 });
203
203
204 this.then(function ( ) {
204 this.then(function ( ) {
205 check_output_area.apply(this, ['pyout', ['text', 'svg']]);
205 check_output_area.apply(this, ['execute_result', ['text', 'svg']]);
206 });
206 });
207
207
208 this.then(function() {
208 this.then(function() {
209 clear_and_execute(this,
209 clear_and_execute(this,
210 "from IPython.core.display import SVG, display; display(SVG(" + svg + "))");
210 "from IPython.core.display import SVG, display; display(SVG(" + svg + "))");
211 });
211 });
212
212
213 this.then(function ( ) {
213 this.then(function ( ) {
214 check_output_area.apply(this, ['display_data', ['text', 'svg']]);
214 check_output_area.apply(this, ['display_data', ['text', 'svg']]);
215 });
215 });
216
216
217 this.thenEvaluate(function() { IPython.notebook.save_notebook(); });
217 this.thenEvaluate(function() { IPython.notebook.save_notebook(); });
218
218
219 this.then(function() {
219 this.then(function() {
220 clear_and_execute(this, [
220 clear_and_execute(this, [
221 "from IPython.core.formatters import HTMLFormatter",
221 "from IPython.core.formatters import HTMLFormatter",
222 "x = HTMLFormatter()",
222 "x = HTMLFormatter()",
223 "x.format_type = 'text/superfancymimetype'",
223 "x.format_type = 'text/superfancymimetype'",
224 "get_ipython().display_formatter.formatters['text/superfancymimetype'] = x",
224 "get_ipython().display_formatter.formatters['text/superfancymimetype'] = x",
225 "from IPython.display import HTML, display",
225 "from IPython.display import HTML, display",
226 'display(HTML("yo"))',
226 'display(HTML("yo"))',
227 "HTML('hello')"].join('\n')
227 "HTML('hello')"].join('\n')
228 );
228 );
229
229
230 });
230 });
231
231
232 this.wait_for_output(0, 1);
232 this.wait_for_output(0, 1);
233
233
234 this.then(function () {
234 this.then(function () {
235 var long_name = 'text/superfancymimetype';
235 var long_name = 'text/superfancymimetype';
236 var result = this.get_output_cell(0);
236 var result = this.get_output_cell(0);
237 this.test.assertTrue(result.hasOwnProperty(long_name),
237 this.test.assertTrue(result.hasOwnProperty(long_name),
238 'display_data custom mimetype ' + long_name);
238 'display_data custom mimetype ' + long_name);
239 var result = this.get_output_cell(0, 1);
239 var result = this.get_output_cell(0, 1);
240 this.test.assertTrue(result.hasOwnProperty(long_name),
240 this.test.assertTrue(result.hasOwnProperty(long_name),
241 'pyout custom mimetype ' + long_name);
241 'execute_result custom mimetype ' + long_name);
242
242
243 });
243 });
244
244
245 });
245 });
@@ -1,32 +1,32
1 //
1 //
2 // Test validation in append_output
2 // Test validation in append_output
3 //
3 //
4 // Invalid output data is stripped and logged.
4 // Invalid output data is stripped and logged.
5 //
5 //
6
6
7 casper.notebook_test(function () {
7 casper.notebook_test(function () {
8 // this.printLog();
8 // this.printLog();
9 var messages = [];
9 var messages = [];
10 this.on('remote.message', function (msg) {
10 this.on('remote.message', function (msg) {
11 messages.push(msg);
11 messages.push(msg);
12 });
12 });
13
13
14 this.evaluate(function () {
14 this.evaluate(function () {
15 var cell = IPython.notebook.get_cell(0);
15 var cell = IPython.notebook.get_cell(0);
16 cell.set_text( "dp = get_ipython().display_pub\n" +
16 cell.set_text( "dp = get_ipython().display_pub\n" +
17 "dp.publish('test', {'text/plain' : '5', 'image/png' : 5})"
17 "dp.publish({'text/plain' : '5', 'image/png' : 5})"
18 );
18 );
19 cell.execute();
19 cell.execute();
20 });
20 });
21
21
22 this.wait_for_output(0);
22 this.wait_for_output(0);
23 this.on('remote.message', function () {});
23 this.on('remote.message', function () {});
24
24
25 this.then(function () {
25 this.then(function () {
26 var output = this.get_output_cell(0);
26 var output = this.get_output_cell(0);
27 this.test.assert(messages.length > 0, "Captured log message");
27 this.test.assert(messages.length > 0, "Captured log message");
28 this.test.assertEquals(messages[messages.length-1], "Invalid type for image/png 5", "Logged Invalid type message");
28 this.test.assertEquals(messages[messages.length-1], "Invalid type for image/png 5", "Logged Invalid type message");
29 this.test.assertEquals(output['image/png'], undefined, "Non-string png data was stripped");
29 this.test.assertEquals(output['image/png'], undefined, "Non-string png data was stripped");
30 this.test.assertEquals(output['text/plain'], '5', "text data is fine");
30 this.test.assertEquals(output['text/plain'], '5', "text data is fine");
31 });
31 });
32 });
32 });
@@ -1,637 +1,626
1 """Base classes to manage a Client's interaction with a running kernel
1 """Base classes to manage a Client's interaction with a running kernel"""
2 """
3
2
4 #-----------------------------------------------------------------------------
3 # Copyright (c) IPython Development Team.
5 # Copyright (C) 2013 The IPython Development Team
4 # Distributed under the terms of the Modified BSD License.
6 #
7 # Distributed under the terms of the BSD License. The full license is in
8 # the file COPYING, distributed as part of this software.
9 #-----------------------------------------------------------------------------
10
11 #-----------------------------------------------------------------------------
12 # Imports
13 #-----------------------------------------------------------------------------
14
5
15 from __future__ import absolute_import
6 from __future__ import absolute_import
16
7
17 # Standard library imports
18 import atexit
8 import atexit
19 import errno
9 import errno
20 from threading import Thread
10 from threading import Thread
21 import time
11 import time
22
12
23 import zmq
13 import zmq
24 # import ZMQError in top-level namespace, to avoid ugly attribute-error messages
14 # import ZMQError in top-level namespace, to avoid ugly attribute-error messages
25 # during garbage collection of threads at exit:
15 # during garbage collection of threads at exit:
26 from zmq import ZMQError
16 from zmq import ZMQError
27 from zmq.eventloop import ioloop, zmqstream
17 from zmq.eventloop import ioloop, zmqstream
28
18
29 # Local imports
19 # Local imports
30 from .channelsabc import (
20 from .channelsabc import (
31 ShellChannelABC, IOPubChannelABC,
21 ShellChannelABC, IOPubChannelABC,
32 HBChannelABC, StdInChannelABC,
22 HBChannelABC, StdInChannelABC,
33 )
23 )
34 from IPython.utils.py3compat import string_types, iteritems
24 from IPython.utils.py3compat import string_types, iteritems
35
25
36 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
37 # Constants and exceptions
27 # Constants and exceptions
38 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
39
29
40 class InvalidPortNumber(Exception):
30 class InvalidPortNumber(Exception):
41 pass
31 pass
42
32
43 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
44 # Utility functions
34 # Utility functions
45 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
46
36
47 # some utilities to validate message structure, these might get moved elsewhere
37 # some utilities to validate message structure, these might get moved elsewhere
48 # if they prove to have more generic utility
38 # if they prove to have more generic utility
49
39
50 def validate_string_list(lst):
40 def validate_string_list(lst):
51 """Validate that the input is a list of strings.
41 """Validate that the input is a list of strings.
52
42
53 Raises ValueError if not."""
43 Raises ValueError if not."""
54 if not isinstance(lst, list):
44 if not isinstance(lst, list):
55 raise ValueError('input %r must be a list' % lst)
45 raise ValueError('input %r must be a list' % lst)
56 for x in lst:
46 for x in lst:
57 if not isinstance(x, string_types):
47 if not isinstance(x, string_types):
58 raise ValueError('element %r in list must be a string' % x)
48 raise ValueError('element %r in list must be a string' % x)
59
49
60
50
61 def validate_string_dict(dct):
51 def validate_string_dict(dct):
62 """Validate that the input is a dict with string keys and values.
52 """Validate that the input is a dict with string keys and values.
63
53
64 Raises ValueError if not."""
54 Raises ValueError if not."""
65 for k,v in iteritems(dct):
55 for k,v in iteritems(dct):
66 if not isinstance(k, string_types):
56 if not isinstance(k, string_types):
67 raise ValueError('key %r in dict must be a string' % k)
57 raise ValueError('key %r in dict must be a string' % k)
68 if not isinstance(v, string_types):
58 if not isinstance(v, string_types):
69 raise ValueError('value %r in dict must be a string' % v)
59 raise ValueError('value %r in dict must be a string' % v)
70
60
71
61
72 #-----------------------------------------------------------------------------
62 #-----------------------------------------------------------------------------
73 # ZMQ Socket Channel classes
63 # ZMQ Socket Channel classes
74 #-----------------------------------------------------------------------------
64 #-----------------------------------------------------------------------------
75
65
76 class ZMQSocketChannel(Thread):
66 class ZMQSocketChannel(Thread):
77 """The base class for the channels that use ZMQ sockets."""
67 """The base class for the channels that use ZMQ sockets."""
78 context = None
68 context = None
79 session = None
69 session = None
80 socket = None
70 socket = None
81 ioloop = None
71 ioloop = None
82 stream = None
72 stream = None
83 _address = None
73 _address = None
84 _exiting = False
74 _exiting = False
85 proxy_methods = []
75 proxy_methods = []
86
76
87 def __init__(self, context, session, address):
77 def __init__(self, context, session, address):
88 """Create a channel.
78 """Create a channel.
89
79
90 Parameters
80 Parameters
91 ----------
81 ----------
92 context : :class:`zmq.Context`
82 context : :class:`zmq.Context`
93 The ZMQ context to use.
83 The ZMQ context to use.
94 session : :class:`session.Session`
84 session : :class:`session.Session`
95 The session to use.
85 The session to use.
96 address : zmq url
86 address : zmq url
97 Standard (ip, port) tuple that the kernel is listening on.
87 Standard (ip, port) tuple that the kernel is listening on.
98 """
88 """
99 super(ZMQSocketChannel, self).__init__()
89 super(ZMQSocketChannel, self).__init__()
100 self.daemon = True
90 self.daemon = True
101
91
102 self.context = context
92 self.context = context
103 self.session = session
93 self.session = session
104 if isinstance(address, tuple):
94 if isinstance(address, tuple):
105 if address[1] == 0:
95 if address[1] == 0:
106 message = 'The port number for a channel cannot be 0.'
96 message = 'The port number for a channel cannot be 0.'
107 raise InvalidPortNumber(message)
97 raise InvalidPortNumber(message)
108 address = "tcp://%s:%i" % address
98 address = "tcp://%s:%i" % address
109 self._address = address
99 self._address = address
110 atexit.register(self._notice_exit)
100 atexit.register(self._notice_exit)
111
101
112 def _notice_exit(self):
102 def _notice_exit(self):
113 self._exiting = True
103 self._exiting = True
114
104
115 def _run_loop(self):
105 def _run_loop(self):
116 """Run my loop, ignoring EINTR events in the poller"""
106 """Run my loop, ignoring EINTR events in the poller"""
117 while True:
107 while True:
118 try:
108 try:
119 self.ioloop.start()
109 self.ioloop.start()
120 except ZMQError as e:
110 except ZMQError as e:
121 if e.errno == errno.EINTR:
111 if e.errno == errno.EINTR:
122 continue
112 continue
123 else:
113 else:
124 raise
114 raise
125 except Exception:
115 except Exception:
126 if self._exiting:
116 if self._exiting:
127 break
117 break
128 else:
118 else:
129 raise
119 raise
130 else:
120 else:
131 break
121 break
132
122
133 def stop(self):
123 def stop(self):
134 """Stop the channel's event loop and join its thread.
124 """Stop the channel's event loop and join its thread.
135
125
136 This calls :meth:`~threading.Thread.join` and returns when the thread
126 This calls :meth:`~threading.Thread.join` and returns when the thread
137 terminates. :class:`RuntimeError` will be raised if
127 terminates. :class:`RuntimeError` will be raised if
138 :meth:`~threading.Thread.start` is called again.
128 :meth:`~threading.Thread.start` is called again.
139 """
129 """
140 if self.ioloop is not None:
130 if self.ioloop is not None:
141 self.ioloop.stop()
131 self.ioloop.stop()
142 self.join()
132 self.join()
143 self.close()
133 self.close()
144
134
145 def close(self):
135 def close(self):
146 if self.ioloop is not None:
136 if self.ioloop is not None:
147 try:
137 try:
148 self.ioloop.close(all_fds=True)
138 self.ioloop.close(all_fds=True)
149 except Exception:
139 except Exception:
150 pass
140 pass
151 if self.socket is not None:
141 if self.socket is not None:
152 try:
142 try:
153 self.socket.close(linger=0)
143 self.socket.close(linger=0)
154 except Exception:
144 except Exception:
155 pass
145 pass
156 self.socket = None
146 self.socket = None
157
147
158 @property
148 @property
159 def address(self):
149 def address(self):
160 """Get the channel's address as a zmq url string.
150 """Get the channel's address as a zmq url string.
161
151
162 These URLS have the form: 'tcp://127.0.0.1:5555'.
152 These URLS have the form: 'tcp://127.0.0.1:5555'.
163 """
153 """
164 return self._address
154 return self._address
165
155
166 def _queue_send(self, msg):
156 def _queue_send(self, msg):
167 """Queue a message to be sent from the IOLoop's thread.
157 """Queue a message to be sent from the IOLoop's thread.
168
158
169 Parameters
159 Parameters
170 ----------
160 ----------
171 msg : message to send
161 msg : message to send
172
162
173 This is threadsafe, as it uses IOLoop.add_callback to give the loop's
163 This is threadsafe, as it uses IOLoop.add_callback to give the loop's
174 thread control of the action.
164 thread control of the action.
175 """
165 """
176 def thread_send():
166 def thread_send():
177 self.session.send(self.stream, msg)
167 self.session.send(self.stream, msg)
178 self.ioloop.add_callback(thread_send)
168 self.ioloop.add_callback(thread_send)
179
169
180 def _handle_recv(self, msg):
170 def _handle_recv(self, msg):
181 """Callback for stream.on_recv.
171 """Callback for stream.on_recv.
182
172
183 Unpacks message, and calls handlers with it.
173 Unpacks message, and calls handlers with it.
184 """
174 """
185 ident,smsg = self.session.feed_identities(msg)
175 ident,smsg = self.session.feed_identities(msg)
186 self.call_handlers(self.session.unserialize(smsg))
176 self.call_handlers(self.session.unserialize(smsg))
187
177
188
178
189
179
190 class ShellChannel(ZMQSocketChannel):
180 class ShellChannel(ZMQSocketChannel):
191 """The shell channel for issuing request/replies to the kernel."""
181 """The shell channel for issuing request/replies to the kernel."""
192
182
193 command_queue = None
183 command_queue = None
194 # flag for whether execute requests should be allowed to call raw_input:
184 # flag for whether execute requests should be allowed to call raw_input:
195 allow_stdin = True
185 allow_stdin = True
196 proxy_methods = [
186 proxy_methods = [
197 'execute',
187 'execute',
198 'complete',
188 'complete',
199 'object_info',
189 'inspect',
200 'history',
190 'history',
201 'kernel_info',
191 'kernel_info',
202 'shutdown',
192 'shutdown',
203 ]
193 ]
204
194
205 def __init__(self, context, session, address):
195 def __init__(self, context, session, address):
206 super(ShellChannel, self).__init__(context, session, address)
196 super(ShellChannel, self).__init__(context, session, address)
207 self.ioloop = ioloop.IOLoop()
197 self.ioloop = ioloop.IOLoop()
208
198
209 def run(self):
199 def run(self):
210 """The thread's main activity. Call start() instead."""
200 """The thread's main activity. Call start() instead."""
211 self.socket = self.context.socket(zmq.DEALER)
201 self.socket = self.context.socket(zmq.DEALER)
212 self.socket.linger = 1000
202 self.socket.linger = 1000
213 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
203 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
214 self.socket.connect(self.address)
204 self.socket.connect(self.address)
215 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
205 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
216 self.stream.on_recv(self._handle_recv)
206 self.stream.on_recv(self._handle_recv)
217 self._run_loop()
207 self._run_loop()
218
208
219 def call_handlers(self, msg):
209 def call_handlers(self, msg):
220 """This method is called in the ioloop thread when a message arrives.
210 """This method is called in the ioloop thread when a message arrives.
221
211
222 Subclasses should override this method to handle incoming messages.
212 Subclasses should override this method to handle incoming messages.
223 It is important to remember that this method is called in the thread
213 It is important to remember that this method is called in the thread
224 so that some logic must be done to ensure that the application level
214 so that some logic must be done to ensure that the application level
225 handlers are called in the application thread.
215 handlers are called in the application thread.
226 """
216 """
227 raise NotImplementedError('call_handlers must be defined in a subclass.')
217 raise NotImplementedError('call_handlers must be defined in a subclass.')
228
218
229 def execute(self, code, silent=False, store_history=True,
219 def execute(self, code, silent=False, store_history=True,
230 user_variables=None, user_expressions=None, allow_stdin=None):
220 user_expressions=None, allow_stdin=None):
231 """Execute code in the kernel.
221 """Execute code in the kernel.
232
222
233 Parameters
223 Parameters
234 ----------
224 ----------
235 code : str
225 code : str
236 A string of Python code.
226 A string of Python code.
237
227
238 silent : bool, optional (default False)
228 silent : bool, optional (default False)
239 If set, the kernel will execute the code as quietly possible, and
229 If set, the kernel will execute the code as quietly possible, and
240 will force store_history to be False.
230 will force store_history to be False.
241
231
242 store_history : bool, optional (default True)
232 store_history : bool, optional (default True)
243 If set, the kernel will store command history. This is forced
233 If set, the kernel will store command history. This is forced
244 to be False if silent is True.
234 to be False if silent is True.
245
235
246 user_variables : list, optional
247 A list of variable names to pull from the user's namespace. They
248 will come back as a dict with these names as keys and their
249 :func:`repr` as values.
250
251 user_expressions : dict, optional
236 user_expressions : dict, optional
252 A dict mapping names to expressions to be evaluated in the user's
237 A dict mapping names to expressions to be evaluated in the user's
253 dict. The expression values are returned as strings formatted using
238 dict. The expression values are returned as strings formatted using
254 :func:`repr`.
239 :func:`repr`.
255
240
256 allow_stdin : bool, optional (default self.allow_stdin)
241 allow_stdin : bool, optional (default self.allow_stdin)
257 Flag for whether the kernel can send stdin requests to frontends.
242 Flag for whether the kernel can send stdin requests to frontends.
258
243
259 Some frontends (e.g. the Notebook) do not support stdin requests.
244 Some frontends (e.g. the Notebook) do not support stdin requests.
260 If raw_input is called from code executed from such a frontend, a
245 If raw_input is called from code executed from such a frontend, a
261 StdinNotImplementedError will be raised.
246 StdinNotImplementedError will be raised.
262
247
263 Returns
248 Returns
264 -------
249 -------
265 The msg_id of the message sent.
250 The msg_id of the message sent.
266 """
251 """
267 if user_variables is None:
268 user_variables = []
269 if user_expressions is None:
252 if user_expressions is None:
270 user_expressions = {}
253 user_expressions = {}
271 if allow_stdin is None:
254 if allow_stdin is None:
272 allow_stdin = self.allow_stdin
255 allow_stdin = self.allow_stdin
273
256
274
257
275 # Don't waste network traffic if inputs are invalid
258 # Don't waste network traffic if inputs are invalid
276 if not isinstance(code, string_types):
259 if not isinstance(code, string_types):
277 raise ValueError('code %r must be a string' % code)
260 raise ValueError('code %r must be a string' % code)
278 validate_string_list(user_variables)
279 validate_string_dict(user_expressions)
261 validate_string_dict(user_expressions)
280
262
281 # Create class for content/msg creation. Related to, but possibly
263 # Create class for content/msg creation. Related to, but possibly
282 # not in Session.
264 # not in Session.
283 content = dict(code=code, silent=silent, store_history=store_history,
265 content = dict(code=code, silent=silent, store_history=store_history,
284 user_variables=user_variables,
285 user_expressions=user_expressions,
266 user_expressions=user_expressions,
286 allow_stdin=allow_stdin,
267 allow_stdin=allow_stdin,
287 )
268 )
288 msg = self.session.msg('execute_request', content)
269 msg = self.session.msg('execute_request', content)
289 self._queue_send(msg)
270 self._queue_send(msg)
290 return msg['header']['msg_id']
271 return msg['header']['msg_id']
291
272
292 def complete(self, text, line, cursor_pos, block=None):
273 def complete(self, code, cursor_pos=None):
293 """Tab complete text in the kernel's namespace.
274 """Tab complete text in the kernel's namespace.
294
275
295 Parameters
276 Parameters
296 ----------
277 ----------
297 text : str
278 code : str
298 The text to complete.
279 The context in which completion is requested.
299 line : str
280 Can be anything between a variable name and an entire cell.
300 The full line of text that is the surrounding context for the
281 cursor_pos : int, optional
301 text to complete.
282 The position of the cursor in the block of code where the completion was requested.
302 cursor_pos : int
283 Default: ``len(code)``
303 The position of the cursor in the line where the completion was
304 requested.
305 block : str, optional
306 The full block of code in which the completion is being requested.
307
284
308 Returns
285 Returns
309 -------
286 -------
310 The msg_id of the message sent.
287 The msg_id of the message sent.
311 """
288 """
312 content = dict(text=text, line=line, block=block, cursor_pos=cursor_pos)
289 if cursor_pos is None:
290 cursor_pos = len(code)
291 content = dict(code=code, cursor_pos=cursor_pos)
313 msg = self.session.msg('complete_request', content)
292 msg = self.session.msg('complete_request', content)
314 self._queue_send(msg)
293 self._queue_send(msg)
315 return msg['header']['msg_id']
294 return msg['header']['msg_id']
316
295
317 def object_info(self, oname, detail_level=0):
296 def inspect(self, code, cursor_pos=None, detail_level=0):
318 """Get metadata information about an object in the kernel's namespace.
297 """Get metadata information about an object in the kernel's namespace.
319
298
299 It is up to the kernel to determine the appropriate object to inspect.
300
320 Parameters
301 Parameters
321 ----------
302 ----------
322 oname : str
303 code : str
323 A string specifying the object name.
304 The context in which info is requested.
305 Can be anything between a variable name and an entire cell.
306 cursor_pos : int, optional
307 The position of the cursor in the block of code where the info was requested.
308 Default: ``len(code)``
324 detail_level : int, optional
309 detail_level : int, optional
325 The level of detail for the introspection (0-2)
310 The level of detail for the introspection (0-2)
326
311
327 Returns
312 Returns
328 -------
313 -------
329 The msg_id of the message sent.
314 The msg_id of the message sent.
330 """
315 """
331 content = dict(oname=oname, detail_level=detail_level)
316 if cursor_pos is None:
332 msg = self.session.msg('object_info_request', content)
317 cursor_pos = len(code)
318 content = dict(code=code, cursor_pos=cursor_pos,
319 detail_level=detail_level,
320 )
321 msg = self.session.msg('inspect_request', content)
333 self._queue_send(msg)
322 self._queue_send(msg)
334 return msg['header']['msg_id']
323 return msg['header']['msg_id']
335
324
336 def history(self, raw=True, output=False, hist_access_type='range', **kwargs):
325 def history(self, raw=True, output=False, hist_access_type='range', **kwargs):
337 """Get entries from the kernel's history list.
326 """Get entries from the kernel's history list.
338
327
339 Parameters
328 Parameters
340 ----------
329 ----------
341 raw : bool
330 raw : bool
342 If True, return the raw input.
331 If True, return the raw input.
343 output : bool
332 output : bool
344 If True, then return the output as well.
333 If True, then return the output as well.
345 hist_access_type : str
334 hist_access_type : str
346 'range' (fill in session, start and stop params), 'tail' (fill in n)
335 'range' (fill in session, start and stop params), 'tail' (fill in n)
347 or 'search' (fill in pattern param).
336 or 'search' (fill in pattern param).
348
337
349 session : int
338 session : int
350 For a range request, the session from which to get lines. Session
339 For a range request, the session from which to get lines. Session
351 numbers are positive integers; negative ones count back from the
340 numbers are positive integers; negative ones count back from the
352 current session.
341 current session.
353 start : int
342 start : int
354 The first line number of a history range.
343 The first line number of a history range.
355 stop : int
344 stop : int
356 The final (excluded) line number of a history range.
345 The final (excluded) line number of a history range.
357
346
358 n : int
347 n : int
359 The number of lines of history to get for a tail request.
348 The number of lines of history to get for a tail request.
360
349
361 pattern : str
350 pattern : str
362 The glob-syntax pattern for a search request.
351 The glob-syntax pattern for a search request.
363
352
364 Returns
353 Returns
365 -------
354 -------
366 The msg_id of the message sent.
355 The msg_id of the message sent.
367 """
356 """
368 content = dict(raw=raw, output=output, hist_access_type=hist_access_type,
357 content = dict(raw=raw, output=output, hist_access_type=hist_access_type,
369 **kwargs)
358 **kwargs)
370 msg = self.session.msg('history_request', content)
359 msg = self.session.msg('history_request', content)
371 self._queue_send(msg)
360 self._queue_send(msg)
372 return msg['header']['msg_id']
361 return msg['header']['msg_id']
373
362
374 def kernel_info(self):
363 def kernel_info(self):
375 """Request kernel info."""
364 """Request kernel info."""
376 msg = self.session.msg('kernel_info_request')
365 msg = self.session.msg('kernel_info_request')
377 self._queue_send(msg)
366 self._queue_send(msg)
378 return msg['header']['msg_id']
367 return msg['header']['msg_id']
379
368
380 def shutdown(self, restart=False):
369 def shutdown(self, restart=False):
381 """Request an immediate kernel shutdown.
370 """Request an immediate kernel shutdown.
382
371
383 Upon receipt of the (empty) reply, client code can safely assume that
372 Upon receipt of the (empty) reply, client code can safely assume that
384 the kernel has shut down and it's safe to forcefully terminate it if
373 the kernel has shut down and it's safe to forcefully terminate it if
385 it's still alive.
374 it's still alive.
386
375
387 The kernel will send the reply via a function registered with Python's
376 The kernel will send the reply via a function registered with Python's
388 atexit module, ensuring it's truly done as the kernel is done with all
377 atexit module, ensuring it's truly done as the kernel is done with all
389 normal operation.
378 normal operation.
390 """
379 """
391 # Send quit message to kernel. Once we implement kernel-side setattr,
380 # Send quit message to kernel. Once we implement kernel-side setattr,
392 # this should probably be done that way, but for now this will do.
381 # this should probably be done that way, but for now this will do.
393 msg = self.session.msg('shutdown_request', {'restart':restart})
382 msg = self.session.msg('shutdown_request', {'restart':restart})
394 self._queue_send(msg)
383 self._queue_send(msg)
395 return msg['header']['msg_id']
384 return msg['header']['msg_id']
396
385
397
386
398
387
399 class IOPubChannel(ZMQSocketChannel):
388 class IOPubChannel(ZMQSocketChannel):
400 """The iopub channel which listens for messages that the kernel publishes.
389 """The iopub channel which listens for messages that the kernel publishes.
401
390
402 This channel is where all output is published to frontends.
391 This channel is where all output is published to frontends.
403 """
392 """
404
393
405 def __init__(self, context, session, address):
394 def __init__(self, context, session, address):
406 super(IOPubChannel, self).__init__(context, session, address)
395 super(IOPubChannel, self).__init__(context, session, address)
407 self.ioloop = ioloop.IOLoop()
396 self.ioloop = ioloop.IOLoop()
408
397
409 def run(self):
398 def run(self):
410 """The thread's main activity. Call start() instead."""
399 """The thread's main activity. Call start() instead."""
411 self.socket = self.context.socket(zmq.SUB)
400 self.socket = self.context.socket(zmq.SUB)
412 self.socket.linger = 1000
401 self.socket.linger = 1000
413 self.socket.setsockopt(zmq.SUBSCRIBE,b'')
402 self.socket.setsockopt(zmq.SUBSCRIBE,b'')
414 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
403 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
415 self.socket.connect(self.address)
404 self.socket.connect(self.address)
416 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
405 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
417 self.stream.on_recv(self._handle_recv)
406 self.stream.on_recv(self._handle_recv)
418 self._run_loop()
407 self._run_loop()
419
408
420 def call_handlers(self, msg):
409 def call_handlers(self, msg):
421 """This method is called in the ioloop thread when a message arrives.
410 """This method is called in the ioloop thread when a message arrives.
422
411
423 Subclasses should override this method to handle incoming messages.
412 Subclasses should override this method to handle incoming messages.
424 It is important to remember that this method is called in the thread
413 It is important to remember that this method is called in the thread
425 so that some logic must be done to ensure that the application leve
414 so that some logic must be done to ensure that the application leve
426 handlers are called in the application thread.
415 handlers are called in the application thread.
427 """
416 """
428 raise NotImplementedError('call_handlers must be defined in a subclass.')
417 raise NotImplementedError('call_handlers must be defined in a subclass.')
429
418
430 def flush(self, timeout=1.0):
419 def flush(self, timeout=1.0):
431 """Immediately processes all pending messages on the iopub channel.
420 """Immediately processes all pending messages on the iopub channel.
432
421
433 Callers should use this method to ensure that :meth:`call_handlers`
422 Callers should use this method to ensure that :meth:`call_handlers`
434 has been called for all messages that have been received on the
423 has been called for all messages that have been received on the
435 0MQ SUB socket of this channel.
424 0MQ SUB socket of this channel.
436
425
437 This method is thread safe.
426 This method is thread safe.
438
427
439 Parameters
428 Parameters
440 ----------
429 ----------
441 timeout : float, optional
430 timeout : float, optional
442 The maximum amount of time to spend flushing, in seconds. The
431 The maximum amount of time to spend flushing, in seconds. The
443 default is one second.
432 default is one second.
444 """
433 """
445 # We do the IOLoop callback process twice to ensure that the IOLoop
434 # We do the IOLoop callback process twice to ensure that the IOLoop
446 # gets to perform at least one full poll.
435 # gets to perform at least one full poll.
447 stop_time = time.time() + timeout
436 stop_time = time.time() + timeout
448 for i in range(2):
437 for i in range(2):
449 self._flushed = False
438 self._flushed = False
450 self.ioloop.add_callback(self._flush)
439 self.ioloop.add_callback(self._flush)
451 while not self._flushed and time.time() < stop_time:
440 while not self._flushed and time.time() < stop_time:
452 time.sleep(0.01)
441 time.sleep(0.01)
453
442
454 def _flush(self):
443 def _flush(self):
455 """Callback for :method:`self.flush`."""
444 """Callback for :method:`self.flush`."""
456 self.stream.flush()
445 self.stream.flush()
457 self._flushed = True
446 self._flushed = True
458
447
459
448
460 class StdInChannel(ZMQSocketChannel):
449 class StdInChannel(ZMQSocketChannel):
461 """The stdin channel to handle raw_input requests that the kernel makes."""
450 """The stdin channel to handle raw_input requests that the kernel makes."""
462
451
463 msg_queue = None
452 msg_queue = None
464 proxy_methods = ['input']
453 proxy_methods = ['input']
465
454
466 def __init__(self, context, session, address):
455 def __init__(self, context, session, address):
467 super(StdInChannel, self).__init__(context, session, address)
456 super(StdInChannel, self).__init__(context, session, address)
468 self.ioloop = ioloop.IOLoop()
457 self.ioloop = ioloop.IOLoop()
469
458
470 def run(self):
459 def run(self):
471 """The thread's main activity. Call start() instead."""
460 """The thread's main activity. Call start() instead."""
472 self.socket = self.context.socket(zmq.DEALER)
461 self.socket = self.context.socket(zmq.DEALER)
473 self.socket.linger = 1000
462 self.socket.linger = 1000
474 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
463 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
475 self.socket.connect(self.address)
464 self.socket.connect(self.address)
476 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
465 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
477 self.stream.on_recv(self._handle_recv)
466 self.stream.on_recv(self._handle_recv)
478 self._run_loop()
467 self._run_loop()
479
468
480 def call_handlers(self, msg):
469 def call_handlers(self, msg):
481 """This method is called in the ioloop thread when a message arrives.
470 """This method is called in the ioloop thread when a message arrives.
482
471
483 Subclasses should override this method to handle incoming messages.
472 Subclasses should override this method to handle incoming messages.
484 It is important to remember that this method is called in the thread
473 It is important to remember that this method is called in the thread
485 so that some logic must be done to ensure that the application leve
474 so that some logic must be done to ensure that the application leve
486 handlers are called in the application thread.
475 handlers are called in the application thread.
487 """
476 """
488 raise NotImplementedError('call_handlers must be defined in a subclass.')
477 raise NotImplementedError('call_handlers must be defined in a subclass.')
489
478
490 def input(self, string):
479 def input(self, string):
491 """Send a string of raw input to the kernel."""
480 """Send a string of raw input to the kernel."""
492 content = dict(value=string)
481 content = dict(value=string)
493 msg = self.session.msg('input_reply', content)
482 msg = self.session.msg('input_reply', content)
494 self._queue_send(msg)
483 self._queue_send(msg)
495
484
496
485
497 class HBChannel(ZMQSocketChannel):
486 class HBChannel(ZMQSocketChannel):
498 """The heartbeat channel which monitors the kernel heartbeat.
487 """The heartbeat channel which monitors the kernel heartbeat.
499
488
500 Note that the heartbeat channel is paused by default. As long as you start
489 Note that the heartbeat channel is paused by default. As long as you start
501 this channel, the kernel manager will ensure that it is paused and un-paused
490 this channel, the kernel manager will ensure that it is paused and un-paused
502 as appropriate.
491 as appropriate.
503 """
492 """
504
493
505 time_to_dead = 3.0
494 time_to_dead = 3.0
506 socket = None
495 socket = None
507 poller = None
496 poller = None
508 _running = None
497 _running = None
509 _pause = None
498 _pause = None
510 _beating = None
499 _beating = None
511
500
512 def __init__(self, context, session, address):
501 def __init__(self, context, session, address):
513 super(HBChannel, self).__init__(context, session, address)
502 super(HBChannel, self).__init__(context, session, address)
514 self._running = False
503 self._running = False
515 self._pause =True
504 self._pause =True
516 self.poller = zmq.Poller()
505 self.poller = zmq.Poller()
517
506
518 def _create_socket(self):
507 def _create_socket(self):
519 if self.socket is not None:
508 if self.socket is not None:
520 # close previous socket, before opening a new one
509 # close previous socket, before opening a new one
521 self.poller.unregister(self.socket)
510 self.poller.unregister(self.socket)
522 self.socket.close()
511 self.socket.close()
523 self.socket = self.context.socket(zmq.REQ)
512 self.socket = self.context.socket(zmq.REQ)
524 self.socket.linger = 1000
513 self.socket.linger = 1000
525 self.socket.connect(self.address)
514 self.socket.connect(self.address)
526
515
527 self.poller.register(self.socket, zmq.POLLIN)
516 self.poller.register(self.socket, zmq.POLLIN)
528
517
529 def _poll(self, start_time):
518 def _poll(self, start_time):
530 """poll for heartbeat replies until we reach self.time_to_dead.
519 """poll for heartbeat replies until we reach self.time_to_dead.
531
520
532 Ignores interrupts, and returns the result of poll(), which
521 Ignores interrupts, and returns the result of poll(), which
533 will be an empty list if no messages arrived before the timeout,
522 will be an empty list if no messages arrived before the timeout,
534 or the event tuple if there is a message to receive.
523 or the event tuple if there is a message to receive.
535 """
524 """
536
525
537 until_dead = self.time_to_dead - (time.time() - start_time)
526 until_dead = self.time_to_dead - (time.time() - start_time)
538 # ensure poll at least once
527 # ensure poll at least once
539 until_dead = max(until_dead, 1e-3)
528 until_dead = max(until_dead, 1e-3)
540 events = []
529 events = []
541 while True:
530 while True:
542 try:
531 try:
543 events = self.poller.poll(1000 * until_dead)
532 events = self.poller.poll(1000 * until_dead)
544 except ZMQError as e:
533 except ZMQError as e:
545 if e.errno == errno.EINTR:
534 if e.errno == errno.EINTR:
546 # ignore interrupts during heartbeat
535 # ignore interrupts during heartbeat
547 # this may never actually happen
536 # this may never actually happen
548 until_dead = self.time_to_dead - (time.time() - start_time)
537 until_dead = self.time_to_dead - (time.time() - start_time)
549 until_dead = max(until_dead, 1e-3)
538 until_dead = max(until_dead, 1e-3)
550 pass
539 pass
551 else:
540 else:
552 raise
541 raise
553 except Exception:
542 except Exception:
554 if self._exiting:
543 if self._exiting:
555 break
544 break
556 else:
545 else:
557 raise
546 raise
558 else:
547 else:
559 break
548 break
560 return events
549 return events
561
550
562 def run(self):
551 def run(self):
563 """The thread's main activity. Call start() instead."""
552 """The thread's main activity. Call start() instead."""
564 self._create_socket()
553 self._create_socket()
565 self._running = True
554 self._running = True
566 self._beating = True
555 self._beating = True
567
556
568 while self._running:
557 while self._running:
569 if self._pause:
558 if self._pause:
570 # just sleep, and skip the rest of the loop
559 # just sleep, and skip the rest of the loop
571 time.sleep(self.time_to_dead)
560 time.sleep(self.time_to_dead)
572 continue
561 continue
573
562
574 since_last_heartbeat = 0.0
563 since_last_heartbeat = 0.0
575 # io.rprint('Ping from HB channel') # dbg
564 # io.rprint('Ping from HB channel') # dbg
576 # no need to catch EFSM here, because the previous event was
565 # no need to catch EFSM here, because the previous event was
577 # either a recv or connect, which cannot be followed by EFSM
566 # either a recv or connect, which cannot be followed by EFSM
578 self.socket.send(b'ping')
567 self.socket.send(b'ping')
579 request_time = time.time()
568 request_time = time.time()
580 ready = self._poll(request_time)
569 ready = self._poll(request_time)
581 if ready:
570 if ready:
582 self._beating = True
571 self._beating = True
583 # the poll above guarantees we have something to recv
572 # the poll above guarantees we have something to recv
584 self.socket.recv()
573 self.socket.recv()
585 # sleep the remainder of the cycle
574 # sleep the remainder of the cycle
586 remainder = self.time_to_dead - (time.time() - request_time)
575 remainder = self.time_to_dead - (time.time() - request_time)
587 if remainder > 0:
576 if remainder > 0:
588 time.sleep(remainder)
577 time.sleep(remainder)
589 continue
578 continue
590 else:
579 else:
591 # nothing was received within the time limit, signal heart failure
580 # nothing was received within the time limit, signal heart failure
592 self._beating = False
581 self._beating = False
593 since_last_heartbeat = time.time() - request_time
582 since_last_heartbeat = time.time() - request_time
594 self.call_handlers(since_last_heartbeat)
583 self.call_handlers(since_last_heartbeat)
595 # and close/reopen the socket, because the REQ/REP cycle has been broken
584 # and close/reopen the socket, because the REQ/REP cycle has been broken
596 self._create_socket()
585 self._create_socket()
597 continue
586 continue
598
587
599 def pause(self):
588 def pause(self):
600 """Pause the heartbeat."""
589 """Pause the heartbeat."""
601 self._pause = True
590 self._pause = True
602
591
603 def unpause(self):
592 def unpause(self):
604 """Unpause the heartbeat."""
593 """Unpause the heartbeat."""
605 self._pause = False
594 self._pause = False
606
595
607 def is_beating(self):
596 def is_beating(self):
608 """Is the heartbeat running and responsive (and not paused)."""
597 """Is the heartbeat running and responsive (and not paused)."""
609 if self.is_alive() and not self._pause and self._beating:
598 if self.is_alive() and not self._pause and self._beating:
610 return True
599 return True
611 else:
600 else:
612 return False
601 return False
613
602
614 def stop(self):
603 def stop(self):
615 """Stop the channel's event loop and join its thread."""
604 """Stop the channel's event loop and join its thread."""
616 self._running = False
605 self._running = False
617 super(HBChannel, self).stop()
606 super(HBChannel, self).stop()
618
607
619 def call_handlers(self, since_last_heartbeat):
608 def call_handlers(self, since_last_heartbeat):
620 """This method is called in the ioloop thread when a message arrives.
609 """This method is called in the ioloop thread when a message arrives.
621
610
622 Subclasses should override this method to handle incoming messages.
611 Subclasses should override this method to handle incoming messages.
623 It is important to remember that this method is called in the thread
612 It is important to remember that this method is called in the thread
624 so that some logic must be done to ensure that the application level
613 so that some logic must be done to ensure that the application level
625 handlers are called in the application thread.
614 handlers are called in the application thread.
626 """
615 """
627 raise NotImplementedError('call_handlers must be defined in a subclass.')
616 raise NotImplementedError('call_handlers must be defined in a subclass.')
628
617
629
618
630 #---------------------------------------------------------------------#-----------------------------------------------------------------------------
619 #---------------------------------------------------------------------#-----------------------------------------------------------------------------
631 # ABC Registration
620 # ABC Registration
632 #-----------------------------------------------------------------------------
621 #-----------------------------------------------------------------------------
633
622
634 ShellChannelABC.register(ShellChannel)
623 ShellChannelABC.register(ShellChannel)
635 IOPubChannelABC.register(IOPubChannel)
624 IOPubChannelABC.register(IOPubChannel)
636 HBChannelABC.register(HBChannel)
625 HBChannelABC.register(HBChannel)
637 StdInChannelABC.register(StdInChannel)
626 StdInChannelABC.register(StdInChannel)
@@ -1,117 +1,113
1 """Abstract base classes for kernel client channels"""
1 """Abstract base classes for kernel client channels"""
2
2
3 #-----------------------------------------------------------------------------
3 # Copyright (c) IPython Development Team.
4 # Copyright (C) 2013 The IPython Development Team
4 # Distributed under the terms of the Modified BSD License.
5 #
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
8 #-----------------------------------------------------------------------------
9
5
10 import abc
6 import abc
11
7
12 from IPython.utils.py3compat import with_metaclass
8 from IPython.utils.py3compat import with_metaclass
13
9
14
10
15 class ChannelABC(with_metaclass(abc.ABCMeta, object)):
11 class ChannelABC(with_metaclass(abc.ABCMeta, object)):
16 """A base class for all channel ABCs."""
12 """A base class for all channel ABCs."""
17
13
18 @abc.abstractmethod
14 @abc.abstractmethod
19 def start(self):
15 def start(self):
20 pass
16 pass
21
17
22 @abc.abstractmethod
18 @abc.abstractmethod
23 def stop(self):
19 def stop(self):
24 pass
20 pass
25
21
26 @abc.abstractmethod
22 @abc.abstractmethod
27 def is_alive(self):
23 def is_alive(self):
28 pass
24 pass
29
25
30
26
31 class ShellChannelABC(ChannelABC):
27 class ShellChannelABC(ChannelABC):
32 """ShellChannel ABC.
28 """ShellChannel ABC.
33
29
34 The docstrings for this class can be found in the base implementation:
30 The docstrings for this class can be found in the base implementation:
35
31
36 `IPython.kernel.channels.ShellChannel`
32 `IPython.kernel.channels.ShellChannel`
37 """
33 """
38
34
39 @abc.abstractproperty
35 @abc.abstractproperty
40 def allow_stdin(self):
36 def allow_stdin(self):
41 pass
37 pass
42
38
43 @abc.abstractmethod
39 @abc.abstractmethod
44 def execute(self, code, silent=False, store_history=True,
40 def execute(self, code, silent=False, store_history=True,
45 user_variables=None, user_expressions=None, allow_stdin=None):
41 user_expressions=None, allow_stdin=None):
46 pass
42 pass
47
43
48 @abc.abstractmethod
44 @abc.abstractmethod
49 def complete(self, text, line, cursor_pos, block=None):
45 def complete(self, text, line, cursor_pos, block=None):
50 pass
46 pass
51
47
52 @abc.abstractmethod
48 @abc.abstractmethod
53 def object_info(self, oname, detail_level=0):
49 def inspect(self, oname, detail_level=0):
54 pass
50 pass
55
51
56 @abc.abstractmethod
52 @abc.abstractmethod
57 def history(self, raw=True, output=False, hist_access_type='range', **kwargs):
53 def history(self, raw=True, output=False, hist_access_type='range', **kwargs):
58 pass
54 pass
59
55
60 @abc.abstractmethod
56 @abc.abstractmethod
61 def kernel_info(self):
57 def kernel_info(self):
62 pass
58 pass
63
59
64 @abc.abstractmethod
60 @abc.abstractmethod
65 def shutdown(self, restart=False):
61 def shutdown(self, restart=False):
66 pass
62 pass
67
63
68
64
69 class IOPubChannelABC(ChannelABC):
65 class IOPubChannelABC(ChannelABC):
70 """IOPubChannel ABC.
66 """IOPubChannel ABC.
71
67
72 The docstrings for this class can be found in the base implementation:
68 The docstrings for this class can be found in the base implementation:
73
69
74 `IPython.kernel.channels.IOPubChannel`
70 `IPython.kernel.channels.IOPubChannel`
75 """
71 """
76
72
77 @abc.abstractmethod
73 @abc.abstractmethod
78 def flush(self, timeout=1.0):
74 def flush(self, timeout=1.0):
79 pass
75 pass
80
76
81
77
82 class StdInChannelABC(ChannelABC):
78 class StdInChannelABC(ChannelABC):
83 """StdInChannel ABC.
79 """StdInChannel ABC.
84
80
85 The docstrings for this class can be found in the base implementation:
81 The docstrings for this class can be found in the base implementation:
86
82
87 `IPython.kernel.channels.StdInChannel`
83 `IPython.kernel.channels.StdInChannel`
88 """
84 """
89
85
90 @abc.abstractmethod
86 @abc.abstractmethod
91 def input(self, string):
87 def input(self, string):
92 pass
88 pass
93
89
94
90
95 class HBChannelABC(ChannelABC):
91 class HBChannelABC(ChannelABC):
96 """HBChannel ABC.
92 """HBChannel ABC.
97
93
98 The docstrings for this class can be found in the base implementation:
94 The docstrings for this class can be found in the base implementation:
99
95
100 `IPython.kernel.channels.HBChannel`
96 `IPython.kernel.channels.HBChannel`
101 """
97 """
102
98
103 @abc.abstractproperty
99 @abc.abstractproperty
104 def time_to_dead(self):
100 def time_to_dead(self):
105 pass
101 pass
106
102
107 @abc.abstractmethod
103 @abc.abstractmethod
108 def pause(self):
104 def pause(self):
109 pass
105 pass
110
106
111 @abc.abstractmethod
107 @abc.abstractmethod
112 def unpause(self):
108 def unpause(self):
113 pass
109 pass
114
110
115 @abc.abstractmethod
111 @abc.abstractmethod
116 def is_beating(self):
112 def is_beating(self):
117 pass
113 pass
@@ -1,201 +1,196
1 """ A kernel client for in-process kernels. """
1 """A kernel client for in-process kernels."""
2
2
3 #-----------------------------------------------------------------------------
3 # Copyright (c) IPython Development Team.
4 # Copyright (C) 2012 The IPython Development Team
4 # Distributed under the terms of the Modified BSD License.
5 #
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
8 #-----------------------------------------------------------------------------
9
10 #-----------------------------------------------------------------------------
11 # Imports
12 #-----------------------------------------------------------------------------
13
5
14 # IPython imports
15 from IPython.kernel.channelsabc import (
6 from IPython.kernel.channelsabc import (
16 ShellChannelABC, IOPubChannelABC,
7 ShellChannelABC, IOPubChannelABC,
17 HBChannelABC, StdInChannelABC,
8 HBChannelABC, StdInChannelABC,
18 )
9 )
19
10
20 # Local imports
21 from .socket import DummySocket
11 from .socket import DummySocket
22
12
23 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
24 # Channel classes
14 # Channel classes
25 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
26
16
27 class InProcessChannel(object):
17 class InProcessChannel(object):
28 """Base class for in-process channels."""
18 """Base class for in-process channels."""
29 proxy_methods = []
19 proxy_methods = []
30
20
31 def __init__(self, client=None):
21 def __init__(self, client=None):
32 super(InProcessChannel, self).__init__()
22 super(InProcessChannel, self).__init__()
33 self.client = client
23 self.client = client
34 self._is_alive = False
24 self._is_alive = False
35
25
36 #--------------------------------------------------------------------------
26 #--------------------------------------------------------------------------
37 # Channel interface
27 # Channel interface
38 #--------------------------------------------------------------------------
28 #--------------------------------------------------------------------------
39
29
40 def is_alive(self):
30 def is_alive(self):
41 return self._is_alive
31 return self._is_alive
42
32
43 def start(self):
33 def start(self):
44 self._is_alive = True
34 self._is_alive = True
45
35
46 def stop(self):
36 def stop(self):
47 self._is_alive = False
37 self._is_alive = False
48
38
49 def call_handlers(self, msg):
39 def call_handlers(self, msg):
50 """ This method is called in the main thread when a message arrives.
40 """ This method is called in the main thread when a message arrives.
51
41
52 Subclasses should override this method to handle incoming messages.
42 Subclasses should override this method to handle incoming messages.
53 """
43 """
54 raise NotImplementedError('call_handlers must be defined in a subclass.')
44 raise NotImplementedError('call_handlers must be defined in a subclass.')
55
45
56 #--------------------------------------------------------------------------
46 #--------------------------------------------------------------------------
57 # InProcessChannel interface
47 # InProcessChannel interface
58 #--------------------------------------------------------------------------
48 #--------------------------------------------------------------------------
59
49
60 def call_handlers_later(self, *args, **kwds):
50 def call_handlers_later(self, *args, **kwds):
61 """ Call the message handlers later.
51 """ Call the message handlers later.
62
52
63 The default implementation just calls the handlers immediately, but this
53 The default implementation just calls the handlers immediately, but this
64 method exists so that GUI toolkits can defer calling the handlers until
54 method exists so that GUI toolkits can defer calling the handlers until
65 after the event loop has run, as expected by GUI frontends.
55 after the event loop has run, as expected by GUI frontends.
66 """
56 """
67 self.call_handlers(*args, **kwds)
57 self.call_handlers(*args, **kwds)
68
58
69 def process_events(self):
59 def process_events(self):
70 """ Process any pending GUI events.
60 """ Process any pending GUI events.
71
61
72 This method will be never be called from a frontend without an event
62 This method will be never be called from a frontend without an event
73 loop (e.g., a terminal frontend).
63 loop (e.g., a terminal frontend).
74 """
64 """
75 raise NotImplementedError
65 raise NotImplementedError
76
66
77
67
78 class InProcessShellChannel(InProcessChannel):
68 class InProcessShellChannel(InProcessChannel):
79 """See `IPython.kernel.channels.ShellChannel` for docstrings."""
69 """See `IPython.kernel.channels.ShellChannel` for docstrings."""
80
70
81 # flag for whether execute requests should be allowed to call raw_input
71 # flag for whether execute requests should be allowed to call raw_input
82 allow_stdin = True
72 allow_stdin = True
83 proxy_methods = [
73 proxy_methods = [
84 'execute',
74 'execute',
85 'complete',
75 'complete',
86 'object_info',
76 'inspect',
87 'history',
77 'history',
88 'shutdown',
78 'shutdown',
89 'kernel_info',
79 'kernel_info',
90 ]
80 ]
91
81
92 #--------------------------------------------------------------------------
82 #--------------------------------------------------------------------------
93 # ShellChannel interface
83 # ShellChannel interface
94 #--------------------------------------------------------------------------
84 #--------------------------------------------------------------------------
95
85
96 def execute(self, code, silent=False, store_history=True,
86 def execute(self, code, silent=False, store_history=True,
97 user_variables=[], user_expressions={}, allow_stdin=None):
87 user_expressions={}, allow_stdin=None):
98 if allow_stdin is None:
88 if allow_stdin is None:
99 allow_stdin = self.allow_stdin
89 allow_stdin = self.allow_stdin
100 content = dict(code=code, silent=silent, store_history=store_history,
90 content = dict(code=code, silent=silent, store_history=store_history,
101 user_variables=user_variables,
102 user_expressions=user_expressions,
91 user_expressions=user_expressions,
103 allow_stdin=allow_stdin)
92 allow_stdin=allow_stdin)
104 msg = self.client.session.msg('execute_request', content)
93 msg = self.client.session.msg('execute_request', content)
105 self._dispatch_to_kernel(msg)
94 self._dispatch_to_kernel(msg)
106 return msg['header']['msg_id']
95 return msg['header']['msg_id']
107
96
108 def complete(self, text, line, cursor_pos, block=None):
97 def complete(self, code, cursor_pos=None):
109 content = dict(text=text, line=line, block=block, cursor_pos=cursor_pos)
98 if cursor_pos is None:
99 cursor_pos = len(code)
100 content = dict(code=code, cursor_pos=cursor_pos)
110 msg = self.client.session.msg('complete_request', content)
101 msg = self.client.session.msg('complete_request', content)
111 self._dispatch_to_kernel(msg)
102 self._dispatch_to_kernel(msg)
112 return msg['header']['msg_id']
103 return msg['header']['msg_id']
113
104
114 def object_info(self, oname, detail_level=0):
105 def inspect(self, code, cursor_pos=None, detail_level=0):
115 content = dict(oname=oname, detail_level=detail_level)
106 if cursor_pos is None:
116 msg = self.client.session.msg('object_info_request', content)
107 cursor_pos = len(code)
108 content = dict(code=code, cursor_pos=cursor_pos,
109 detail_level=detail_level,
110 )
111 msg = self.client.session.msg('inspect_request', content)
117 self._dispatch_to_kernel(msg)
112 self._dispatch_to_kernel(msg)
118 return msg['header']['msg_id']
113 return msg['header']['msg_id']
119
114
120 def history(self, raw=True, output=False, hist_access_type='range', **kwds):
115 def history(self, raw=True, output=False, hist_access_type='range', **kwds):
121 content = dict(raw=raw, output=output,
116 content = dict(raw=raw, output=output,
122 hist_access_type=hist_access_type, **kwds)
117 hist_access_type=hist_access_type, **kwds)
123 msg = self.client.session.msg('history_request', content)
118 msg = self.client.session.msg('history_request', content)
124 self._dispatch_to_kernel(msg)
119 self._dispatch_to_kernel(msg)
125 return msg['header']['msg_id']
120 return msg['header']['msg_id']
126
121
127 def shutdown(self, restart=False):
122 def shutdown(self, restart=False):
128 # FIXME: What to do here?
123 # FIXME: What to do here?
129 raise NotImplementedError('Cannot shutdown in-process kernel')
124 raise NotImplementedError('Cannot shutdown in-process kernel')
130
125
131 def kernel_info(self):
126 def kernel_info(self):
132 """Request kernel info."""
127 """Request kernel info."""
133 msg = self.client.session.msg('kernel_info_request')
128 msg = self.client.session.msg('kernel_info_request')
134 self._dispatch_to_kernel(msg)
129 self._dispatch_to_kernel(msg)
135 return msg['header']['msg_id']
130 return msg['header']['msg_id']
136
131
137 #--------------------------------------------------------------------------
132 #--------------------------------------------------------------------------
138 # Protected interface
133 # Protected interface
139 #--------------------------------------------------------------------------
134 #--------------------------------------------------------------------------
140
135
141 def _dispatch_to_kernel(self, msg):
136 def _dispatch_to_kernel(self, msg):
142 """ Send a message to the kernel and handle a reply.
137 """ Send a message to the kernel and handle a reply.
143 """
138 """
144 kernel = self.client.kernel
139 kernel = self.client.kernel
145 if kernel is None:
140 if kernel is None:
146 raise RuntimeError('Cannot send request. No kernel exists.')
141 raise RuntimeError('Cannot send request. No kernel exists.')
147
142
148 stream = DummySocket()
143 stream = DummySocket()
149 self.client.session.send(stream, msg)
144 self.client.session.send(stream, msg)
150 msg_parts = stream.recv_multipart()
145 msg_parts = stream.recv_multipart()
151 kernel.dispatch_shell(stream, msg_parts)
146 kernel.dispatch_shell(stream, msg_parts)
152
147
153 idents, reply_msg = self.client.session.recv(stream, copy=False)
148 idents, reply_msg = self.client.session.recv(stream, copy=False)
154 self.call_handlers_later(reply_msg)
149 self.call_handlers_later(reply_msg)
155
150
156
151
157 class InProcessIOPubChannel(InProcessChannel):
152 class InProcessIOPubChannel(InProcessChannel):
158 """See `IPython.kernel.channels.IOPubChannel` for docstrings."""
153 """See `IPython.kernel.channels.IOPubChannel` for docstrings."""
159
154
160 def flush(self, timeout=1.0):
155 def flush(self, timeout=1.0):
161 pass
156 pass
162
157
163
158
164 class InProcessStdInChannel(InProcessChannel):
159 class InProcessStdInChannel(InProcessChannel):
165 """See `IPython.kernel.channels.StdInChannel` for docstrings."""
160 """See `IPython.kernel.channels.StdInChannel` for docstrings."""
166
161
167 proxy_methods = ['input']
162 proxy_methods = ['input']
168
163
169 def input(self, string):
164 def input(self, string):
170 kernel = self.client.kernel
165 kernel = self.client.kernel
171 if kernel is None:
166 if kernel is None:
172 raise RuntimeError('Cannot send input reply. No kernel exists.')
167 raise RuntimeError('Cannot send input reply. No kernel exists.')
173 kernel.raw_input_str = string
168 kernel.raw_input_str = string
174
169
175
170
176 class InProcessHBChannel(InProcessChannel):
171 class InProcessHBChannel(InProcessChannel):
177 """See `IPython.kernel.channels.HBChannel` for docstrings."""
172 """See `IPython.kernel.channels.HBChannel` for docstrings."""
178
173
179 time_to_dead = 3.0
174 time_to_dead = 3.0
180
175
181 def __init__(self, *args, **kwds):
176 def __init__(self, *args, **kwds):
182 super(InProcessHBChannel, self).__init__(*args, **kwds)
177 super(InProcessHBChannel, self).__init__(*args, **kwds)
183 self._pause = True
178 self._pause = True
184
179
185 def pause(self):
180 def pause(self):
186 self._pause = True
181 self._pause = True
187
182
188 def unpause(self):
183 def unpause(self):
189 self._pause = False
184 self._pause = False
190
185
191 def is_beating(self):
186 def is_beating(self):
192 return not self._pause
187 return not self._pause
193
188
194 #-----------------------------------------------------------------------------
189 #-----------------------------------------------------------------------------
195 # ABC Registration
190 # ABC Registration
196 #-----------------------------------------------------------------------------
191 #-----------------------------------------------------------------------------
197
192
198 ShellChannelABC.register(InProcessShellChannel)
193 ShellChannelABC.register(InProcessShellChannel)
199 IOPubChannelABC.register(InProcessIOPubChannel)
194 IOPubChannelABC.register(InProcessIOPubChannel)
200 HBChannelABC.register(InProcessHBChannel)
195 HBChannelABC.register(InProcessHBChannel)
201 StdInChannelABC.register(InProcessStdInChannel)
196 StdInChannelABC.register(InProcessStdInChannel)
@@ -1,182 +1,172
1 """An in-process kernel"""
1 """An in-process kernel"""
2
2
3 #-----------------------------------------------------------------------------
3 # Copyright (c) IPython Development Team.
4 # Copyright (C) 2012 The IPython Development Team
4 # Distributed under the terms of the Modified BSD License.
5 #
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
8 #-----------------------------------------------------------------------------
9
10 #-----------------------------------------------------------------------------
11 # Imports
12 #-----------------------------------------------------------------------------
13
5
14 # Standard library imports
15 from contextlib import contextmanager
6 from contextlib import contextmanager
16 import logging
7 import logging
17 import sys
8 import sys
18
9
19 # Local imports
20 from IPython.core.interactiveshell import InteractiveShellABC
10 from IPython.core.interactiveshell import InteractiveShellABC
21 from IPython.utils.jsonutil import json_clean
11 from IPython.utils.jsonutil import json_clean
22 from IPython.utils.traitlets import Any, Enum, Instance, List, Type
12 from IPython.utils.traitlets import Any, Enum, Instance, List, Type
23 from IPython.kernel.zmq.ipkernel import Kernel
13 from IPython.kernel.zmq.ipkernel import Kernel
24 from IPython.kernel.zmq.zmqshell import ZMQInteractiveShell
14 from IPython.kernel.zmq.zmqshell import ZMQInteractiveShell
25
15
26 from .socket import DummySocket
16 from .socket import DummySocket
27
17
28 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
29 # Main kernel class
19 # Main kernel class
30 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
31
21
32 class InProcessKernel(Kernel):
22 class InProcessKernel(Kernel):
33
23
34 #-------------------------------------------------------------------------
24 #-------------------------------------------------------------------------
35 # InProcessKernel interface
25 # InProcessKernel interface
36 #-------------------------------------------------------------------------
26 #-------------------------------------------------------------------------
37
27
38 # The frontends connected to this kernel.
28 # The frontends connected to this kernel.
39 frontends = List(
29 frontends = List(
40 Instance('IPython.kernel.inprocess.client.InProcessKernelClient')
30 Instance('IPython.kernel.inprocess.client.InProcessKernelClient')
41 )
31 )
42
32
43 # The GUI environment that the kernel is running under. This need not be
33 # The GUI environment that the kernel is running under. This need not be
44 # specified for the normal operation for the kernel, but is required for
34 # specified for the normal operation for the kernel, but is required for
45 # IPython's GUI support (including pylab). The default is 'inline' because
35 # IPython's GUI support (including pylab). The default is 'inline' because
46 # it is safe under all GUI toolkits.
36 # it is safe under all GUI toolkits.
47 gui = Enum(('tk', 'gtk', 'wx', 'qt', 'qt4', 'inline'),
37 gui = Enum(('tk', 'gtk', 'wx', 'qt', 'qt4', 'inline'),
48 default_value='inline')
38 default_value='inline')
49
39
50 raw_input_str = Any()
40 raw_input_str = Any()
51 stdout = Any()
41 stdout = Any()
52 stderr = Any()
42 stderr = Any()
53
43
54 #-------------------------------------------------------------------------
44 #-------------------------------------------------------------------------
55 # Kernel interface
45 # Kernel interface
56 #-------------------------------------------------------------------------
46 #-------------------------------------------------------------------------
57
47
58 shell_class = Type()
48 shell_class = Type()
59 shell_streams = List()
49 shell_streams = List()
60 control_stream = Any()
50 control_stream = Any()
61 iopub_socket = Instance(DummySocket, ())
51 iopub_socket = Instance(DummySocket, ())
62 stdin_socket = Instance(DummySocket, ())
52 stdin_socket = Instance(DummySocket, ())
63
53
64 def __init__(self, **traits):
54 def __init__(self, **traits):
65 # When an InteractiveShell is instantiated by our base class, it binds
55 # When an InteractiveShell is instantiated by our base class, it binds
66 # the current values of sys.stdout and sys.stderr.
56 # the current values of sys.stdout and sys.stderr.
67 with self._redirected_io():
57 with self._redirected_io():
68 super(InProcessKernel, self).__init__(**traits)
58 super(InProcessKernel, self).__init__(**traits)
69
59
70 self.iopub_socket.on_trait_change(self._io_dispatch, 'message_sent')
60 self.iopub_socket.on_trait_change(self._io_dispatch, 'message_sent')
71 self.shell.kernel = self
61 self.shell.kernel = self
72
62
73 def execute_request(self, stream, ident, parent):
63 def execute_request(self, stream, ident, parent):
74 """ Override for temporary IO redirection. """
64 """ Override for temporary IO redirection. """
75 with self._redirected_io():
65 with self._redirected_io():
76 super(InProcessKernel, self).execute_request(stream, ident, parent)
66 super(InProcessKernel, self).execute_request(stream, ident, parent)
77
67
78 def start(self):
68 def start(self):
79 """ Override registration of dispatchers for streams. """
69 """ Override registration of dispatchers for streams. """
80 self.shell.exit_now = False
70 self.shell.exit_now = False
81
71
82 def _abort_queue(self, stream):
72 def _abort_queue(self, stream):
83 """ The in-process kernel doesn't abort requests. """
73 """ The in-process kernel doesn't abort requests. """
84 pass
74 pass
85
75
86 def _raw_input(self, prompt, ident, parent):
76 def _input_request(self, prompt, ident, parent, password=False):
87 # Flush output before making the request.
77 # Flush output before making the request.
88 self.raw_input_str = None
78 self.raw_input_str = None
89 sys.stderr.flush()
79 sys.stderr.flush()
90 sys.stdout.flush()
80 sys.stdout.flush()
91
81
92 # Send the input request.
82 # Send the input request.
93 content = json_clean(dict(prompt=prompt))
83 content = json_clean(dict(prompt=prompt, password=password))
94 msg = self.session.msg(u'input_request', content, parent)
84 msg = self.session.msg(u'input_request', content, parent)
95 for frontend in self.frontends:
85 for frontend in self.frontends:
96 if frontend.session.session == parent['header']['session']:
86 if frontend.session.session == parent['header']['session']:
97 frontend.stdin_channel.call_handlers(msg)
87 frontend.stdin_channel.call_handlers(msg)
98 break
88 break
99 else:
89 else:
100 logging.error('No frontend found for raw_input request')
90 logging.error('No frontend found for raw_input request')
101 return str()
91 return str()
102
92
103 # Await a response.
93 # Await a response.
104 while self.raw_input_str is None:
94 while self.raw_input_str is None:
105 frontend.stdin_channel.process_events()
95 frontend.stdin_channel.process_events()
106 return self.raw_input_str
96 return self.raw_input_str
107
97
108 #-------------------------------------------------------------------------
98 #-------------------------------------------------------------------------
109 # Protected interface
99 # Protected interface
110 #-------------------------------------------------------------------------
100 #-------------------------------------------------------------------------
111
101
112 @contextmanager
102 @contextmanager
113 def _redirected_io(self):
103 def _redirected_io(self):
114 """ Temporarily redirect IO to the kernel.
104 """ Temporarily redirect IO to the kernel.
115 """
105 """
116 sys_stdout, sys_stderr = sys.stdout, sys.stderr
106 sys_stdout, sys_stderr = sys.stdout, sys.stderr
117 sys.stdout, sys.stderr = self.stdout, self.stderr
107 sys.stdout, sys.stderr = self.stdout, self.stderr
118 yield
108 yield
119 sys.stdout, sys.stderr = sys_stdout, sys_stderr
109 sys.stdout, sys.stderr = sys_stdout, sys_stderr
120
110
121 #------ Trait change handlers --------------------------------------------
111 #------ Trait change handlers --------------------------------------------
122
112
123 def _io_dispatch(self):
113 def _io_dispatch(self):
124 """ Called when a message is sent to the IO socket.
114 """ Called when a message is sent to the IO socket.
125 """
115 """
126 ident, msg = self.session.recv(self.iopub_socket, copy=False)
116 ident, msg = self.session.recv(self.iopub_socket, copy=False)
127 for frontend in self.frontends:
117 for frontend in self.frontends:
128 frontend.iopub_channel.call_handlers(msg)
118 frontend.iopub_channel.call_handlers(msg)
129
119
130 #------ Trait initializers -----------------------------------------------
120 #------ Trait initializers -----------------------------------------------
131
121
132 def _log_default(self):
122 def _log_default(self):
133 return logging.getLogger(__name__)
123 return logging.getLogger(__name__)
134
124
135 def _session_default(self):
125 def _session_default(self):
136 from IPython.kernel.zmq.session import Session
126 from IPython.kernel.zmq.session import Session
137 return Session(parent=self)
127 return Session(parent=self)
138
128
139 def _shell_class_default(self):
129 def _shell_class_default(self):
140 return InProcessInteractiveShell
130 return InProcessInteractiveShell
141
131
142 def _stdout_default(self):
132 def _stdout_default(self):
143 from IPython.kernel.zmq.iostream import OutStream
133 from IPython.kernel.zmq.iostream import OutStream
144 return OutStream(self.session, self.iopub_socket, u'stdout', pipe=False)
134 return OutStream(self.session, self.iopub_socket, u'stdout', pipe=False)
145
135
146 def _stderr_default(self):
136 def _stderr_default(self):
147 from IPython.kernel.zmq.iostream import OutStream
137 from IPython.kernel.zmq.iostream import OutStream
148 return OutStream(self.session, self.iopub_socket, u'stderr', pipe=False)
138 return OutStream(self.session, self.iopub_socket, u'stderr', pipe=False)
149
139
150 #-----------------------------------------------------------------------------
140 #-----------------------------------------------------------------------------
151 # Interactive shell subclass
141 # Interactive shell subclass
152 #-----------------------------------------------------------------------------
142 #-----------------------------------------------------------------------------
153
143
154 class InProcessInteractiveShell(ZMQInteractiveShell):
144 class InProcessInteractiveShell(ZMQInteractiveShell):
155
145
156 kernel = Instance('IPython.kernel.inprocess.ipkernel.InProcessKernel')
146 kernel = Instance('IPython.kernel.inprocess.ipkernel.InProcessKernel')
157
147
158 #-------------------------------------------------------------------------
148 #-------------------------------------------------------------------------
159 # InteractiveShell interface
149 # InteractiveShell interface
160 #-------------------------------------------------------------------------
150 #-------------------------------------------------------------------------
161
151
162 def enable_gui(self, gui=None):
152 def enable_gui(self, gui=None):
163 """Enable GUI integration for the kernel."""
153 """Enable GUI integration for the kernel."""
164 from IPython.kernel.zmq.eventloops import enable_gui
154 from IPython.kernel.zmq.eventloops import enable_gui
165 if not gui:
155 if not gui:
166 gui = self.kernel.gui
156 gui = self.kernel.gui
167 return enable_gui(gui, kernel=self.kernel)
157 return enable_gui(gui, kernel=self.kernel)
168
158
169 def enable_matplotlib(self, gui=None):
159 def enable_matplotlib(self, gui=None):
170 """Enable matplotlib integration for the kernel."""
160 """Enable matplotlib integration for the kernel."""
171 if not gui:
161 if not gui:
172 gui = self.kernel.gui
162 gui = self.kernel.gui
173 return super(InProcessInteractiveShell, self).enable_matplotlib(gui)
163 return super(InProcessInteractiveShell, self).enable_matplotlib(gui)
174
164
175 def enable_pylab(self, gui=None, import_all=True, welcome_message=False):
165 def enable_pylab(self, gui=None, import_all=True, welcome_message=False):
176 """Activate pylab support at runtime."""
166 """Activate pylab support at runtime."""
177 if not gui:
167 if not gui:
178 gui = self.kernel.gui
168 gui = self.kernel.gui
179 return super(InProcessInteractiveShell, self).enable_pylab(gui, import_all,
169 return super(InProcessInteractiveShell, self).enable_pylab(gui, import_all,
180 welcome_message)
170 welcome_message)
181
171
182 InteractiveShellABC.register(InProcessInteractiveShell)
172 InteractiveShellABC.register(InProcessInteractiveShell)
@@ -1,111 +1,104
1 #-------------------------------------------------------------------------------
1 # Copyright (c) IPython Development Team.
2 # Copyright (C) 2012 The IPython Development Team
2 # Distributed under the terms of the Modified BSD License.
3 #
4 # Distributed under the terms of the BSD License. The full license is in
5 # the file COPYING, distributed as part of this software.
6 #-------------------------------------------------------------------------------
7
3
8 #-----------------------------------------------------------------------------
9 # Imports
10 #-----------------------------------------------------------------------------
11 from __future__ import print_function
4 from __future__ import print_function
12
5
13 # Standard library imports
14 import unittest
6 import unittest
15
7
16 # Local imports
17 from IPython.kernel.inprocess.blocking import BlockingInProcessKernelClient
8 from IPython.kernel.inprocess.blocking import BlockingInProcessKernelClient
18 from IPython.kernel.inprocess.manager import InProcessKernelManager
9 from IPython.kernel.inprocess.manager import InProcessKernelManager
19
10
20 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
21 # Test case
12 # Test case
22 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
23
14
24 class InProcessKernelManagerTestCase(unittest.TestCase):
15 class InProcessKernelManagerTestCase(unittest.TestCase):
25
16
26 def test_interface(self):
17 def test_interface(self):
27 """ Does the in-process kernel manager implement the basic KM interface?
18 """ Does the in-process kernel manager implement the basic KM interface?
28 """
19 """
29 km = InProcessKernelManager()
20 km = InProcessKernelManager()
30 self.assert_(not km.has_kernel)
21 self.assert_(not km.has_kernel)
31
22
32 km.start_kernel()
23 km.start_kernel()
33 self.assert_(km.has_kernel)
24 self.assert_(km.has_kernel)
34 self.assert_(km.kernel is not None)
25 self.assert_(km.kernel is not None)
35
26
36 kc = BlockingInProcessKernelClient(kernel=km.kernel)
27 kc = BlockingInProcessKernelClient(kernel=km.kernel)
37 self.assert_(not kc.channels_running)
28 self.assert_(not kc.channels_running)
38
29
39 kc.start_channels()
30 kc.start_channels()
40 self.assert_(kc.channels_running)
31 self.assert_(kc.channels_running)
41
32
42 old_kernel = km.kernel
33 old_kernel = km.kernel
43 km.restart_kernel()
34 km.restart_kernel()
44 self.assert_(km.kernel is not None)
35 self.assert_(km.kernel is not None)
45 self.assertNotEquals(km.kernel, old_kernel)
36 self.assertNotEquals(km.kernel, old_kernel)
46
37
47 km.shutdown_kernel()
38 km.shutdown_kernel()
48 self.assert_(not km.has_kernel)
39 self.assert_(not km.has_kernel)
49
40
50 self.assertRaises(NotImplementedError, km.interrupt_kernel)
41 self.assertRaises(NotImplementedError, km.interrupt_kernel)
51 self.assertRaises(NotImplementedError, km.signal_kernel, 9)
42 self.assertRaises(NotImplementedError, km.signal_kernel, 9)
52
43
53 kc.stop_channels()
44 kc.stop_channels()
54 self.assert_(not kc.channels_running)
45 self.assert_(not kc.channels_running)
55
46
56 def test_execute(self):
47 def test_execute(self):
57 """ Does executing code in an in-process kernel work?
48 """ Does executing code in an in-process kernel work?
58 """
49 """
59 km = InProcessKernelManager()
50 km = InProcessKernelManager()
60 km.start_kernel()
51 km.start_kernel()
61 kc = BlockingInProcessKernelClient(kernel=km.kernel)
52 kc = BlockingInProcessKernelClient(kernel=km.kernel)
62 kc.start_channels()
53 kc.start_channels()
63 kc.execute('foo = 1')
54 kc.execute('foo = 1')
64 self.assertEquals(km.kernel.shell.user_ns['foo'], 1)
55 self.assertEquals(km.kernel.shell.user_ns['foo'], 1)
65
56
66 def test_complete(self):
57 def test_complete(self):
67 """ Does requesting completion from an in-process kernel work?
58 """ Does requesting completion from an in-process kernel work?
68 """
59 """
69 km = InProcessKernelManager()
60 km = InProcessKernelManager()
70 km.start_kernel()
61 km.start_kernel()
71 kc = BlockingInProcessKernelClient(kernel=km.kernel)
62 kc = BlockingInProcessKernelClient(kernel=km.kernel)
72 kc.start_channels()
63 kc.start_channels()
73 km.kernel.shell.push({'my_bar': 0, 'my_baz': 1})
64 km.kernel.shell.push({'my_bar': 0, 'my_baz': 1})
74 kc.complete('my_ba', 'my_ba', 5)
65 kc.complete('my_ba', 5)
75 msg = kc.get_shell_msg()
66 msg = kc.get_shell_msg()
76 self.assertEqual(msg['header']['msg_type'], 'complete_reply')
67 self.assertEqual(msg['header']['msg_type'], 'complete_reply')
77 self.assertEqual(sorted(msg['content']['matches']),
68 self.assertEqual(sorted(msg['content']['matches']),
78 ['my_bar', 'my_baz'])
69 ['my_bar', 'my_baz'])
79
70
80 def test_object_info(self):
71 def test_inspect(self):
81 """ Does requesting object information from an in-process kernel work?
72 """ Does requesting object information from an in-process kernel work?
82 """
73 """
83 km = InProcessKernelManager()
74 km = InProcessKernelManager()
84 km.start_kernel()
75 km.start_kernel()
85 kc = BlockingInProcessKernelClient(kernel=km.kernel)
76 kc = BlockingInProcessKernelClient(kernel=km.kernel)
86 kc.start_channels()
77 kc.start_channels()
87 km.kernel.shell.user_ns['foo'] = 1
78 km.kernel.shell.user_ns['foo'] = 1
88 kc.object_info('foo')
79 kc.inspect('foo')
89 msg = kc.get_shell_msg()
80 msg = kc.get_shell_msg()
90 self.assertEquals(msg['header']['msg_type'], 'object_info_reply')
81 self.assertEqual(msg['header']['msg_type'], 'inspect_reply')
91 self.assertEquals(msg['content']['name'], 'foo')
82 content = msg['content']
92 self.assertEquals(msg['content']['type_name'], 'int')
83 assert content['found']
84 text = content['data']['text/plain']
85 self.assertIn('int', text)
93
86
94 def test_history(self):
87 def test_history(self):
95 """ Does requesting history from an in-process kernel work?
88 """ Does requesting history from an in-process kernel work?
96 """
89 """
97 km = InProcessKernelManager()
90 km = InProcessKernelManager()
98 km.start_kernel()
91 km.start_kernel()
99 kc = BlockingInProcessKernelClient(kernel=km.kernel)
92 kc = BlockingInProcessKernelClient(kernel=km.kernel)
100 kc.start_channels()
93 kc.start_channels()
101 kc.execute('%who')
94 kc.execute('%who')
102 kc.history(hist_access_type='tail', n=1)
95 kc.history(hist_access_type='tail', n=1)
103 msg = kc.shell_channel.get_msgs()[-1]
96 msg = kc.shell_channel.get_msgs()[-1]
104 self.assertEquals(msg['header']['msg_type'], 'history_reply')
97 self.assertEquals(msg['header']['msg_type'], 'history_reply')
105 history = msg['content']['history']
98 history = msg['content']['history']
106 self.assertEquals(len(history), 1)
99 self.assertEquals(len(history), 1)
107 self.assertEquals(history[0][2], '%who')
100 self.assertEquals(history[0][2], '%who')
108
101
109
102
110 if __name__ == '__main__':
103 if __name__ == '__main__':
111 unittest.main()
104 unittest.main()
@@ -1,105 +1,53
1 """Abstract base class for kernel managers."""
1 """Abstract base class for kernel managers."""
2
2
3 #-----------------------------------------------------------------------------
3 # Copyright (c) IPython Development Team.
4 # Copyright (C) 2013 The IPython Development Team
4 # Distributed under the terms of the Modified BSD License.
5 #
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
8 #-----------------------------------------------------------------------------
9
5
10 import abc
6 import abc
11
7
12 from IPython.utils.py3compat import with_metaclass
8 from IPython.utils.py3compat import with_metaclass
13
9
14
10
15 class KernelManagerABC(with_metaclass(abc.ABCMeta, object)):
11 class KernelManagerABC(with_metaclass(abc.ABCMeta, object)):
16 """KernelManager ABC.
12 """KernelManager ABC.
17
13
18 The docstrings for this class can be found in the base implementation:
14 The docstrings for this class can be found in the base implementation:
19
15
20 `IPython.kernel.kernelmanager.KernelManager`
16 `IPython.kernel.kernelmanager.KernelManager`
21 """
17 """
22
18
23 @abc.abstractproperty
19 @abc.abstractproperty
24 def kernel(self):
20 def kernel(self):
25 pass
21 pass
26
22
27 @abc.abstractproperty
28 def shell_channel_class(self):
29 pass
30
31 @abc.abstractproperty
32 def iopub_channel_class(self):
33 pass
34
35 @abc.abstractproperty
36 def hb_channel_class(self):
37 pass
38
39 @abc.abstractproperty
40 def stdin_channel_class(self):
41 pass
42
43 #--------------------------------------------------------------------------
44 # Channel management methods
45 #--------------------------------------------------------------------------
46
47 @abc.abstractmethod
48 def start_channels(self, shell=True, iopub=True, stdin=True, hb=True):
49 pass
50
51 @abc.abstractmethod
52 def stop_channels(self):
53 pass
54
55 @abc.abstractproperty
56 def channels_running(self):
57 pass
58
59 @abc.abstractproperty
60 def shell_channel(self):
61 pass
62
63 @abc.abstractproperty
64 def iopub_channel(self):
65 pass
66
67 @abc.abstractproperty
68 def stdin_channel(self):
69 pass
70
71 @abc.abstractproperty
72 def hb_channel(self):
73 pass
74
75 #--------------------------------------------------------------------------
23 #--------------------------------------------------------------------------
76 # Kernel management
24 # Kernel management
77 #--------------------------------------------------------------------------
25 #--------------------------------------------------------------------------
78
26
79 @abc.abstractmethod
27 @abc.abstractmethod
80 def start_kernel(self, **kw):
28 def start_kernel(self, **kw):
81 pass
29 pass
82
30
83 @abc.abstractmethod
31 @abc.abstractmethod
84 def shutdown_kernel(self, now=False, restart=False):
32 def shutdown_kernel(self, now=False, restart=False):
85 pass
33 pass
86
34
87 @abc.abstractmethod
35 @abc.abstractmethod
88 def restart_kernel(self, now=False, **kw):
36 def restart_kernel(self, now=False, **kw):
89 pass
37 pass
90
38
91 @abc.abstractproperty
39 @abc.abstractproperty
92 def has_kernel(self):
40 def has_kernel(self):
93 pass
41 pass
94
42
95 @abc.abstractmethod
43 @abc.abstractmethod
96 def interrupt_kernel(self):
44 def interrupt_kernel(self):
97 pass
45 pass
98
46
99 @abc.abstractmethod
47 @abc.abstractmethod
100 def signal_kernel(self, signum):
48 def signal_kernel(self, signum):
101 pass
49 pass
102
50
103 @abc.abstractmethod
51 @abc.abstractmethod
104 def is_alive(self):
52 def is_alive(self):
105 pass
53 pass
@@ -1,446 +1,409
1 """Test suite for our zeromq-based message specification.
1 """Test suite for our zeromq-based message specification."""
2 """
2
3 #-----------------------------------------------------------------------------
3 # Copyright (c) IPython Development Team.
4 # Copyright (C) 2010 The IPython Development Team
4 # Distributed under the terms of the Modified BSD License.
5 #
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING.txt, distributed as part of this software.
8 #-----------------------------------------------------------------------------
9
5
10 import re
6 import re
7 from distutils.version import LooseVersion as V
11 from subprocess import PIPE
8 from subprocess import PIPE
12 try:
9 try:
13 from queue import Empty # Py 3
10 from queue import Empty # Py 3
14 except ImportError:
11 except ImportError:
15 from Queue import Empty # Py 2
12 from Queue import Empty # Py 2
16
13
17 import nose.tools as nt
14 import nose.tools as nt
18
15
19 from IPython.kernel import KernelManager
16 from IPython.kernel import KernelManager
20
17
21 from IPython.utils.traitlets import (
18 from IPython.utils.traitlets import (
22 HasTraits, TraitError, Bool, Unicode, Dict, Integer, List, Enum, Any,
19 HasTraits, TraitError, Bool, Unicode, Dict, Integer, List, Enum, Any,
23 )
20 )
24 from IPython.utils.py3compat import string_types, iteritems
21 from IPython.utils.py3compat import string_types, iteritems
25
22
26 from .utils import TIMEOUT, start_global_kernel, flush_channels, execute
23 from .utils import TIMEOUT, start_global_kernel, flush_channels, execute
27
24
28 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
29 # Globals
26 # Globals
30 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
31 KC = None
28 KC = None
32
29
33 def setup():
30 def setup():
34 global KC
31 global KC
35 KC = start_global_kernel()
32 KC = start_global_kernel()
36
33
37 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
38 # Message Spec References
35 # Message Spec References
39 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
40
37
41 class Reference(HasTraits):
38 class Reference(HasTraits):
42
39
43 """
40 """
44 Base class for message spec specification testing.
41 Base class for message spec specification testing.
45
42
46 This class is the core of the message specification test. The
43 This class is the core of the message specification test. The
47 idea is that child classes implement trait attributes for each
44 idea is that child classes implement trait attributes for each
48 message keys, so that message keys can be tested against these
45 message keys, so that message keys can be tested against these
49 traits using :meth:`check` method.
46 traits using :meth:`check` method.
50
47
51 """
48 """
52
49
53 def check(self, d):
50 def check(self, d):
54 """validate a dict against our traits"""
51 """validate a dict against our traits"""
55 for key in self.trait_names():
52 for key in self.trait_names():
56 nt.assert_in(key, d)
53 nt.assert_in(key, d)
57 # FIXME: always allow None, probably not a good idea
54 # FIXME: always allow None, probably not a good idea
58 if d[key] is None:
55 if d[key] is None:
59 continue
56 continue
60 try:
57 try:
61 setattr(self, key, d[key])
58 setattr(self, key, d[key])
62 except TraitError as e:
59 except TraitError as e:
63 nt.assert_true(False, str(e))
60 assert False, str(e)
61
62
63 class Version(Unicode):
64 def __init__(self, *args, **kwargs):
65 self.min = kwargs.pop('min', None)
66 self.max = kwargs.pop('max', None)
67 kwargs['default_value'] = self.min
68 super(Version, self).__init__(*args, **kwargs)
69
70 def validate(self, obj, value):
71 if self.min and V(value) < V(self.min):
72 raise TraitError("bad version: %s < %s" % (value, self.min))
73 if self.max and (V(value) > V(self.max)):
74 raise TraitError("bad version: %s > %s" % (value, self.max))
64
75
65
76
66 class RMessage(Reference):
77 class RMessage(Reference):
67 msg_id = Unicode()
78 msg_id = Unicode()
68 msg_type = Unicode()
79 msg_type = Unicode()
69 header = Dict()
80 header = Dict()
70 parent_header = Dict()
81 parent_header = Dict()
71 content = Dict()
82 content = Dict()
83
84 def check(self, d):
85 super(RMessage, self).check(d)
86 RHeader().check(self.header)
87 if self.parent_header:
88 RHeader().check(self.parent_header)
72
89
73 class RHeader(Reference):
90 class RHeader(Reference):
74 msg_id = Unicode()
91 msg_id = Unicode()
75 msg_type = Unicode()
92 msg_type = Unicode()
76 session = Unicode()
93 session = Unicode()
77 username = Unicode()
94 username = Unicode()
95 version = Version(min='5.0')
78
96
79 class RContent(Reference):
97 mime_pat = re.compile(r'^[\w\-\+\.]+/[\w\-\+\.]+$')
80 status = Enum((u'ok', u'error'))
98
99 class MimeBundle(Reference):
100 metadata = Dict()
101 data = Dict()
102 def _data_changed(self, name, old, new):
103 for k,v in iteritems(new):
104 assert mime_pat.match(k)
105 nt.assert_is_instance(v, string_types)
81
106
107 # shell replies
82
108
83 class ExecuteReply(Reference):
109 class ExecuteReply(Reference):
84 execution_count = Integer()
110 execution_count = Integer()
85 status = Enum((u'ok', u'error'))
111 status = Enum((u'ok', u'error'))
86
112
87 def check(self, d):
113 def check(self, d):
88 Reference.check(self, d)
114 Reference.check(self, d)
89 if d['status'] == 'ok':
115 if d['status'] == 'ok':
90 ExecuteReplyOkay().check(d)
116 ExecuteReplyOkay().check(d)
91 elif d['status'] == 'error':
117 elif d['status'] == 'error':
92 ExecuteReplyError().check(d)
118 ExecuteReplyError().check(d)
93
119
94
120
95 class ExecuteReplyOkay(Reference):
121 class ExecuteReplyOkay(Reference):
96 payload = List(Dict)
122 payload = List(Dict)
97 user_variables = Dict()
98 user_expressions = Dict()
123 user_expressions = Dict()
99
124
100
125
101 class ExecuteReplyError(Reference):
126 class ExecuteReplyError(Reference):
102 ename = Unicode()
127 ename = Unicode()
103 evalue = Unicode()
128 evalue = Unicode()
104 traceback = List(Unicode)
129 traceback = List(Unicode)
105
130
106
131
107 class OInfoReply(Reference):
132 class InspectReply(MimeBundle):
108 name = Unicode()
109 found = Bool()
133 found = Bool()
110 ismagic = Bool()
111 isalias = Bool()
112 namespace = Enum((u'builtin', u'magics', u'alias', u'Interactive'))
113 type_name = Unicode()
114 string_form = Unicode()
115 base_class = Unicode()
116 length = Integer()
117 file = Unicode()
118 definition = Unicode()
119 argspec = Dict()
120 init_definition = Unicode()
121 docstring = Unicode()
122 init_docstring = Unicode()
123 class_docstring = Unicode()
124 call_def = Unicode()
125 call_docstring = Unicode()
126 source = Unicode()
127
128 def check(self, d):
129 Reference.check(self, d)
130 if d['argspec'] is not None:
131 ArgSpec().check(d['argspec'])
132
134
133
135
134 class ArgSpec(Reference):
136 class ArgSpec(Reference):
135 args = List(Unicode)
137 args = List(Unicode)
136 varargs = Unicode()
138 varargs = Unicode()
137 varkw = Unicode()
139 varkw = Unicode()
138 defaults = List()
140 defaults = List()
139
141
140
142
141 class Status(Reference):
143 class Status(Reference):
142 execution_state = Enum((u'busy', u'idle', u'starting'))
144 execution_state = Enum((u'busy', u'idle', u'starting'))
143
145
144
146
145 class CompleteReply(Reference):
147 class CompleteReply(Reference):
146 matches = List(Unicode)
148 matches = List(Unicode)
147
149 cursor_start = Integer()
148
150 cursor_end = Integer()
149 def Version(num, trait=Integer):
151 status = Unicode()
150 return List(trait, default_value=[0] * num, minlen=num, maxlen=num)
151
152
152
153
153 class KernelInfoReply(Reference):
154 class KernelInfoReply(Reference):
154
155 protocol_version = Version(min='5.0')
155 protocol_version = Version(2)
156 implementation = Unicode('ipython')
156 ipython_version = Version(4, Any)
157 implementation_version = Version(min='2.1')
157 language_version = Version(3)
158 language_version = Version(min='2.7')
158 language = Unicode()
159 language = Unicode('python')
159
160 banner = Unicode()
160 def _ipython_version_changed(self, name, old, new):
161 for v in new:
162 assert isinstance(v, int) or isinstance(v, string_types), \
163 'expected int or string as version component, got {0!r}'.format(v)
164
161
165
162
166 # IOPub messages
163 # IOPub messages
167
164
168 class PyIn(Reference):
165 class ExecuteInput(Reference):
169 code = Unicode()
166 code = Unicode()
170 execution_count = Integer()
167 execution_count = Integer()
171
168
172
169
173 PyErr = ExecuteReplyError
170 Error = ExecuteReplyError
174
171
175
172
176 class Stream(Reference):
173 class Stream(Reference):
177 name = Enum((u'stdout', u'stderr'))
174 name = Enum((u'stdout', u'stderr'))
178 data = Unicode()
175 data = Unicode()
179
176
180
177
181 mime_pat = re.compile(r'\w+/\w+')
178 class DisplayData(MimeBundle):
179 pass
182
180
183 class DisplayData(Reference):
184 source = Unicode()
185 metadata = Dict()
186 data = Dict()
187 def _data_changed(self, name, old, new):
188 for k,v in iteritems(new):
189 assert mime_pat.match(k)
190 nt.assert_is_instance(v, string_types)
191
181
192
182 class ExecuteResult(MimeBundle):
193 class PyOut(Reference):
194 execution_count = Integer()
183 execution_count = Integer()
195 data = Dict()
196 def _data_changed(self, name, old, new):
197 for k,v in iteritems(new):
198 assert mime_pat.match(k)
199 nt.assert_is_instance(v, string_types)
200
184
201
185
202 references = {
186 references = {
203 'execute_reply' : ExecuteReply(),
187 'execute_reply' : ExecuteReply(),
204 'object_info_reply' : OInfoReply(),
188 'inspect_reply' : InspectReply(),
205 'status' : Status(),
189 'status' : Status(),
206 'complete_reply' : CompleteReply(),
190 'complete_reply' : CompleteReply(),
207 'kernel_info_reply': KernelInfoReply(),
191 'kernel_info_reply': KernelInfoReply(),
208 'pyin' : PyIn(),
192 'execute_input' : ExecuteInput(),
209 'pyout' : PyOut(),
193 'execute_result' : ExecuteResult(),
210 'pyerr' : PyErr(),
194 'error' : Error(),
211 'stream' : Stream(),
195 'stream' : Stream(),
212 'display_data' : DisplayData(),
196 'display_data' : DisplayData(),
197 'header' : RHeader(),
213 }
198 }
214 """
199 """
215 Specifications of `content` part of the reply messages.
200 Specifications of `content` part of the reply messages.
216 """
201 """
217
202
218
203
219 def validate_message(msg, msg_type=None, parent=None):
204 def validate_message(msg, msg_type=None, parent=None):
220 """validate a message
205 """validate a message
221
206
222 This is a generator, and must be iterated through to actually
207 This is a generator, and must be iterated through to actually
223 trigger each test.
208 trigger each test.
224
209
225 If msg_type and/or parent are given, the msg_type and/or parent msg_id
210 If msg_type and/or parent are given, the msg_type and/or parent msg_id
226 are compared with the given values.
211 are compared with the given values.
227 """
212 """
228 RMessage().check(msg)
213 RMessage().check(msg)
229 if msg_type:
214 if msg_type:
230 nt.assert_equal(msg['msg_type'], msg_type)
215 nt.assert_equal(msg['msg_type'], msg_type)
231 if parent:
216 if parent:
232 nt.assert_equal(msg['parent_header']['msg_id'], parent)
217 nt.assert_equal(msg['parent_header']['msg_id'], parent)
233 content = msg['content']
218 content = msg['content']
234 ref = references[msg['msg_type']]
219 ref = references[msg['msg_type']]
235 ref.check(content)
220 ref.check(content)
236
221
237
222
238 #-----------------------------------------------------------------------------
223 #-----------------------------------------------------------------------------
239 # Tests
224 # Tests
240 #-----------------------------------------------------------------------------
225 #-----------------------------------------------------------------------------
241
226
242 # Shell channel
227 # Shell channel
243
228
244 def test_execute():
229 def test_execute():
245 flush_channels()
230 flush_channels()
246
231
247 msg_id = KC.execute(code='x=1')
232 msg_id = KC.execute(code='x=1')
248 reply = KC.get_shell_msg(timeout=TIMEOUT)
233 reply = KC.get_shell_msg(timeout=TIMEOUT)
249 validate_message(reply, 'execute_reply', msg_id)
234 validate_message(reply, 'execute_reply', msg_id)
250
235
251
236
252 def test_execute_silent():
237 def test_execute_silent():
253 flush_channels()
238 flush_channels()
254 msg_id, reply = execute(code='x=1', silent=True)
239 msg_id, reply = execute(code='x=1', silent=True)
255
240
256 # flush status=idle
241 # flush status=idle
257 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
242 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
258 validate_message(status, 'status', msg_id)
243 validate_message(status, 'status', msg_id)
259 nt.assert_equal(status['content']['execution_state'], 'idle')
244 nt.assert_equal(status['content']['execution_state'], 'idle')
260
245
261 nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
246 nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
262 count = reply['execution_count']
247 count = reply['execution_count']
263
248
264 msg_id, reply = execute(code='x=2', silent=True)
249 msg_id, reply = execute(code='x=2', silent=True)
265
250
266 # flush status=idle
251 # flush status=idle
267 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
252 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
268 validate_message(status, 'status', msg_id)
253 validate_message(status, 'status', msg_id)
269 nt.assert_equal(status['content']['execution_state'], 'idle')
254 nt.assert_equal(status['content']['execution_state'], 'idle')
270
255
271 nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
256 nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
272 count_2 = reply['execution_count']
257 count_2 = reply['execution_count']
273 nt.assert_equal(count_2, count)
258 nt.assert_equal(count_2, count)
274
259
275
260
276 def test_execute_error():
261 def test_execute_error():
277 flush_channels()
262 flush_channels()
278
263
279 msg_id, reply = execute(code='1/0')
264 msg_id, reply = execute(code='1/0')
280 nt.assert_equal(reply['status'], 'error')
265 nt.assert_equal(reply['status'], 'error')
281 nt.assert_equal(reply['ename'], 'ZeroDivisionError')
266 nt.assert_equal(reply['ename'], 'ZeroDivisionError')
282
267
283 pyerr = KC.iopub_channel.get_msg(timeout=TIMEOUT)
268 error = KC.iopub_channel.get_msg(timeout=TIMEOUT)
284 validate_message(pyerr, 'pyerr', msg_id)
269 validate_message(error, 'error', msg_id)
285
270
286
271
287 def test_execute_inc():
272 def test_execute_inc():
288 """execute request should increment execution_count"""
273 """execute request should increment execution_count"""
289 flush_channels()
274 flush_channels()
290
275
291 msg_id, reply = execute(code='x=1')
276 msg_id, reply = execute(code='x=1')
292 count = reply['execution_count']
277 count = reply['execution_count']
293
278
294 flush_channels()
279 flush_channels()
295
280
296 msg_id, reply = execute(code='x=2')
281 msg_id, reply = execute(code='x=2')
297 count_2 = reply['execution_count']
282 count_2 = reply['execution_count']
298 nt.assert_equal(count_2, count+1)
283 nt.assert_equal(count_2, count+1)
299
284
300
285
301 def test_user_variables():
302 flush_channels()
303
304 msg_id, reply = execute(code='x=1', user_variables=['x'])
305 user_variables = reply['user_variables']
306 nt.assert_equal(user_variables, {u'x': {
307 u'status': u'ok',
308 u'data': {u'text/plain': u'1'},
309 u'metadata': {},
310 }})
311
312
313 def test_user_variables_fail():
314 flush_channels()
315
316 msg_id, reply = execute(code='x=1', user_variables=['nosuchname'])
317 user_variables = reply['user_variables']
318 foo = user_variables['nosuchname']
319 nt.assert_equal(foo['status'], 'error')
320 nt.assert_equal(foo['ename'], 'KeyError')
321
322
323 def test_user_expressions():
286 def test_user_expressions():
324 flush_channels()
287 flush_channels()
325
288
326 msg_id, reply = execute(code='x=1', user_expressions=dict(foo='x+1'))
289 msg_id, reply = execute(code='x=1', user_expressions=dict(foo='x+1'))
327 user_expressions = reply['user_expressions']
290 user_expressions = reply['user_expressions']
328 nt.assert_equal(user_expressions, {u'foo': {
291 nt.assert_equal(user_expressions, {u'foo': {
329 u'status': u'ok',
292 u'status': u'ok',
330 u'data': {u'text/plain': u'2'},
293 u'data': {u'text/plain': u'2'},
331 u'metadata': {},
294 u'metadata': {},
332 }})
295 }})
333
296
334
297
335 def test_user_expressions_fail():
298 def test_user_expressions_fail():
336 flush_channels()
299 flush_channels()
337
300
338 msg_id, reply = execute(code='x=0', user_expressions=dict(foo='nosuchname'))
301 msg_id, reply = execute(code='x=0', user_expressions=dict(foo='nosuchname'))
339 user_expressions = reply['user_expressions']
302 user_expressions = reply['user_expressions']
340 foo = user_expressions['foo']
303 foo = user_expressions['foo']
341 nt.assert_equal(foo['status'], 'error')
304 nt.assert_equal(foo['status'], 'error')
342 nt.assert_equal(foo['ename'], 'NameError')
305 nt.assert_equal(foo['ename'], 'NameError')
343
306
344
307
345 def test_oinfo():
308 def test_oinfo():
346 flush_channels()
309 flush_channels()
347
310
348 msg_id = KC.object_info('a')
311 msg_id = KC.inspect('a')
349 reply = KC.get_shell_msg(timeout=TIMEOUT)
312 reply = KC.get_shell_msg(timeout=TIMEOUT)
350 validate_message(reply, 'object_info_reply', msg_id)
313 validate_message(reply, 'inspect_reply', msg_id)
351
314
352
315
353 def test_oinfo_found():
316 def test_oinfo_found():
354 flush_channels()
317 flush_channels()
355
318
356 msg_id, reply = execute(code='a=5')
319 msg_id, reply = execute(code='a=5')
357
320
358 msg_id = KC.object_info('a')
321 msg_id = KC.inspect('a')
359 reply = KC.get_shell_msg(timeout=TIMEOUT)
322 reply = KC.get_shell_msg(timeout=TIMEOUT)
360 validate_message(reply, 'object_info_reply', msg_id)
323 validate_message(reply, 'inspect_reply', msg_id)
361 content = reply['content']
324 content = reply['content']
362 assert content['found']
325 assert content['found']
363 argspec = content['argspec']
326 text = content['data']['text/plain']
364 nt.assert_is(argspec, None)
327 nt.assert_in('Type:', text)
328 nt.assert_in('Docstring:', text)
365
329
366
330
367 def test_oinfo_detail():
331 def test_oinfo_detail():
368 flush_channels()
332 flush_channels()
369
333
370 msg_id, reply = execute(code='ip=get_ipython()')
334 msg_id, reply = execute(code='ip=get_ipython()')
371
335
372 msg_id = KC.object_info('ip.object_inspect', detail_level=2)
336 msg_id = KC.inspect('ip.object_inspect', cursor_pos=10, detail_level=1)
373 reply = KC.get_shell_msg(timeout=TIMEOUT)
337 reply = KC.get_shell_msg(timeout=TIMEOUT)
374 validate_message(reply, 'object_info_reply', msg_id)
338 validate_message(reply, 'inspect_reply', msg_id)
375 content = reply['content']
339 content = reply['content']
376 assert content['found']
340 assert content['found']
377 argspec = content['argspec']
341 text = content['data']['text/plain']
378 nt.assert_is_instance(argspec, dict, "expected non-empty argspec dict, got %r" % argspec)
342 nt.assert_in('Definition:', text)
379 nt.assert_equal(argspec['defaults'], [0])
343 nt.assert_in('Source:', text)
380
344
381
345
382 def test_oinfo_not_found():
346 def test_oinfo_not_found():
383 flush_channels()
347 flush_channels()
384
348
385 msg_id = KC.object_info('dne')
349 msg_id = KC.inspect('dne')
386 reply = KC.get_shell_msg(timeout=TIMEOUT)
350 reply = KC.get_shell_msg(timeout=TIMEOUT)
387 validate_message(reply, 'object_info_reply', msg_id)
351 validate_message(reply, 'inspect_reply', msg_id)
388 content = reply['content']
352 content = reply['content']
389 nt.assert_false(content['found'])
353 nt.assert_false(content['found'])
390
354
391
355
392 def test_complete():
356 def test_complete():
393 flush_channels()
357 flush_channels()
394
358
395 msg_id, reply = execute(code="alpha = albert = 5")
359 msg_id, reply = execute(code="alpha = albert = 5")
396
360
397 msg_id = KC.complete('al', 'al', 2)
361 msg_id = KC.complete('al', 2)
398 reply = KC.get_shell_msg(timeout=TIMEOUT)
362 reply = KC.get_shell_msg(timeout=TIMEOUT)
399 validate_message(reply, 'complete_reply', msg_id)
363 validate_message(reply, 'complete_reply', msg_id)
400 matches = reply['content']['matches']
364 matches = reply['content']['matches']
401 for name in ('alpha', 'albert'):
365 for name in ('alpha', 'albert'):
402 nt.assert_in(name, matches)
366 nt.assert_in(name, matches)
403
367
404
368
405 def test_kernel_info_request():
369 def test_kernel_info_request():
406 flush_channels()
370 flush_channels()
407
371
408 msg_id = KC.kernel_info()
372 msg_id = KC.kernel_info()
409 reply = KC.get_shell_msg(timeout=TIMEOUT)
373 reply = KC.get_shell_msg(timeout=TIMEOUT)
410 validate_message(reply, 'kernel_info_reply', msg_id)
374 validate_message(reply, 'kernel_info_reply', msg_id)
411
375
412
376
413 def test_single_payload():
377 def test_single_payload():
414 flush_channels()
378 flush_channels()
415 msg_id, reply = execute(code="for i in range(3):\n"+
379 msg_id, reply = execute(code="for i in range(3):\n"+
416 " x=range?\n")
380 " x=range?\n")
417 payload = reply['payload']
381 payload = reply['payload']
418 next_input_pls = [pl for pl in payload if pl["source"] == "set_next_input"]
382 next_input_pls = [pl for pl in payload if pl["source"] == "set_next_input"]
419 nt.assert_equal(len(next_input_pls), 1)
383 nt.assert_equal(len(next_input_pls), 1)
420
384
421
385
422 # IOPub channel
386 # IOPub channel
423
387
424
388
425 def test_stream():
389 def test_stream():
426 flush_channels()
390 flush_channels()
427
391
428 msg_id, reply = execute("print('hi')")
392 msg_id, reply = execute("print('hi')")
429
393
430 stdout = KC.iopub_channel.get_msg(timeout=TIMEOUT)
394 stdout = KC.iopub_channel.get_msg(timeout=TIMEOUT)
431 validate_message(stdout, 'stream', msg_id)
395 validate_message(stdout, 'stream', msg_id)
432 content = stdout['content']
396 content = stdout['content']
433 nt.assert_equal(content['name'], u'stdout')
434 nt.assert_equal(content['data'], u'hi\n')
397 nt.assert_equal(content['data'], u'hi\n')
435
398
436
399
437 def test_display_data():
400 def test_display_data():
438 flush_channels()
401 flush_channels()
439
402
440 msg_id, reply = execute("from IPython.core.display import display; display(1)")
403 msg_id, reply = execute("from IPython.core.display import display; display(1)")
441
404
442 display = KC.iopub_channel.get_msg(timeout=TIMEOUT)
405 display = KC.iopub_channel.get_msg(timeout=TIMEOUT)
443 validate_message(display, 'display_data', parent=msg_id)
406 validate_message(display, 'display_data', parent=msg_id)
444 data = display['content']['data']
407 data = display['content']['data']
445 nt.assert_equal(data['text/plain'], u'1')
408 nt.assert_equal(data['text/plain'], u'1')
446
409
@@ -1,179 +1,171
1 """utilities for testing IPython kernels"""
1 """utilities for testing IPython kernels"""
2
2
3 #-------------------------------------------------------------------------------
3 # Copyright (c) IPython Development Team.
4 # Copyright (C) 2013 The IPython Development Team
4 # Distributed under the terms of the Modified BSD License.
5 #
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
8 #-------------------------------------------------------------------------------
9
10 #-------------------------------------------------------------------------------
11 # Imports
12 #-------------------------------------------------------------------------------
13
5
14 import atexit
6 import atexit
15
7
16 from contextlib import contextmanager
8 from contextlib import contextmanager
17 from subprocess import PIPE, STDOUT
9 from subprocess import PIPE, STDOUT
18 try:
10 try:
19 from queue import Empty # Py 3
11 from queue import Empty # Py 3
20 except ImportError:
12 except ImportError:
21 from Queue import Empty # Py 2
13 from Queue import Empty # Py 2
22
14
23 import nose
15 import nose
24 import nose.tools as nt
16 import nose.tools as nt
25
17
26 from IPython.kernel import KernelManager
18 from IPython.kernel import KernelManager
27
19
28 #-------------------------------------------------------------------------------
20 #-------------------------------------------------------------------------------
29 # Globals
21 # Globals
30 #-------------------------------------------------------------------------------
22 #-------------------------------------------------------------------------------
31
23
32 STARTUP_TIMEOUT = 60
24 STARTUP_TIMEOUT = 60
33 TIMEOUT = 15
25 TIMEOUT = 15
34
26
35 KM = None
27 KM = None
36 KC = None
28 KC = None
37
29
38 #-------------------------------------------------------------------------------
30 #-------------------------------------------------------------------------------
39 # code
31 # code
40 #-------------------------------------------------------------------------------
32 #-------------------------------------------------------------------------------
41
33
42
34
43 def start_new_kernel(argv=None):
35 def start_new_kernel(argv=None):
44 """start a new kernel, and return its Manager and Client"""
36 """start a new kernel, and return its Manager and Client"""
45 km = KernelManager()
37 km = KernelManager()
46 kwargs = dict(stdout=nose.iptest_stdstreams_fileno(), stderr=STDOUT)
38 kwargs = dict(stdout=nose.iptest_stdstreams_fileno(), stderr=STDOUT)
47 if argv:
39 if argv:
48 kwargs['extra_arguments'] = argv
40 kwargs['extra_arguments'] = argv
49 km.start_kernel(**kwargs)
41 km.start_kernel(**kwargs)
50 kc = km.client()
42 kc = km.client()
51 kc.start_channels()
43 kc.start_channels()
52
44
53 msg_id = kc.kernel_info()
45 msg_id = kc.kernel_info()
54 kc.get_shell_msg(block=True, timeout=STARTUP_TIMEOUT)
46 kc.get_shell_msg(block=True, timeout=STARTUP_TIMEOUT)
55 flush_channels(kc)
47 flush_channels(kc)
56 return km, kc
48 return km, kc
57
49
58 def flush_channels(kc=None):
50 def flush_channels(kc=None):
59 """flush any messages waiting on the queue"""
51 """flush any messages waiting on the queue"""
60 from .test_message_spec import validate_message
52 from .test_message_spec import validate_message
61
53
62 if kc is None:
54 if kc is None:
63 kc = KC
55 kc = KC
64 for channel in (kc.shell_channel, kc.iopub_channel):
56 for channel in (kc.shell_channel, kc.iopub_channel):
65 while True:
57 while True:
66 try:
58 try:
67 msg = channel.get_msg(block=True, timeout=0.1)
59 msg = channel.get_msg(block=True, timeout=0.1)
68 except Empty:
60 except Empty:
69 break
61 break
70 else:
62 else:
71 validate_message(msg)
63 validate_message(msg)
72
64
73
65
74 def execute(code='', kc=None, **kwargs):
66 def execute(code='', kc=None, **kwargs):
75 """wrapper for doing common steps for validating an execution request"""
67 """wrapper for doing common steps for validating an execution request"""
76 from .test_message_spec import validate_message
68 from .test_message_spec import validate_message
77 if kc is None:
69 if kc is None:
78 kc = KC
70 kc = KC
79 msg_id = kc.execute(code=code, **kwargs)
71 msg_id = kc.execute(code=code, **kwargs)
80 reply = kc.get_shell_msg(timeout=TIMEOUT)
72 reply = kc.get_shell_msg(timeout=TIMEOUT)
81 validate_message(reply, 'execute_reply', msg_id)
73 validate_message(reply, 'execute_reply', msg_id)
82 busy = kc.get_iopub_msg(timeout=TIMEOUT)
74 busy = kc.get_iopub_msg(timeout=TIMEOUT)
83 validate_message(busy, 'status', msg_id)
75 validate_message(busy, 'status', msg_id)
84 nt.assert_equal(busy['content']['execution_state'], 'busy')
76 nt.assert_equal(busy['content']['execution_state'], 'busy')
85
77
86 if not kwargs.get('silent'):
78 if not kwargs.get('silent'):
87 pyin = kc.get_iopub_msg(timeout=TIMEOUT)
79 execute_input = kc.get_iopub_msg(timeout=TIMEOUT)
88 validate_message(pyin, 'pyin', msg_id)
80 validate_message(execute_input, 'execute_input', msg_id)
89 nt.assert_equal(pyin['content']['code'], code)
81 nt.assert_equal(execute_input['content']['code'], code)
90
82
91 return msg_id, reply['content']
83 return msg_id, reply['content']
92
84
93 def start_global_kernel():
85 def start_global_kernel():
94 """start the global kernel (if it isn't running) and return its client"""
86 """start the global kernel (if it isn't running) and return its client"""
95 global KM, KC
87 global KM, KC
96 if KM is None:
88 if KM is None:
97 KM, KC = start_new_kernel()
89 KM, KC = start_new_kernel()
98 atexit.register(stop_global_kernel)
90 atexit.register(stop_global_kernel)
99 return KC
91 return KC
100
92
101 @contextmanager
93 @contextmanager
102 def kernel():
94 def kernel():
103 """Context manager for the global kernel instance
95 """Context manager for the global kernel instance
104
96
105 Should be used for most kernel tests
97 Should be used for most kernel tests
106
98
107 Returns
99 Returns
108 -------
100 -------
109 kernel_client: connected KernelClient instance
101 kernel_client: connected KernelClient instance
110 """
102 """
111 yield start_global_kernel()
103 yield start_global_kernel()
112
104
113 def uses_kernel(test_f):
105 def uses_kernel(test_f):
114 """Decorator for tests that use the global kernel"""
106 """Decorator for tests that use the global kernel"""
115 def wrapped_test():
107 def wrapped_test():
116 with kernel() as kc:
108 with kernel() as kc:
117 test_f(kc)
109 test_f(kc)
118 wrapped_test.__doc__ = test_f.__doc__
110 wrapped_test.__doc__ = test_f.__doc__
119 wrapped_test.__name__ = test_f.__name__
111 wrapped_test.__name__ = test_f.__name__
120 return wrapped_test
112 return wrapped_test
121
113
122 def stop_global_kernel():
114 def stop_global_kernel():
123 """Stop the global shared kernel instance, if it exists"""
115 """Stop the global shared kernel instance, if it exists"""
124 global KM, KC
116 global KM, KC
125 KC.stop_channels()
117 KC.stop_channels()
126 KC = None
118 KC = None
127 if KM is None:
119 if KM is None:
128 return
120 return
129 KM.shutdown_kernel(now=True)
121 KM.shutdown_kernel(now=True)
130 KM = None
122 KM = None
131
123
132 @contextmanager
124 @contextmanager
133 def new_kernel(argv=None):
125 def new_kernel(argv=None):
134 """Context manager for a new kernel in a subprocess
126 """Context manager for a new kernel in a subprocess
135
127
136 Should only be used for tests where the kernel must not be re-used.
128 Should only be used for tests where the kernel must not be re-used.
137
129
138 Returns
130 Returns
139 -------
131 -------
140 kernel_client: connected KernelClient instance
132 kernel_client: connected KernelClient instance
141 """
133 """
142 km, kc = start_new_kernel(argv)
134 km, kc = start_new_kernel(argv)
143 try:
135 try:
144 yield kc
136 yield kc
145 finally:
137 finally:
146 kc.stop_channels()
138 kc.stop_channels()
147 km.shutdown_kernel(now=True)
139 km.shutdown_kernel(now=True)
148
140
149
141
150 def assemble_output(iopub):
142 def assemble_output(iopub):
151 """assemble stdout/err from an execution"""
143 """assemble stdout/err from an execution"""
152 stdout = ''
144 stdout = ''
153 stderr = ''
145 stderr = ''
154 while True:
146 while True:
155 msg = iopub.get_msg(block=True, timeout=1)
147 msg = iopub.get_msg(block=True, timeout=1)
156 msg_type = msg['msg_type']
148 msg_type = msg['msg_type']
157 content = msg['content']
149 content = msg['content']
158 if msg_type == 'status' and content['execution_state'] == 'idle':
150 if msg_type == 'status' and content['execution_state'] == 'idle':
159 # idle message signals end of output
151 # idle message signals end of output
160 break
152 break
161 elif msg['msg_type'] == 'stream':
153 elif msg['msg_type'] == 'stream':
162 if content['name'] == 'stdout':
154 if content['name'] == 'stdout':
163 stdout += content['data']
155 stdout += content['data']
164 elif content['name'] == 'stderr':
156 elif content['name'] == 'stderr':
165 stderr += content['data']
157 stderr += content['data']
166 else:
158 else:
167 raise KeyError("bad stream: %r" % content['name'])
159 raise KeyError("bad stream: %r" % content['name'])
168 else:
160 else:
169 # other output, ignored
161 # other output, ignored
170 pass
162 pass
171 return stdout, stderr
163 return stdout, stderr
172
164
173 def wait_for_idle(kc):
165 def wait_for_idle(kc):
174 while True:
166 while True:
175 msg = kc.iopub_channel.get_msg(block=True, timeout=1)
167 msg = kc.iopub_channel.get_msg(block=True, timeout=1)
176 msg_type = msg['msg_type']
168 msg_type = msg['msg_type']
177 content = msg['content']
169 content = msg['content']
178 if msg_type == 'status' and content['execution_state'] == 'idle':
170 if msg_type == 'status' and content['execution_state'] == 'idle':
179 break
171 break
@@ -1,67 +1,70
1 """Replacements for sys.displayhook that publish over ZMQ.
1 """Replacements for sys.displayhook that publish over ZMQ."""
2 """
2
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
5
3 import sys
6 import sys
4
7
5 from IPython.core.displayhook import DisplayHook
8 from IPython.core.displayhook import DisplayHook
6 from IPython.kernel.inprocess.socket import SocketABC
9 from IPython.kernel.inprocess.socket import SocketABC
7 from IPython.utils.jsonutil import encode_images
10 from IPython.utils.jsonutil import encode_images
8 from IPython.utils.py3compat import builtin_mod
11 from IPython.utils.py3compat import builtin_mod
9 from IPython.utils.traitlets import Instance, Dict
12 from IPython.utils.traitlets import Instance, Dict
10 from .session import extract_header, Session
13 from .session import extract_header, Session
11
14
12 class ZMQDisplayHook(object):
15 class ZMQDisplayHook(object):
13 """A simple displayhook that publishes the object's repr over a ZeroMQ
16 """A simple displayhook that publishes the object's repr over a ZeroMQ
14 socket."""
17 socket."""
15 topic=b'pyout'
18 topic=b'execute_result'
16
19
17 def __init__(self, session, pub_socket):
20 def __init__(self, session, pub_socket):
18 self.session = session
21 self.session = session
19 self.pub_socket = pub_socket
22 self.pub_socket = pub_socket
20 self.parent_header = {}
23 self.parent_header = {}
21
24
22 def __call__(self, obj):
25 def __call__(self, obj):
23 if obj is None:
26 if obj is None:
24 return
27 return
25
28
26 builtin_mod._ = obj
29 builtin_mod._ = obj
27 sys.stdout.flush()
30 sys.stdout.flush()
28 sys.stderr.flush()
31 sys.stderr.flush()
29 msg = self.session.send(self.pub_socket, u'pyout', {u'data':repr(obj)},
32 msg = self.session.send(self.pub_socket, u'execute_result', {u'data':repr(obj)},
30 parent=self.parent_header, ident=self.topic)
33 parent=self.parent_header, ident=self.topic)
31
34
32 def set_parent(self, parent):
35 def set_parent(self, parent):
33 self.parent_header = extract_header(parent)
36 self.parent_header = extract_header(parent)
34
37
35
38
36 class ZMQShellDisplayHook(DisplayHook):
39 class ZMQShellDisplayHook(DisplayHook):
37 """A displayhook subclass that publishes data using ZeroMQ. This is intended
40 """A displayhook subclass that publishes data using ZeroMQ. This is intended
38 to work with an InteractiveShell instance. It sends a dict of different
41 to work with an InteractiveShell instance. It sends a dict of different
39 representations of the object."""
42 representations of the object."""
40 topic=None
43 topic=None
41
44
42 session = Instance(Session)
45 session = Instance(Session)
43 pub_socket = Instance(SocketABC)
46 pub_socket = Instance(SocketABC)
44 parent_header = Dict({})
47 parent_header = Dict({})
45
48
46 def set_parent(self, parent):
49 def set_parent(self, parent):
47 """Set the parent for outbound messages."""
50 """Set the parent for outbound messages."""
48 self.parent_header = extract_header(parent)
51 self.parent_header = extract_header(parent)
49
52
50 def start_displayhook(self):
53 def start_displayhook(self):
51 self.msg = self.session.msg(u'pyout', {}, parent=self.parent_header)
54 self.msg = self.session.msg(u'execute_result', {}, parent=self.parent_header)
52
55
53 def write_output_prompt(self):
56 def write_output_prompt(self):
54 """Write the output prompt."""
57 """Write the output prompt."""
55 self.msg['content']['execution_count'] = self.prompt_count
58 self.msg['content']['execution_count'] = self.prompt_count
56
59
57 def write_format_data(self, format_dict, md_dict=None):
60 def write_format_data(self, format_dict, md_dict=None):
58 self.msg['content']['data'] = encode_images(format_dict)
61 self.msg['content']['data'] = encode_images(format_dict)
59 self.msg['content']['metadata'] = md_dict
62 self.msg['content']['metadata'] = md_dict
60
63
61 def finish_displayhook(self):
64 def finish_displayhook(self):
62 """Finish up all displayhook activities."""
65 """Finish up all displayhook activities."""
63 sys.stdout.flush()
66 sys.stdout.flush()
64 sys.stderr.flush()
67 sys.stderr.flush()
65 self.session.send(self.pub_socket, self.msg, ident=self.topic)
68 self.session.send(self.pub_socket, self.msg, ident=self.topic)
66 self.msg = None
69 self.msg = None
67
70
@@ -1,800 +1,854
1 #!/usr/bin/env python
2 """An interactive kernel that talks to frontends over 0MQ."""
1 """An interactive kernel that talks to frontends over 0MQ."""
3
2
4 #-----------------------------------------------------------------------------
3 # Copyright (c) IPython Development Team.
5 # Imports
4 # Distributed under the terms of the Modified BSD License.
6 #-----------------------------------------------------------------------------
5
7 from __future__ import print_function
6 from __future__ import print_function
8
7
9 # Standard library imports
8 import getpass
10 import sys
9 import sys
11 import time
10 import time
12 import traceback
11 import traceback
13 import logging
12 import logging
14 import uuid
13 import uuid
15
14
16 from datetime import datetime
15 from datetime import datetime
17 from signal import (
16 from signal import (
18 signal, default_int_handler, SIGINT
17 signal, default_int_handler, SIGINT
19 )
18 )
20
19
21 # System library imports
22 import zmq
20 import zmq
23 from zmq.eventloop import ioloop
21 from zmq.eventloop import ioloop
24 from zmq.eventloop.zmqstream import ZMQStream
22 from zmq.eventloop.zmqstream import ZMQStream
25
23
26 # Local imports
27 from IPython.config.configurable import Configurable
24 from IPython.config.configurable import Configurable
28 from IPython.core.error import StdinNotImplementedError
25 from IPython.core.error import StdinNotImplementedError
29 from IPython.core import release
26 from IPython.core import release
30 from IPython.utils import py3compat
27 from IPython.utils import py3compat
31 from IPython.utils.py3compat import builtin_mod, unicode_type, string_types
28 from IPython.utils.py3compat import builtin_mod, unicode_type, string_types
32 from IPython.utils.jsonutil import json_clean
29 from IPython.utils.jsonutil import json_clean
30 from IPython.utils.tokenutil import token_at_cursor
33 from IPython.utils.traitlets import (
31 from IPython.utils.traitlets import (
34 Any, Instance, Float, Dict, List, Set, Integer, Unicode,
32 Any, Instance, Float, Dict, List, Set, Integer, Unicode,
35 Type, Bool,
33 Type, Bool,
36 )
34 )
37
35
38 from .serialize import serialize_object, unpack_apply_message
36 from .serialize import serialize_object, unpack_apply_message
39 from .session import Session
37 from .session import Session
40 from .zmqshell import ZMQInteractiveShell
38 from .zmqshell import ZMQInteractiveShell
41
39
42
40
43 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
44 # Main kernel class
42 # Main kernel class
45 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
46
44
47 protocol_version = list(release.kernel_protocol_version_info)
45 protocol_version = release.kernel_protocol_version
48 ipython_version = list(release.version_info)
46 ipython_version = release.version
49 language_version = list(sys.version_info[:3])
47 language_version = sys.version.split()[0]
50
48
51
49
52 class Kernel(Configurable):
50 class Kernel(Configurable):
53
51
54 #---------------------------------------------------------------------------
52 #---------------------------------------------------------------------------
55 # Kernel interface
53 # Kernel interface
56 #---------------------------------------------------------------------------
54 #---------------------------------------------------------------------------
57
55
58 # attribute to override with a GUI
56 # attribute to override with a GUI
59 eventloop = Any(None)
57 eventloop = Any(None)
60 def _eventloop_changed(self, name, old, new):
58 def _eventloop_changed(self, name, old, new):
61 """schedule call to eventloop from IOLoop"""
59 """schedule call to eventloop from IOLoop"""
62 loop = ioloop.IOLoop.instance()
60 loop = ioloop.IOLoop.instance()
63 loop.add_callback(self.enter_eventloop)
61 loop.add_callback(self.enter_eventloop)
64
62
65 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
63 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
66 shell_class = Type(ZMQInteractiveShell)
64 shell_class = Type(ZMQInteractiveShell)
67
65
68 session = Instance(Session)
66 session = Instance(Session)
69 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
67 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
70 shell_streams = List()
68 shell_streams = List()
71 control_stream = Instance(ZMQStream)
69 control_stream = Instance(ZMQStream)
72 iopub_socket = Instance(zmq.Socket)
70 iopub_socket = Instance(zmq.Socket)
73 stdin_socket = Instance(zmq.Socket)
71 stdin_socket = Instance(zmq.Socket)
74 log = Instance(logging.Logger)
72 log = Instance(logging.Logger)
75
73
76 user_module = Any()
74 user_module = Any()
77 def _user_module_changed(self, name, old, new):
75 def _user_module_changed(self, name, old, new):
78 if self.shell is not None:
76 if self.shell is not None:
79 self.shell.user_module = new
77 self.shell.user_module = new
80
78
81 user_ns = Instance(dict, args=None, allow_none=True)
79 user_ns = Instance(dict, args=None, allow_none=True)
82 def _user_ns_changed(self, name, old, new):
80 def _user_ns_changed(self, name, old, new):
83 if self.shell is not None:
81 if self.shell is not None:
84 self.shell.user_ns = new
82 self.shell.user_ns = new
85 self.shell.init_user_ns()
83 self.shell.init_user_ns()
86
84
87 # identities:
85 # identities:
88 int_id = Integer(-1)
86 int_id = Integer(-1)
89 ident = Unicode()
87 ident = Unicode()
90
88
91 def _ident_default(self):
89 def _ident_default(self):
92 return unicode_type(uuid.uuid4())
90 return unicode_type(uuid.uuid4())
93
91
94 # Private interface
92 # Private interface
95
93
96 _darwin_app_nap = Bool(True, config=True,
94 _darwin_app_nap = Bool(True, config=True,
97 help="""Whether to use appnope for compatiblity with OS X App Nap.
95 help="""Whether to use appnope for compatiblity with OS X App Nap.
98
96
99 Only affects OS X >= 10.9.
97 Only affects OS X >= 10.9.
100 """
98 """
101 )
99 )
102
100
101 # track associations with current request
102 _allow_stdin = Bool(False)
103 _parent_header = Dict()
104 _parent_ident = Any(b'')
103 # Time to sleep after flushing the stdout/err buffers in each execute
105 # Time to sleep after flushing the stdout/err buffers in each execute
104 # cycle. While this introduces a hard limit on the minimal latency of the
106 # cycle. While this introduces a hard limit on the minimal latency of the
105 # execute cycle, it helps prevent output synchronization problems for
107 # execute cycle, it helps prevent output synchronization problems for
106 # clients.
108 # clients.
107 # Units are in seconds. The minimum zmq latency on local host is probably
109 # Units are in seconds. The minimum zmq latency on local host is probably
108 # ~150 microseconds, set this to 500us for now. We may need to increase it
110 # ~150 microseconds, set this to 500us for now. We may need to increase it
109 # a little if it's not enough after more interactive testing.
111 # a little if it's not enough after more interactive testing.
110 _execute_sleep = Float(0.0005, config=True)
112 _execute_sleep = Float(0.0005, config=True)
111
113
112 # Frequency of the kernel's event loop.
114 # Frequency of the kernel's event loop.
113 # Units are in seconds, kernel subclasses for GUI toolkits may need to
115 # Units are in seconds, kernel subclasses for GUI toolkits may need to
114 # adapt to milliseconds.
116 # adapt to milliseconds.
115 _poll_interval = Float(0.05, config=True)
117 _poll_interval = Float(0.05, config=True)
116
118
117 # If the shutdown was requested over the network, we leave here the
119 # If the shutdown was requested over the network, we leave here the
118 # necessary reply message so it can be sent by our registered atexit
120 # necessary reply message so it can be sent by our registered atexit
119 # handler. This ensures that the reply is only sent to clients truly at
121 # handler. This ensures that the reply is only sent to clients truly at
120 # the end of our shutdown process (which happens after the underlying
122 # the end of our shutdown process (which happens after the underlying
121 # IPython shell's own shutdown).
123 # IPython shell's own shutdown).
122 _shutdown_message = None
124 _shutdown_message = None
123
125
124 # This is a dict of port number that the kernel is listening on. It is set
126 # This is a dict of port number that the kernel is listening on. It is set
125 # by record_ports and used by connect_request.
127 # by record_ports and used by connect_request.
126 _recorded_ports = Dict()
128 _recorded_ports = Dict()
127
129
128 # A reference to the Python builtin 'raw_input' function.
130 # A reference to the Python builtin 'raw_input' function.
129 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
131 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
130 _sys_raw_input = Any()
132 _sys_raw_input = Any()
131 _sys_eval_input = Any()
133 _sys_eval_input = Any()
132
134
133 # set of aborted msg_ids
135 # set of aborted msg_ids
134 aborted = Set()
136 aborted = Set()
135
137
136
138
137 def __init__(self, **kwargs):
139 def __init__(self, **kwargs):
138 super(Kernel, self).__init__(**kwargs)
140 super(Kernel, self).__init__(**kwargs)
139
141
140 # Initialize the InteractiveShell subclass
142 # Initialize the InteractiveShell subclass
141 self.shell = self.shell_class.instance(parent=self,
143 self.shell = self.shell_class.instance(parent=self,
142 profile_dir = self.profile_dir,
144 profile_dir = self.profile_dir,
143 user_module = self.user_module,
145 user_module = self.user_module,
144 user_ns = self.user_ns,
146 user_ns = self.user_ns,
145 kernel = self,
147 kernel = self,
146 )
148 )
147 self.shell.displayhook.session = self.session
149 self.shell.displayhook.session = self.session
148 self.shell.displayhook.pub_socket = self.iopub_socket
150 self.shell.displayhook.pub_socket = self.iopub_socket
149 self.shell.displayhook.topic = self._topic('pyout')
151 self.shell.displayhook.topic = self._topic('execute_result')
150 self.shell.display_pub.session = self.session
152 self.shell.display_pub.session = self.session
151 self.shell.display_pub.pub_socket = self.iopub_socket
153 self.shell.display_pub.pub_socket = self.iopub_socket
152 self.shell.data_pub.session = self.session
154 self.shell.data_pub.session = self.session
153 self.shell.data_pub.pub_socket = self.iopub_socket
155 self.shell.data_pub.pub_socket = self.iopub_socket
154
156
155 # TMP - hack while developing
157 # TMP - hack while developing
156 self.shell._reply_content = None
158 self.shell._reply_content = None
157
159
158 # Build dict of handlers for message types
160 # Build dict of handlers for message types
159 msg_types = [ 'execute_request', 'complete_request',
161 msg_types = [ 'execute_request', 'complete_request',
160 'object_info_request', 'history_request',
162 'inspect_request', 'history_request',
161 'kernel_info_request',
163 'kernel_info_request',
162 'connect_request', 'shutdown_request',
164 'connect_request', 'shutdown_request',
163 'apply_request',
165 'apply_request',
164 ]
166 ]
165 self.shell_handlers = {}
167 self.shell_handlers = {}
166 for msg_type in msg_types:
168 for msg_type in msg_types:
167 self.shell_handlers[msg_type] = getattr(self, msg_type)
169 self.shell_handlers[msg_type] = getattr(self, msg_type)
168
170
169 comm_msg_types = [ 'comm_open', 'comm_msg', 'comm_close' ]
171 comm_msg_types = [ 'comm_open', 'comm_msg', 'comm_close' ]
170 comm_manager = self.shell.comm_manager
172 comm_manager = self.shell.comm_manager
171 for msg_type in comm_msg_types:
173 for msg_type in comm_msg_types:
172 self.shell_handlers[msg_type] = getattr(comm_manager, msg_type)
174 self.shell_handlers[msg_type] = getattr(comm_manager, msg_type)
173
175
174 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
176 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
175 self.control_handlers = {}
177 self.control_handlers = {}
176 for msg_type in control_msg_types:
178 for msg_type in control_msg_types:
177 self.control_handlers[msg_type] = getattr(self, msg_type)
179 self.control_handlers[msg_type] = getattr(self, msg_type)
178
180
179
181
180 def dispatch_control(self, msg):
182 def dispatch_control(self, msg):
181 """dispatch control requests"""
183 """dispatch control requests"""
182 idents,msg = self.session.feed_identities(msg, copy=False)
184 idents,msg = self.session.feed_identities(msg, copy=False)
183 try:
185 try:
184 msg = self.session.unserialize(msg, content=True, copy=False)
186 msg = self.session.unserialize(msg, content=True, copy=False)
185 except:
187 except:
186 self.log.error("Invalid Control Message", exc_info=True)
188 self.log.error("Invalid Control Message", exc_info=True)
187 return
189 return
188
190
189 self.log.debug("Control received: %s", msg)
191 self.log.debug("Control received: %s", msg)
190
192
191 header = msg['header']
193 header = msg['header']
192 msg_id = header['msg_id']
194 msg_id = header['msg_id']
193 msg_type = header['msg_type']
195 msg_type = header['msg_type']
194
196
195 handler = self.control_handlers.get(msg_type, None)
197 handler = self.control_handlers.get(msg_type, None)
196 if handler is None:
198 if handler is None:
197 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
199 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
198 else:
200 else:
199 try:
201 try:
200 handler(self.control_stream, idents, msg)
202 handler(self.control_stream, idents, msg)
201 except Exception:
203 except Exception:
202 self.log.error("Exception in control handler:", exc_info=True)
204 self.log.error("Exception in control handler:", exc_info=True)
203
205
204 def dispatch_shell(self, stream, msg):
206 def dispatch_shell(self, stream, msg):
205 """dispatch shell requests"""
207 """dispatch shell requests"""
206 # flush control requests first
208 # flush control requests first
207 if self.control_stream:
209 if self.control_stream:
208 self.control_stream.flush()
210 self.control_stream.flush()
209
211
210 idents,msg = self.session.feed_identities(msg, copy=False)
212 idents,msg = self.session.feed_identities(msg, copy=False)
211 try:
213 try:
212 msg = self.session.unserialize(msg, content=True, copy=False)
214 msg = self.session.unserialize(msg, content=True, copy=False)
213 except:
215 except:
214 self.log.error("Invalid Message", exc_info=True)
216 self.log.error("Invalid Message", exc_info=True)
215 return
217 return
216
218
217 header = msg['header']
219 header = msg['header']
218 msg_id = header['msg_id']
220 msg_id = header['msg_id']
219 msg_type = msg['header']['msg_type']
221 msg_type = msg['header']['msg_type']
220
222
221 # Print some info about this message and leave a '--->' marker, so it's
223 # Print some info about this message and leave a '--->' marker, so it's
222 # easier to trace visually the message chain when debugging. Each
224 # easier to trace visually the message chain when debugging. Each
223 # handler prints its message at the end.
225 # handler prints its message at the end.
224 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
226 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
225 self.log.debug(' Content: %s\n --->\n ', msg['content'])
227 self.log.debug(' Content: %s\n --->\n ', msg['content'])
226
228
227 if msg_id in self.aborted:
229 if msg_id in self.aborted:
228 self.aborted.remove(msg_id)
230 self.aborted.remove(msg_id)
229 # is it safe to assume a msg_id will not be resubmitted?
231 # is it safe to assume a msg_id will not be resubmitted?
230 reply_type = msg_type.split('_')[0] + '_reply'
232 reply_type = msg_type.split('_')[0] + '_reply'
231 status = {'status' : 'aborted'}
233 status = {'status' : 'aborted'}
232 md = {'engine' : self.ident}
234 md = {'engine' : self.ident}
233 md.update(status)
235 md.update(status)
234 reply_msg = self.session.send(stream, reply_type, metadata=md,
236 reply_msg = self.session.send(stream, reply_type, metadata=md,
235 content=status, parent=msg, ident=idents)
237 content=status, parent=msg, ident=idents)
236 return
238 return
237
239
238 handler = self.shell_handlers.get(msg_type, None)
240 handler = self.shell_handlers.get(msg_type, None)
239 if handler is None:
241 if handler is None:
240 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
242 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
241 else:
243 else:
242 # ensure default_int_handler during handler call
244 # ensure default_int_handler during handler call
243 sig = signal(SIGINT, default_int_handler)
245 sig = signal(SIGINT, default_int_handler)
246 self.log.debug("%s: %s", msg_type, msg)
244 try:
247 try:
245 handler(stream, idents, msg)
248 handler(stream, idents, msg)
246 except Exception:
249 except Exception:
247 self.log.error("Exception in message handler:", exc_info=True)
250 self.log.error("Exception in message handler:", exc_info=True)
248 finally:
251 finally:
249 signal(SIGINT, sig)
252 signal(SIGINT, sig)
250
253
251 def enter_eventloop(self):
254 def enter_eventloop(self):
252 """enter eventloop"""
255 """enter eventloop"""
253 self.log.info("entering eventloop %s", self.eventloop)
256 self.log.info("entering eventloop %s", self.eventloop)
254 for stream in self.shell_streams:
257 for stream in self.shell_streams:
255 # flush any pending replies,
258 # flush any pending replies,
256 # which may be skipped by entering the eventloop
259 # which may be skipped by entering the eventloop
257 stream.flush(zmq.POLLOUT)
260 stream.flush(zmq.POLLOUT)
258 # restore default_int_handler
261 # restore default_int_handler
259 signal(SIGINT, default_int_handler)
262 signal(SIGINT, default_int_handler)
260 while self.eventloop is not None:
263 while self.eventloop is not None:
261 try:
264 try:
262 self.eventloop(self)
265 self.eventloop(self)
263 except KeyboardInterrupt:
266 except KeyboardInterrupt:
264 # Ctrl-C shouldn't crash the kernel
267 # Ctrl-C shouldn't crash the kernel
265 self.log.error("KeyboardInterrupt caught in kernel")
268 self.log.error("KeyboardInterrupt caught in kernel")
266 continue
269 continue
267 else:
270 else:
268 # eventloop exited cleanly, this means we should stop (right?)
271 # eventloop exited cleanly, this means we should stop (right?)
269 self.eventloop = None
272 self.eventloop = None
270 break
273 break
271 self.log.info("exiting eventloop")
274 self.log.info("exiting eventloop")
272
275
273 def start(self):
276 def start(self):
274 """register dispatchers for streams"""
277 """register dispatchers for streams"""
275 self.shell.exit_now = False
278 self.shell.exit_now = False
276 if self.control_stream:
279 if self.control_stream:
277 self.control_stream.on_recv(self.dispatch_control, copy=False)
280 self.control_stream.on_recv(self.dispatch_control, copy=False)
278
281
279 def make_dispatcher(stream):
282 def make_dispatcher(stream):
280 def dispatcher(msg):
283 def dispatcher(msg):
281 return self.dispatch_shell(stream, msg)
284 return self.dispatch_shell(stream, msg)
282 return dispatcher
285 return dispatcher
283
286
284 for s in self.shell_streams:
287 for s in self.shell_streams:
285 s.on_recv(make_dispatcher(s), copy=False)
288 s.on_recv(make_dispatcher(s), copy=False)
286
289
287 # publish idle status
290 # publish idle status
288 self._publish_status('starting')
291 self._publish_status('starting')
289
292
290 def do_one_iteration(self):
293 def do_one_iteration(self):
291 """step eventloop just once"""
294 """step eventloop just once"""
292 if self.control_stream:
295 if self.control_stream:
293 self.control_stream.flush()
296 self.control_stream.flush()
294 for stream in self.shell_streams:
297 for stream in self.shell_streams:
295 # handle at most one request per iteration
298 # handle at most one request per iteration
296 stream.flush(zmq.POLLIN, 1)
299 stream.flush(zmq.POLLIN, 1)
297 stream.flush(zmq.POLLOUT)
300 stream.flush(zmq.POLLOUT)
298
301
299
302
300 def record_ports(self, ports):
303 def record_ports(self, ports):
301 """Record the ports that this kernel is using.
304 """Record the ports that this kernel is using.
302
305
303 The creator of the Kernel instance must call this methods if they
306 The creator of the Kernel instance must call this methods if they
304 want the :meth:`connect_request` method to return the port numbers.
307 want the :meth:`connect_request` method to return the port numbers.
305 """
308 """
306 self._recorded_ports = ports
309 self._recorded_ports = ports
307
310
308 #---------------------------------------------------------------------------
311 #---------------------------------------------------------------------------
309 # Kernel request handlers
312 # Kernel request handlers
310 #---------------------------------------------------------------------------
313 #---------------------------------------------------------------------------
311
314
312 def _make_metadata(self, other=None):
315 def _make_metadata(self, other=None):
313 """init metadata dict, for execute/apply_reply"""
316 """init metadata dict, for execute/apply_reply"""
314 new_md = {
317 new_md = {
315 'dependencies_met' : True,
318 'dependencies_met' : True,
316 'engine' : self.ident,
319 'engine' : self.ident,
317 'started': datetime.now(),
320 'started': datetime.now(),
318 }
321 }
319 if other:
322 if other:
320 new_md.update(other)
323 new_md.update(other)
321 return new_md
324 return new_md
322
325
323 def _publish_pyin(self, code, parent, execution_count):
326 def _publish_execute_input(self, code, parent, execution_count):
324 """Publish the code request on the pyin stream."""
327 """Publish the code request on the iopub stream."""
325
328
326 self.session.send(self.iopub_socket, u'pyin',
329 self.session.send(self.iopub_socket, u'execute_input',
327 {u'code':code, u'execution_count': execution_count},
330 {u'code':code, u'execution_count': execution_count},
328 parent=parent, ident=self._topic('pyin')
331 parent=parent, ident=self._topic('execute_input')
329 )
332 )
330
333
331 def _publish_status(self, status, parent=None):
334 def _publish_status(self, status, parent=None):
332 """send status (busy/idle) on IOPub"""
335 """send status (busy/idle) on IOPub"""
333 self.session.send(self.iopub_socket,
336 self.session.send(self.iopub_socket,
334 u'status',
337 u'status',
335 {u'execution_state': status},
338 {u'execution_state': status},
336 parent=parent,
339 parent=parent,
337 ident=self._topic('status'),
340 ident=self._topic('status'),
338 )
341 )
342
343 def _forward_input(self, allow_stdin=False):
344 """Forward raw_input and getpass to the current frontend.
339
345
340
346 via input_request
347 """
348 self._allow_stdin = allow_stdin
349
350 if py3compat.PY3:
351 self._sys_raw_input = builtin_mod.input
352 builtin_mod.input = self.raw_input
353 else:
354 self._sys_raw_input = builtin_mod.raw_input
355 self._sys_eval_input = builtin_mod.input
356 builtin_mod.raw_input = self.raw_input
357 builtin_mod.input = lambda prompt='': eval(self.raw_input(prompt))
358 self._save_getpass = getpass.getpass
359 getpass.getpass = self.getpass
360
361 def _restore_input(self):
362 """Restore raw_input, getpass"""
363 if py3compat.PY3:
364 builtin_mod.input = self._sys_raw_input
365 else:
366 builtin_mod.raw_input = self._sys_raw_input
367 builtin_mod.input = self._sys_eval_input
368
369 getpass.getpass = self._save_getpass
370
371 def set_parent(self, ident, parent):
372 """Set the current parent_header
373
374 Side effects (IOPub messages) and replies are associated with
375 the request that caused them via the parent_header.
376
377 The parent identity is used to route input_request messages
378 on the stdin channel.
379 """
380 self._parent_ident = ident
381 self._parent_header = parent
382 self.shell.set_parent(parent)
383
341 def execute_request(self, stream, ident, parent):
384 def execute_request(self, stream, ident, parent):
342 """handle an execute_request"""
385 """handle an execute_request"""
343
386
344 self._publish_status(u'busy', parent)
387 self._publish_status(u'busy', parent)
345
388
346 try:
389 try:
347 content = parent[u'content']
390 content = parent[u'content']
348 code = py3compat.cast_unicode_py2(content[u'code'])
391 code = py3compat.cast_unicode_py2(content[u'code'])
349 silent = content[u'silent']
392 silent = content[u'silent']
350 store_history = content.get(u'store_history', not silent)
393 store_history = content.get(u'store_history', not silent)
351 except:
394 except:
352 self.log.error("Got bad msg: ")
395 self.log.error("Got bad msg: ")
353 self.log.error("%s", parent)
396 self.log.error("%s", parent)
354 return
397 return
355
398
356 md = self._make_metadata(parent['metadata'])
399 md = self._make_metadata(parent['metadata'])
357
400
358 shell = self.shell # we'll need this a lot here
401 shell = self.shell # we'll need this a lot here
359
402
360 # Replace raw_input. Note that is not sufficient to replace
403 self._forward_input(content.get('allow_stdin', False))
361 # raw_input in the user namespace.
362 if content.get('allow_stdin', False):
363 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
364 input = lambda prompt='': eval(raw_input(prompt))
365 else:
366 raw_input = input = lambda prompt='' : self._no_raw_input()
367
368 if py3compat.PY3:
369 self._sys_raw_input = builtin_mod.input
370 builtin_mod.input = raw_input
371 else:
372 self._sys_raw_input = builtin_mod.raw_input
373 self._sys_eval_input = builtin_mod.input
374 builtin_mod.raw_input = raw_input
375 builtin_mod.input = input
376
377 # Set the parent message of the display hook and out streams.
404 # Set the parent message of the display hook and out streams.
378 shell.set_parent(parent)
405 self.set_parent(ident, parent)
379
406
380 # Re-broadcast our input for the benefit of listening clients, and
407 # Re-broadcast our input for the benefit of listening clients, and
381 # start computing output
408 # start computing output
382 if not silent:
409 if not silent:
383 self._publish_pyin(code, parent, shell.execution_count)
410 self._publish_execute_input(code, parent, shell.execution_count)
384
411
385 reply_content = {}
412 reply_content = {}
386 # FIXME: the shell calls the exception handler itself.
413 # FIXME: the shell calls the exception handler itself.
387 shell._reply_content = None
414 shell._reply_content = None
388 try:
415 try:
389 shell.run_cell(code, store_history=store_history, silent=silent)
416 shell.run_cell(code, store_history=store_history, silent=silent)
390 except:
417 except:
391 status = u'error'
418 status = u'error'
392 # FIXME: this code right now isn't being used yet by default,
419 # FIXME: this code right now isn't being used yet by default,
393 # because the run_cell() call above directly fires off exception
420 # because the run_cell() call above directly fires off exception
394 # reporting. This code, therefore, is only active in the scenario
421 # reporting. This code, therefore, is only active in the scenario
395 # where runlines itself has an unhandled exception. We need to
422 # where runlines itself has an unhandled exception. We need to
396 # uniformize this, for all exception construction to come from a
423 # uniformize this, for all exception construction to come from a
397 # single location in the codbase.
424 # single location in the codbase.
398 etype, evalue, tb = sys.exc_info()
425 etype, evalue, tb = sys.exc_info()
399 tb_list = traceback.format_exception(etype, evalue, tb)
426 tb_list = traceback.format_exception(etype, evalue, tb)
400 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
427 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
401 else:
428 else:
402 status = u'ok'
429 status = u'ok'
403 finally:
430 finally:
404 # Restore raw_input.
431 self._restore_input()
405 if py3compat.PY3:
406 builtin_mod.input = self._sys_raw_input
407 else:
408 builtin_mod.raw_input = self._sys_raw_input
409 builtin_mod.input = self._sys_eval_input
410
432
411 reply_content[u'status'] = status
433 reply_content[u'status'] = status
412
434
413 # Return the execution counter so clients can display prompts
435 # Return the execution counter so clients can display prompts
414 reply_content['execution_count'] = shell.execution_count - 1
436 reply_content['execution_count'] = shell.execution_count - 1
415
437
416 # FIXME - fish exception info out of shell, possibly left there by
438 # FIXME - fish exception info out of shell, possibly left there by
417 # runlines. We'll need to clean up this logic later.
439 # runlines. We'll need to clean up this logic later.
418 if shell._reply_content is not None:
440 if shell._reply_content is not None:
419 reply_content.update(shell._reply_content)
441 reply_content.update(shell._reply_content)
420 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
442 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
421 reply_content['engine_info'] = e_info
443 reply_content['engine_info'] = e_info
422 # reset after use
444 # reset after use
423 shell._reply_content = None
445 shell._reply_content = None
424
446
425 if 'traceback' in reply_content:
447 if 'traceback' in reply_content:
426 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
448 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
427
449
428
450
429 # At this point, we can tell whether the main code execution succeeded
451 # At this point, we can tell whether the main code execution succeeded
430 # or not. If it did, we proceed to evaluate user_variables/expressions
452 # or not. If it did, we proceed to evaluate user_expressions
431 if reply_content['status'] == 'ok':
453 if reply_content['status'] == 'ok':
432 reply_content[u'user_variables'] = \
433 shell.user_variables(content.get(u'user_variables', []))
434 reply_content[u'user_expressions'] = \
454 reply_content[u'user_expressions'] = \
435 shell.user_expressions(content.get(u'user_expressions', {}))
455 shell.user_expressions(content.get(u'user_expressions', {}))
436 else:
456 else:
437 # If there was an error, don't even try to compute variables or
457 # If there was an error, don't even try to compute expressions
438 # expressions
439 reply_content[u'user_variables'] = {}
440 reply_content[u'user_expressions'] = {}
458 reply_content[u'user_expressions'] = {}
441
459
442 # Payloads should be retrieved regardless of outcome, so we can both
460 # Payloads should be retrieved regardless of outcome, so we can both
443 # recover partial output (that could have been generated early in a
461 # recover partial output (that could have been generated early in a
444 # block, before an error) and clear the payload system always.
462 # block, before an error) and clear the payload system always.
445 reply_content[u'payload'] = shell.payload_manager.read_payload()
463 reply_content[u'payload'] = shell.payload_manager.read_payload()
446 # Be agressive about clearing the payload because we don't want
464 # Be agressive about clearing the payload because we don't want
447 # it to sit in memory until the next execute_request comes in.
465 # it to sit in memory until the next execute_request comes in.
448 shell.payload_manager.clear_payload()
466 shell.payload_manager.clear_payload()
449
467
450 # Flush output before sending the reply.
468 # Flush output before sending the reply.
451 sys.stdout.flush()
469 sys.stdout.flush()
452 sys.stderr.flush()
470 sys.stderr.flush()
453 # FIXME: on rare occasions, the flush doesn't seem to make it to the
471 # FIXME: on rare occasions, the flush doesn't seem to make it to the
454 # clients... This seems to mitigate the problem, but we definitely need
472 # clients... This seems to mitigate the problem, but we definitely need
455 # to better understand what's going on.
473 # to better understand what's going on.
456 if self._execute_sleep:
474 if self._execute_sleep:
457 time.sleep(self._execute_sleep)
475 time.sleep(self._execute_sleep)
458
476
459 # Send the reply.
477 # Send the reply.
460 reply_content = json_clean(reply_content)
478 reply_content = json_clean(reply_content)
461
479
462 md['status'] = reply_content['status']
480 md['status'] = reply_content['status']
463 if reply_content['status'] == 'error' and \
481 if reply_content['status'] == 'error' and \
464 reply_content['ename'] == 'UnmetDependency':
482 reply_content['ename'] == 'UnmetDependency':
465 md['dependencies_met'] = False
483 md['dependencies_met'] = False
466
484
467 reply_msg = self.session.send(stream, u'execute_reply',
485 reply_msg = self.session.send(stream, u'execute_reply',
468 reply_content, parent, metadata=md,
486 reply_content, parent, metadata=md,
469 ident=ident)
487 ident=ident)
470
488
471 self.log.debug("%s", reply_msg)
489 self.log.debug("%s", reply_msg)
472
490
473 if not silent and reply_msg['content']['status'] == u'error':
491 if not silent and reply_msg['content']['status'] == u'error':
474 self._abort_queues()
492 self._abort_queues()
475
493
476 self._publish_status(u'idle', parent)
494 self._publish_status(u'idle', parent)
477
495
478 def complete_request(self, stream, ident, parent):
496 def complete_request(self, stream, ident, parent):
479 txt, matches = self._complete(parent)
497 content = parent['content']
498 code = content['code']
499 cursor_pos = content['cursor_pos']
500
501 txt, matches = self.shell.complete('', code, cursor_pos)
480 matches = {'matches' : matches,
502 matches = {'matches' : matches,
481 'matched_text' : txt,
503 'cursor_end' : cursor_pos,
504 'cursor_start' : cursor_pos - len(txt),
505 'metadata' : {},
482 'status' : 'ok'}
506 'status' : 'ok'}
483 matches = json_clean(matches)
507 matches = json_clean(matches)
484 completion_msg = self.session.send(stream, 'complete_reply',
508 completion_msg = self.session.send(stream, 'complete_reply',
485 matches, parent, ident)
509 matches, parent, ident)
486 self.log.debug("%s", completion_msg)
510 self.log.debug("%s", completion_msg)
487
511
488 def object_info_request(self, stream, ident, parent):
512 def inspect_request(self, stream, ident, parent):
489 content = parent['content']
513 content = parent['content']
490 object_info = self.shell.object_inspect(content['oname'],
514
491 detail_level = content.get('detail_level', 0)
515 name = token_at_cursor(content['code'], content['cursor_pos'])
492 )
516 info = self.shell.object_inspect(name)
517
518 reply_content = {'status' : 'ok'}
519 reply_content['data'] = data = {}
520 reply_content['metadata'] = {}
521 reply_content['found'] = info['found']
522 if info['found']:
523 info_text = self.shell.object_inspect_text(
524 name,
525 detail_level=content.get('detail_level', 0),
526 )
527 reply_content['data']['text/plain'] = info_text
493 # Before we send this object over, we scrub it for JSON usage
528 # Before we send this object over, we scrub it for JSON usage
494 oinfo = json_clean(object_info)
529 reply_content = json_clean(reply_content)
495 msg = self.session.send(stream, 'object_info_reply',
530 msg = self.session.send(stream, 'inspect_reply',
496 oinfo, parent, ident)
531 reply_content, parent, ident)
497 self.log.debug("%s", msg)
532 self.log.debug("%s", msg)
498
533
499 def history_request(self, stream, ident, parent):
534 def history_request(self, stream, ident, parent):
500 # We need to pull these out, as passing **kwargs doesn't work with
535 # We need to pull these out, as passing **kwargs doesn't work with
501 # unicode keys before Python 2.6.5.
536 # unicode keys before Python 2.6.5.
502 hist_access_type = parent['content']['hist_access_type']
537 hist_access_type = parent['content']['hist_access_type']
503 raw = parent['content']['raw']
538 raw = parent['content']['raw']
504 output = parent['content']['output']
539 output = parent['content']['output']
505 if hist_access_type == 'tail':
540 if hist_access_type == 'tail':
506 n = parent['content']['n']
541 n = parent['content']['n']
507 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
542 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
508 include_latest=True)
543 include_latest=True)
509
544
510 elif hist_access_type == 'range':
545 elif hist_access_type == 'range':
511 session = parent['content']['session']
546 session = parent['content']['session']
512 start = parent['content']['start']
547 start = parent['content']['start']
513 stop = parent['content']['stop']
548 stop = parent['content']['stop']
514 hist = self.shell.history_manager.get_range(session, start, stop,
549 hist = self.shell.history_manager.get_range(session, start, stop,
515 raw=raw, output=output)
550 raw=raw, output=output)
516
551
517 elif hist_access_type == 'search':
552 elif hist_access_type == 'search':
518 n = parent['content'].get('n')
553 n = parent['content'].get('n')
519 unique = parent['content'].get('unique', False)
554 unique = parent['content'].get('unique', False)
520 pattern = parent['content']['pattern']
555 pattern = parent['content']['pattern']
521 hist = self.shell.history_manager.search(
556 hist = self.shell.history_manager.search(
522 pattern, raw=raw, output=output, n=n, unique=unique)
557 pattern, raw=raw, output=output, n=n, unique=unique)
523
558
524 else:
559 else:
525 hist = []
560 hist = []
526 hist = list(hist)
561 hist = list(hist)
527 content = {'history' : hist}
562 content = {'history' : hist}
528 content = json_clean(content)
563 content = json_clean(content)
529 msg = self.session.send(stream, 'history_reply',
564 msg = self.session.send(stream, 'history_reply',
530 content, parent, ident)
565 content, parent, ident)
531 self.log.debug("Sending history reply with %i entries", len(hist))
566 self.log.debug("Sending history reply with %i entries", len(hist))
532
567
533 def connect_request(self, stream, ident, parent):
568 def connect_request(self, stream, ident, parent):
534 if self._recorded_ports is not None:
569 if self._recorded_ports is not None:
535 content = self._recorded_ports.copy()
570 content = self._recorded_ports.copy()
536 else:
571 else:
537 content = {}
572 content = {}
538 msg = self.session.send(stream, 'connect_reply',
573 msg = self.session.send(stream, 'connect_reply',
539 content, parent, ident)
574 content, parent, ident)
540 self.log.debug("%s", msg)
575 self.log.debug("%s", msg)
541
576
542 def kernel_info_request(self, stream, ident, parent):
577 def kernel_info_request(self, stream, ident, parent):
543 vinfo = {
578 vinfo = {
544 'protocol_version': protocol_version,
579 'protocol_version': protocol_version,
545 'ipython_version': ipython_version,
580 'implementation': 'ipython',
581 'implementation_version': ipython_version,
546 'language_version': language_version,
582 'language_version': language_version,
547 'language': 'python',
583 'language': 'python',
584 'banner': self.shell.banner,
548 }
585 }
549 msg = self.session.send(stream, 'kernel_info_reply',
586 msg = self.session.send(stream, 'kernel_info_reply',
550 vinfo, parent, ident)
587 vinfo, parent, ident)
551 self.log.debug("%s", msg)
588 self.log.debug("%s", msg)
552
589
553 def shutdown_request(self, stream, ident, parent):
590 def shutdown_request(self, stream, ident, parent):
554 self.shell.exit_now = True
591 self.shell.exit_now = True
555 content = dict(status='ok')
592 content = dict(status='ok')
556 content.update(parent['content'])
593 content.update(parent['content'])
557 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
594 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
558 # same content, but different msg_id for broadcasting on IOPub
595 # same content, but different msg_id for broadcasting on IOPub
559 self._shutdown_message = self.session.msg(u'shutdown_reply',
596 self._shutdown_message = self.session.msg(u'shutdown_reply',
560 content, parent
597 content, parent
561 )
598 )
562
599
563 self._at_shutdown()
600 self._at_shutdown()
564 # call sys.exit after a short delay
601 # call sys.exit after a short delay
565 loop = ioloop.IOLoop.instance()
602 loop = ioloop.IOLoop.instance()
566 loop.add_timeout(time.time()+0.1, loop.stop)
603 loop.add_timeout(time.time()+0.1, loop.stop)
567
604
568 #---------------------------------------------------------------------------
605 #---------------------------------------------------------------------------
569 # Engine methods
606 # Engine methods
570 #---------------------------------------------------------------------------
607 #---------------------------------------------------------------------------
571
608
572 def apply_request(self, stream, ident, parent):
609 def apply_request(self, stream, ident, parent):
573 try:
610 try:
574 content = parent[u'content']
611 content = parent[u'content']
575 bufs = parent[u'buffers']
612 bufs = parent[u'buffers']
576 msg_id = parent['header']['msg_id']
613 msg_id = parent['header']['msg_id']
577 except:
614 except:
578 self.log.error("Got bad msg: %s", parent, exc_info=True)
615 self.log.error("Got bad msg: %s", parent, exc_info=True)
579 return
616 return
580
617
581 self._publish_status(u'busy', parent)
618 self._publish_status(u'busy', parent)
582
619
583 # Set the parent message of the display hook and out streams.
620 # Set the parent message of the display hook and out streams.
584 shell = self.shell
621 shell = self.shell
585 shell.set_parent(parent)
622 shell.set_parent(parent)
586
623
587 # pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
588 # self.iopub_socket.send(pyin_msg)
589 # self.session.send(self.iopub_socket, u'pyin', {u'code':code},parent=parent)
590 md = self._make_metadata(parent['metadata'])
624 md = self._make_metadata(parent['metadata'])
591 try:
625 try:
592 working = shell.user_ns
626 working = shell.user_ns
593
627
594 prefix = "_"+str(msg_id).replace("-","")+"_"
628 prefix = "_"+str(msg_id).replace("-","")+"_"
595
629
596 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
630 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
597
631
598 fname = getattr(f, '__name__', 'f')
632 fname = getattr(f, '__name__', 'f')
599
633
600 fname = prefix+"f"
634 fname = prefix+"f"
601 argname = prefix+"args"
635 argname = prefix+"args"
602 kwargname = prefix+"kwargs"
636 kwargname = prefix+"kwargs"
603 resultname = prefix+"result"
637 resultname = prefix+"result"
604
638
605 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
639 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
606 # print ns
640 # print ns
607 working.update(ns)
641 working.update(ns)
608 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
642 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
609 try:
643 try:
610 exec(code, shell.user_global_ns, shell.user_ns)
644 exec(code, shell.user_global_ns, shell.user_ns)
611 result = working.get(resultname)
645 result = working.get(resultname)
612 finally:
646 finally:
613 for key in ns:
647 for key in ns:
614 working.pop(key)
648 working.pop(key)
615
649
616 result_buf = serialize_object(result,
650 result_buf = serialize_object(result,
617 buffer_threshold=self.session.buffer_threshold,
651 buffer_threshold=self.session.buffer_threshold,
618 item_threshold=self.session.item_threshold,
652 item_threshold=self.session.item_threshold,
619 )
653 )
620
654
621 except:
655 except:
622 # invoke IPython traceback formatting
656 # invoke IPython traceback formatting
623 shell.showtraceback()
657 shell.showtraceback()
624 # FIXME - fish exception info out of shell, possibly left there by
658 # FIXME - fish exception info out of shell, possibly left there by
625 # run_code. We'll need to clean up this logic later.
659 # run_code. We'll need to clean up this logic later.
626 reply_content = {}
660 reply_content = {}
627 if shell._reply_content is not None:
661 if shell._reply_content is not None:
628 reply_content.update(shell._reply_content)
662 reply_content.update(shell._reply_content)
629 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
663 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
630 reply_content['engine_info'] = e_info
664 reply_content['engine_info'] = e_info
631 # reset after use
665 # reset after use
632 shell._reply_content = None
666 shell._reply_content = None
633
667
634 self.session.send(self.iopub_socket, u'pyerr', reply_content, parent=parent,
668 self.session.send(self.iopub_socket, u'error', reply_content, parent=parent,
635 ident=self._topic('pyerr'))
669 ident=self._topic('error'))
636 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
670 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
637 result_buf = []
671 result_buf = []
638
672
639 if reply_content['ename'] == 'UnmetDependency':
673 if reply_content['ename'] == 'UnmetDependency':
640 md['dependencies_met'] = False
674 md['dependencies_met'] = False
641 else:
675 else:
642 reply_content = {'status' : 'ok'}
676 reply_content = {'status' : 'ok'}
643
677
644 # put 'ok'/'error' status in header, for scheduler introspection:
678 # put 'ok'/'error' status in header, for scheduler introspection:
645 md['status'] = reply_content['status']
679 md['status'] = reply_content['status']
646
680
647 # flush i/o
681 # flush i/o
648 sys.stdout.flush()
682 sys.stdout.flush()
649 sys.stderr.flush()
683 sys.stderr.flush()
650
684
651 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
685 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
652 parent=parent, ident=ident,buffers=result_buf, metadata=md)
686 parent=parent, ident=ident,buffers=result_buf, metadata=md)
653
687
654 self._publish_status(u'idle', parent)
688 self._publish_status(u'idle', parent)
655
689
656 #---------------------------------------------------------------------------
690 #---------------------------------------------------------------------------
657 # Control messages
691 # Control messages
658 #---------------------------------------------------------------------------
692 #---------------------------------------------------------------------------
659
693
660 def abort_request(self, stream, ident, parent):
694 def abort_request(self, stream, ident, parent):
661 """abort a specifig msg by id"""
695 """abort a specifig msg by id"""
662 msg_ids = parent['content'].get('msg_ids', None)
696 msg_ids = parent['content'].get('msg_ids', None)
663 if isinstance(msg_ids, string_types):
697 if isinstance(msg_ids, string_types):
664 msg_ids = [msg_ids]
698 msg_ids = [msg_ids]
665 if not msg_ids:
699 if not msg_ids:
666 self.abort_queues()
700 self.abort_queues()
667 for mid in msg_ids:
701 for mid in msg_ids:
668 self.aborted.add(str(mid))
702 self.aborted.add(str(mid))
669
703
670 content = dict(status='ok')
704 content = dict(status='ok')
671 reply_msg = self.session.send(stream, 'abort_reply', content=content,
705 reply_msg = self.session.send(stream, 'abort_reply', content=content,
672 parent=parent, ident=ident)
706 parent=parent, ident=ident)
673 self.log.debug("%s", reply_msg)
707 self.log.debug("%s", reply_msg)
674
708
675 def clear_request(self, stream, idents, parent):
709 def clear_request(self, stream, idents, parent):
676 """Clear our namespace."""
710 """Clear our namespace."""
677 self.shell.reset(False)
711 self.shell.reset(False)
678 msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
712 msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
679 content = dict(status='ok'))
713 content = dict(status='ok'))
680
714
681
715
682 #---------------------------------------------------------------------------
716 #---------------------------------------------------------------------------
683 # Protected interface
717 # Protected interface
684 #---------------------------------------------------------------------------
718 #---------------------------------------------------------------------------
685
719
686 def _wrap_exception(self, method=None):
720 def _wrap_exception(self, method=None):
687 # import here, because _wrap_exception is only used in parallel,
721 # import here, because _wrap_exception is only used in parallel,
688 # and parallel has higher min pyzmq version
722 # and parallel has higher min pyzmq version
689 from IPython.parallel.error import wrap_exception
723 from IPython.parallel.error import wrap_exception
690 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method=method)
724 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method=method)
691 content = wrap_exception(e_info)
725 content = wrap_exception(e_info)
692 return content
726 return content
693
727
694 def _topic(self, topic):
728 def _topic(self, topic):
695 """prefixed topic for IOPub messages"""
729 """prefixed topic for IOPub messages"""
696 if self.int_id >= 0:
730 if self.int_id >= 0:
697 base = "engine.%i" % self.int_id
731 base = "engine.%i" % self.int_id
698 else:
732 else:
699 base = "kernel.%s" % self.ident
733 base = "kernel.%s" % self.ident
700
734
701 return py3compat.cast_bytes("%s.%s" % (base, topic))
735 return py3compat.cast_bytes("%s.%s" % (base, topic))
702
736
703 def _abort_queues(self):
737 def _abort_queues(self):
704 for stream in self.shell_streams:
738 for stream in self.shell_streams:
705 if stream:
739 if stream:
706 self._abort_queue(stream)
740 self._abort_queue(stream)
707
741
708 def _abort_queue(self, stream):
742 def _abort_queue(self, stream):
709 poller = zmq.Poller()
743 poller = zmq.Poller()
710 poller.register(stream.socket, zmq.POLLIN)
744 poller.register(stream.socket, zmq.POLLIN)
711 while True:
745 while True:
712 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
746 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
713 if msg is None:
747 if msg is None:
714 return
748 return
715
749
716 self.log.info("Aborting:")
750 self.log.info("Aborting:")
717 self.log.info("%s", msg)
751 self.log.info("%s", msg)
718 msg_type = msg['header']['msg_type']
752 msg_type = msg['header']['msg_type']
719 reply_type = msg_type.split('_')[0] + '_reply'
753 reply_type = msg_type.split('_')[0] + '_reply'
720
754
721 status = {'status' : 'aborted'}
755 status = {'status' : 'aborted'}
722 md = {'engine' : self.ident}
756 md = {'engine' : self.ident}
723 md.update(status)
757 md.update(status)
724 reply_msg = self.session.send(stream, reply_type, metadata=md,
758 reply_msg = self.session.send(stream, reply_type, metadata=md,
725 content=status, parent=msg, ident=idents)
759 content=status, parent=msg, ident=idents)
726 self.log.debug("%s", reply_msg)
760 self.log.debug("%s", reply_msg)
727 # We need to wait a bit for requests to come in. This can probably
761 # We need to wait a bit for requests to come in. This can probably
728 # be set shorter for true asynchronous clients.
762 # be set shorter for true asynchronous clients.
729 poller.poll(50)
763 poller.poll(50)
730
764
731
765
732 def _no_raw_input(self):
766 def _no_raw_input(self):
733 """Raise StdinNotImplentedError if active frontend doesn't support
767 """Raise StdinNotImplentedError if active frontend doesn't support
734 stdin."""
768 stdin."""
735 raise StdinNotImplementedError("raw_input was called, but this "
769 raise StdinNotImplementedError("raw_input was called, but this "
736 "frontend does not support stdin.")
770 "frontend does not support stdin.")
771
772 def getpass(self, prompt=''):
773 """Forward getpass to frontends
737
774
738 def _raw_input(self, prompt, ident, parent):
775 Raises
776 ------
777 StdinNotImplentedError if active frontend doesn't support stdin.
778 """
779 if not self._allow_stdin:
780 raise StdinNotImplementedError(
781 "getpass was called, but this frontend does not support input requests."
782 )
783 return self._input_request(prompt,
784 self._parent_ident,
785 self._parent_header,
786 password=True,
787 )
788
789 def raw_input(self, prompt=''):
790 """Forward raw_input to frontends
791
792 Raises
793 ------
794 StdinNotImplentedError if active frontend doesn't support stdin.
795 """
796 if not self._allow_stdin:
797 raise StdinNotImplementedError(
798 "raw_input was called, but this frontend does not support input requests."
799 )
800 return self._input_request(prompt,
801 self._parent_ident,
802 self._parent_header,
803 password=False,
804 )
805
806 def _input_request(self, prompt, ident, parent, password=False):
739 # Flush output before making the request.
807 # Flush output before making the request.
740 sys.stderr.flush()
808 sys.stderr.flush()
741 sys.stdout.flush()
809 sys.stdout.flush()
742 # flush the stdin socket, to purge stale replies
810 # flush the stdin socket, to purge stale replies
743 while True:
811 while True:
744 try:
812 try:
745 self.stdin_socket.recv_multipart(zmq.NOBLOCK)
813 self.stdin_socket.recv_multipart(zmq.NOBLOCK)
746 except zmq.ZMQError as e:
814 except zmq.ZMQError as e:
747 if e.errno == zmq.EAGAIN:
815 if e.errno == zmq.EAGAIN:
748 break
816 break
749 else:
817 else:
750 raise
818 raise
751
819
752 # Send the input request.
820 # Send the input request.
753 content = json_clean(dict(prompt=prompt))
821 content = json_clean(dict(prompt=prompt, password=password))
754 self.session.send(self.stdin_socket, u'input_request', content, parent,
822 self.session.send(self.stdin_socket, u'input_request', content, parent,
755 ident=ident)
823 ident=ident)
756
824
757 # Await a response.
825 # Await a response.
758 while True:
826 while True:
759 try:
827 try:
760 ident, reply = self.session.recv(self.stdin_socket, 0)
828 ident, reply = self.session.recv(self.stdin_socket, 0)
761 except Exception:
829 except Exception:
762 self.log.warn("Invalid Message:", exc_info=True)
830 self.log.warn("Invalid Message:", exc_info=True)
763 except KeyboardInterrupt:
831 except KeyboardInterrupt:
764 # re-raise KeyboardInterrupt, to truncate traceback
832 # re-raise KeyboardInterrupt, to truncate traceback
765 raise KeyboardInterrupt
833 raise KeyboardInterrupt
766 else:
834 else:
767 break
835 break
768 try:
836 try:
769 value = py3compat.unicode_to_str(reply['content']['value'])
837 value = py3compat.unicode_to_str(reply['content']['value'])
770 except:
838 except:
771 self.log.error("Got bad raw_input reply: ")
839 self.log.error("Bad input_reply: %s", parent)
772 self.log.error("%s", parent)
773 value = ''
840 value = ''
774 if value == '\x04':
841 if value == '\x04':
775 # EOF
842 # EOF
776 raise EOFError
843 raise EOFError
777 return value
844 return value
778
845
779 def _complete(self, msg):
780 c = msg['content']
781 try:
782 cpos = int(c['cursor_pos'])
783 except:
784 # If we don't get something that we can convert to an integer, at
785 # least attempt the completion guessing the cursor is at the end of
786 # the text, if there's any, and otherwise of the line
787 cpos = len(c['text'])
788 if cpos==0:
789 cpos = len(c['line'])
790 return self.shell.complete(c['text'], c['line'], cpos)
791
792 def _at_shutdown(self):
846 def _at_shutdown(self):
793 """Actions taken at shutdown by the kernel, called by python's atexit.
847 """Actions taken at shutdown by the kernel, called by python's atexit.
794 """
848 """
795 # io.rprint("Kernel at_shutdown") # dbg
849 # io.rprint("Kernel at_shutdown") # dbg
796 if self._shutdown_message is not None:
850 if self._shutdown_message is not None:
797 self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown'))
851 self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown'))
798 self.log.debug("%s", self._shutdown_message)
852 self.log.debug("%s", self._shutdown_message)
799 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
853 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
800
854
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now