##// END OF EJS Templates
Merge pull request #1640 from minrk/embedkernel...
Fernando Perez -
r6626:14dcd993 merge
parent child Browse files
Show More
@@ -0,0 +1,153 b''
1 """test IPython.embed_kernel()"""
2
3 #-------------------------------------------------------------------------------
4 # Copyright (C) 2012 The IPython Development Team
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
14 import os
15 import shutil
16 import sys
17 import tempfile
18 import time
19
20 from subprocess import Popen, PIPE
21
22 import nose.tools as nt
23
24 from IPython.zmq.blockingkernelmanager import BlockingKernelManager
25 from IPython.utils import path
26
27
28 #-------------------------------------------------------------------------------
29 # Tests
30 #-------------------------------------------------------------------------------
31
32 def setup():
33 """setup temporary IPYTHONDIR for tests"""
34 global IPYTHONDIR
35 global env
36 global save_get_ipython_dir
37
38 IPYTHONDIR = tempfile.mkdtemp()
39 env = dict(IPYTHONDIR=IPYTHONDIR)
40 save_get_ipython_dir = path.get_ipython_dir
41 path.get_ipython_dir = lambda : IPYTHONDIR
42
43
44 def teardown():
45 path.get_ipython_dir = save_get_ipython_dir
46
47 try:
48 shutil.rmtree(IPYTHONDIR)
49 except (OSError, IOError):
50 # no such file
51 pass
52
53
54 def _launch_kernel(cmd):
55 """start an embedded kernel in a subprocess, and wait for it to be ready
56
57 Returns
58 -------
59 kernel, kernel_manager: Popen instance and connected KernelManager
60 """
61 kernel = Popen([sys.executable, '-c', cmd], stdout=PIPE, stderr=PIPE, env=env)
62 connection_file = os.path.join(IPYTHONDIR,
63 'profile_default',
64 'security',
65 'kernel-%i.json' % kernel.pid
66 )
67 # wait for connection file to exist, timeout after 5s
68 tic = time.time()
69 while not os.path.exists(connection_file) and kernel.poll() is None and time.time() < tic + 5:
70 time.sleep(0.1)
71
72 if not os.path.exists(connection_file):
73 if kernel.poll() is None:
74 kernel.terminate()
75 raise IOError("Connection file %r never arrived" % connection_file)
76
77 if kernel.poll() is not None:
78 raise IOError("Kernel failed to start")
79
80 km = BlockingKernelManager(connection_file=connection_file)
81 km.load_connection_file()
82 km.start_channels()
83
84 return kernel, km
85
86 def test_embed_kernel_basic():
87 """IPython.embed_kernel() is basically functional"""
88 cmd = '\n'.join([
89 'from IPython import embed_kernel',
90 'def go():',
91 ' a=5',
92 ' b="hi there"',
93 ' embed_kernel()',
94 'go()',
95 '',
96 ])
97
98 kernel, km = _launch_kernel(cmd)
99 shell = km.shell_channel
100
101 # oinfo a (int)
102 msg_id = shell.object_info('a')
103 msg = shell.get_msg(block=True, timeout=2)
104 content = msg['content']
105 nt.assert_true(content['found'])
106
107 msg_id = shell.execute("c=a*2")
108 msg = shell.get_msg(block=True, timeout=2)
109 content = msg['content']
110 nt.assert_equals(content['status'], u'ok')
111
112 # oinfo c (should be 10)
113 msg_id = shell.object_info('c')
114 msg = shell.get_msg(block=True, timeout=2)
115 content = msg['content']
116 nt.assert_true(content['found'])
117 nt.assert_equals(content['string_form'], u'10')
118
119 def test_embed_kernel_namespace():
120 """IPython.embed_kernel() inherits calling namespace"""
121 cmd = '\n'.join([
122 'from IPython import embed_kernel',
123 'def go():',
124 ' a=5',
125 ' b="hi there"',
126 ' embed_kernel()',
127 'go()',
128 '',
129 ])
130
131 kernel, km = _launch_kernel(cmd)
132 shell = km.shell_channel
133
134 # oinfo a (int)
135 msg_id = shell.object_info('a')
136 msg = shell.get_msg(block=True, timeout=2)
137 content = msg['content']
138 nt.assert_true(content['found'])
139 nt.assert_equals(content['string_form'], u'5')
140
141 # oinfo b (str)
142 msg_id = shell.object_info('b')
143 msg = shell.get_msg(block=True, timeout=2)
144 content = msg['content']
145 nt.assert_true(content['found'])
146 nt.assert_equals(content['string_form'], u'hi there')
147
148 # oinfo c (undefined)
149 msg_id = shell.object_info('c')
150 msg = shell.get_msg(block=True, timeout=2)
151 content = msg['content']
152 nt.assert_false(content['found'])
153
@@ -44,10 +44,12 b' from .config.loader import Config'
44 44 from .core import release
45 45 from .core.application import Application
46 46 from .frontend.terminal.embed import embed
47
47 48 from .core.error import TryNext
48 49 from .core.interactiveshell import InteractiveShell
49 50 from .testing import test
50 51 from .utils.sysinfo import sys_info
52 from .utils.frame import extract_module_locals
51 53
52 54 # Release data
53 55 __author__ = ''
@@ -55,3 +57,30 b' for author, email in release.authors.itervalues():'
55 57 __author__ += author + ' <' + email + '>\n'
56 58 __license__ = release.license
57 59 __version__ = release.version
60
61 def embed_kernel(module=None, local_ns=None, **kwargs):
62 """Embed and start an IPython kernel in a given scope.
63
64 Parameters
65 ----------
66 module : ModuleType, optional
67 The module to load into IPython globals (default: caller)
68 local_ns : dict, optional
69 The namespace to load into IPython user namespace (default: caller)
70
71 kwargs : various, optional
72 Further keyword args are relayed to the KernelApp constructor,
73 allowing configuration of the Kernel. Will only have an effect
74 on the first embed_kernel call for a given process.
75
76 """
77
78 (caller_module, caller_locals) = extract_module_locals(1)
79 if module is None:
80 module = caller_module
81 if local_ns is None:
82 local_ns = caller_locals
83
84 # Only import .zmq when we really need it
85 from .zmq.ipkernel import embed_kernel as real_embed_kernel
86 real_embed_kernel(module=module, local_ns=local_ns, **kwargs)
@@ -85,3 +85,10 b" def debugx(expr,pre_msg=''):"
85 85 # deactivate it by uncommenting the following line, which makes it a no-op
86 86 #def debugx(expr,pre_msg=''): pass
87 87
88 def extract_module_locals(depth=0):
89 """Returns (module, locals) of the funciton `depth` frames away from the caller"""
90 f = sys._getframe(depth + 1)
91 global_ns = f.f_globals
92 module = sys.modules[global_ns['__name__']]
93 return (module, f.f_locals)
94
@@ -39,6 +39,7 b' from IPython.core.shellapp import ('
39 39 )
40 40 from IPython.utils import io
41 41 from IPython.utils import py3compat
42 from IPython.utils.frame import extract_module_locals
42 43 from IPython.utils.jsonutil import json_clean
43 44 from IPython.utils.traitlets import (
44 45 Any, Instance, Float, Dict, CaselessStrEnum
@@ -70,6 +71,17 b' class Kernel(Configurable):'
70 71 iopub_socket = Instance('zmq.Socket')
71 72 stdin_socket = Instance('zmq.Socket')
72 73 log = Instance(logging.Logger)
74
75 user_module = Instance('types.ModuleType')
76 def _user_module_changed(self, name, old, new):
77 if self.shell is not None:
78 self.shell.user_module = new
79
80 user_ns = Dict(default_value=None)
81 def _user_ns_changed(self, name, old, new):
82 if self.shell is not None:
83 self.shell.user_ns = new
84 self.shell.init_user_ns()
73 85
74 86 # Private interface
75 87
@@ -110,6 +122,8 b' class Kernel(Configurable):'
110 122 # Initialize the InteractiveShell subclass
111 123 self.shell = ZMQInteractiveShell.instance(config=self.config,
112 124 profile_dir = self.profile_dir,
125 user_module = self.user_module,
126 user_ns = self.user_ns,
113 127 )
114 128 self.shell.displayhook.session = self.session
115 129 self.shell.displayhook.pub_socket = self.iopub_socket
@@ -571,6 +585,7 b' class IPKernelApp(KernelApp, InteractiveShellApp):'
571 585 aliases = Dict(aliases)
572 586 flags = Dict(flags)
573 587 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
588
574 589 # configurables
575 590 pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'inline', 'auto'],
576 591 config=True,
@@ -649,6 +664,40 b' def launch_kernel(*args, **kwargs):'
649 664 *args, **kwargs)
650 665
651 666
667 def embed_kernel(module=None, local_ns=None, **kwargs):
668 """Embed and start an IPython kernel in a given scope.
669
670 Parameters
671 ----------
672 module : ModuleType, optional
673 The module to load into IPython globals (default: caller)
674 local_ns : dict, optional
675 The namespace to load into IPython user namespace (default: caller)
676
677 kwargs : various, optional
678 Further keyword args are relayed to the KernelApp constructor,
679 allowing configuration of the Kernel. Will only have an effect
680 on the first embed_kernel call for a given process.
681
682 """
683 # get the app if it exists, or set it up if it doesn't
684 if IPKernelApp.initialized():
685 app = IPKernelApp.instance()
686 else:
687 app = IPKernelApp.instance(**kwargs)
688 app.initialize([])
689
690 # load the calling scope if not given
691 (caller_module, caller_locals) = extract_module_locals(1)
692 if module is None:
693 module = caller_module
694 if local_ns is None:
695 local_ns = caller_locals
696
697 app.kernel.user_module = module
698 app.kernel.user_ns = local_ns
699 app.start()
700
652 701 def main():
653 702 """Run an IPKernel as an application"""
654 703 app = IPKernelApp.instance()
@@ -669,6 +669,14 b' your Python programs for this to work (detailed examples follow later)::'
669 669
670 670 embed() # this call anywhere in your program will start IPython
671 671
672 .. note::
673
674 As of 0.13, you can embed an IPython *kernel*, for use with qtconsole,
675 etc. via ``IPython.embed_kernel()`` instead of ``IPython.embed()``.
676 It should function just the same as regular embed, but you connect
677 an external frontend rather than IPython starting up in the local
678 terminal.
679
672 680 You can run embedded instances even in code which is itself being run at
673 681 the IPython interactive prompt with '%run <filename>'. Since it's easy
674 682 to get lost as to where you are (in your top-level IPython or in your
General Comments 0
You need to be logged in to leave comments. Login now