Show More
@@ -2363,10 +2363,38 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2363 | 2363 | # Things related to extracting values/expressions from kernel and user_ns |
|
2364 | 2364 | #------------------------------------------------------------------------- |
|
2365 | 2365 | |
|
2366 |
def _s |
|
|
2367 | etype, value = sys.exc_info()[:2] | |
|
2368 | return u'[ERROR] {e.__name__}: {v}'.format(e=etype, v=value) | |
|
2366 | def _user_obj_error(self): | |
|
2367 | """return simple exception dict | |
|
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 | 2398 | def user_variables(self, names): |
|
2371 | 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 | 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 | 2411 | out = {} |
|
2383 | 2412 | user_ns = self.user_ns |
|
2413 | ||
|
2384 | 2414 | for varname in names: |
|
2385 | 2415 | try: |
|
2386 |
value = re |
|
|
2416 | value = self._format_user_obj(user_ns[varname]) | |
|
2387 | 2417 | except: |
|
2388 |
value = self._s |
|
|
2418 | value = self._user_obj_error() | |
|
2389 | 2419 | out[varname] = value |
|
2390 | 2420 | return out |
|
2391 | 2421 | |
@@ -2401,17 +2431,18 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2401 | 2431 | |
|
2402 | 2432 | Returns |
|
2403 | 2433 | ------- |
|
2404 |
A dict, keyed like the input expressions dict, with the r |
|
|
2405 | value. | |
|
2434 | A dict, keyed like the input expressions dict, with the rich mime-typed | |
|
2435 | display_data of each value. | |
|
2406 | 2436 | """ |
|
2407 | 2437 | out = {} |
|
2408 | 2438 | user_ns = self.user_ns |
|
2409 | 2439 | global_ns = self.user_global_ns |
|
2440 | ||
|
2410 | 2441 | for key, expr in expressions.iteritems(): |
|
2411 | 2442 | try: |
|
2412 |
value = re |
|
|
2443 | value = self._format_user_obj(eval(expr, global_ns, user_ns)) | |
|
2413 | 2444 | except: |
|
2414 |
value = self._s |
|
|
2445 | value = self._user_obj_error() | |
|
2415 | 2446 | out[key] = value |
|
2416 | 2447 | return out |
|
2417 | 2448 |
@@ -15,6 +15,7 b'' | |||
|
15 | 15 | # Imports |
|
16 | 16 | #----------------------------------------------------------------------------- |
|
17 | 17 | # Stdlib |
|
18 | import json | |
|
18 | 19 | import os |
|
19 | 20 | import re |
|
20 | 21 | import sys |
@@ -326,16 +327,6 b' class MagicsManager(Configurable):' | |||
|
326 | 327 | """Return descriptive string with automagic status.""" |
|
327 | 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 | 330 | def lsmagic(self): |
|
340 | 331 | """Return a dict of currently available magic functions. |
|
341 | 332 |
@@ -15,6 +15,7 b' from __future__ import print_function' | |||
|
15 | 15 | |
|
16 | 16 | # Stdlib |
|
17 | 17 | import io |
|
18 | import json | |
|
18 | 19 | import sys |
|
19 | 20 | from pprint import pformat |
|
20 | 21 | |
@@ -33,6 +34,55 b' from IPython.utils.warn import warn, error' | |||
|
33 | 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 | 86 | @magics_class |
|
37 | 87 | class BasicMagics(Magics): |
|
38 | 88 | """Magics that provide central IPython functionality. |
@@ -124,24 +174,10 b' class BasicMagics(Magics):' | |||
|
124 | 174 | magic_escapes['cell'], name, |
|
125 | 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 | 177 | @line_magic |
|
142 | 178 | def lsmagic(self, parameter_s=''): |
|
143 | 179 | """List currently available magic functions.""" |
|
144 | print(self._lsmagic()) | |
|
180 | return MagicsDisplay(self.shell.magics_manager) | |
|
145 | 181 | |
|
146 | 182 | def _magic_docs(self, brief=False, rest=False): |
|
147 | 183 | """Return docstrings from magic functions.""" |
@@ -578,3 +578,72 b' class TestAstTransformError(unittest.TestCase):' | |||
|
578 | 578 | def test__IPYTHON__(): |
|
579 | 579 | # This shouldn't raise a NameError, that's all |
|
580 | 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 | 241 | content = msg['content'] |
|
242 | 242 | status = content['status'] |
|
243 | 243 | if status == 'ok': |
|
244 |
self._max_session_history= |
|
|
244 | self._max_session_history = int( | |
|
245 | content['user_expressions']['hlen']['data']['text/plain'] | |
|
246 | ) | |
|
245 | 247 | |
|
246 | 248 | def save_magic(self): |
|
247 | 249 | # update the session history length |
@@ -20,15 +20,17 b' Authors:' | |||
|
20 | 20 | #----------------------------------------------------------------------------- |
|
21 | 21 | |
|
22 | 22 | # stdlib imports |
|
23 |
import |
|
|
23 | import json | |
|
24 | 24 | import re |
|
25 | import sys | |
|
25 | 26 | import webbrowser |
|
26 | import ast | |
|
27 | 27 | from threading import Thread |
|
28 | 28 | |
|
29 | 29 | # System library imports |
|
30 | 30 | from IPython.external.qt import QtGui,QtCore |
|
31 | 31 | |
|
32 | from IPython.core.magic import magic_escapes | |
|
33 | ||
|
32 | 34 | def background(f): |
|
33 | 35 | """call a function in a simple thread, to prevent blocking""" |
|
34 | 36 | t = Thread(target=f) |
@@ -615,43 +617,42 b' class MainWindow(QtGui.QMainWindow):' | |||
|
615 | 617 | inner_dynamic_magic.__name__ = "dynamics_magic_s" |
|
616 | 618 | return inner_dynamic_magic |
|
617 | 619 | |
|
618 |
def populate_all_magic_menu(self, |
|
|
619 |
"""Clean "All Magics..." menu and repopulate it with ` |
|
|
620 | def populate_all_magic_menu(self, display_data=None): | |
|
621 | """Clean "All Magics..." menu and repopulate it with `display_data` | |
|
620 | 622 | |
|
621 | 623 | Parameters |
|
622 | 624 | ---------- |
|
623 | listofmagic : string, | |
|
624 | repr() of a list of strings, send back by the kernel | |
|
625 | display_data : dict, | |
|
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 | 630 | for k,v in self._magic_menu_dict.items(): |
|
632 | 631 | v.clear() |
|
633 | 632 | self.all_magic_menu.clear() |
|
634 | ||
|
635 | ||
|
636 | mlist=ast.literal_eval(listofmagic) | |
|
637 | for magic in mlist: | |
|
638 | cell = (magic['type'] == 'cell') | |
|
639 | name = magic['name'] | |
|
640 | mclass = magic['class'] | |
|
641 | if cell : | |
|
642 | prefix='%%' | |
|
643 |
|
|
|
644 | prefix='%' | |
|
645 | magic_menu = self._get_magic_menu(mclass) | |
|
646 | ||
|
647 | pmagic = '%s%s'%(prefix,name) | |
|
648 | ||
|
649 | xaction = QtGui.QAction(pmagic, | |
|
650 |
|
|
|
651 | triggered=self._make_dynamic_magic(pmagic) | |
|
652 |
|
|
|
653 | magic_menu.addAction(xaction) | |
|
654 | self.all_magic_menu.addAction(xaction) | |
|
633 | ||
|
634 | if not display_data: | |
|
635 | return | |
|
636 | ||
|
637 | if display_data['status'] != 'ok': | |
|
638 | self.log.warn("%%lsmagic user-expression failed: %s" % display_data) | |
|
639 | return | |
|
640 | ||
|
641 | mdict = json.loads(display_data['data'].get('application/json', {})) | |
|
642 | ||
|
643 | for mtype in sorted(mdict): | |
|
644 | subdict = mdict[mtype] | |
|
645 | prefix = magic_escapes[mtype] | |
|
646 | for name in sorted(subdict): | |
|
647 | mclass = subdict[name] | |
|
648 | magic_menu = self._get_magic_menu(mclass) | |
|
649 | pmagic = prefix + name | |
|
650 | xaction = QtGui.QAction(pmagic, | |
|
651 | self, | |
|
652 | triggered=self._make_dynamic_magic(pmagic) | |
|
653 | ) | |
|
654 | magic_menu.addAction(xaction) | |
|
655 | self.all_magic_menu.addAction(xaction) | |
|
655 | 656 | |
|
656 | 657 | def update_all_magic_menu(self): |
|
657 | 658 | """ Update the list of magics in the "All Magics..." Menu |
@@ -660,7 +661,7 b' class MainWindow(QtGui.QMainWindow):' | |||
|
660 | 661 | menu with the list received back |
|
661 | 662 | |
|
662 | 663 | """ |
|
663 |
self.active_frontend._silent_exec_callback('get_ipython().magic |
|
|
664 | self.active_frontend._silent_exec_callback('get_ipython().magic("lsmagic")', | |
|
664 | 665 | self.populate_all_magic_menu) |
|
665 | 666 | |
|
666 | 667 | def _get_magic_menu(self,menuidentifier, menulabel=None): |
@@ -361,7 +361,21 b' def test_user_variables():' | |||
|
361 | 361 | |
|
362 | 362 | msg_id, reply = execute(code='x=1', user_variables=['x']) |
|
363 | 363 | user_variables = reply['user_variables'] |
|
364 |
nt.assert_equal(user_variables, {u'x' |
|
|
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 | 381 | def test_user_expressions(): |
@@ -369,7 +383,21 b' def test_user_expressions():' | |||
|
369 | 383 | |
|
370 | 384 | msg_id, reply = execute(code='x=1', user_expressions=dict(foo='x+1')) |
|
371 | 385 | user_expressions = reply['user_expressions'] |
|
372 |
nt.assert_equal(user_expressions, {u'foo' |
|
|
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 | 403 | @dec.parametric |
@@ -472,27 +472,6 b' class KernelMagics(Magics):' | |||
|
472 | 472 | else: |
|
473 | 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 | 476 | class ZMQInteractiveShell(InteractiveShell): |
|
498 | 477 | """A subclass of InteractiveShell for ZMQ.""" |
@@ -572,7 +551,7 b' class ZMQInteractiveShell(InteractiveShell):' | |||
|
572 | 551 | exc_content = { |
|
573 | 552 | u'traceback' : stb, |
|
574 | 553 | u'ename' : unicode(etype.__name__), |
|
575 | u'evalue' : safe_unicode(evalue) | |
|
554 | u'evalue' : py3compat.safe_unicode(evalue), | |
|
576 | 555 | } |
|
577 | 556 | |
|
578 | 557 | dh = self.displayhook |
@@ -50,6 +50,27 b' def _modify_str_or_docstring(str_change_func):' | |||
|
50 | 50 | return doc |
|
51 | 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 | 74 | if sys.version_info[0] >= 3: |
|
54 | 75 | PY3 = True |
|
55 | 76 |
@@ -171,8 +171,9 b' Message type: ``execute_request``::' | |||
|
171 | 171 | # is forced to be False. |
|
172 | 172 | 'store_history' : bool, |
|
173 | 173 | |
|
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. | |
|
174 | # A list of variable names from the user's namespace to be retrieved. | |
|
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 | 177 | 'user_variables' : list, |
|
177 | 178 | |
|
178 | 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