Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Simple bug-fix #149

Closed
wants to merge 5 commits into from

3 participants

@rkern

The result_display hook was removed from the list of available hooks. My beautiful pretty extension was broken. It made me sad.

@ellisonbg
Owner

Was this sufficient to fix the the pretty extension?

@rkern

No, it just allows the hook to be registered as the extension is written to do. No one does anything with that hook in the newkernel branch. This still makes me sad. What exactly needs to be decided about the hooks (per the TODO comment in displayhook.py)?

fperez added some commits
@ellisonbg
Owner

The big picture with the hook is that we wan to move away from global things towards using good encapsulation/OO design. Also hook became a bit of cat chasing its tail. The core calls the hooks calls the core calls the... Thus, as we are refactoring the core, we are removing hooks wherever possibly. For result_display this happened when we refactored the display hook this summer. To get the functionality back, we simply need to create a simple API on the display hook object in the core and then have the extension call that API. In the long run, we want to come up with a nice generic way of handling hook type things on our classes, but we don't need to get this worked out before we fix result_display. I just added this to my todo list on the Google doc...

fperez added some commits
@fperez fperez Add function signature info to calltips.
Also removed custom ObjectInfo namedtuple according to code review
(left as a dict for now, we can make it a list later if really needed).

Added the start of some testing for the object inspector and updated
the messaging spec.
f875d43
@fperez fperez Remove leftover files from bzr. 742d5ef
@rkern

Can that API be a CommandChainDispatcher on the DisplayHook? Or is the design of CommandChainDispatcher part of the problem you have with hook mechanism? I fear that you will either end up reinventing CommandChainDispatcher or coming up with slightly different mechanisms for each of the extension points.

@rkern

I switched branches around, so this pull request doesn't make sense anymore. But my question still stands: What do you want that API to look like?

@ellisonbg
Owner

There are a couple of issues that are driving the reconsideration of the hooks:

  • The global nature of the hooks are the biggest issue. We want to move to a model where the different components of the IPython core have their own extension points that are local to those classes and encapusulated therin.
  • The notion of an IPython extension point goes far beyond the current "hooks" model. As we have been refactoring the core, we are introducing other non-hook APIs that are meant to be part of the developer API for the core.
  • I would say that at the current time, it is not yet clear what all the extension APIs should look like and we haven't spent much time thinking about it.
  • It is very possible that the CommandChainDispatcher would be one way we would offer extension APIs. We would need to think about how to fit that into the more encapsulated context of a class. I have even thought about using traits/traitlets to declare extension points.

I should mention that the other part of this picture is that there are still some aspects of the display hook and payload system that we need to work out. We are currently using our new payload system to bring back things like the result of page calls and inline graphics. Some of this stuff may end up being moved to the display hook.

While there are lots of larger issues still being played out, I don't see any reason we can't add back some sort of simple extension API for the display hook that the pretty printer extension can use.

@rkern

Alternately, we can use pretty as the default implementation and use its extension API to allow people to add individual formatters for individual types. I suspect that's what most people want to extend.

@fperez
Owner

Robert, I had my gmail filters set a little too aggressively and hadn't seen this, sorry; Brian just pointed me to it.

I saw you closed this one and opened an updated pull request, do you prefer to continue the discussion there?

@rkern

We can discuss here since it's pretty clear that merging in the changes that just add back the result_display hook are not what we want. I do not have another pull request open for this.

@rkern

Or ipython-dev, really.

@ellisonbg
Owner

Fernando and I talked about the display hook logic a bit yesterday. Let's continue this discussion on ipython-dev though.

@fperez
Owner

OK, I'm a bit swamped today but I'll be happy to continue tomorrow on ipython-dev until we find an approach we're happy with for the long run.

@minrk minrk referenced this pull request from a commit in minrk/ipython
@Carreau Carreau remove pypandoc
Closes #149 (replacement of it since refactor)
1f16ad5
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Sep 18, 2010
  1. @fperez
  2. @fperez

    Document the code execution semantics much more carefully.

    fperez authored
    Also renamed user var/expression functions to match the fields in the
    execute request.
  3. @fperez

    Add function signature info to calltips.

    fperez authored
    Also removed custom ObjectInfo namedtuple according to code review
    (left as a dict for now, we can make it a list later if really needed).
    
    Added the start of some testing for the object inspector and updated
    the messaging spec.
  4. @fperez

    Remove leftover files from bzr.

    fperez authored
