##// END OF EJS Templates
Merge pull request #3319 from minrk/user-expressions...
Min RK -
r10828:2c614f32 merge
parent child Browse files
Show More
@@ -2363,10 +2363,38 b' class InteractiveShell(SingletonConfigurable):'
2363 # Things related to extracting values/expressions from kernel and user_ns
2363 # Things related to extracting values/expressions from kernel and user_ns
2364 #-------------------------------------------------------------------------
2364 #-------------------------------------------------------------------------
2365
2365
2366 def _simple_error(self):
2366 def _user_obj_error(self):
2367 etype, value = sys.exc_info()[:2]
2367 """return simple exception dict
2368 return u'[ERROR] {e.__name__}: {v}'.format(e=etype, v=value)
2368
2369 for use in user_variables / expressions
2370 """
2371
2372 etype, evalue, tb = self._get_exc_info()
2373 stb = self.InteractiveTB.get_exception_only(etype, evalue)
2374
2375 exc_info = {
2376 u'status' : 'error',
2377 u'traceback' : stb,
2378 u'ename' : unicode(etype.__name__),
2379 u'evalue' : py3compat.safe_unicode(evalue),
2380 }
2369
2381
2382 return exc_info
2383
2384 def _format_user_obj(self, obj):
2385 """format a user object to display dict
2386
2387 for use in user_expressions / variables
2388 """
2389
2390 data, md = self.display_formatter.format(obj)
2391 value = {
2392 'status' : 'ok',
2393 'data' : data,
2394 'metadata' : md,
2395 }
2396 return value
2397
2370 def user_variables(self, names):
2398 def user_variables(self, names):
2371 """Get a list of variable names from the user's namespace.
2399 """Get a list of variable names from the user's namespace.
2372
2400
@@ -2377,15 +2405,17 b' class InteractiveShell(SingletonConfigurable):'
2377
2405
2378 Returns
2406 Returns
2379 -------
2407 -------
2380 A dict, keyed by the input names and with the repr() of each value.
2408 A dict, keyed by the input names and with the rich mime-type repr(s) of each value.
2409 Each element will be a sub-dict of the same form as a display_data message.
2381 """
2410 """
2382 out = {}
2411 out = {}
2383 user_ns = self.user_ns
2412 user_ns = self.user_ns
2413
2384 for varname in names:
2414 for varname in names:
2385 try:
2415 try:
2386 value = repr(user_ns[varname])
2416 value = self._format_user_obj(user_ns[varname])
2387 except:
2417 except:
2388 value = self._simple_error()
2418 value = self._user_obj_error()
2389 out[varname] = value
2419 out[varname] = value
2390 return out
2420 return out
2391
2421
@@ -2401,17 +2431,18 b' class InteractiveShell(SingletonConfigurable):'
2401
2431
2402 Returns
2432 Returns
2403 -------
2433 -------
2404 A dict, keyed like the input expressions dict, with the repr() of each
2434 A dict, keyed like the input expressions dict, with the rich mime-typed
2405 value.
2435 display_data of each value.
2406 """
2436 """
2407 out = {}
2437 out = {}
2408 user_ns = self.user_ns
2438 user_ns = self.user_ns
2409 global_ns = self.user_global_ns
2439 global_ns = self.user_global_ns
2440
2410 for key, expr in expressions.iteritems():
2441 for key, expr in expressions.iteritems():
2411 try:
2442 try:
2412 value = repr(eval(expr, global_ns, user_ns))
2443 value = self._format_user_obj(eval(expr, global_ns, user_ns))
2413 except:
2444 except:
2414 value = self._simple_error()
2445 value = self._user_obj_error()
2415 out[key] = value
2446 out[key] = value
2416 return out
2447 return out
2417
2448
@@ -15,6 +15,7 b''
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Stdlib
17 # Stdlib
18 import json
18 import os
19 import os
19 import re
20 import re
20 import sys
21 import sys
@@ -326,16 +327,6 b' class MagicsManager(Configurable):'
326 """Return descriptive string with automagic status."""
327 """Return descriptive string with automagic status."""
327 return self._auto_status[self.auto_magic]
328 return self._auto_status[self.auto_magic]
328
329
329 def lsmagic_info(self):
330 magic_list = []
331 for m_type in self.magics :
332 for m_name,mgc in self.magics[m_type].items():
333 try :
334 magic_list.append({'name':m_name,'type':m_type,'class':mgc.im_class.__name__})
335 except AttributeError :
336 magic_list.append({'name':m_name,'type':m_type,'class':'Other'})
337 return magic_list
338
339 def lsmagic(self):
330 def lsmagic(self):
340 """Return a dict of currently available magic functions.
331 """Return a dict of currently available magic functions.
341
332
@@ -15,6 +15,7 b' from __future__ import print_function'
15
15
16 # Stdlib
16 # Stdlib
17 import io
17 import io
18 import json
18 import sys
19 import sys
19 from pprint import pformat
20 from pprint import pformat
20
21
@@ -33,6 +34,55 b' from IPython.utils.warn import warn, error'
33 # Magics class implementation
34 # Magics class implementation
34 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
35
36
37 class MagicsDisplay(object):
38 def __init__(self, magics_manager):
39 self.magics_manager = magics_manager
40
41 def _lsmagic(self):
42 """The main implementation of the %lsmagic"""
43 mesc = magic_escapes['line']
44 cesc = magic_escapes['cell']
45 mman = self.magics_manager
46 magics = mman.lsmagic()
47 out = ['Available line magics:',
48 mesc + (' '+mesc).join(sorted(magics['line'])),
49 '',
50 'Available cell magics:',
51 cesc + (' '+cesc).join(sorted(magics['cell'])),
52 '',
53 mman.auto_status()]
54 return '\n'.join(out)
55
56 def _repr_pretty_(self, p, cycle):
57 p.text(self._lsmagic())
58
59 def __str__(self):
60 return self._lsmagic()
61
62 def _jsonable(self):
63 """turn magics dict into jsonable dict of the same structure
64
65 replaces object instances with their class names as strings
66 """
67 magic_dict = {}
68 mman = self.magics_manager
69 magics = mman.lsmagic()
70 for key, subdict in magics.items():
71 d = {}
72 magic_dict[key] = d
73 for name, obj in subdict.items():
74 try:
75 classname = obj.im_class.__name__
76 except AttributeError:
77 classname = 'Other'
78
79 d[name] = classname
80 return magic_dict
81
82 def _repr_json_(self):
83 return json.dumps(self._jsonable())
84
85
36 @magics_class
86 @magics_class
37 class BasicMagics(Magics):
87 class BasicMagics(Magics):
38 """Magics that provide central IPython functionality.
88 """Magics that provide central IPython functionality.
@@ -124,24 +174,10 b' class BasicMagics(Magics):'
124 magic_escapes['cell'], name,
174 magic_escapes['cell'], name,
125 magic_escapes['cell'], target))
175 magic_escapes['cell'], target))
126
176
127 def _lsmagic(self):
128 mesc = magic_escapes['line']
129 cesc = magic_escapes['cell']
130 mman = self.shell.magics_manager
131 magics = mman.lsmagic()
132 out = ['Available line magics:',
133 mesc + (' '+mesc).join(sorted(magics['line'])),
134 '',
135 'Available cell magics:',
136 cesc + (' '+cesc).join(sorted(magics['cell'])),
137 '',
138 mman.auto_status()]
139 return '\n'.join(out)
140
141 @line_magic
177 @line_magic
142 def lsmagic(self, parameter_s=''):
178 def lsmagic(self, parameter_s=''):
143 """List currently available magic functions."""
179 """List currently available magic functions."""
144 print(self._lsmagic())
180 return MagicsDisplay(self.shell.magics_manager)
145
181
146 def _magic_docs(self, brief=False, rest=False):
182 def _magic_docs(self, brief=False, rest=False):
147 """Return docstrings from magic functions."""
183 """Return docstrings from magic functions."""
@@ -578,3 +578,72 b' class TestAstTransformError(unittest.TestCase):'
578 def test__IPYTHON__():
578 def test__IPYTHON__():
579 # This shouldn't raise a NameError, that's all
579 # This shouldn't raise a NameError, that's all
580 __IPYTHON__
580 __IPYTHON__
581
582
583 class DummyRepr(object):
584 def __repr__(self):
585 return "DummyRepr"
586
587 def _repr_html_(self):
588 return "<b>dummy</b>"
589
590 def _repr_javascript_(self):
591 return "console.log('hi');", {'key': 'value'}
592
593
594 def test_user_variables():
595 # enable all formatters
596 ip.display_formatter.active_types = ip.display_formatter.format_types
597
598 ip.user_ns['dummy'] = d = DummyRepr()
599 keys = set(['dummy', 'doesnotexist'])
600 r = ip.user_variables(keys)
601
602 nt.assert_equal(keys, set(r.keys()))
603 dummy = r['dummy']
604 nt.assert_equal(set(['status', 'data', 'metadata']), set(dummy.keys()))
605 nt.assert_equal(dummy['status'], 'ok')
606 data = dummy['data']
607 metadata = dummy['metadata']
608 nt.assert_equal(data.get('text/html'), d._repr_html_())
609 js, jsmd = d._repr_javascript_()
610 nt.assert_equal(data.get('application/javascript'), js)
611 nt.assert_equal(metadata.get('application/javascript'), jsmd)
612
613 dne = r['doesnotexist']
614 nt.assert_equal(dne['status'], 'error')
615 nt.assert_equal(dne['ename'], 'KeyError')
616
617 # back to text only
618 ip.display_formatter.active_types = ['text/plain']
619
620 def test_user_expression():
621 # enable all formatters
622 ip.display_formatter.active_types = ip.display_formatter.format_types
623 query = {
624 'a' : '1 + 2',
625 'b' : '1/0',
626 }
627 r = ip.user_expressions(query)
628 import pprint
629 pprint.pprint(r)
630 nt.assert_equal(r.keys(), query.keys())
631 a = r['a']
632 nt.assert_equal(set(['status', 'data', 'metadata']), set(a.keys()))
633 nt.assert_equal(a['status'], 'ok')
634 data = a['data']
635 metadata = a['metadata']
636 nt.assert_equal(data.get('text/plain'), '3')
637
638 b = r['b']
639 nt.assert_equal(b['status'], 'error')
640 nt.assert_equal(b['ename'], 'ZeroDivisionError')
641
642 # back to text only
643 ip.display_formatter.active_types = ['text/plain']
644
645
646
647
648
649
@@ -241,7 +241,9 b' class HistoryConsoleWidget(ConsoleWidget):'
241 content = msg['content']
241 content = msg['content']
242 status = content['status']
242 status = content['status']
243 if status == 'ok':
243 if status == 'ok':
244 self._max_session_history=(int(content['user_expressions']['hlen']))
244 self._max_session_history = int(
245 content['user_expressions']['hlen']['data']['text/plain']
246 )
245
247
246 def save_magic(self):
248 def save_magic(self):
247 # update the session history length
249 # update the session history length
@@ -20,15 +20,17 b' Authors:'
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 # stdlib imports
22 # stdlib imports
23 import sys
23 import json
24 import re
24 import re
25 import sys
25 import webbrowser
26 import webbrowser
26 import ast
27 from threading import Thread
27 from threading import Thread
28
28
29 # System library imports
29 # System library imports
30 from IPython.external.qt import QtGui,QtCore
30 from IPython.external.qt import QtGui,QtCore
31
31
32 from IPython.core.magic import magic_escapes
33
32 def background(f):
34 def background(f):
33 """call a function in a simple thread, to prevent blocking"""
35 """call a function in a simple thread, to prevent blocking"""
34 t = Thread(target=f)
36 t = Thread(target=f)
@@ -615,43 +617,42 b' class MainWindow(QtGui.QMainWindow):'
615 inner_dynamic_magic.__name__ = "dynamics_magic_s"
617 inner_dynamic_magic.__name__ = "dynamics_magic_s"
616 return inner_dynamic_magic
618 return inner_dynamic_magic
617
619
618 def populate_all_magic_menu(self, listofmagic=None):
620 def populate_all_magic_menu(self, display_data=None):
619 """Clean "All Magics..." menu and repopulate it with `listofmagic`
621 """Clean "All Magics..." menu and repopulate it with `display_data`
620
622
621 Parameters
623 Parameters
622 ----------
624 ----------
623 listofmagic : string,
625 display_data : dict,
624 repr() of a list of strings, send back by the kernel
626 dict of display_data for the magics dict of a MagicsManager.
627 Expects json data, as the result of %lsmagic
625
628
626 Notes
627 -----
628 `listofmagic`is a repr() of list because it is fed with the result of
629 a 'user_expression'
630 """
629 """
631 for k,v in self._magic_menu_dict.items():
630 for k,v in self._magic_menu_dict.items():
632 v.clear()
631 v.clear()
633 self.all_magic_menu.clear()
632 self.all_magic_menu.clear()
634
633
635
634 if not display_data:
636 mlist=ast.literal_eval(listofmagic)
635 return
637 for magic in mlist:
636
638 cell = (magic['type'] == 'cell')
637 if display_data['status'] != 'ok':
639 name = magic['name']
638 self.log.warn("%%lsmagic user-expression failed: %s" % display_data)
640 mclass = magic['class']
639 return
641 if cell :
640
642 prefix='%%'
641 mdict = json.loads(display_data['data'].get('application/json', {}))
643 else :
642
644 prefix='%'
643 for mtype in sorted(mdict):
645 magic_menu = self._get_magic_menu(mclass)
644 subdict = mdict[mtype]
646
645 prefix = magic_escapes[mtype]
647 pmagic = '%s%s'%(prefix,name)
646 for name in sorted(subdict):
648
647 mclass = subdict[name]
649 xaction = QtGui.QAction(pmagic,
648 magic_menu = self._get_magic_menu(mclass)
650 self,
649 pmagic = prefix + name
651 triggered=self._make_dynamic_magic(pmagic)
650 xaction = QtGui.QAction(pmagic,
652 )
651 self,
653 magic_menu.addAction(xaction)
652 triggered=self._make_dynamic_magic(pmagic)
654 self.all_magic_menu.addAction(xaction)
653 )
654 magic_menu.addAction(xaction)
655 self.all_magic_menu.addAction(xaction)
655
656
656 def update_all_magic_menu(self):
657 def update_all_magic_menu(self):
657 """ Update the list of magics in the "All Magics..." Menu
658 """ Update the list of magics in the "All Magics..." Menu
@@ -660,7 +661,7 b' class MainWindow(QtGui.QMainWindow):'
660 menu with the list received back
661 menu with the list received back
661
662
662 """
663 """
663 self.active_frontend._silent_exec_callback('get_ipython().magics_manager.lsmagic_info()',
664 self.active_frontend._silent_exec_callback('get_ipython().magic("lsmagic")',
664 self.populate_all_magic_menu)
665 self.populate_all_magic_menu)
665
666
666 def _get_magic_menu(self,menuidentifier, menulabel=None):
667 def _get_magic_menu(self,menuidentifier, menulabel=None):
@@ -361,7 +361,21 b' def test_user_variables():'
361
361
362 msg_id, reply = execute(code='x=1', user_variables=['x'])
362 msg_id, reply = execute(code='x=1', user_variables=['x'])
363 user_variables = reply['user_variables']
363 user_variables = reply['user_variables']
364 nt.assert_equal(user_variables, {u'x' : u'1'})
364 nt.assert_equal(user_variables, {u'x': {
365 u'status': u'ok',
366 u'data': {u'text/plain': u'1'},
367 u'metadata': {},
368 }})
369
370
371 def test_user_variables_fail():
372 flush_channels()
373
374 msg_id, reply = execute(code='x=1', user_variables=['nosuchname'])
375 user_variables = reply['user_variables']
376 foo = user_variables['nosuchname']
377 nt.assert_equal(foo['status'], 'error')
378 nt.assert_equal(foo['ename'], 'KeyError')
365
379
366
380
367 def test_user_expressions():
381 def test_user_expressions():
@@ -369,7 +383,21 b' def test_user_expressions():'
369
383
370 msg_id, reply = execute(code='x=1', user_expressions=dict(foo='x+1'))
384 msg_id, reply = execute(code='x=1', user_expressions=dict(foo='x+1'))
371 user_expressions = reply['user_expressions']
385 user_expressions = reply['user_expressions']
372 nt.assert_equal(user_expressions, {u'foo' : u'2'})
386 nt.assert_equal(user_expressions, {u'foo': {
387 u'status': u'ok',
388 u'data': {u'text/plain': u'2'},
389 u'metadata': {},
390 }})
391
392
393 def test_user_expressions_fail():
394 flush_channels()
395
396 msg_id, reply = execute(code='x=0', user_expressions=dict(foo='nosuchname'))
397 user_expressions = reply['user_expressions']
398 foo = user_expressions['foo']
399 nt.assert_equal(foo['status'], 'error')
400 nt.assert_equal(foo['ename'], 'NameError')
373
401
374
402
375 @dec.parametric
403 @dec.parametric
@@ -472,27 +472,6 b' class KernelMagics(Magics):'
472 else:
472 else:
473 print("Autosave disabled")
473 print("Autosave disabled")
474
474
475 def safe_unicode(e):
476 """unicode(e) with various fallbacks. Used for exceptions, which may not be
477 safe to call unicode() on.
478 """
479 try:
480 return unicode(e)
481 except UnicodeError:
482 pass
483
484 try:
485 return py3compat.str_to_unicode(str(e))
486 except UnicodeError:
487 pass
488
489 try:
490 return py3compat.str_to_unicode(repr(e))
491 except UnicodeError:
492 pass
493
494 return u'Unrecoverably corrupt evalue'
495
496
475
497 class ZMQInteractiveShell(InteractiveShell):
476 class ZMQInteractiveShell(InteractiveShell):
498 """A subclass of InteractiveShell for ZMQ."""
477 """A subclass of InteractiveShell for ZMQ."""
@@ -572,7 +551,7 b' class ZMQInteractiveShell(InteractiveShell):'
572 exc_content = {
551 exc_content = {
573 u'traceback' : stb,
552 u'traceback' : stb,
574 u'ename' : unicode(etype.__name__),
553 u'ename' : unicode(etype.__name__),
575 u'evalue' : safe_unicode(evalue)
554 u'evalue' : py3compat.safe_unicode(evalue),
576 }
555 }
577
556
578 dh = self.displayhook
557 dh = self.displayhook
@@ -50,6 +50,27 b' def _modify_str_or_docstring(str_change_func):'
50 return doc
50 return doc
51 return wrapper
51 return wrapper
52
52
53 def safe_unicode(e):
54 """unicode(e) with various fallbacks. Used for exceptions, which may not be
55 safe to call unicode() on.
56 """
57 try:
58 return unicode(e)
59 except UnicodeError:
60 pass
61
62 try:
63 return py3compat.str_to_unicode(str(e))
64 except UnicodeError:
65 pass
66
67 try:
68 return py3compat.str_to_unicode(repr(e))
69 except UnicodeError:
70 pass
71
72 return u'Unrecoverably corrupt evalue'
73
53 if sys.version_info[0] >= 3:
74 if sys.version_info[0] >= 3:
54 PY3 = True
75 PY3 = True
55
76
@@ -171,8 +171,9 b' Message type: ``execute_request``::'
171 # is forced to be False.
171 # is forced to be False.
172 'store_history' : bool,
172 'store_history' : bool,
173
173
174 # A list of variable names from the user's namespace to be retrieved. What
174 # A list of variable names from the user's namespace to be retrieved.
175 # returns is a JSON string of the variable's repr(), not a python object.
175 # What returns is a rich representation of each variable (dict keyed by name).
176 # See the display_data content for the structure of the representation data.
176 'user_variables' : list,
177 'user_variables' : list,
177
178
178 # Similarly, a dict mapping names to expressions to be evaluated in the
179 # Similarly, a dict mapping names to expressions to be evaluated in the
General Comments 0
You need to be logged in to leave comments. Login now