From 6c66e2b3928a46ad015e73f440880b3708847baa 2011-05-27 18:25:31 From: MinRK Date: 2011-05-27 18:25:31 Subject: [PATCH] Merge PR #459 (issue 297) closes gh-110 closes gh-459 --- diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 2795b52..037b86b 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -1872,22 +1872,44 @@ class InteractiveShell(SingletonConfigurable, Magic): # Things related to the running of system commands #------------------------------------------------------------------------- - def system(self, cmd): - """Call the given cmd in a subprocess. + def system_piped(self, cmd): + """Call the given cmd in a subprocess, piping stdout/err Parameters ---------- cmd : str - Command to execute (can not end in '&', as bacground processes are - not supported. + Command to execute (can not end in '&', as background processes are + not supported. Should not be a command that expects input + other than simple text. """ - # We do not support backgrounding processes because we either use - # pexpect or pipes to read from. Users can always just call - # os.system() if they really want a background process. - if cmd.endswith('&'): + if cmd.rstrip().endswith('&'): + # this is *far* from a rigorous test + # We do not support backgrounding processes because we either use + # pexpect or pipes to read from. Users can always just call + # os.system() or use ip.system=ip.system_raw + # if they really want a background process. raise OSError("Background processes not supported.") - - return system(self.var_expand(cmd, depth=2)) + + # we explicitly do NOT return the subprocess status code, because + # a non-None value would trigger :func:`sys.displayhook` calls. + # Instead, we store the exit_code in user_ns. + self.user_ns['_exit_code'] = system(self.var_expand(cmd, depth=2)) + + def system_raw(self, cmd): + """Call the given cmd in a subprocess using os.system + + Parameters + ---------- + cmd : str + Command to execute. + """ + # We explicitly do NOT return the subprocess status code, because + # a non-None value would trigger :func:`sys.displayhook` calls. + # Instead, we store the exit_code in user_ns. + self.user_ns['_exit_code'] = os.system(self.var_expand(cmd, depth=2)) + + # use piped system by default, because it is better behaved + system = system_piped def getoutput(self, cmd, split=True): """Get output (possibly including stderr) from a subprocess. @@ -1905,7 +1927,8 @@ class InteractiveShell(SingletonConfigurable, Magic): manipulation of line-based output. You can use '?' on them for details. """ - if cmd.endswith('&'): + if cmd.rstrip().endswith('&'): + # this is *far* from a rigorous test raise OSError("Background processes not supported.") out = getoutput(self.var_expand(cmd, depth=2)) if split: @@ -2172,7 +2195,9 @@ class InteractiveShell(SingletonConfigurable, Magic): prefilter_failed = False if len(cell.splitlines()) == 1: try: - cell = self.prefilter_manager.prefilter_line(cell) + # use prefilter_lines to handle trailing newlines + # restore trailing newline for ast.parse + cell = self.prefilter_manager.prefilter_lines(cell) + '\n' except AliasError as e: error(e) prefilter_failed=True diff --git a/IPython/core/tests/test_interactiveshell.py b/IPython/core/tests/test_interactiveshell.py index 904e6f8..b4e5fcf 100644 --- a/IPython/core/tests/test_interactiveshell.py +++ b/IPython/core/tests/test_interactiveshell.py @@ -106,4 +106,11 @@ class InteractiveShellTestCase(unittest.TestCase): err = io.stderr.getvalue() io.stderr = save_err self.assertEquals(err.split(':')[0], 'ERROR') + + def test_trailing_newline(self): + """test that running !(command) does not raise a SyntaxError""" + ip = get_ipython() + ip.run_cell('!(true)\n', False) + ip.run_cell('!(true)\n\n\n', False) + diff --git a/IPython/frontend/terminal/interactiveshell.py b/IPython/frontend/terminal/interactiveshell.py index e8412ae..ede323c 100644 --- a/IPython/frontend/terminal/interactiveshell.py +++ b/IPython/frontend/terminal/interactiveshell.py @@ -86,6 +86,12 @@ class TerminalInteractiveShell(InteractiveShell): config=config, ipython_dir=ipython_dir, user_ns=user_ns, user_global_ns=user_global_ns, custom_exceptions=custom_exceptions ) + # use os.system instead of utils.process.system by default, except on Windows + if os.name == 'nt': + self.system = self.system_piped + else: + self.system = self.system_raw + self.init_term_title() self.init_usage(usage) self.init_banner(banner1, banner2, display_banner) diff --git a/IPython/utils/_process_posix.py b/IPython/utils/_process_posix.py index e10945a..19df99b 100644 --- a/IPython/utils/_process_posix.py +++ b/IPython/utils/_process_posix.py @@ -130,9 +130,7 @@ class ProcessHandler(object): 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. + int : child's exitstatus """ pcmd = self._make_cmd(cmd) # Patterns to match on the output, for pexpect. We read input and @@ -181,6 +179,7 @@ class ProcessHandler(object): finally: # Ensure the subprocess really is terminated child.terminate(force=True) + return child.exitstatus def _make_cmd(self, cmd): return '%s -c "%s"' % (self.sh, cmd) diff --git a/IPython/utils/_process_win32.py b/IPython/utils/_process_win32.py index 1d8bd94..094cc9b 100644 --- a/IPython/utils/_process_win32.py +++ b/IPython/utils/_process_win32.py @@ -96,6 +96,9 @@ def _system_body(p): line = line.decode(enc, 'replace') print(line, file=sys.stderr) + # Wait to finish for returncode + return p.wait() + def system(cmd): """Win32 version of os.system() that works with network shares. @@ -116,7 +119,7 @@ def system(cmd): with AvoidUNCPath() as path: if path is not None: cmd = '"pushd %s &&"%s' % (path, cmd) - process_handler(cmd, _system_body) + return process_handler(cmd, _system_body) def getoutput(cmd):