Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add IPython.embed_kernel() #1357

Merged
merged 9 commits into from Apr 23, 2012
13 changes: 13 additions & 0 deletions IPython/__init__.py
Expand Up @@ -44,14 +44,27 @@
from .core import release
from .core.application import Application
from .frontend.terminal.embed import embed

from .core.error import TryNext
from .core.interactiveshell import InteractiveShell
from .testing import test
from .utils.sysinfo import sys_info
from .utils.frame import extract_module_locals_above

# Release data
__author__ = ''
for author, email in release.authors.itervalues():
__author__ += author + ' <' + email + '>\n'
__license__ = release.license
__version__ = release.version

def embed_kernel(module=None, local_ns=None):
"""Call this to embed an IPython kernel at the current point in your program. """
(caller_module, caller_locals) = extract_module_locals_above()
if module is None:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this logic to get the calling scope should be inside the embed_kernel function in ipkernel, so that it has exactly the same API if it's imported from there.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @takluyver here.

module = caller_module
if local_ns is None:
local_ns = caller_locals
# Only import .zmq when we really need it
from .zmq.ipkernel import embed_kernel as real_embed_kernel
real_embed_kernel(module, local_ns)
13 changes: 13 additions & 0 deletions IPython/utils/frame.py
Expand Up @@ -85,3 +85,16 @@ def debugx(expr,pre_msg=''):
# deactivate it by uncommenting the following line, which makes it a no-op
#def debugx(expr,pre_msg=''): pass

def extract_module_locals(depth=0):
"""Returns (module, locals) of the funciton `depth` frames away from the caller"""
f = sys._getframe(depth + 1)
global_ns = f.f_globals
module = sys.modules[global_ns['__name__']]
return (module, f.f_locals)

def extract_module_locals_above():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function seems overly specific. Why not just replace its one use with extract_module_locals(1)? I see that there is an extract_vars_above function, but I don't think it's ever actually used.

Maybe I just bristle at the word above 'above'. extract_caller_module_locals, similar to a previous patch of yours, makes more sense to me.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think caller properly conveys what this is doing: that would imply it looks at the frame which calls this function, whereas its looking at the frame above that. above seems less ambiguous.

I agree that the more general function is probably sufficient for the limited use of this, though.

"""Returns (module, locals) of the funciton calling the caller.
Like extract_module_locals() with a specified depth of 1."""
# we're one frame away from the target, the function we call would be two frames away
return extract_module_locals(1 + 1)

10 changes: 10 additions & 0 deletions IPython/zmq/ipkernel.py
Expand Up @@ -70,6 +70,8 @@ class Kernel(Configurable):
iopub_socket = Instance('zmq.Socket')
stdin_socket = Instance('zmq.Socket')
log = Instance(logging.Logger)
user_module = Instance('types.ModuleType')
user_ns = Dict(default_value=None)

# Private interface

Expand Down Expand Up @@ -110,6 +112,8 @@ def __init__(self, **kwargs):
# Initialize the InteractiveShell subclass
self.shell = ZMQInteractiveShell.instance(config=self.config,
profile_dir = self.profile_dir,
user_module = self.user_module,
user_ns = self.user_ns,
)
self.shell.displayhook.session = self.session
self.shell.displayhook.pub_socket = self.iopub_socket
Expand Down Expand Up @@ -585,6 +589,8 @@ def init_kernel(self):
stdin_socket=self.stdin_socket,
log=self.log,
profile_dir=self.profile_dir,
user_module=self.user_module,
user_ns=self.user_ns,
)
self.kernel = kernel
kernel.record_ports(self.ports)
Expand Down Expand Up @@ -639,6 +645,10 @@ def launch_kernel(*args, **kwargs):
return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
*args, **kwargs)

def embed_kernel(module, local_ns):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function needs a docstring. @scottt: our dev guide describes our doc guidelines (which basically amount to pep8 and using the numpy docstring standard).

app = IPKernelApp.instance(user_module=module, user_ns=local_ns)
app.initialize([])
app.start()

def main():
"""Run an IPKernel as an application"""
Expand Down