entry_point.py
199 lines
| 6.8 KiB
| text/x-python
|
PythonLexer
epatters
|
r2778 | """ Defines helper functions for creating kernel entry points and process | ||
launchers. | ||||
""" | ||||
# Standard library imports. | ||||
Brian Granger
|
r2873 | import os | ||
epatters
|
r2778 | import socket | ||
from subprocess import Popen | ||||
import sys | ||||
# System library imports. | ||||
import zmq | ||||
# Local imports. | ||||
Fernando Perez
|
r2853 | from IPython.core.ultratb import FormattedTB | ||
epatters
|
r2778 | from IPython.external.argparse import ArgumentParser | ||
Fernando Perez
|
r2853 | from IPython.utils import io | ||
epatters
|
r2778 | from exitpoller import ExitPollerUnix, ExitPollerWindows | ||
from displayhook import DisplayHook | ||||
from iostream import OutStream | ||||
from session import Session | ||||
def bind_port(socket, ip, port): | ||||
""" Binds the specified ZMQ socket. If the port is zero, a random port is | ||||
chosen. Returns the port that was bound. | ||||
""" | ||||
connection = 'tcp://%s' % ip | ||||
if port <= 0: | ||||
port = socket.bind_to_random_port(connection) | ||||
else: | ||||
connection += ':%i' % port | ||||
socket.bind(connection) | ||||
return port | ||||
def make_argument_parser(): | ||||
""" Creates an ArgumentParser for the generic arguments supported by all | ||||
kernel entry points. | ||||
""" | ||||
parser = ArgumentParser() | ||||
parser.add_argument('--ip', type=str, default='127.0.0.1', | ||||
help='set the kernel\'s IP address [default: local]') | ||||
parser.add_argument('--xrep', type=int, metavar='PORT', default=0, | ||||
help='set the XREP channel port [default: random]') | ||||
parser.add_argument('--pub', type=int, metavar='PORT', default=0, | ||||
help='set the PUB channel port [default: random]') | ||||
parser.add_argument('--req', type=int, metavar='PORT', default=0, | ||||
help='set the REQ channel port [default: random]') | ||||
if sys.platform == 'win32': | ||||
parser.add_argument('--parent', type=int, metavar='HANDLE', | ||||
default=0, help='kill this process if the process ' | ||||
'with HANDLE dies') | ||||
else: | ||||
parser.add_argument('--parent', action='store_true', | ||||
help='kill this process if its parent dies') | ||||
return parser | ||||
epatters
|
r2795 | def make_kernel(namespace, kernel_factory, | ||
out_stream_factory=None, display_hook_factory=None): | ||||
epatters
|
r2778 | """ Creates a kernel. | ||
""" | ||||
Fernando Perez
|
r2853 | # Install minimal exception handling | ||
epatters
|
r2888 | sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor', | ||
ostream=sys.__stdout__) | ||||
Fernando Perez
|
r2853 | |||
epatters
|
r2778 | # Create a context, a session, and the kernel sockets. | ||
Fernando Perez
|
r2875 | io.raw_print("Starting the kernel...") | ||
epatters
|
r2778 | context = zmq.Context() | ||
session = Session(username=u'kernel') | ||||
reply_socket = context.socket(zmq.XREP) | ||||
xrep_port = bind_port(reply_socket, namespace.ip, namespace.xrep) | ||||
Fernando Perez
|
r2875 | io.raw_print("XREP Channel on port", xrep_port) | ||
epatters
|
r2778 | |||
pub_socket = context.socket(zmq.PUB) | ||||
pub_port = bind_port(pub_socket, namespace.ip, namespace.pub) | ||||
Fernando Perez
|
r2875 | io.raw_print("PUB Channel on port", pub_port) | ||
epatters
|
r2778 | |||
req_socket = context.socket(zmq.XREQ) | ||||
req_port = bind_port(req_socket, namespace.ip, namespace.req) | ||||
Fernando Perez
|
r2875 | io.raw_print("REQ Channel on port", req_port) | ||
epatters
|
r2778 | |||
# Redirect input streams and set a display hook. | ||||
epatters
|
r2795 | if out_stream_factory: | ||
Brian Granger
|
r2873 | pass | ||
epatters
|
r2795 | sys.stdout = out_stream_factory(session, pub_socket, u'stdout') | ||
sys.stderr = out_stream_factory(session, pub_socket, u'stderr') | ||||
if display_hook_factory: | ||||
sys.displayhook = display_hook_factory(session, pub_socket) | ||||
epatters
|
r2778 | |||
# Create the kernel. | ||||
return kernel_factory(session=session, reply_socket=reply_socket, | ||||
pub_socket=pub_socket, req_socket=req_socket) | ||||
def start_kernel(namespace, kernel): | ||||
""" Starts a kernel. | ||||
""" | ||||
# Configure this kernel/process to die on parent termination, if necessary. | ||||
if namespace.parent: | ||||
if sys.platform == 'win32': | ||||
poller = ExitPollerWindows(namespace.parent) | ||||
else: | ||||
poller = ExitPollerUnix() | ||||
poller.start() | ||||
# Start the kernel mainloop. | ||||
kernel.start() | ||||
def make_default_main(kernel_factory): | ||||
""" Creates the simplest possible kernel entry point. | ||||
""" | ||||
def main(): | ||||
namespace = make_argument_parser().parse_args() | ||||
epatters
|
r2795 | kernel = make_kernel(namespace, kernel_factory, OutStream, DisplayHook) | ||
epatters
|
r2778 | start_kernel(namespace, kernel) | ||
return main | ||||
def base_launch_kernel(code, xrep_port=0, pub_port=0, req_port=0, | ||||
independent=False, extra_arguments=[]): | ||||
""" Launches a localhost kernel, binding to the specified ports. | ||||
Parameters | ||||
---------- | ||||
code : str, | ||||
A string of Python code that imports and executes a kernel entry point. | ||||
xrep_port : int, optional | ||||
The port to use for XREP channel. | ||||
pub_port : int, optional | ||||
The port to use for the SUB channel. | ||||
req_port : int, optional | ||||
The port to use for the REQ (raw input) channel. | ||||
independent : bool, optional (default False) | ||||
If set, the kernel process is guaranteed to survive if this process | ||||
dies. If not set, an effort is made to ensure that the kernel is killed | ||||
when this process dies. Note that in this case it is still good practice | ||||
to kill kernels manually before exiting. | ||||
extra_arguments = list, optional | ||||
A list of extra arguments to pass when executing the launch code. | ||||
Returns | ||||
------- | ||||
A tuple of form: | ||||
(kernel_process, xrep_port, pub_port, req_port) | ||||
where kernel_process is a Popen object and the ports are integers. | ||||
""" | ||||
# Find open ports as necessary. | ||||
ports = [] | ||||
ports_needed = int(xrep_port <= 0) + int(pub_port <= 0) + int(req_port <= 0) | ||||
for i in xrange(ports_needed): | ||||
sock = socket.socket() | ||||
sock.bind(('', 0)) | ||||
ports.append(sock) | ||||
for i, sock in enumerate(ports): | ||||
port = sock.getsockname()[1] | ||||
sock.close() | ||||
ports[i] = port | ||||
if xrep_port <= 0: | ||||
xrep_port = ports.pop(0) | ||||
if pub_port <= 0: | ||||
pub_port = ports.pop(0) | ||||
if req_port <= 0: | ||||
req_port = ports.pop(0) | ||||
# Build the kernel launch command. | ||||
arguments = [ sys.executable, '-c', code, '--xrep', str(xrep_port), | ||||
'--pub', str(pub_port), '--req', str(req_port) ] | ||||
arguments.extend(extra_arguments) | ||||
# Spawn a kernel. | ||||
if independent: | ||||
if sys.platform == 'win32': | ||||
proc = Popen(['start', '/b'] + arguments, shell=True) | ||||
else: | ||||
proc = Popen(arguments, preexec_fn=lambda: os.setsid()) | ||||
else: | ||||
if sys.platform == 'win32': | ||||
from _subprocess import DuplicateHandle, GetCurrentProcess, \ | ||||
DUPLICATE_SAME_ACCESS | ||||
pid = GetCurrentProcess() | ||||
handle = DuplicateHandle(pid, pid, pid, 0, | ||||
True, # Inheritable by new processes. | ||||
DUPLICATE_SAME_ACCESS) | ||||
proc = Popen(arguments + ['--parent', str(int(handle))]) | ||||
else: | ||||
proc = Popen(arguments + ['--parent']) | ||||
return proc, xrep_port, pub_port, req_port | ||||