##// 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 from .core import release
44 from .core import release
45 from .core.application import Application
45 from .core.application import Application
46 from .frontend.terminal.embed import embed
46 from .frontend.terminal.embed import embed
47
47 from .core.error import TryNext
48 from .core.error import TryNext
48 from .core.interactiveshell import InteractiveShell
49 from .core.interactiveshell import InteractiveShell
49 from .testing import test
50 from .testing import test
50 from .utils.sysinfo import sys_info
51 from .utils.sysinfo import sys_info
52 from .utils.frame import extract_module_locals
51
53
52 # Release data
54 # Release data
53 __author__ = ''
55 __author__ = ''
@@ -55,3 +57,30 b' for author, email in release.authors.itervalues():'
55 __author__ += author + ' <' + email + '>\n'
57 __author__ += author + ' <' + email + '>\n'
56 __license__ = release.license
58 __license__ = release.license
57 __version__ = release.version
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 # deactivate it by uncommenting the following line, which makes it a no-op
85 # deactivate it by uncommenting the following line, which makes it a no-op
86 #def debugx(expr,pre_msg=''): pass
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 from IPython.utils import io
40 from IPython.utils import io
41 from IPython.utils import py3compat
41 from IPython.utils import py3compat
42 from IPython.utils.frame import extract_module_locals
42 from IPython.utils.jsonutil import json_clean
43 from IPython.utils.jsonutil import json_clean
43 from IPython.utils.traitlets import (
44 from IPython.utils.traitlets import (
44 Any, Instance, Float, Dict, CaselessStrEnum
45 Any, Instance, Float, Dict, CaselessStrEnum
@@ -71,6 +72,17 b' class Kernel(Configurable):'
71 stdin_socket = Instance('zmq.Socket')
72 stdin_socket = Instance('zmq.Socket')
72 log = Instance(logging.Logger)
73 log = Instance(logging.Logger)
73
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()
85
74 # Private interface
86 # Private interface
75
87
76 # Time to sleep after flushing the stdout/err buffers in each execute
88 # Time to sleep after flushing the stdout/err buffers in each execute
@@ -110,6 +122,8 b' class Kernel(Configurable):'
110 # Initialize the InteractiveShell subclass
122 # Initialize the InteractiveShell subclass
111 self.shell = ZMQInteractiveShell.instance(config=self.config,
123 self.shell = ZMQInteractiveShell.instance(config=self.config,
112 profile_dir = self.profile_dir,
124 profile_dir = self.profile_dir,
125 user_module = self.user_module,
126 user_ns = self.user_ns,
113 )
127 )
114 self.shell.displayhook.session = self.session
128 self.shell.displayhook.session = self.session
115 self.shell.displayhook.pub_socket = self.iopub_socket
129 self.shell.displayhook.pub_socket = self.iopub_socket
@@ -571,6 +585,7 b' class IPKernelApp(KernelApp, InteractiveShellApp):'
571 aliases = Dict(aliases)
585 aliases = Dict(aliases)
572 flags = Dict(flags)
586 flags = Dict(flags)
573 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
587 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
588
574 # configurables
589 # configurables
575 pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'inline', 'auto'],
590 pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'inline', 'auto'],
576 config=True,
591 config=True,
@@ -649,6 +664,40 b' def launch_kernel(*args, **kwargs):'
649 *args, **kwargs)
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 def main():
701 def main():
653 """Run an IPKernel as an application"""
702 """Run an IPKernel as an application"""
654 app = IPKernelApp.instance()
703 app = IPKernelApp.instance()
@@ -669,6 +669,14 b' your Python programs for this to work (detailed examples follow later)::'
669
669
670 embed() # this call anywhere in your program will start IPython
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 You can run embedded instances even in code which is itself being run at
680 You can run embedded instances even in code which is itself being run at
673 the IPython interactive prompt with '%run <filename>'. Since it's easy
681 the IPython interactive prompt with '%run <filename>'. Since it's easy
674 to get lost as to where you are (in your top-level IPython or in your
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