From 25e21eb9f34d6b08b1450b0ec0076cb247d0482d Mon Sep 17 00:00:00 2001 From: Scott Tsai Date: Tue, 31 Jan 2012 04:08:29 +0800 Subject: [PATCH 1/9] add IPython.embed_kernel() This patch adds IPython.embed_kernel() as a public API. Embedding an IPython kernel in an application is useful when you want to use IPython.embed() but don't have a terminal attached on stdin and stdout. My use case is a modern gdb with Python API support #!/usr/bin/gdb --python import IPython; IPython.embed_kernel() this way I get to use ipython to explore the GDB API without the readline librarry in gdb and ipython fighting over the terminal settings. A Google search revealed that other people were interetsted in this use case as well: http://mail.scipy.org/pipermail/ipython-dev/2011-July/007928.html --- IPython/__init__.py | 4 ++++ IPython/zmq/ipkernel.py | 27 +++++++++++++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/IPython/__init__.py b/IPython/__init__.py index 0252940c746..21d3bcef017 100644 --- a/IPython/__init__.py +++ b/IPython/__init__.py @@ -44,6 +44,10 @@ from .core import release from .core.application import Application from .frontend.terminal.embed import embed +try: + from .zmq.ipkernel import embed_kernel +except ImportError: + pass from .core.error import TryNext from .core.interactiveshell import InteractiveShell from .testing import test diff --git a/IPython/zmq/ipkernel.py b/IPython/zmq/ipkernel.py index edc5c9ac330..85660b6e506 100755 --- a/IPython/zmq/ipkernel.py +++ b/IPython/zmq/ipkernel.py @@ -100,7 +100,7 @@ class Kernel(Configurable): - def __init__(self, **kwargs): + def __init__(self, user_module=None, user_ns=None, **kwargs): super(Kernel, self).__init__(**kwargs) # Before we even start up the shell, register *first* our exit handlers @@ -110,6 +110,8 @@ def __init__(self, **kwargs): # Initialize the InteractiveShell subclass self.shell = ZMQInteractiveShell.instance(config=self.config, profile_dir = self.profile_dir, + user_module=user_module, + user_ns=user_ns, ) self.shell.displayhook.session = self.session self.shell.displayhook.pub_socket = self.iopub_socket @@ -585,6 +587,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) @@ -639,12 +643,27 @@ def launch_kernel(*args, **kwargs): return base_launch_kernel('from IPython.zmq.ipkernel import main; main()', *args, **kwargs) +def caller_module_and_locals(): + """Returns (module, locals) of the caller""" + caller = sys._getframe(1).f_back + global_ns = caller.f_globals + module = sys.modules[global_ns['__name__']] + return (module, caller.f_locals) + +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) = caller_module_and_locals() + if module is None: + module = caller_module + if local_ns is None: + local_ns = caller_locals + app = IPKernelApp.instance(user_module=module, user_ns=local_ns) + app.initialize() + app.start() def main(): """Run an IPKernel as an application""" - app = IPKernelApp.instance() - app.initialize() - app.start() + embed_kernel() if __name__ == '__main__': From 8f53ec7e8a7d872b4c1c47210a77e50059c84247 Mon Sep 17 00:00:00 2001 From: Scott Tsai Date: Tue, 31 Jan 2012 09:17:03 +0800 Subject: [PATCH 2/9] zmq.ipkernel: don't call embed_kernel() in main() --- IPython/zmq/ipkernel.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/IPython/zmq/ipkernel.py b/IPython/zmq/ipkernel.py index 85660b6e506..9626a5cd199 100755 --- a/IPython/zmq/ipkernel.py +++ b/IPython/zmq/ipkernel.py @@ -663,7 +663,9 @@ def embed_kernel(module=None, local_ns=None): def main(): """Run an IPKernel as an application""" - embed_kernel() + app = IPKernelApp.instance() + app.initialize() + app.start() if __name__ == '__main__': From a92b812800a97806c48252b7e8ab842ba953f44f Mon Sep 17 00:00:00 2001 From: Scott Tsai Date: Tue, 31 Jan 2012 09:32:37 +0800 Subject: [PATCH 3/9] zmq.Kernel: turn user_{module,ns} into traitlets --- IPython/zmq/ipkernel.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/IPython/zmq/ipkernel.py b/IPython/zmq/ipkernel.py index 9626a5cd199..4826c26e66d 100755 --- a/IPython/zmq/ipkernel.py +++ b/IPython/zmq/ipkernel.py @@ -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 @@ -100,7 +102,7 @@ class Kernel(Configurable): - def __init__(self, user_module=None, user_ns=None, **kwargs): + def __init__(self, **kwargs): super(Kernel, self).__init__(**kwargs) # Before we even start up the shell, register *first* our exit handlers @@ -110,8 +112,8 @@ def __init__(self, user_module=None, user_ns=None, **kwargs): # Initialize the InteractiveShell subclass self.shell = ZMQInteractiveShell.instance(config=self.config, profile_dir = self.profile_dir, - user_module=user_module, - user_ns=user_ns, + user_module = self.user_module, + user_ns = self.user_ns, ) self.shell.displayhook.session = self.session self.shell.displayhook.pub_socket = self.iopub_socket From a96b676927865808735886b34bd49b8e55fd08b3 Mon Sep 17 00:00:00 2001 From: Scott Tsai Date: Tue, 31 Jan 2012 10:31:02 +0800 Subject: [PATCH 4/9] embed_kernel: give a clear error message on pyzmq ImportError --- IPython/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/IPython/__init__.py b/IPython/__init__.py index 21d3bcef017..66ea1c4aab4 100644 --- a/IPython/__init__.py +++ b/IPython/__init__.py @@ -47,7 +47,9 @@ try: from .zmq.ipkernel import embed_kernel except ImportError: - pass + def embed_kernel(*args, **kwargs): + raise ImportError("IPython.embed_kernel requires pyzmq >= 2.14") + from .core.error import TryNext from .core.interactiveshell import InteractiveShell from .testing import test From a2ec8bdf79857c65d59c16d64975e8b4ffbac987 Mon Sep 17 00:00:00 2001 From: Scott Tsai Date: Tue, 31 Jan 2012 10:48:50 +0800 Subject: [PATCH 5/9] embed_kernel: fix pyzmq version requirement error message --- IPython/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IPython/__init__.py b/IPython/__init__.py index 66ea1c4aab4..a91003b316f 100644 --- a/IPython/__init__.py +++ b/IPython/__init__.py @@ -48,7 +48,7 @@ from .zmq.ipkernel import embed_kernel except ImportError: def embed_kernel(*args, **kwargs): - raise ImportError("IPython.embed_kernel requires pyzmq >= 2.14") + raise ImportError("IPython.embed_kernel requires pyzmq >= 2.1.4") from .core.error import TryNext from .core.interactiveshell import InteractiveShell From 75b852da393422658518a51ed7e619b25fbbd5a8 Mon Sep 17 00:00:00 2001 From: Scott Tsai Date: Tue, 31 Jan 2012 21:49:46 +0800 Subject: [PATCH 6/9] embed_kernel: pass [] to IPKernelApp.initialize to prevent argv parsing https://github.com/ipython/ipython/pull/1357/files#r400153 Stop embed_kernel() from parsing sys.argv since to support being called from a environments with their own command line args. --- IPython/zmq/ipkernel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IPython/zmq/ipkernel.py b/IPython/zmq/ipkernel.py index 4826c26e66d..2927ddeba41 100755 --- a/IPython/zmq/ipkernel.py +++ b/IPython/zmq/ipkernel.py @@ -660,7 +660,7 @@ def embed_kernel(module=None, local_ns=None): if local_ns is None: local_ns = caller_locals app = IPKernelApp.instance(user_module=module, user_ns=local_ns) - app.initialize() + app.initialize([]) app.start() def main(): From 087e9c3b22ed647531e3b18daed4e7ac294d9ff1 Mon Sep 17 00:00:00 2001 From: Scott Tsai Date: Sun, 11 Mar 2012 10:22:36 +0800 Subject: [PATCH 7/9] embed_kernel: only import zmq when needed --- IPython/__init__.py | 23 ++++++++++++++++++----- IPython/zmq/ipkernel.py | 15 +-------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/IPython/__init__.py b/IPython/__init__.py index a91003b316f..3f030e2861d 100644 --- a/IPython/__init__.py +++ b/IPython/__init__.py @@ -44,11 +44,6 @@ from .core import release from .core.application import Application from .frontend.terminal.embed import embed -try: - from .zmq.ipkernel import embed_kernel -except ImportError: - def embed_kernel(*args, **kwargs): - raise ImportError("IPython.embed_kernel requires pyzmq >= 2.1.4") from .core.error import TryNext from .core.interactiveshell import InteractiveShell @@ -61,3 +56,21 @@ def embed_kernel(*args, **kwargs): __author__ += author + ' <' + email + '>\n' __license__ = release.license __version__ = release.version + +def caller_module_and_locals(): + """Returns (module, locals) of the caller""" + caller = sys._getframe(2) + global_ns = caller.f_globals + module = sys.modules[global_ns['__name__']] + return (module, caller.f_locals) + +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) = caller_module_and_locals() + if module is None: + 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) diff --git a/IPython/zmq/ipkernel.py b/IPython/zmq/ipkernel.py index 2927ddeba41..968ca3f95df 100755 --- a/IPython/zmq/ipkernel.py +++ b/IPython/zmq/ipkernel.py @@ -645,20 +645,7 @@ def launch_kernel(*args, **kwargs): return base_launch_kernel('from IPython.zmq.ipkernel import main; main()', *args, **kwargs) -def caller_module_and_locals(): - """Returns (module, locals) of the caller""" - caller = sys._getframe(1).f_back - global_ns = caller.f_globals - module = sys.modules[global_ns['__name__']] - return (module, caller.f_locals) - -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) = caller_module_and_locals() - if module is None: - module = caller_module - if local_ns is None: - local_ns = caller_locals +def embed_kernel(module, local_ns): app = IPKernelApp.instance(user_module=module, user_ns=local_ns) app.initialize([]) app.start() From 030fc4686acf44b5ffe89a596451df0f0e0c91c5 Mon Sep 17 00:00:00 2001 From: Scott Tsai Date: Mon, 12 Mar 2012 19:21:33 +0800 Subject: [PATCH 8/9] Move caller_module_and_locals() to IPython.util.frame --- IPython/__init__.py | 8 +------- IPython/utils/frame.py | 7 +++++++ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/IPython/__init__.py b/IPython/__init__.py index 3f030e2861d..6bf231b6afc 100644 --- a/IPython/__init__.py +++ b/IPython/__init__.py @@ -49,6 +49,7 @@ from .core.interactiveshell import InteractiveShell from .testing import test from .utils.sysinfo import sys_info +from .utils.frame import caller_module_and_locals # Release data __author__ = '' @@ -57,13 +58,6 @@ __license__ = release.license __version__ = release.version -def caller_module_and_locals(): - """Returns (module, locals) of the caller""" - caller = sys._getframe(2) - global_ns = caller.f_globals - module = sys.modules[global_ns['__name__']] - return (module, caller.f_locals) - 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) = caller_module_and_locals() diff --git a/IPython/utils/frame.py b/IPython/utils/frame.py index 2fb61421a50..3b19072a279 100644 --- a/IPython/utils/frame.py +++ b/IPython/utils/frame.py @@ -85,3 +85,10 @@ 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 caller_module_and_locals(): + """Returns (module, locals) of the caller""" + caller = sys._getframe(2) + global_ns = caller.f_globals + module = sys.modules[global_ns['__name__']] + return (module, caller.f_locals) + From a61f44535f6be5d6f844886e0854cb2152fac592 Mon Sep 17 00:00:00 2001 From: Scott Tsai Date: Mon, 12 Mar 2012 23:10:12 +0800 Subject: [PATCH 9/9] Refactor caller_module_locals() into extract_module_locals() --- IPython/__init__.py | 4 ++-- IPython/utils/frame.py | 16 +++++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/IPython/__init__.py b/IPython/__init__.py index 6bf231b6afc..df5434645ea 100644 --- a/IPython/__init__.py +++ b/IPython/__init__.py @@ -49,7 +49,7 @@ from .core.interactiveshell import InteractiveShell from .testing import test from .utils.sysinfo import sys_info -from .utils.frame import caller_module_and_locals +from .utils.frame import extract_module_locals_above # Release data __author__ = '' @@ -60,7 +60,7 @@ 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) = caller_module_and_locals() + (caller_module, caller_locals) = extract_module_locals_above() if module is None: module = caller_module if local_ns is None: diff --git a/IPython/utils/frame.py b/IPython/utils/frame.py index 3b19072a279..cd3563ac22e 100644 --- a/IPython/utils/frame.py +++ b/IPython/utils/frame.py @@ -85,10 +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 caller_module_and_locals(): - """Returns (module, locals) of the caller""" - caller = sys._getframe(2) - global_ns = caller.f_globals +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, caller.f_locals) + return (module, f.f_locals) + +def extract_module_locals_above(): + """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)