##// END OF EJS Templates
move IPython.zmq.entry_point to IPython.utils.kernel
MinRK -
Show More
@@ -1,4 +1,4 b''
1 """Utilities for connecting to kernels
1 """Utilities for working with kernels and their connection files
2
2
3 Authors:
3 Authors:
4
4
@@ -7,7 +7,7 b' Authors:'
7 """
7 """
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2011 The IPython Development Team
10 # Copyright (C) 2013 The IPython Development Team
11 #
11 #
12 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
13 # the file COPYING, distributed as part of this software.
@@ -20,23 +20,104 b' Authors:'
20 import glob
20 import glob
21 import json
21 import json
22 import os
22 import os
23 import socket
23 import sys
24 import sys
24 from getpass import getpass
25 from getpass import getpass
25 from subprocess import Popen, PIPE
26 from subprocess import Popen, PIPE
27 import tempfile
26
28
27 # external imports
29 # external imports
28 from IPython.external.ssh import tunnel
30 from IPython.external.ssh import tunnel
29
31
30 # IPython imports
32 # IPython imports
31 from IPython.core.profiledir import ProfileDir
33 from IPython.core.profiledir import ProfileDir
34 from IPython.utils.localinterfaces import LOCALHOST
32 from IPython.utils.path import filefind, get_ipython_dir
35 from IPython.utils.path import filefind, get_ipython_dir
33 from IPython.utils.py3compat import str_to_bytes
36 from IPython.utils.py3compat import str_to_bytes, bytes_to_str
34
37
35
38
36 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
37 # Functions
40 # Working with Connection Files
38 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
39
42
43 def write_connection_file(fname=None, shell_port=0, iopub_port=0, stdin_port=0, hb_port=0,
44 ip=LOCALHOST, key=b'', transport='tcp'):
45 """Generates a JSON config file, including the selection of random ports.
46
47 Parameters
48 ----------
49
50 fname : unicode
51 The path to the file to write
52
53 shell_port : int, optional
54 The port to use for ROUTER channel.
55
56 iopub_port : int, optional
57 The port to use for the SUB channel.
58
59 stdin_port : int, optional
60 The port to use for the REQ (raw input) channel.
61
62 hb_port : int, optional
63 The port to use for the hearbeat REP channel.
64
65 ip : str, optional
66 The ip address the kernel will bind to.
67
68 key : str, optional
69 The Session key used for HMAC authentication.
70
71 """
72 # default to temporary connector file
73 if not fname:
74 fname = tempfile.mktemp('.json')
75
76 # Find open ports as necessary.
77
78 ports = []
79 ports_needed = int(shell_port <= 0) + int(iopub_port <= 0) + \
80 int(stdin_port <= 0) + int(hb_port <= 0)
81 if transport == 'tcp':
82 for i in range(ports_needed):
83 sock = socket.socket()
84 sock.bind(('', 0))
85 ports.append(sock)
86 for i, sock in enumerate(ports):
87 port = sock.getsockname()[1]
88 sock.close()
89 ports[i] = port
90 else:
91 N = 1
92 for i in range(ports_needed):
93 while os.path.exists("%s-%s" % (ip, str(N))):
94 N += 1
95 ports.append(N)
96 N += 1
97 if shell_port <= 0:
98 shell_port = ports.pop(0)
99 if iopub_port <= 0:
100 iopub_port = ports.pop(0)
101 if stdin_port <= 0:
102 stdin_port = ports.pop(0)
103 if hb_port <= 0:
104 hb_port = ports.pop(0)
105
106 cfg = dict( shell_port=shell_port,
107 iopub_port=iopub_port,
108 stdin_port=stdin_port,
109 hb_port=hb_port,
110 )
111 cfg['ip'] = ip
112 cfg['key'] = bytes_to_str(key)
113 cfg['transport'] = transport
114
115 with open(fname, 'w') as f:
116 f.write(json.dumps(cfg, indent=2))
117
118 return fname, cfg
119
120
40 def get_connection_file(app=None):
121 def get_connection_file(app=None):
41 """Return the path to the connection file of an app
122 """Return the path to the connection file of an app
42
123
@@ -53,6 +134,7 b' def get_connection_file(app=None):'
53 app = IPKernelApp.instance()
134 app = IPKernelApp.instance()
54 return filefind(app.connection_file, ['.', app.profile_dir.security_dir])
135 return filefind(app.connection_file, ['.', app.profile_dir.security_dir])
55
136
137
56 def find_connection_file(filename, profile=None):
138 def find_connection_file(filename, profile=None):
57 """find a connection file, and return its absolute path.
139 """find a connection file, and return its absolute path.
58
140
@@ -122,6 +204,7 b' def find_connection_file(filename, profile=None):'
122 # get most recent match, by access time:
204 # get most recent match, by access time:
123 return sorted(matches, key=lambda f: os.stat(f).st_atime)[-1]
205 return sorted(matches, key=lambda f: os.stat(f).st_atime)[-1]
124
206
207
125 def get_connection_info(connection_file=None, unpack=False, profile=None):
208 def get_connection_info(connection_file=None, unpack=False, profile=None):
126 """Return the connection information for the current Kernel.
209 """Return the connection information for the current Kernel.
127
210
@@ -163,6 +246,7 b' def get_connection_info(connection_file=None, unpack=False, profile=None):'
163 info['key'] = str_to_bytes(info.get('key', ''))
246 info['key'] = str_to_bytes(info.get('key', ''))
164 return info
247 return info
165
248
249
166 def connect_qtconsole(connection_file=None, argv=None, profile=None):
250 def connect_qtconsole(connection_file=None, argv=None, profile=None):
167 """Connect a qtconsole to the current kernel.
251 """Connect a qtconsole to the current kernel.
168
252
@@ -204,6 +288,7 b' def connect_qtconsole(connection_file=None, argv=None, profile=None):'
204
288
205 return Popen([sys.executable, '-c', cmd, '--existing', cf] + argv, stdout=PIPE, stderr=PIPE)
289 return Popen([sys.executable, '-c', cmd, '--existing', cf] + argv, stdout=PIPE, stderr=PIPE)
206
290
291
207 def tunnel_to_kernel(connection_info, sshserver, sshkey=None):
292 def tunnel_to_kernel(connection_info, sshserver, sshkey=None):
208 """tunnel connections to a kernel via ssh
293 """tunnel connections to a kernel via ssh
209
294
@@ -251,7 +336,11 b' def tunnel_to_kernel(connection_info, sshserver, sshkey=None):'
251 tunnel.ssh_tunnel(lp, rp, sshserver, remote_ip, sshkey, password)
336 tunnel.ssh_tunnel(lp, rp, sshserver, remote_ip, sshkey, password)
252
337
253 return tuple(lports)
338 return tuple(lports)
254
339
340
341 #-----------------------------------------------------------------------------
342 # Launching Kernels
343 #-----------------------------------------------------------------------------
255
344
256 def swallow_argv(argv, aliases=None, flags=None):
345 def swallow_argv(argv, aliases=None, flags=None):
257 """strip frontend-specific aliases and flags from an argument list
346 """strip frontend-specific aliases and flags from an argument list
@@ -313,3 +402,168 b' def swallow_argv(argv, aliases=None, flags=None):'
313 # return shortened list
402 # return shortened list
314 return stripped
403 return stripped
315
404
405
406 def make_ipkernel_cmd(code, executable=None, extra_arguments=[], **kw):
407 """Build Popen command list for launching an IPython kernel.
408
409 Parameters
410 ----------
411 code : str,
412 A string of Python code that imports and executes a kernel entry point.
413
414 executable : str, optional (default sys.executable)
415 The Python executable to use for the kernel process.
416
417 extra_arguments : list, optional
418 A list of extra arguments to pass when executing the launch code.
419
420 Returns
421 -------
422
423 A Popen command list
424 """
425
426 # Build the kernel launch command.
427 if executable is None:
428 executable = sys.executable
429 arguments = [ executable, '-c', code, '-f', '{connection_file}' ]
430 arguments.extend(extra_arguments)
431
432 # Spawn a kernel.
433 if sys.platform == 'win32':
434
435 # If the kernel is running on pythonw and stdout/stderr are not been
436 # re-directed, it will crash when more than 4KB of data is written to
437 # stdout or stderr. This is a bug that has been with Python for a very
438 # long time; see http://bugs.python.org/issue706263.
439 # A cleaner solution to this problem would be to pass os.devnull to
440 # Popen directly. Unfortunately, that does not work.
441 if executable.endswith('pythonw.exe'):
442 arguments.append('--no-stdout')
443 arguments.append('--no-stderr')
444
445 return arguments
446
447
448 def launch_kernel(cmd, stdin=None, stdout=None, stderr=None,
449 independent=False,
450 cwd=None, ipython_kernel=True,
451 **kw
452 ):
453 """ Launches a localhost kernel, binding to the specified ports.
454
455 Parameters
456 ----------
457 cmd : Popen list,
458 A string of Python code that imports and executes a kernel entry point.
459
460 stdin, stdout, stderr : optional (default None)
461 Standards streams, as defined in subprocess.Popen.
462
463 independent : bool, optional (default False)
464 If set, the kernel process is guaranteed to survive if this process
465 dies. If not set, an effort is made to ensure that the kernel is killed
466 when this process dies. Note that in this case it is still good practice
467 to kill kernels manually before exiting.
468
469 cwd : path, optional
470 The working dir of the kernel process (default: cwd of this process).
471
472 ipython_kernel : bool, optional
473 Whether the kernel is an official IPython one,
474 and should get a bit of special treatment.
475
476 Returns
477 -------
478
479 Popen instance for the kernel subprocess
480 """
481
482 # Popen will fail (sometimes with a deadlock) if stdin, stdout, and stderr
483 # are invalid. Unfortunately, there is in general no way to detect whether
484 # they are valid. The following two blocks redirect them to (temporary)
485 # pipes in certain important cases.
486
487 # If this process has been backgrounded, our stdin is invalid. Since there
488 # is no compelling reason for the kernel to inherit our stdin anyway, we'll
489 # place this one safe and always redirect.
490 redirect_in = True
491 _stdin = PIPE if stdin is None else stdin
492
493 # If this process in running on pythonw, we know that stdin, stdout, and
494 # stderr are all invalid.
495 redirect_out = sys.executable.endswith('pythonw.exe')
496 if redirect_out:
497 _stdout = PIPE if stdout is None else stdout
498 _stderr = PIPE if stderr is None else stderr
499 else:
500 _stdout, _stderr = stdout, stderr
501
502 # Spawn a kernel.
503 if sys.platform == 'win32':
504 from IPython.zmq.parentpoller import ParentPollerWindows
505 # Create a Win32 event for interrupting the kernel.
506 interrupt_event = ParentPollerWindows.create_interrupt_event()
507 if ipython_kernel:
508 cmd += [ '--interrupt=%i' % interrupt_event ]
509
510 # If the kernel is running on pythonw and stdout/stderr are not been
511 # re-directed, it will crash when more than 4KB of data is written to
512 # stdout or stderr. This is a bug that has been with Python for a very
513 # long time; see http://bugs.python.org/issue706263.
514 # A cleaner solution to this problem would be to pass os.devnull to
515 # Popen directly. Unfortunately, that does not work.
516 if cmd[0].endswith('pythonw.exe'):
517 if stdout is None:
518 cmd.append('--no-stdout')
519 if stderr is None:
520 cmd.append('--no-stderr')
521
522 # Launch the kernel process.
523 if independent:
524 proc = Popen(cmd,
525 creationflags=512, # CREATE_NEW_PROCESS_GROUP
526 stdin=_stdin, stdout=_stdout, stderr=_stderr)
527 else:
528 if ipython_kernel:
529 try:
530 from _winapi import DuplicateHandle, GetCurrentProcess, \
531 DUPLICATE_SAME_ACCESS
532 except:
533 from _subprocess import DuplicateHandle, GetCurrentProcess, \
534 DUPLICATE_SAME_ACCESS
535 pid = GetCurrentProcess()
536 handle = DuplicateHandle(pid, pid, pid, 0,
537 True, # Inheritable by new processes.
538 DUPLICATE_SAME_ACCESS)
539 cmd +=[ '--parent=%i' % handle ]
540
541
542 proc = Popen(cmd,
543 stdin=_stdin, stdout=_stdout, stderr=_stderr, cwd=cwd)
544
545 # Attach the interrupt event to the Popen objet so it can be used later.
546 proc.win32_interrupt_event = interrupt_event
547
548 else:
549 if independent:
550 proc = Popen(cmd, preexec_fn=lambda: os.setsid(),
551 stdin=_stdin, stdout=_stdout, stderr=_stderr, cwd=cwd)
552 else:
553 if ipython_kernel:
554 cmd += ['--parent=1']
555 proc = Popen(cmd,
556 stdin=_stdin, stdout=_stdout, stderr=_stderr, cwd=cwd)
557
558 # Clean up pipes created to work around Popen bug.
559 if redirect_in:
560 if stdin is None:
561 proc.stdin.close()
562 if redirect_out:
563 if stdout is None:
564 proc.stdout.close()
565 if stderr is None:
566 proc.stderr.close()
567
568 return proc
569
@@ -40,8 +40,8 b' from IPython.utils.traitlets import ('
40 DottedObjectName,
40 DottedObjectName,
41 )
41 )
42 from IPython.utils.importstring import import_item
42 from IPython.utils.importstring import import_item
43 from IPython.utils.kernel import write_connection_file
43 # local imports
44 # local imports
44 from IPython.zmq.entry_point import write_connection_file
45 from IPython.zmq.heartbeat import Heartbeat
45 from IPython.zmq.heartbeat import Heartbeat
46 from IPython.zmq.parentpoller import ParentPollerUnix, ParentPollerWindows
46 from IPython.zmq.parentpoller import ParentPollerUnix, ParentPollerWindows
47 from IPython.zmq.session import (
47 from IPython.zmq.session import (
@@ -40,7 +40,7 b' from IPython.utils.traitlets import ('
40 Any, Instance, Type, Unicode, List, Integer, Bool, CaselessStrEnum
40 Any, Instance, Type, Unicode, List, Integer, Bool, CaselessStrEnum
41 )
41 )
42 from IPython.utils.py3compat import str_to_bytes
42 from IPython.utils.py3compat import str_to_bytes
43 from IPython.zmq.entry_point import (
43 from IPython.utils.kernel import (
44 write_connection_file,
44 write_connection_file,
45 make_ipkernel_cmd,
45 make_ipkernel_cmd,
46 launch_kernel,
46 launch_kernel,
@@ -925,14 +925,9 b' class KernelManager(Configurable):'
925
925
926 Parameters:
926 Parameters:
927 -----------
927 -----------
928 launcher : callable, optional (default None)
929 A custom function for launching the kernel process (generally a
930 wrapper around ``entry_point.base_launch_kernel``). In most cases,
931 it should not be necessary to use this parameter.
932
933 **kw : optional
928 **kw : optional
934 keyword arguments that are passed down into the launcher
929 keyword arguments that are passed down to build the kernel_cmd
935 callable.
930 and launching the kernel (e.g. Popen kwargs).
936 """
931 """
937 if self.transport == 'tcp' and self.ip not in LOCAL_IPS:
932 if self.transport == 'tcp' and self.ip not in LOCAL_IPS:
938 raise RuntimeError("Can only launch a kernel on a local interface. "
933 raise RuntimeError("Can only launch a kernel on a local interface. "
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now