Commits on Sep 20, 2010
  1. @rkern
This page is out of date. Refresh to see the latest.
View
4 .bzrignore
@@ -1,4 +0,0 @@
-# IPython-specific files and pattersn that bzr should ignore
-docs/dist
-docs/build/*
-docs/source/api/generated
View
2  .checkeol
@@ -1,2 +0,0 @@
-[LF]
-*.py
View
50 IPython/core/interactiveshell.py
@@ -155,7 +155,11 @@ class InteractiveShell(Configurable, Magic):
exit_now = CBool(False)
filename = Str("<ipython console>")
ipython_dir= Unicode('', config=True) # Set to get_ipython_dir() in __init__
- input_splitter = Instance('IPython.core.inputsplitter.IPythonInputSplitter')
+
+ # Input splitter, to split entire cells of input into either individual
+ # interactive statements or whole blocks.
+ input_splitter = Instance('IPython.core.inputsplitter.IPythonInputSplitter',
+ (), {})
logstart = CBool(False, config=True)
logfile = Str('', config=True)
logappend = Str('', config=True)
@@ -393,10 +397,6 @@ def init_instance_attrs(self):
# Indentation management
self.indent_current_nsp = 0
- # Input splitter, to split entire cells of input into either individual
- # interactive statements or whole blocks.
- self.input_splitter = IPythonInputSplitter()
-
def init_environment(self):
"""Any changes we need to make to the user's environment."""
pass
@@ -1058,6 +1058,7 @@ def push(self, variables, interactive=True):
#-------------------------------------------------------------------------
# Things related to object introspection
#-------------------------------------------------------------------------
+
def _ofind(self, oname, namespaces=None):
"""Find an object in the available namespaces.
@@ -1195,10 +1196,10 @@ def _inspect(self, meth, oname, namespaces=None, **kw):
def object_inspect(self, oname):
info = self._object_find(oname)
if info.found:
- return self.inspector.info(info.obj, info=info)
+ return self.inspector.info(info.obj, oname, info=info)
else:
- return oinspect.mk_object_info({'found' : False})
-
+ return oinspect.object_info(name=oname, found=False)
+
#-------------------------------------------------------------------------
# Things related to history management
#-------------------------------------------------------------------------
@@ -1963,10 +1964,17 @@ def _simple_error(self):
etype, value = sys.exc_info()[:2]
return u'[ERROR] {e.__name__}: {v}'.format(e=etype, v=value)
- def get_user_variables(self, names):
+ def user_variables(self, names):
"""Get a list of variable names from the user's namespace.
- The return value is a dict with the repr() of each value.
+ Parameters
+ ----------
+ names : list of strings
+ A list of names of variables to be read from the user namespace.
+
+ Returns
+ -------
+ A dict, keyed by the input names and with the repr() of each value.
"""
out = {}
user_ns = self.user_ns
@@ -1978,10 +1986,20 @@ def get_user_variables(self, names):
out[varname] = value
return out
- def eval_expressions(self, expressions):
+ def user_expressions(self, expressions):
"""Evaluate a dict of expressions in the user's namespace.
- The return value is a dict with the repr() of each value.
+ Parameters
+ ----------
+ expressions : dict
+ A dict with string keys and string values. The expression values
+ should be valid Python expressions, each of which will be evaluated
+ in the user namespace.
+
+ Returns
+ -------
+ A dict, keyed like the input expressions dict, with the repr() of each
+ value.
"""
out = {}
user_ns = self.user_ns
@@ -2120,10 +2138,10 @@ def run_cell(self, cell):
If there's more than one block, it depends:
- - if the last one is a single line long, run all but the last in
- 'exec' mode and the very last one in 'single' mode. This makes it
- easy to type simple expressions at the end to see computed values.
- - otherwise (last one is also multiline), run all in 'exec' mode
+ - if the last one is no more than two lines long, run all but the last
+ in 'exec' mode and the very last one in 'single' mode. This makes it
+ easy to type simple expressions at the end to see computed values. -
+ otherwise (last one is also multiline), run all in 'exec' mode
When code is executed in 'single' mode, :func:`sys.displayhook` fires,
results are displayed and output prompts are computed. In 'exec' mode,
View
134 IPython/core/oinspect.py
@@ -76,18 +76,15 @@
'call_def', 'call_docstring',
# These won't be printed but will be used to determine how to
# format the object
- 'ismagic', 'isalias', 'argspec', 'found',
+ 'ismagic', 'isalias', 'argspec', 'found', 'name',
]
-ObjectInfo = namedtuple('ObjectInfo', info_fields)
-
-
-def mk_object_info(kw):
- """Make a f"""
+def object_info(**kw):
+ """Make an object info dict with all fields present."""
infodict = dict(izip_longest(info_fields, [None]))
infodict.update(kw)
- return ObjectInfo(**infodict)
+ return infodict
def getdoc(obj):
@@ -161,11 +158,76 @@ def getargspec(obj):
func_obj = obj
elif inspect.ismethod(obj):
func_obj = obj.im_func
+ elif hasattr(obj, '__call__'):
+ func_obj = obj.__call__
else:
raise TypeError('arg is not a Python function')
args, varargs, varkw = inspect.getargs(func_obj.func_code)
return args, varargs, varkw, func_obj.func_defaults
+
+def format_argspec(argspec):
+ """Format argspect, convenience wrapper around inspect's.
+
+ This takes a dict instead of ordered arguments and calls
+ inspect.format_argspec with the arguments in the necessary order.
+ """
+ return inspect.formatargspec(argspec['args'], argspec['varargs'],
+ argspec['varkw'], argspec['defaults'])
+
+
+def call_tip(oinfo, format_call=True):
+ """Extract call tip data from an oinfo dict.
+
+ Parameters
+ ----------
+ oinfo : dict
+
+ format_call : bool, optional
+ If True, the call line is formatted and returned as a string. If not, a
+ tuple of (name, argspec) is returned.
+
+ Returns
+ -------
+ call_info : None, str or (str, dict) tuple.
+ When format_call is True, the whole call information is formattted as a
+ single string. Otherwise, the object's name and its argspec dict are
+ returned. If no call information is available, None is returned.
+
+ docstring : str or None
+ The most relevant docstring for calling purposes is returned, if
+ available. The priority is: call docstring for callable instances, then
+ constructor docstring for classes, then main object's docstring otherwise
+ (regular functions).
+ """
+ # Get call definition
+ argspec = oinfo['argspec']
+ if argspec is None:
+ call_line = None
+ else:
+ # Callable objects will have 'self' as their first argument, prune
+ # it out if it's there for clarity (since users do *not* pass an
+ # extra first argument explicitly).
+ try:
+ has_self = argspec['args'][0] == 'self'
+ except (KeyError, IndexError):
+ pass
+ else:
+ if has_self:
+ argspec['args'] = argspec['args'][1:]
+
+ call_line = oinfo['name']+format_argspec(argspec)
+
+ # Now get docstring.
+ # The priority is: call docstring, constructor docstring, main one.
+ doc = oinfo['call_docstring']
+ if doc is None:
+ doc = oinfo['init_docstring']
+ if doc is None:
+ doc = oinfo['docstring']
+
+ return call_line, doc
+
#****************************************************************************
# Class definitions
@@ -178,7 +240,9 @@ def writeln(self,*arg,**kw):
class Inspector:
- def __init__(self,color_table,code_color_table,scheme,
+ def __init__(self, color_table=InspectColors,
+ code_color_table=PyColorize.ANSICodeColors,
+ scheme='NoColor',
str_detail_level=0):
self.color_table = color_table
self.parser = PyColorize.Parser(code_color_table,out='str')
@@ -565,6 +629,7 @@ def info(self, obj, oname='', formatter=None, info=None, detail_level=0):
ismagic = info.ismagic
isalias = info.isalias
ospace = info.namespace
+
# Get docstring, special-casing aliases:
if isalias:
if not callable(obj):
@@ -583,9 +648,8 @@ def info(self, obj, oname='', formatter=None, info=None, detail_level=0):
if formatter is not None:
ds = formatter(ds)
- # store output in a dict, we'll later convert it to an ObjectInfo. We
- # initialize it here and fill it as we go
- out = dict(found=True, isalias=isalias, ismagic=ismagic)
+ # store output in a dict, we initialize it here and fill it as we go
+ out = dict(name=oname, found=True, isalias=isalias, ismagic=ismagic)
string_max = 200 # max size of strings to show (snipped if longer)
shalf = int((string_max -5)/2)
@@ -650,17 +714,14 @@ def info(self, obj, oname='', formatter=None, info=None, detail_level=0):
binary_file = True
# reconstruct the function definition and print it:
- defln = self._getdef(obj,oname)
+ defln = self._getdef(obj, oname)
if defln:
out['definition'] = self.format(defln)
- args, varargs, varkw, func_defaults = getargspec(obj)
- out['argspec'] = dict(args=args, varargs=varargs,
- varkw=varkw, func_defaults=func_defaults)
-
+
# Docstrings only in detail 0 mode, since source contains them (we
# avoid repetitions). If source fails, we add them back, see below.
if ds and detail_level == 0:
- out['docstring'] = indent(ds)
+ out['docstring'] = ds
# Original source code for any callable
if detail_level:
@@ -700,11 +761,11 @@ def info(self, obj, oname='', formatter=None, info=None, detail_level=0):
if init_def:
out['init_definition'] = self.format(init_def)
if init_ds:
- out['init_docstring'] = indent(init_ds)
+ out['init_docstring'] = init_ds
+
# and class docstring for instances:
elif obj_type is types.InstanceType or \
- isinstance(obj,object):
-
+ isinstance(obj, object):
# First, check whether the instance docstring is identical to the
# class one, and print it separately if they don't coincide. In
# most cases they will, but it's nice to print all the info for
@@ -723,7 +784,7 @@ def info(self, obj, oname='', formatter=None, info=None, detail_level=0):
class_ds.startswith('module(name[,') ):
class_ds = None
if class_ds and ds != class_ds:
- out['class_docstring'] = indent(class_ds)
+ out['class_docstring'] = class_ds
# Next, try to show constructor docstrings
try:
@@ -735,11 +796,11 @@ def info(self, obj, oname='', formatter=None, info=None, detail_level=0):
except AttributeError:
init_ds = None
if init_ds:
- out['init_docstring'] = indent(init_ds)
+ out['init_docstring'] = init_ds
# Call form docstring for callable instances
- if hasattr(obj,'__call__'):
- call_def = self._getdef(obj.__call__,oname)
+ if hasattr(obj, '__call__'):
+ call_def = self._getdef(obj.__call__, oname)
if call_def is not None:
out['call_def'] = self.format(call_def)
call_ds = getdoc(obj.__call__)
@@ -747,9 +808,30 @@ def info(self, obj, oname='', formatter=None, info=None, detail_level=0):
if call_ds and call_ds.startswith('x.__call__(...) <==> x(...)'):
call_ds = None
if call_ds:
- out['call_docstring'] = indent(call_ds)
+ out['call_docstring'] = call_ds
+
+ # Compute the object's argspec as a callable. The key is to decide
+ # whether to pull it from the object itself, from its __init__ or
+ # from its __call__ method.
+
+ if inspect.isclass(obj):
+ callable_obj = obj.__init__
+ elif callable(obj):
+ callable_obj = obj
+ else:
+ callable_obj = None
+
+ if callable_obj:
+ try:
+ args, varargs, varkw, defaults = getargspec(callable_obj)
+ except (TypeError, AttributeError):
+ # For extensions/builtins we can't retrieve the argspec
+ pass
+ else:
+ out['argspec'] = dict(args=args, varargs=varargs,
+ varkw=varkw, defaults=defaults)
- return mk_object_info(out)
+ return object_info(**out)
def psearch(self,pattern,ns_table,ns_search=[],
View
89 IPython/core/tests/test_oinspect.py
@@ -0,0 +1,89 @@
+"""Tests for the object inspection functionality.
+"""
+#-----------------------------------------------------------------------------
+# Copyright (C) 2010 The IPython Development Team.
+#
+# Distributed under the terms of the BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
+#-----------------------------------------------------------------------------
+# Imports
+#-----------------------------------------------------------------------------
+from __future__ import print_function
+
+# Stdlib imports
+
+# Third-party imports
+import nose.tools as nt
+
+# Our own imports
+from .. import oinspect
+
+#-----------------------------------------------------------------------------
+# Globals and constants
+#-----------------------------------------------------------------------------
+
+inspector = oinspect.Inspector()
+
+#-----------------------------------------------------------------------------
+# Local utilities
+#-----------------------------------------------------------------------------
+
+# A few generic objects we can then inspect in the tests below
+
+class Call(object):
+ """This is the class docstring."""
+
+ def __init__(self, x, y=1):
+ """This is the constructor docstring."""
+
+ def __call__(self, *a, **kw):
+ """This is the call docstring."""
+
+ def method(self, x, z=2):
+ """Some method's docstring"""
+
+def f(x, y=2, *a, **kw):
+ """A simple function."""
+
+def g(y, z=3, *a, **kw):
+ pass # no docstring
+
+
+def check_calltip(obj, name, call, docstring):
+ """Generic check pattern all calltip tests will use"""
+ info = inspector.info(obj, name)
+ call_line, ds = oinspect.call_tip(info)
+ nt.assert_equal(call_line, call)
+ nt.assert_equal(ds, docstring)
+
+#-----------------------------------------------------------------------------
+# Tests
+#-----------------------------------------------------------------------------
+
+def test_calltip_class():
+ check_calltip(Call, 'Call', 'Call(x, y=1)', Call.__init__.__doc__)
+
+
+def test_calltip_instance():
+ c = Call(1)
+ check_calltip(c, 'c', 'c(*a, **kw)', c.__call__.__doc__)
+
+
+def test_calltip_method():
+ c = Call(1)
+ check_calltip(c.method, 'c.method', 'c.method(x, z=2)', c.method.__doc__)
+
+
+def test_calltip_function():
+ check_calltip(f, 'f', 'f(x, y=2, *a, **kw)', f.__doc__)
+
+
+def test_calltip_function2():
+ check_calltip(g, 'g', 'g(y, z=3, *a, **kw)', '<no docstring>')
+
+
+def test_calltip_builtin():
+ check_calltip(sum, 'sum', None, sum.__doc__)
View
19 IPython/frontend/qt/console/call_tip_widget.py
@@ -122,15 +122,20 @@ def showEvent(self, event):
# 'CallTipWidget' interface
#--------------------------------------------------------------------------
- def show_docstring(self, doc, maxlines=20):
- """ Attempts to show the specified docstring at the current cursor
- location. The docstring is dedented and possibly truncated for
+ def show_call_info(self, call_line=None, doc=None, maxlines=20):
+ """ Attempts to show the specified call line and docstring at the
+ current cursor location. The docstring is possibly truncated for
length.
"""
- doc = dedent(doc.rstrip()).lstrip()
- match = re.match("(?:[^\n]*\n){%i}" % maxlines, doc)
- if match:
- doc = doc[:match.end()] + '\n[Documentation continues...]'
+ if doc:
+ match = re.match("(?:[^\n]*\n){%i}" % maxlines, doc)
+ if match:
+ doc = doc[:match.end()] + '\n[Documentation continues...]'
+ else:
+ doc = ''
+
+ if call_line:
+ doc = '\n\n'.join([call_line, doc])
return self.show_tip(doc)
def show_tip(self, tip):
View
11 IPython/frontend/qt/console/frontend_widget.py
@@ -10,6 +10,7 @@
# Local imports
from IPython.core.inputsplitter import InputSplitter, transform_classic_prompt
+from IPython.core.oinspect import call_tip
from IPython.frontend.qt.base_frontend_mixin import BaseFrontendMixin
from IPython.utils.traitlets import Bool
from bracket_matcher import BracketMatcher
@@ -334,9 +335,13 @@ def _handle_object_info_reply(self, rep):
info = self._request_info.get('call_tip')
if info and info.id == rep['parent_header']['msg_id'] and \
info.pos == cursor.position():
- doc = rep['content']['docstring']
- if doc:
- self._call_tip_widget.show_docstring(doc)
+ # Get the information for a call tip. For now we format the call
+ # line as string, later we can pass False to format_call and
+ # syntax-highlight it ourselves for nicer formatting in the
+ # calltip.
+ call_info, doc = call_tip(rep['content'], format_call=True)
+ if call_info or doc:
+ self._call_tip_widget.show_call_info(call_info, doc)
def _handle_pyout(self, msg):
""" Handle display hook output.
View
9 IPython/zmq/ipkernel.py
@@ -250,9 +250,9 @@ def execute_request(self, ident, parent):
# or not. If it did, we proceed to evaluate user_variables/expressions
if reply_content['status'] == 'ok':
reply_content[u'user_variables'] = \
- shell.get_user_variables(content[u'user_variables'])
+ shell.user_variables(content[u'user_variables'])
reply_content[u'user_expressions'] = \
- shell.eval_expressions(content[u'user_expressions'])
+ shell.user_expressions(content[u'user_expressions'])
else:
# If there was an error, don't even try to compute variables or
# expressions
@@ -303,9 +303,8 @@ def complete_request(self, ident, parent):
def object_info_request(self, ident, parent):
object_info = self.shell.object_inspect(parent['content']['oname'])
- # Before we send this object over, we turn it into a dict and we scrub
- # it for JSON usage
- oinfo = json_clean(object_info._asdict())
+ # Before we send this object over, we scrub it for JSON usage
+ oinfo = json_clean(object_info)
msg = self.session.send(self.reply_socket, 'object_info_reply',
oinfo, parent, ident)
io.raw_print(msg)
View
150 docs/source/development/messaging.txt
@@ -160,30 +160,23 @@ Message type: ``execute_request``::
'user_expressions' : dict,
}
-The ``code`` field contains a single string, but this may be a multiline
-string. The kernel is responsible for splitting this into possibly more than
-one block and deciding whether to compile these in 'single' or 'exec' mode.
-We're still sorting out this policy. The current inputsplitter is capable of
-splitting the input for blocks that can all be run as 'single', but in the long
-run it may prove cleaner to only use 'single' mode for truly single-line
-inputs, and run all multiline input in 'exec' mode. This would preserve the
-natural behavior of single-line inputs while allowing long cells to behave more
-likea a script. This design will be refined as we complete the implementation.
+The ``code`` field contains a single string (possibly multiline). The kernel
+is responsible for splitting this into one or more independent execution blocks
+and deciding whether to compile these in 'single' or 'exec' mode (see below for
+detailed execution semantics).
The ``user_`` fields deserve a detailed explanation. In the past, IPython had
the notion of a prompt string that allowed arbitrary code to be evaluated, and
this was put to good use by many in creating prompts that displayed system
status, path information, and even more esoteric uses like remote instrument
status aqcuired over the network. But now that IPython has a clean separation
-between the kernel and the clients, the notion of embedding 'prompt'
-maninpulations into the kernel itself feels awkward. Prompts should be a
-frontend-side feature, and it should be even possible for different frontends
-to display different prompts while interacting with the same kernel.
+between the kernel and the clients, the kernel has no prompt knowledge; prompts
+are a frontend-side feature, and it should be even possible for different
+frontends to display different prompts while interacting with the same kernel.
-We have therefore abandoned the idea of a 'prompt string' to be evaluated by
-the kernel, and instead provide the ability to retrieve from the user's
-namespace information after the execution of the main ``code``, with two fields
-of the execution request:
+The kernel now provides the ability to retrieve data from the user's namespace
+after the execution of the main ``code``, thanks to two fields in the
+``execute_request`` message:
- ``user_variables``: If only variables from the user's namespace are needed, a
list of variable names can be passed and a dict with these names as keys and
@@ -205,32 +198,106 @@ terminal, etc).
empty code string and ``silent=True``.
Execution semantics
- Upon completion of the execution request, the kernel *always* sends a
- reply, with a status code indicating what happened and additional data
- depending on the outcome.
+~~~~~~~~~~~~~~~~~~~
- The ``code`` field is executed first, and then the ``user_variables`` and
- ``user_expressions`` are computed. This ensures that any error in the
- latter don't harm the main code execution.
+When the silent flag is false, the execution of use code consists of the
+following phases (in silent mode, only the ``code`` field is executed):
- Any error in retrieving the ``user_variables`` or evaluating the
- ``user_expressions`` will result in a simple error message in the return
- fields of the form::
+1. Run the ``pre_runcode_hook``.
- [ERROR] ExceptionType: Exception message
+2. Execute the ``code`` field, see below for details.
+
+3. If #2 succeeds, compute ``user_variables`` and ``user_expressions`` are
+ computed. This ensures that any error in the latter don't harm the main
+ code execution.
+
+4. Call any method registered with :meth:`register_post_execute`.
+
+.. warning::
+
+ The API for running code before/after the main code block is likely to
+ change soon. Both the ``pre_runcode_hook`` and the
+ :meth:`register_post_execute` are susceptible to modification, as we find a
+ consistent model for both.
+
+To understand how the ``code`` field is executed, one must know that Python
+code can be compiled in one of three modes (controlled by the ``mode`` argument
+to the :func:`compile` builtin):
+
+*single*
+ Valid for a single interactive statement (though the source can contain
+ multiple lines, such as a for loop). When compiled in this mode, the
+ generated bytecode contains special instructions that trigger the calling of
+ :func:`sys.displayhook` for any expression in the block that returns a value.
+ This means that a single statement can actually produce multiple calls to
+ :func:`sys.displayhook`, if for example it contains a loop where each
+ iteration computes an unassigned expression would generate 10 calls::
+
+ for i in range(10):
+ i**2
+
+*exec*
+ An arbitrary amount of source code, this is how modules are compiled.
+ :func:`sys.displayhook` is *never* implicitly called.
+
+*eval*
+ A single expression that returns a value. :func:`sys.displayhook` is *never*
+ implicitly called.
+
+
+The ``code`` field is split into individual blocks each of which is valid for
+execution in 'single' mode, and then:
+
+- If there is only a single block: it is executed in 'single' mode.
+
+- If there is more than one block:
+
+ * if the last one is a single line long, run all but the last in 'exec' mode
+ and the very last one in 'single' mode. This makes it easy to type simple
+ expressions at the end to see computed values.
+
+ * if the last one is no more than two lines long, run all but the last in
+ 'exec' mode and the very last one in 'single' mode. This makes it easy to
+ type simple expressions at the end to see computed values. - otherwise
+ (last one is also multiline), run all in 'exec' mode
+
+ * otherwise (last one is also multiline), run all in 'exec' mode as a single
+ unit.
+
+Any error in retrieving the ``user_variables`` or evaluating the
+``user_expressions`` will result in a simple error message in the return fields
+of the form::
+
+ [ERROR] ExceptionType: Exception message
+
+The user can simply send the same variable name or expression for evaluation to
+see a regular traceback.
+
+Errors in any registered post_execute functions are also reported similarly,
+and the failing function is removed from the post_execution set so that it does
+not continue triggering failures.
+
+Upon completion of the execution request, the kernel *always* sends a reply,
+with a status code indicating what happened and additional data depending on
+the outcome. See :ref:`below <execution_results>` for the possible return
+codes and associated data.
- The user can simply send the same variable name or expression for
- evaluation to see a regular traceback.
Execution counter (old prompt number)
- The kernel has a single, monotonically increasing counter of all execution
- requests that are made with ``silent=False``. This counter is used to
- populate the ``In[n]``, ``Out[n]`` and ``_n`` variables, so clients will
- likely want to display it in some form to the user, which will typically
- (but not necessarily) be done in the prompts. The value of this counter
- will be returned as the ``execution_count`` field of all ``execute_reply```
- messages.
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The kernel has a single, monotonically increasing counter of all execution
+requests that are made with ``silent=False``. This counter is used to populate
+the ``In[n]``, ``Out[n]`` and ``_n`` variables, so clients will likely want to
+display it in some form to the user, which will typically (but not necessarily)
+be done in the prompts. The value of this counter will be returned as the
+``execution_count`` field of all ``execute_reply`` messages.
+
+.. _execution_results:
+
+Execution results
+~~~~~~~~~~~~~~~~~
+
Message type: ``execute_reply``::
content = {
@@ -303,6 +370,11 @@ happens when the kernel was interrupted by a signal.
Kernel attribute access
-----------------------
+.. warning::
+
+ This part of the messaging spec is not actually implemented in the kernel
+ yet.
+
While this protocol does not specify full RPC access to arbitrary methods of
the kernel object, the kernel does allow read (and in some cases write) access
to certain attributes.
@@ -361,6 +433,7 @@ Message type: ``setattr_reply``::
}
+
Object information
------------------
@@ -389,6 +462,9 @@ field names that IPython prints at the terminal.
Message type: ``object_info_reply``::
content = {
+ # The name the object was requested under
+ 'name' : str,
+
# Boolean flag indicating whether the named object was found or not. If
# it's false, all other fields will be empty.
'found' : bool,
@@ -438,7 +514,7 @@ Message type: ``object_info_reply``::
# that these must be matched *in reverse* with the 'args'
# list above, since the first positional args have no default
# value at all.
- func_defaults : list,
+ defaults : list,
},
# For instances, provide the constructor signature (the definition of
Something went wrong with that request. Please try again.