diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 64d52b3..441a000 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -57,7 +57,7 @@ from IPython.utils.doctestreload import doctest_reload from IPython.utils.io import ask_yes_no, rprint from IPython.utils.ipstruct import Struct from IPython.utils.path import get_home_dir, get_ipython_dir, HomeDirError -from IPython.utils.process import system, getoutput, getoutputerror +from IPython.utils.process import system, getoutput from IPython.utils.strdispatch import StrDispatch from IPython.utils.syspathcontext import prepended_to_syspath from IPython.utils.text import num_ini_spaces @@ -1667,12 +1667,6 @@ class InteractiveShell(Configurable, Magic): raise OSError("Background processes not supported.") return getoutput(self.var_expand(cmd, depth=2)) - def getoutputerror(self, cmd): - """Get stdout and stderr from a subprocess.""" - if cmd.endswith('&'): - raise OSError("Background processes not supported.") - return getoutputerror(self.var_expand(cmd, depth=2)) - #------------------------------------------------------------------------- # Things related to aliases #------------------------------------------------------------------------- diff --git a/IPython/core/magic.py b/IPython/core/magic.py index 8ba1ba9..3ce9ce8 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -3072,9 +3072,7 @@ Defaulting color scheme to 'NoColor'""" except ValueError: var,cmd = '','' # If all looks ok, proceed - out,err = self.shell.getoutputerror(cmd) - if err: - print >> IPython.utils.io.Term.cerr, err + out = self.shell.getoutput(cmd) if opts.has_key('l'): out = SList(out.split('\n')) else: @@ -3122,10 +3120,9 @@ Defaulting color scheme to 'NoColor'""" system commands.""" if parameter_s: - out,err = self.shell.getoutputerror(parameter_s) - if err: - print >> IPython.utils.io.Term.cerr, err - return SList(out.split('\n')) + out = self.shell.getoutput(parameter_s) + if out is not None: + return SList(out.splitlines()) def magic_r(self, parameter_s=''): """Repeat previous input. diff --git a/IPython/core/page.py b/IPython/core/page.py index e8a234e..ae905e9 100755 --- a/IPython/core/page.py +++ b/IPython/core/page.py @@ -37,7 +37,7 @@ from IPython.core.error import TryNext from IPython.utils.cursesimport import use_curses from IPython.utils.data import chop import IPython.utils.io -from IPython.utils.process import xsys +from IPython.utils.process import system from IPython.utils.terminal import get_terminal_size @@ -210,7 +210,7 @@ def page_file(fname, start=0, pager_cmd=None): try: if os.environ['TERM'] in ['emacs','dumb']: raise EnvironmentError - xsys(pager_cmd + ' ' + fname) + system(pager_cmd + ' ' + fname) except: try: if start > 0: diff --git a/IPython/utils/_process_common.py b/IPython/utils/_process_common.py new file mode 100644 index 0000000..37ad198 --- /dev/null +++ b/IPython/utils/_process_common.py @@ -0,0 +1,119 @@ +"""Common utilities for the various process_* implementations. + +This file is only meant to be imported by the platform-specific implementations +of subprocess utilities, and it contains tools that are common to all of them. +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2010 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- +import subprocess +import sys + +#----------------------------------------------------------------------------- +# Function definitions +#----------------------------------------------------------------------------- + +def read_no_interrupt(p): + """Read from a pipe ignoring EINTR errors. + + This is necessary because when reading from pipes with GUI event loops + running in the background, often interrupts are raised that stop the + command from completing.""" + import errno + + try: + return p.read() + except IOError, err: + if err.errno != errno.EINTR: + raise + + +def process_handler(cmd, callback, stderr=subprocess.PIPE): + """Open a command in a shell subprocess and execute a callback. + + This function provides common scaffolding for creating subprocess.Popen() + calls. It creates a Popen object and then calls the callback with it. + + Parameters + ---------- + cmd : str + A string to be executed with the underlying system shell (by calling + :func:`Popen` with ``shell=True``. + + callback : callable + A one-argument function that will be called with the Popen object. + + stderr : file descriptor number, optional + By default this is set to ``subprocess.PIPE``, but you can also pass the + value ``subprocess.STDOUT`` to force the subprocess' stderr to go into + the same file descriptor as its stdout. This is useful to read stdout + and stderr combined in the order they are generated. + + Returns + ------- + The return value of the provided callback is returned. + """ + sys.stdout.flush() + sys.stderr.flush() + p = subprocess.Popen(cmd, shell=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=stderr, + close_fds=True) + + try: + out = callback(p) + except KeyboardInterrupt: + print('^C') + sys.stdout.flush() + sys.stderr.flush() + out = None + finally: + # Make really sure that we don't leave processes behind, in case the + # call above raises an exception + # We start by assuming the subprocess finished (to avoid NameErrors + # later depending on the path taken) + if p.returncode is None: + try: + p.terminate() + p.poll() + except OSError: + pass + # One last try on our way out + if p.returncode is None: + try: + p.kill() + except OSError: + pass + + return out + + +def getoutputerror(cmd): + """Return (standard output, standard error) of executing cmd in a shell. + + Accepts the same arguments as os.system(). + + Parameters + ---------- + cmd : str + A command to be executed in the system shell. + + Returns + ------- + stdout : str + stderr : str + """ + + out_err = process_handler(cmd, lambda p: p.communicate()) + if out_err is None: + out_err = '', '' + return out_err diff --git a/IPython/utils/_process_posix.py b/IPython/utils/_process_posix.py new file mode 100644 index 0000000..46a40b4 --- /dev/null +++ b/IPython/utils/_process_posix.py @@ -0,0 +1,169 @@ +"""Posix-specific implementation of process utilities. + +This file is only meant to be imported by process.py, not by end-users. +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2010 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- +from __future__ import print_function + +# Stdlib +import subprocess as sp +import sys + +# Third-party +# We ship our own copy of pexpect (it's a single file) to minimize dependencies +# for users, but it's only used if we don't find the system copy. +try: + import pexpect +except ImportError: + from IPython.external import pexpect + +# Our own +from .autoattr import auto_attr + +#----------------------------------------------------------------------------- +# Function definitions +#----------------------------------------------------------------------------- + +def _find_cmd(cmd): + """Find the full path to a command using which.""" + + return sp.Popen(['/usr/bin/env', 'which', cmd], + stdout=sp.PIPE).communicate()[0] + + +class ProcessHandler(object): + """Execute subprocesses under the control of pexpect. + """ + # Timeout in seconds to wait on each reading of the subprocess' output. + # This should not be set too low to avoid cpu overusage from our side, + # since we read in a loop whose period is controlled by this timeout. + read_timeout = 0.05 + + # Timeout to give a process if we receive SIGINT, between sending the + # SIGINT to the process and forcefully terminating it. + terminate_timeout = 0.2 + + # File object where stdout and stderr of the subprocess will be written + logfile = None + + # Shell to call for subprocesses to execute + sh = None + + @auto_attr + def sh(self): + sh = pexpect.which('sh') + if sh is None: + raise OSError('"sh" shell not found') + return sh + + def __init__(self, logfile=None, read_timeout=None, terminate_timeout=None): + """Arguments are used for pexpect calls.""" + self.read_timeout = (ProcessHandler.read_timeout if read_timeout is + None else read_timeout) + self.terminate_timeout = (ProcessHandler.terminate_timeout if + terminate_timeout is None else + terminate_timeout) + self.logfile = sys.stdout if logfile is None else logfile + + def getoutput(self, cmd): + """Run a command and return its stdout/stderr as a string. + + Parameters + ---------- + cmd : str + A command to be executed in the system shell. + + Returns + ------- + output : str + A string containing the combination of stdout and stderr from the + subprocess, in whatever order the subprocess originally wrote to its + file descriptors (so the order of the information in this string is the + correct order as would be seen if running the command in a terminal). + """ + pcmd = self._make_cmd(cmd) + try: + return pexpect.run(pcmd).replace('\r\n', '\n') + except KeyboardInterrupt: + print('^C', file=sys.stderr, end='') + + def system(self, cmd): + """Execute a command in a subshell. + + Parameters + ---------- + cmd : str + A command to be executed in the system shell. + + Returns + ------- + None : we explicitly do NOT return the subprocess status code, as this + utility is meant to be used extensively in IPython, where any return + value would trigger :func:`sys.displayhook` calls. + """ + pcmd = self._make_cmd(cmd) + # Patterns to match on the output, for pexpect. We read input and + # allow either a short timeout or EOF + patterns = [pexpect.TIMEOUT, pexpect.EOF] + # the index of the EOF pattern in the list. + EOF_index = 1 # Fix this index if you change the list!! + # The size of the output stored so far in the process output buffer. + # Since pexpect only appends to this buffer, each time we print we + # record how far we've printed, so that next time we only print *new* + # content from the buffer. + out_size = 0 + try: + # Since we're not really searching the buffer for text patterns, we + # can set pexpect's search window to be tiny and it won't matter. + # We only search for the 'patterns' timeout or EOF, which aren't in + # the text itself. + child = pexpect.spawn(pcmd, searchwindowsize=1) + flush = sys.stdout.flush + while True: + # res is the index of the pattern that caused the match, so we + # know whether we've finished (if we matched EOF) or not + res_idx = child.expect_list(patterns, self.read_timeout) + print(child.before[out_size:], end='') + flush() + # Update the pointer to what we've already printed + out_size = len(child.before) + if res_idx==EOF_index: + break + except KeyboardInterrupt: + # We need to send ^C to the process. The ascii code for '^C' is 3 + # (the character is known as ETX for 'End of Text', see + # curses.ascii.ETX). + child.sendline(chr(3)) + # Read and print any more output the program might produce on its + # way out. + try: + out_size = len(child.before) + child.expect_list(patterns, self.terminate_timeout) + print(child.before[out_size:], end='') + except KeyboardInterrupt: + # Impatient users tend to type it multiple times + pass + finally: + # Ensure the subprocess really is terminated + child.terminate(force=True) + + def _make_cmd(self, cmd): + return '%s -c "%s"' % (self.sh, cmd) + + + +# Make objects with a functional interface for outside use +__ph = ProcessHandler() + +system = __ph.system +getoutput = __ph.getoutput diff --git a/IPython/utils/_process_win32.py b/IPython/utils/_process_win32.py new file mode 100644 index 0000000..5c59634 --- /dev/null +++ b/IPython/utils/_process_win32.py @@ -0,0 +1,137 @@ +"""Windows-specific implementation of process utilities. + +This file is only meant to be imported by process.py, not by end-users. +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2010 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- +from __future__ import print_function + +# stdlib +import os +import sys + +from subprocess import STDOUT + +# our own imports +from ._process_common import read_no_interrupt, process_handler + +#----------------------------------------------------------------------------- +# Function definitions +#----------------------------------------------------------------------------- + +class AvoidUNCPath(object): + """A context manager to protect command execution from UNC paths. + + In the Win32 API, commands can't be invoked with the cwd being a UNC path. + This context manager temporarily changes directory to the 'C:' drive on + entering, and restores the original working directory on exit. + + The context manager returns the starting working directory *if* it made a + change and None otherwise, so that users can apply the necessary adjustment + to their system calls in the event of a change. + + Example + ------- + :: + cmd = 'dir' + with AvoidUNCPath() as path: + if path is not None: + cmd = '"pushd %s &&"%s' % (path, cmd) + os.system(cmd) + """ + def __enter__(self): + self.path = os.getcwd() + self.is_unc_path = self.path.startswith(r"\\") + if self.is_unc_path: + # change to c drive (as cmd.exe cannot handle UNC addresses) + os.chdir("C:") + return self.path + + def __exit__(self, exc_type, exc_value, traceback): + if self.is_unc_path: + os.chdir(self.path) + + +def _find_cmd(cmd): + """Find the full path to a .bat or .exe using the win32api module.""" + try: + from win32api import SearchPath + except ImportError: + raise ImportError('you need to have pywin32 installed for this to work') + else: + PATH = os.environ['PATH'] + extensions = ['.exe', '.com', '.bat', '.py'] + path = None + for ext in extensions: + try: + path = SearchPath(PATH, cmd + ext)[0] + except: + pass + if path is None: + raise OSError("command %r not found" % cmd) + else: + return path + + +def _system_body(p): + """Callback for _system.""" + for line in read_no_interrupt(p.stdout).splitlines(): + print(line, file=sys.stdout) + for line in read_no_interrupt(p.stderr).splitlines(): + print(line, file=sys.stderr) + + +def system(cmd): + """Win32 version of os.system() that works with network shares. + + Note that this implementation returns None, as meant for use in IPython. + + Parameters + ---------- + cmd : str + A command to be executed in the system shell. + + Returns + ------- + None : we explicitly do NOT return the subprocess status code, as this + utility is meant to be used extensively in IPython, where any return value + would trigger :func:`sys.displayhook` calls. + """ + with AvoidUNCPath() as path: + if path is not None: + cmd = '"pushd %s &&"%s' % (path, cmd) + process_handler(cmd, _system_body) + + +def getoutput(cmd): + """Return standard output of executing cmd in a shell. + + Accepts the same arguments as os.system(). + + Parameters + ---------- + cmd : str + A command to be executed in the system shell. + + Returns + ------- + stdout : str + """ + + with AvoidUNCPath() as path: + if path is not None: + cmd = '"pushd %s &&"%s' % (path, cmd) + out = process_handler(cmd, lambda p: p.communicate()[0], STDOUT) + + if out is None: + out = '' + return out diff --git a/IPython/utils/autoattr.py b/IPython/utils/autoattr.py index 3252eaf..0536d98 100644 --- a/IPython/utils/autoattr.py +++ b/IPython/utils/autoattr.py @@ -1,6 +1,4 @@ -#!/usr/bin/env python -# encoding: utf-8 -"""Descriptor support for NIPY. +"""Descriptor utilities. Utilities to support special Python descriptors [1,2], in particular the use of a useful pattern for properties we call 'one time properties'. These are @@ -24,6 +22,10 @@ Notes ----- This module is taken from the NiPy project (http://neuroimaging.scipy.org/site/index.html), and is BSD licensed. + +Authors +------- +- Fernando Perez. """ #----------------------------------------------------------------------------- diff --git a/IPython/utils/path.py b/IPython/utils/path.py index 8e2e199..69cf58f 100644 --- a/IPython/utils/path.py +++ b/IPython/utils/path.py @@ -18,7 +18,7 @@ import os import sys import IPython -from IPython.utils.process import xsys +from IPython.utils.process import system from IPython.utils.importstring import import_item #----------------------------------------------------------------------------- @@ -341,5 +341,5 @@ def target_update(target,deps,cmd): command if target is outdated.""" if target_outdated(target,deps): - xsys(cmd) + system(cmd) diff --git a/IPython/utils/process.py b/IPython/utils/process.py index 3a2ac78..af5879d 100644 --- a/IPython/utils/process.py +++ b/IPython/utils/process.py @@ -13,13 +13,20 @@ Utilities for working with external processes. #----------------------------------------------------------------------------- # Imports #----------------------------------------------------------------------------- +from __future__ import print_function +# Stdlib import os import sys import shlex -import subprocess -from IPython.utils.terminal import set_term_title +# Our own +if sys.platform == 'win32': + from ._process_win32 import _find_cmd, system, getoutput, AvoidUNCPath +else: + from ._process_posix import _find_cmd, system, getoutput + +from ._process_common import getoutputerror #----------------------------------------------------------------------------- # Code @@ -30,39 +37,6 @@ class FindCmdError(Exception): pass -def _find_cmd(cmd): - """Find the full path to a command using which.""" - return os.popen('which %s' % cmd).read().strip() - - -if os.name == 'posix': - def _find_cmd(cmd): - """Find the full path to a command using which.""" - return getoutputerror('/usr/bin/env which %s' % cmd)[0] - - -if sys.platform == 'win32': - def _find_cmd(cmd): - """Find the full path to a .bat or .exe using the win32api module.""" - try: - from win32api import SearchPath - except ImportError: - raise ImportError('you need to have pywin32 installed for this to work') - else: - PATH = os.environ['PATH'] - extensions = ['.exe', '.com', '.bat', '.py'] - path = None - for ext in extensions: - try: - path = SearchPath(PATH,cmd + ext)[0] - except: - pass - if path is None: - raise OSError("command %r not found" % cmd) - else: - return path - - def find_cmd(cmd): """Find absolute path to executable cmd in a cross platform manner. @@ -87,7 +61,7 @@ def find_cmd(cmd): if cmd == 'python': return os.path.abspath(sys.executable) try: - path = _find_cmd(cmd) + path = _find_cmd(cmd).rstrip() except OSError: raise FindCmdError('command could not be found: %s' % cmd) # which returns empty if not found @@ -147,28 +121,6 @@ def arg_split(s, posix=False): return list(lex) -def system(cmd, verbose=0, debug=0, header=''): - """Execute a system command, return its exit status. - - Options: - - - verbose (0): print the command to be executed. - - - debug (0): only print, do not actually execute. - - - header (''): Header to print on screen prior to the executed command (it - is only prepended to the command, no newlines are added). - - Note: a stateful version of this function is available through the - SystemExec class.""" - - stat = 0 - if verbose or debug: print header+cmd - sys.stdout.flush() - if not debug: stat = os.system(cmd) - return stat - - def abbrev_cwd(): """ Return abbreviated version of cwd, e.g. d:mydir """ cwd = os.getcwd().replace('\\','/') @@ -186,182 +138,3 @@ def abbrev_cwd(): return (drivepart + ( cwd == '/' and '/' or tail)) - - -# This function is used by ipython in a lot of places to make system calls. -# We need it to be slightly different under win32, due to the vagaries of -# 'network shares'. A win32 override is below. - -def shell(cmd, verbose=0, debug=0, header=''): - """Execute a command in the system shell, always return None. - - Options: - - - verbose (0): print the command to be executed. - - - debug (0): only print, do not actually execute. - - - header (''): Header to print on screen prior to the executed command (it - is only prepended to the command, no newlines are added). - - Note: this is similar to system(), but it returns None so it can - be conveniently used in interactive loops without getting the return value - (typically 0) printed many times.""" - - stat = 0 - if verbose or debug: print header+cmd - # flush stdout so we don't mangle python's buffering - sys.stdout.flush() - - if not debug: - set_term_title("IPy " + cmd) - os.system(cmd) - set_term_title("IPy " + abbrev_cwd()) - -# override shell() for win32 to deal with network shares -if os.name in ('nt','dos'): - - shell_ori = shell - - def shell(cmd, verbose=0, debug=0, header=''): - if os.getcwd().startswith(r"\\"): - path = os.getcwd() - # change to c drive (cannot be on UNC-share when issuing os.system, - # as cmd.exe cannot handle UNC addresses) - os.chdir("c:") - # issue pushd to the UNC-share and then run the command - try: - shell_ori('"pushd %s&&"'%path+cmd,verbose,debug,header) - finally: - os.chdir(path) - else: - shell_ori(cmd,verbose,debug,header) - - shell.__doc__ = shell_ori.__doc__ - - -def getoutput(cmd, verbose=0, debug=0, header='', split=0): - """Dummy substitute for perl's backquotes. - - Executes a command and returns the output. - - Accepts the same arguments as system(), plus: - - - split(0): if true, the output is returned as a list split on newlines. - - Note: a stateful version of this function is available through the - SystemExec class. - - This is pretty much deprecated and rarely used, getoutputerror may be - what you need. - - """ - - if verbose or debug: print header+cmd - if not debug: - pipe = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout - output = pipe.read() - # stipping last \n is here for backwards compat. - if output.endswith('\n'): - output = output[:-1] - if split: - return output.split('\n') - else: - return output - - -# for compatibility with older naming conventions -xsys = system - - -def getoutputerror(cmd, verbose=0, debug=0, header='', split=0): - """Return (standard output,standard error) of executing cmd in a shell. - - Accepts the same arguments as system(), plus: - - - split(0): if true, each of stdout/err is returned as a list split on - newlines. - - Note: a stateful version of this function is available through the - SystemExec class.""" - - if verbose or debug: print header+cmd - if not cmd: - if split: - return [],[] - else: - return '','' - if not debug: - p = subprocess.Popen(cmd, shell=True, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - close_fds=True) - pin, pout, perr = (p.stdin, p.stdout, p.stderr) - - tout = pout.read().rstrip() - terr = perr.read().rstrip() - pin.close() - pout.close() - perr.close() - if split: - return tout.split('\n'),terr.split('\n') - else: - return tout,terr - - -class SystemExec: - """Access the system and getoutput functions through a stateful interface. - - Note: here we refer to the system and getoutput functions from this - library, not the ones from the standard python library. - - This class offers the system and getoutput functions as methods, but the - verbose, debug and header parameters can be set for the instance (at - creation time or later) so that they don't need to be specified on each - call. - - For efficiency reasons, there's no way to override the parameters on a - per-call basis other than by setting instance attributes. If you need - local overrides, it's best to directly call system() or getoutput(). - - The following names are provided as alternate options: - - xsys: alias to system - - bq: alias to getoutput - - An instance can then be created as: - >>> sysexec = SystemExec(verbose=1,debug=0,header='Calling: ') - """ - - def __init__(self, verbose=0, debug=0, header='', split=0): - """Specify the instance's values for verbose, debug and header.""" - self.verbose = verbose - self.debug = debug - self.header = header - self.split = split - - def system(self, cmd): - """Stateful interface to system(), with the same keyword parameters.""" - - system(cmd, self.verbose, self.debug, self.header) - - def shell(self, cmd): - """Stateful interface to shell(), with the same keyword parameters.""" - - shell(cmd, self.verbose, self.debug, self.header) - - xsys = system # alias - - def getoutput(self, cmd): - """Stateful interface to getoutput().""" - - return getoutput(cmd, self.verbose, self.debug, self.header, self.split) - - def getoutputerror(self, cmd): - """Stateful interface to getoutputerror().""" - - return getoutputerror(cmd, self.verbose, self.debug, self.header, self.split) - - bq = getoutput # alias - - diff --git a/IPython/utils/tests/test_process.py b/IPython/utils/tests/test_process.py index c0bae05..b89fedf 100644 --- a/IPython/utils/tests/test_process.py +++ b/IPython/utils/tests/test_process.py @@ -15,11 +15,14 @@ Tests for platutils.py #----------------------------------------------------------------------------- import sys +from unittest import TestCase import nose.tools as nt -from IPython.utils.process import find_cmd, FindCmdError, arg_split +from IPython.utils.process import (find_cmd, FindCmdError, arg_split, + system, getoutput, getoutputerror) from IPython.testing import decorators as dec +from IPython.testing import tools as tt #----------------------------------------------------------------------------- # Tests @@ -66,3 +69,27 @@ def test_arg_split(): ] for argstr, argv in tests: nt.assert_equal(arg_split(argstr), argv) + + +class SubProcessTestCase(TestCase, tt.TempFileMixin): + def setUp(self): + """Make a valid python temp file.""" + lines = ["from __future__ import print_function", + "import sys", + "print('on stdout', end='', file=sys.stdout)", + "print('on stderr', end='', file=sys.stderr)", + "sys.stdout.flush()", + "sys.stderr.flush()"] + self.mktmp('\n'.join(lines)) + + def test_system(self): + system('python "%s"' % self.fname) + + def test_getoutput(self): + out = getoutput('python "%s"' % self.fname) + self.assertEquals(out, 'on stdout') + + def test_getoutput(self): + out, err = getoutputerror('python "%s"' % self.fname) + self.assertEquals(out, 'on stdout') + self.assertEquals(err, 'on stderr') diff --git a/IPython/zmq/zmqshell.py b/IPython/zmq/zmqshell.py index 96b7f85..538caa2 100644 --- a/IPython/zmq/zmqshell.py +++ b/IPython/zmq/zmqshell.py @@ -19,9 +19,6 @@ from __future__ import print_function import inspect import os import re -import sys - -from subprocess import Popen, PIPE # Our own from IPython.core.interactiveshell import ( @@ -83,26 +80,6 @@ class ZMQInteractiveShell(InteractiveShell): displayhook_class = Type(ZMQDisplayHook) - def system(self, cmd): - cmd = self.var_expand(cmd, depth=2).strip() - - # Runnning a bacgkrounded process from within the gui isn't supported - # because we do p.wait() at the end. So instead of silently blocking - # we simply refuse to run in this mode, to avoid surprising the user. - if cmd.endswith('&'): - raise OSError("Background processes not supported.") - - sys.stdout.flush() - sys.stderr.flush() - p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE) - for line in p.stdout.read().split('\n'): - if len(line) > 0: - print(line) - for line in p.stderr.read().split('\n'): - if len(line) > 0: - print(line, file=sys.stderr) - p.wait() - def init_io(self): # This will just use sys.stdout and sys.stderr. If you want to # override sys.stdout and sys.stderr themselves, you need to do that