From 9c06d407bd082c35e6941817dfd901b786d1ff70 2014-12-05 20:42:20 From: Thomas Kluyver Date: 2014-12-05 20:42:20 Subject: [PATCH] Merge pull request #7099 from minrk/parent-env remove special handling of ipython_kernel in launcher --- diff --git a/IPython/kernel/launcher.py b/IPython/kernel/launcher.py index 171fbaa..6f60e52 100644 --- a/IPython/kernel/launcher.py +++ b/IPython/kernel/launcher.py @@ -101,25 +101,13 @@ def make_ipkernel_cmd(mod='IPython.kernel', executable=None, extra_arguments=[], executable = sys.executable arguments = [ executable, '-m', mod, '-f', '{connection_file}' ] arguments.extend(extra_arguments) - - if sys.platform == 'win32': - - # If the kernel is running on pythonw and stdout/stderr are not been - # re-directed, it will crash when more than 4KB of data is written to - # stdout or stderr. This is a bug that has been with Python for a very - # long time; see http://bugs.python.org/issue706263. - # A cleaner solution to this problem would be to pass os.devnull to - # Popen directly. Unfortunately, that does not work. - if executable.endswith('pythonw.exe'): - arguments.append('--no-stdout') - arguments.append('--no-stderr') - + return arguments def launch_kernel(cmd, stdin=None, stdout=None, stderr=None, env=None, independent=False, - cwd=None, ipython_kernel=True, + cwd=None, **kw ): """ Launches a localhost kernel, binding to the specified ports. @@ -141,10 +129,6 @@ def launch_kernel(cmd, stdin=None, stdout=None, stderr=None, env=None, cwd : path, optional The working dir of the kernel process (default: cwd of this process). - ipython_kernel : bool, optional - Whether the kernel is an official IPython one, - and should get a bit of special treatment. - Returns ------- @@ -166,14 +150,22 @@ def launch_kernel(cmd, stdin=None, stdout=None, stderr=None, env=None, # stderr are all invalid. redirect_out = sys.executable.endswith('pythonw.exe') if redirect_out: - _stdout = PIPE if stdout is None else stdout - _stderr = PIPE if stderr is None else stderr + blackhole = open(os.devnull, 'w') + _stdout = blackhole if stdout is None else stdout + _stderr = blackhole if stderr is None else stderr else: _stdout, _stderr = stdout, stderr env = env if (env is not None) else os.environ.copy() encoding = getdefaultencoding(prefer_stream=False) + kwargs = dict( + stdin=_stdin, + stdout=_stdout, + stderr=_stderr, + cwd=cwd, + env=env, + ) # Spawn a kernel. if sys.platform == 'win32': @@ -181,73 +173,49 @@ def launch_kernel(cmd, stdin=None, stdout=None, stderr=None, env=None, cmd = [ cast_bytes_py2(c, encoding) for c in cmd ] if cwd: cwd = cast_bytes_py2(cwd, sys.getfilesystemencoding() or 'ascii') + kwargs['cwd'] = cwd from IPython.kernel.zmq.parentpoller import ParentPollerWindows - # Create a Win32 event for interrupting the kernel. + # Create a Win32 event for interrupting the kernel + # and store it in an environment variable. interrupt_event = ParentPollerWindows.create_interrupt_event() - # Store this in an environment variable for third party kernels, but at - # present, our own kernel expects this as a command line argument. - env["IPY_INTERRUPT_EVENT"] = str(interrupt_event) - if ipython_kernel: - cmd += [ '--interrupt=%i' % interrupt_event ] - - # If the kernel is running on pythonw and stdout/stderr are not been - # re-directed, it will crash when more than 4KB of data is written to - # stdout or stderr. This is a bug that has been with Python for a very - # long time; see http://bugs.python.org/issue706263. - # A cleaner solution to this problem would be to pass os.devnull to - # Popen directly. Unfortunately, that does not work. - if cmd[0].endswith('pythonw.exe'): - if stdout is None: - cmd.append('--no-stdout') - if stderr is None: - cmd.append('--no-stderr') - - # Launch the kernel process. + env["JPY_INTERRUPT_EVENT"] = str(interrupt_event) + # deprecated old env name: + env["IPY_INTERRUPT_EVENT"] = env["JPY_INTERRUPT_EVENT"] + + try: + from _winapi import DuplicateHandle, GetCurrentProcess, \ + DUPLICATE_SAME_ACCESS, CREATE_NEW_PROCESS_GROUP + except: + from _subprocess import DuplicateHandle, GetCurrentProcess, \ + DUPLICATE_SAME_ACCESS, CREATE_NEW_PROCESS_GROUP + # Launch the kernel process if independent: - proc = Popen(cmd, - creationflags=512, # CREATE_NEW_PROCESS_GROUP - stdin=_stdin, stdout=_stdout, stderr=_stderr, env=env) + kwargs['creationflags'] = CREATE_NEW_PROCESS_GROUP else: - if ipython_kernel: - try: - from _winapi import DuplicateHandle, GetCurrentProcess, \ - DUPLICATE_SAME_ACCESS - except: - from _subprocess import DuplicateHandle, GetCurrentProcess, \ - DUPLICATE_SAME_ACCESS - pid = GetCurrentProcess() - handle = DuplicateHandle(pid, pid, pid, 0, - True, # Inheritable by new processes. - DUPLICATE_SAME_ACCESS) - cmd +=[ '--parent=%i' % handle ] - - - proc = Popen(cmd, - stdin=_stdin, stdout=_stdout, stderr=_stderr, cwd=cwd, env=env) + pid = GetCurrentProcess() + handle = DuplicateHandle(pid, pid, pid, 0, + True, # Inheritable by new processes. + DUPLICATE_SAME_ACCESS) + env['JPY_PARENT_PID'] = str(handle) + + proc = Popen(cmd, **kwargs) # Attach the interrupt event to the Popen objet so it can be used later. proc.win32_interrupt_event = interrupt_event else: if independent: - proc = Popen(cmd, preexec_fn=lambda: os.setsid(), - stdin=_stdin, stdout=_stdout, stderr=_stderr, cwd=cwd, env=env) + kwargs['preexec_fn'] = lambda: os.setsid() else: - if ipython_kernel: - cmd += ['--parent=1'] - proc = Popen(cmd, - stdin=_stdin, stdout=_stdout, stderr=_stderr, cwd=cwd, env=env) + env['JPY_PARENT_PID'] = str(os.getpid()) + + proc = Popen(cmd, **kwargs) # Clean up pipes created to work around Popen bug. if redirect_in: if stdin is None: proc.stdin.close() - if redirect_out: - if stdout is None: - proc.stdout.close() - if stderr is None: - proc.stderr.close() return proc diff --git a/IPython/kernel/manager.py b/IPython/kernel/manager.py index 857bd82..ad55deb 100644 --- a/IPython/kernel/manager.py +++ b/IPython/kernel/manager.py @@ -237,7 +237,6 @@ class KernelManager(ConnectionFileMixin): env.update(self.kernel_spec.env or {}) # launch the kernel subprocess self.kernel = self._launch_kernel(kernel_cmd, env=env, - ipython_kernel=self.ipython_kernel, **kw) self.start_restarter() self._connect_control_socket() diff --git a/IPython/kernel/zmq/kernelapp.py b/IPython/kernel/zmq/kernelapp.py index c47368a..34c7c01 100644 --- a/IPython/kernel/zmq/kernelapp.py +++ b/IPython/kernel/zmq/kernelapp.py @@ -53,11 +53,8 @@ kernel_aliases.update({ 'stdin' : 'IPKernelApp.stdin_port', 'control' : 'IPKernelApp.control_port', 'f' : 'IPKernelApp.connection_file', - 'parent': 'IPKernelApp.parent_handle', 'transport': 'IPKernelApp.transport', }) -if sys.platform.startswith('win'): - kernel_aliases['interrupt'] = 'IPKernelApp.interrupt' kernel_flags = dict(base_flags) kernel_flags.update({ @@ -133,11 +130,11 @@ class IPKernelApp(BaseIPythonApplication, InteractiveShellApp, config=True, help="The importstring for the DisplayHook factory") # polling - parent_handle = Integer(0, config=True, + parent_handle = Integer(int(os.environ.get('JPY_PARENT_PID') or 0), config=True, help="""kill this process if its parent dies. On Windows, the argument specifies the HANDLE of the parent process, otherwise it is simply boolean. """) - interrupt = Integer(0, config=True, + interrupt = Integer(int(os.environ.get('JPY_INTERRUPT_EVENT') or 0), config=True, help="""ONLY USED ON WINDOWS Interrupt this process when the parent is signaled. """)