Show More
@@ -0,0 +1,119 b'' | |||
|
1 | """Common utilities for the various process_* implementations. | |
|
2 | ||
|
3 | This file is only meant to be imported by the platform-specific implementations | |
|
4 | of subprocess utilities, and it contains tools that are common to all of them. | |
|
5 | """ | |
|
6 | ||
|
7 | #----------------------------------------------------------------------------- | |
|
8 | # Copyright (C) 2010 The IPython Development Team | |
|
9 | # | |
|
10 | # Distributed under the terms of the BSD License. The full license is in | |
|
11 | # the file COPYING, distributed as part of this software. | |
|
12 | #----------------------------------------------------------------------------- | |
|
13 | ||
|
14 | #----------------------------------------------------------------------------- | |
|
15 | # Imports | |
|
16 | #----------------------------------------------------------------------------- | |
|
17 | import subprocess | |
|
18 | import sys | |
|
19 | ||
|
20 | #----------------------------------------------------------------------------- | |
|
21 | # Function definitions | |
|
22 | #----------------------------------------------------------------------------- | |
|
23 | ||
|
24 | def read_no_interrupt(p): | |
|
25 | """Read from a pipe ignoring EINTR errors. | |
|
26 | ||
|
27 | This is necessary because when reading from pipes with GUI event loops | |
|
28 | running in the background, often interrupts are raised that stop the | |
|
29 | command from completing.""" | |
|
30 | import errno | |
|
31 | ||
|
32 | try: | |
|
33 | return p.read() | |
|
34 | except IOError, err: | |
|
35 | if err.errno != errno.EINTR: | |
|
36 | raise | |
|
37 | ||
|
38 | ||
|
39 | def process_handler(cmd, callback, stderr=subprocess.PIPE): | |
|
40 | """Open a command in a shell subprocess and execute a callback. | |
|
41 | ||
|
42 | This function provides common scaffolding for creating subprocess.Popen() | |
|
43 | calls. It creates a Popen object and then calls the callback with it. | |
|
44 | ||
|
45 | Parameters | |
|
46 | ---------- | |
|
47 | cmd : str | |
|
48 | A string to be executed with the underlying system shell (by calling | |
|
49 | :func:`Popen` with ``shell=True``. | |
|
50 | ||
|
51 | callback : callable | |
|
52 | A one-argument function that will be called with the Popen object. | |
|
53 | ||
|
54 | stderr : file descriptor number, optional | |
|
55 | By default this is set to ``subprocess.PIPE``, but you can also pass the | |
|
56 | value ``subprocess.STDOUT`` to force the subprocess' stderr to go into | |
|
57 | the same file descriptor as its stdout. This is useful to read stdout | |
|
58 | and stderr combined in the order they are generated. | |
|
59 | ||
|
60 | Returns | |
|
61 | ------- | |
|
62 | The return value of the provided callback is returned. | |
|
63 | """ | |
|
64 | sys.stdout.flush() | |
|
65 | sys.stderr.flush() | |
|
66 | p = subprocess.Popen(cmd, shell=True, | |
|
67 | stdin=subprocess.PIPE, | |
|
68 | stdout=subprocess.PIPE, | |
|
69 | stderr=stderr, | |
|
70 | close_fds=True) | |
|
71 | ||
|
72 | try: | |
|
73 | out = callback(p) | |
|
74 | except KeyboardInterrupt: | |
|
75 | print('^C') | |
|
76 | sys.stdout.flush() | |
|
77 | sys.stderr.flush() | |
|
78 | out = None | |
|
79 | finally: | |
|
80 | # Make really sure that we don't leave processes behind, in case the | |
|
81 | # call above raises an exception | |
|
82 | # We start by assuming the subprocess finished (to avoid NameErrors | |
|
83 | # later depending on the path taken) | |
|
84 | if p.returncode is None: | |
|
85 | try: | |
|
86 | p.terminate() | |
|
87 | p.poll() | |
|
88 | except OSError: | |
|
89 | pass | |
|
90 | # One last try on our way out | |
|
91 | if p.returncode is None: | |
|
92 | try: | |
|
93 | p.kill() | |
|
94 | except OSError: | |
|
95 | pass | |
|
96 | ||
|
97 | return out | |
|
98 | ||
|
99 | ||
|
100 | def getoutputerror(cmd): | |
|
101 | """Return (standard output, standard error) of executing cmd in a shell. | |
|
102 | ||
|
103 | Accepts the same arguments as os.system(). | |
|
104 | ||
|
105 | Parameters | |
|
106 | ---------- | |
|
107 | cmd : str | |
|
108 | A command to be executed in the system shell. | |
|
109 | ||
|
110 | Returns | |
|
111 | ------- | |
|
112 | stdout : str | |
|
113 | stderr : str | |
|
114 | """ | |
|
115 | ||
|
116 | out_err = process_handler(cmd, lambda p: p.communicate()) | |
|
117 | if out_err is None: | |
|
118 | out_err = '', '' | |
|
119 | return out_err |
@@ -0,0 +1,169 b'' | |||
|
1 | """Posix-specific implementation of process utilities. | |
|
2 | ||
|
3 | This file is only meant to be imported by process.py, not by end-users. | |
|
4 | """ | |
|
5 | ||
|
6 | #----------------------------------------------------------------------------- | |
|
7 | # Copyright (C) 2010 The IPython Development Team | |
|
8 | # | |
|
9 | # Distributed under the terms of the BSD License. The full license is in | |
|
10 | # the file COPYING, distributed as part of this software. | |
|
11 | #----------------------------------------------------------------------------- | |
|
12 | ||
|
13 | #----------------------------------------------------------------------------- | |
|
14 | # Imports | |
|
15 | #----------------------------------------------------------------------------- | |
|
16 | from __future__ import print_function | |
|
17 | ||
|
18 | # Stdlib | |
|
19 | import subprocess as sp | |
|
20 | import sys | |
|
21 | ||
|
22 | # Third-party | |
|
23 | # We ship our own copy of pexpect (it's a single file) to minimize dependencies | |
|
24 | # for users, but it's only used if we don't find the system copy. | |
|
25 | try: | |
|
26 | import pexpect | |
|
27 | except ImportError: | |
|
28 | from IPython.external import pexpect | |
|
29 | ||
|
30 | # Our own | |
|
31 | from .autoattr import auto_attr | |
|
32 | ||
|
33 | #----------------------------------------------------------------------------- | |
|
34 | # Function definitions | |
|
35 | #----------------------------------------------------------------------------- | |
|
36 | ||
|
37 | def _find_cmd(cmd): | |
|
38 | """Find the full path to a command using which.""" | |
|
39 | ||
|
40 | return sp.Popen(['/usr/bin/env', 'which', cmd], | |
|
41 | stdout=sp.PIPE).communicate()[0] | |
|
42 | ||
|
43 | ||
|
44 | class ProcessHandler(object): | |
|
45 | """Execute subprocesses under the control of pexpect. | |
|
46 | """ | |
|
47 | # Timeout in seconds to wait on each reading of the subprocess' output. | |
|
48 | # This should not be set too low to avoid cpu overusage from our side, | |
|
49 | # since we read in a loop whose period is controlled by this timeout. | |
|
50 | read_timeout = 0.05 | |
|
51 | ||
|
52 | # Timeout to give a process if we receive SIGINT, between sending the | |
|
53 | # SIGINT to the process and forcefully terminating it. | |
|
54 | terminate_timeout = 0.2 | |
|
55 | ||
|
56 | # File object where stdout and stderr of the subprocess will be written | |
|
57 | logfile = None | |
|
58 | ||
|
59 | # Shell to call for subprocesses to execute | |
|
60 | sh = None | |
|
61 | ||
|
62 | @auto_attr | |
|
63 | def sh(self): | |
|
64 | sh = pexpect.which('sh') | |
|
65 | if sh is None: | |
|
66 | raise OSError('"sh" shell not found') | |
|
67 | return sh | |
|
68 | ||
|
69 | def __init__(self, logfile=None, read_timeout=None, terminate_timeout=None): | |
|
70 | """Arguments are used for pexpect calls.""" | |
|
71 | self.read_timeout = (ProcessHandler.read_timeout if read_timeout is | |
|
72 | None else read_timeout) | |
|
73 | self.terminate_timeout = (ProcessHandler.terminate_timeout if | |
|
74 | terminate_timeout is None else | |
|
75 | terminate_timeout) | |
|
76 | self.logfile = sys.stdout if logfile is None else logfile | |
|
77 | ||
|
78 | def getoutput(self, cmd): | |
|
79 | """Run a command and return its stdout/stderr as a string. | |
|
80 | ||
|
81 | Parameters | |
|
82 | ---------- | |
|
83 | cmd : str | |
|
84 | A command to be executed in the system shell. | |
|
85 | ||
|
86 | Returns | |
|
87 | ------- | |
|
88 | output : str | |
|
89 | A string containing the combination of stdout and stderr from the | |
|
90 | subprocess, in whatever order the subprocess originally wrote to its | |
|
91 | file descriptors (so the order of the information in this string is the | |
|
92 | correct order as would be seen if running the command in a terminal). | |
|
93 | """ | |
|
94 | pcmd = self._make_cmd(cmd) | |
|
95 | try: | |
|
96 | return pexpect.run(pcmd).replace('\r\n', '\n') | |
|
97 | except KeyboardInterrupt: | |
|
98 | print('^C', file=sys.stderr, end='') | |
|
99 | ||
|
100 | def system(self, cmd): | |
|
101 | """Execute a command in a subshell. | |
|
102 | ||
|
103 | Parameters | |
|
104 | ---------- | |
|
105 | cmd : str | |
|
106 | A command to be executed in the system shell. | |
|
107 | ||
|
108 | Returns | |
|
109 | ------- | |
|
110 | None : we explicitly do NOT return the subprocess status code, as this | |
|
111 | utility is meant to be used extensively in IPython, where any return | |
|
112 | value would trigger :func:`sys.displayhook` calls. | |
|
113 | """ | |
|
114 | pcmd = self._make_cmd(cmd) | |
|
115 | # Patterns to match on the output, for pexpect. We read input and | |
|
116 | # allow either a short timeout or EOF | |
|
117 | patterns = [pexpect.TIMEOUT, pexpect.EOF] | |
|
118 | # the index of the EOF pattern in the list. | |
|
119 | EOF_index = 1 # Fix this index if you change the list!! | |
|
120 | # The size of the output stored so far in the process output buffer. | |
|
121 | # Since pexpect only appends to this buffer, each time we print we | |
|
122 | # record how far we've printed, so that next time we only print *new* | |
|
123 | # content from the buffer. | |
|
124 | out_size = 0 | |
|
125 | try: | |
|
126 | # Since we're not really searching the buffer for text patterns, we | |
|
127 | # can set pexpect's search window to be tiny and it won't matter. | |
|
128 | # We only search for the 'patterns' timeout or EOF, which aren't in | |
|
129 | # the text itself. | |
|
130 | child = pexpect.spawn(pcmd, searchwindowsize=1) | |
|
131 | flush = sys.stdout.flush | |
|
132 | while True: | |
|
133 | # res is the index of the pattern that caused the match, so we | |
|
134 | # know whether we've finished (if we matched EOF) or not | |
|
135 | res_idx = child.expect_list(patterns, self.read_timeout) | |
|
136 | print(child.before[out_size:], end='') | |
|
137 | flush() | |
|
138 | # Update the pointer to what we've already printed | |
|
139 | out_size = len(child.before) | |
|
140 | if res_idx==EOF_index: | |
|
141 | break | |
|
142 | except KeyboardInterrupt: | |
|
143 | # We need to send ^C to the process. The ascii code for '^C' is 3 | |
|
144 | # (the character is known as ETX for 'End of Text', see | |
|
145 | # curses.ascii.ETX). | |
|
146 | child.sendline(chr(3)) | |
|
147 | # Read and print any more output the program might produce on its | |
|
148 | # way out. | |
|
149 | try: | |
|
150 | out_size = len(child.before) | |
|
151 | child.expect_list(patterns, self.terminate_timeout) | |
|
152 | print(child.before[out_size:], end='') | |
|
153 | except KeyboardInterrupt: | |
|
154 | # Impatient users tend to type it multiple times | |
|
155 | pass | |
|
156 | finally: | |
|
157 | # Ensure the subprocess really is terminated | |
|
158 | child.terminate(force=True) | |
|
159 | ||
|
160 | def _make_cmd(self, cmd): | |
|
161 | return '%s -c "%s"' % (self.sh, cmd) | |
|
162 | ||
|
163 | ||
|
164 | ||
|
165 | # Make objects with a functional interface for outside use | |
|
166 | __ph = ProcessHandler() | |
|
167 | ||
|
168 | system = __ph.system | |
|
169 | getoutput = __ph.getoutput |
@@ -0,0 +1,137 b'' | |||
|
1 | """Windows-specific implementation of process utilities. | |
|
2 | ||
|
3 | This file is only meant to be imported by process.py, not by end-users. | |
|
4 | """ | |
|
5 | ||
|
6 | #----------------------------------------------------------------------------- | |
|
7 | # Copyright (C) 2010 The IPython Development Team | |
|
8 | # | |
|
9 | # Distributed under the terms of the BSD License. The full license is in | |
|
10 | # the file COPYING, distributed as part of this software. | |
|
11 | #----------------------------------------------------------------------------- | |
|
12 | ||
|
13 | #----------------------------------------------------------------------------- | |
|
14 | # Imports | |
|
15 | #----------------------------------------------------------------------------- | |
|
16 | from __future__ import print_function | |
|
17 | ||
|
18 | # stdlib | |
|
19 | import os | |
|
20 | import sys | |
|
21 | ||
|
22 | from subprocess import STDOUT | |
|
23 | ||
|
24 | # our own imports | |
|
25 | from ._process_common import read_no_interrupt, process_handler | |
|
26 | ||
|
27 | #----------------------------------------------------------------------------- | |
|
28 | # Function definitions | |
|
29 | #----------------------------------------------------------------------------- | |
|
30 | ||
|
31 | class AvoidUNCPath(object): | |
|
32 | """A context manager to protect command execution from UNC paths. | |
|
33 | ||
|
34 | In the Win32 API, commands can't be invoked with the cwd being a UNC path. | |
|
35 | This context manager temporarily changes directory to the 'C:' drive on | |
|
36 | entering, and restores the original working directory on exit. | |
|
37 | ||
|
38 | The context manager returns the starting working directory *if* it made a | |
|
39 | change and None otherwise, so that users can apply the necessary adjustment | |
|
40 | to their system calls in the event of a change. | |
|
41 | ||
|
42 | Example | |
|
43 | ------- | |
|
44 | :: | |
|
45 | cmd = 'dir' | |
|
46 | with AvoidUNCPath() as path: | |
|
47 | if path is not None: | |
|
48 | cmd = '"pushd %s &&"%s' % (path, cmd) | |
|
49 | os.system(cmd) | |
|
50 | """ | |
|
51 | def __enter__(self): | |
|
52 | self.path = os.getcwd() | |
|
53 | self.is_unc_path = self.path.startswith(r"\\") | |
|
54 | if self.is_unc_path: | |
|
55 | # change to c drive (as cmd.exe cannot handle UNC addresses) | |
|
56 | os.chdir("C:") | |
|
57 | return self.path | |
|
58 | ||
|
59 | def __exit__(self, exc_type, exc_value, traceback): | |
|
60 | if self.is_unc_path: | |
|
61 | os.chdir(self.path) | |
|
62 | ||
|
63 | ||
|
64 | def _find_cmd(cmd): | |
|
65 | """Find the full path to a .bat or .exe using the win32api module.""" | |
|
66 | try: | |
|
67 | from win32api import SearchPath | |
|
68 | except ImportError: | |
|
69 | raise ImportError('you need to have pywin32 installed for this to work') | |
|
70 | else: | |
|
71 | PATH = os.environ['PATH'] | |
|
72 | extensions = ['.exe', '.com', '.bat', '.py'] | |
|
73 | path = None | |
|
74 | for ext in extensions: | |
|
75 | try: | |
|
76 | path = SearchPath(PATH, cmd + ext)[0] | |
|
77 | except: | |
|
78 | pass | |
|
79 | if path is None: | |
|
80 | raise OSError("command %r not found" % cmd) | |
|
81 | else: | |
|
82 | return path | |
|
83 | ||
|
84 | ||
|
85 | def _system_body(p): | |
|
86 | """Callback for _system.""" | |
|
87 | for line in read_no_interrupt(p.stdout).splitlines(): | |
|
88 | print(line, file=sys.stdout) | |
|
89 | for line in read_no_interrupt(p.stderr).splitlines(): | |
|
90 | print(line, file=sys.stderr) | |
|
91 | ||
|
92 | ||
|
93 | def system(cmd): | |
|
94 | """Win32 version of os.system() that works with network shares. | |
|
95 | ||
|
96 | Note that this implementation returns None, as meant for use in IPython. | |
|
97 | ||
|
98 | Parameters | |
|
99 | ---------- | |
|
100 | cmd : str | |
|
101 | A command to be executed in the system shell. | |
|
102 | ||
|
103 | Returns | |
|
104 | ------- | |
|
105 | None : we explicitly do NOT return the subprocess status code, as this | |
|
106 | utility is meant to be used extensively in IPython, where any return value | |
|
107 | would trigger :func:`sys.displayhook` calls. | |
|
108 | """ | |
|
109 | with AvoidUNCPath() as path: | |
|
110 | if path is not None: | |
|
111 | cmd = '"pushd %s &&"%s' % (path, cmd) | |
|
112 | process_handler(cmd, _system_body) | |
|
113 | ||
|
114 | ||
|
115 | def getoutput(cmd): | |
|
116 | """Return standard output of executing cmd in a shell. | |
|
117 | ||
|
118 | Accepts the same arguments as os.system(). | |
|
119 | ||
|
120 | Parameters | |
|
121 | ---------- | |
|
122 | cmd : str | |
|
123 | A command to be executed in the system shell. | |
|
124 | ||
|
125 | Returns | |
|
126 | ------- | |
|
127 | stdout : str | |
|
128 | """ | |
|
129 | ||
|
130 | with AvoidUNCPath() as path: | |
|
131 | if path is not None: | |
|
132 | cmd = '"pushd %s &&"%s' % (path, cmd) | |
|
133 | out = process_handler(cmd, lambda p: p.communicate()[0], STDOUT) | |
|
134 | ||
|
135 | if out is None: | |
|
136 | out = '' | |
|
137 | return out |
@@ -57,7 +57,7 b' from IPython.utils.doctestreload import doctest_reload' | |||
|
57 | 57 | from IPython.utils.io import ask_yes_no, rprint |
|
58 | 58 | from IPython.utils.ipstruct import Struct |
|
59 | 59 | from IPython.utils.path import get_home_dir, get_ipython_dir, HomeDirError |
|
60 |
from IPython.utils.process import system, getoutput |
|
|
60 | from IPython.utils.process import system, getoutput | |
|
61 | 61 | from IPython.utils.strdispatch import StrDispatch |
|
62 | 62 | from IPython.utils.syspathcontext import prepended_to_syspath |
|
63 | 63 | from IPython.utils.text import num_ini_spaces |
@@ -1667,12 +1667,6 b' class InteractiveShell(Configurable, Magic):' | |||
|
1667 | 1667 | raise OSError("Background processes not supported.") |
|
1668 | 1668 | return getoutput(self.var_expand(cmd, depth=2)) |
|
1669 | 1669 | |
|
1670 | def getoutputerror(self, cmd): | |
|
1671 | """Get stdout and stderr from a subprocess.""" | |
|
1672 | if cmd.endswith('&'): | |
|
1673 | raise OSError("Background processes not supported.") | |
|
1674 | return getoutputerror(self.var_expand(cmd, depth=2)) | |
|
1675 | ||
|
1676 | 1670 | #------------------------------------------------------------------------- |
|
1677 | 1671 | # Things related to aliases |
|
1678 | 1672 | #------------------------------------------------------------------------- |
@@ -3072,9 +3072,7 b' Defaulting color scheme to \'NoColor\'"""' | |||
|
3072 | 3072 | except ValueError: |
|
3073 | 3073 | var,cmd = '','' |
|
3074 | 3074 | # If all looks ok, proceed |
|
3075 |
out |
|
|
3076 | if err: | |
|
3077 | print >> IPython.utils.io.Term.cerr, err | |
|
3075 | out = self.shell.getoutput(cmd) | |
|
3078 | 3076 | if opts.has_key('l'): |
|
3079 | 3077 | out = SList(out.split('\n')) |
|
3080 | 3078 | else: |
@@ -3122,10 +3120,9 b' Defaulting color scheme to \'NoColor\'"""' | |||
|
3122 | 3120 | system commands.""" |
|
3123 | 3121 | |
|
3124 | 3122 | if parameter_s: |
|
3125 |
out |
|
|
3126 |
if e |
|
|
3127 | print >> IPython.utils.io.Term.cerr, err | |
|
3128 | return SList(out.split('\n')) | |
|
3123 | out = self.shell.getoutput(parameter_s) | |
|
3124 | if out is not None: | |
|
3125 | return SList(out.splitlines()) | |
|
3129 | 3126 | |
|
3130 | 3127 | def magic_r(self, parameter_s=''): |
|
3131 | 3128 | """Repeat previous input. |
@@ -37,7 +37,7 b' from IPython.core.error import TryNext' | |||
|
37 | 37 | from IPython.utils.cursesimport import use_curses |
|
38 | 38 | from IPython.utils.data import chop |
|
39 | 39 | import IPython.utils.io |
|
40 |
from IPython.utils.process import |
|
|
40 | from IPython.utils.process import system | |
|
41 | 41 | from IPython.utils.terminal import get_terminal_size |
|
42 | 42 | |
|
43 | 43 | |
@@ -210,7 +210,7 b' def page_file(fname, start=0, pager_cmd=None):' | |||
|
210 | 210 | try: |
|
211 | 211 | if os.environ['TERM'] in ['emacs','dumb']: |
|
212 | 212 | raise EnvironmentError |
|
213 |
|
|
|
213 | system(pager_cmd + ' ' + fname) | |
|
214 | 214 | except: |
|
215 | 215 | try: |
|
216 | 216 | if start > 0: |
@@ -1,6 +1,4 b'' | |||
|
1 | #!/usr/bin/env python | |
|
2 | # encoding: utf-8 | |
|
3 | """Descriptor support for NIPY. | |
|
1 | """Descriptor utilities. | |
|
4 | 2 | |
|
5 | 3 | Utilities to support special Python descriptors [1,2], in particular the use of |
|
6 | 4 | a useful pattern for properties we call 'one time properties'. These are |
@@ -24,6 +22,10 b' Notes' | |||
|
24 | 22 | ----- |
|
25 | 23 | This module is taken from the NiPy project |
|
26 | 24 | (http://neuroimaging.scipy.org/site/index.html), and is BSD licensed. |
|
25 | ||
|
26 | Authors | |
|
27 | ------- | |
|
28 | - Fernando Perez. | |
|
27 | 29 | """ |
|
28 | 30 | |
|
29 | 31 | #----------------------------------------------------------------------------- |
@@ -18,7 +18,7 b' import os' | |||
|
18 | 18 | import sys |
|
19 | 19 | |
|
20 | 20 | import IPython |
|
21 |
from IPython.utils.process import |
|
|
21 | from IPython.utils.process import system | |
|
22 | 22 | from IPython.utils.importstring import import_item |
|
23 | 23 | |
|
24 | 24 | #----------------------------------------------------------------------------- |
@@ -341,5 +341,5 b' def target_update(target,deps,cmd):' | |||
|
341 | 341 | command if target is outdated.""" |
|
342 | 342 | |
|
343 | 343 | if target_outdated(target,deps): |
|
344 |
|
|
|
344 | system(cmd) | |
|
345 | 345 |
@@ -13,13 +13,20 b' Utilities for working with external processes.' | |||
|
13 | 13 | #----------------------------------------------------------------------------- |
|
14 | 14 | # Imports |
|
15 | 15 | #----------------------------------------------------------------------------- |
|
16 | from __future__ import print_function | |
|
16 | 17 | |
|
18 | # Stdlib | |
|
17 | 19 | import os |
|
18 | 20 | import sys |
|
19 | 21 | import shlex |
|
20 | import subprocess | |
|
21 | 22 | |
|
22 | from IPython.utils.terminal import set_term_title | |
|
23 | # Our own | |
|
24 | if sys.platform == 'win32': | |
|
25 | from ._process_win32 import _find_cmd, system, getoutput, AvoidUNCPath | |
|
26 | else: | |
|
27 | from ._process_posix import _find_cmd, system, getoutput | |
|
28 | ||
|
29 | from ._process_common import getoutputerror | |
|
23 | 30 | |
|
24 | 31 | #----------------------------------------------------------------------------- |
|
25 | 32 | # Code |
@@ -30,39 +37,6 b' class FindCmdError(Exception):' | |||
|
30 | 37 | pass |
|
31 | 38 | |
|
32 | 39 | |
|
33 | def _find_cmd(cmd): | |
|
34 | """Find the full path to a command using which.""" | |
|
35 | return os.popen('which %s' % cmd).read().strip() | |
|
36 | ||
|
37 | ||
|
38 | if os.name == 'posix': | |
|
39 | def _find_cmd(cmd): | |
|
40 | """Find the full path to a command using which.""" | |
|
41 | return getoutputerror('/usr/bin/env which %s' % cmd)[0] | |
|
42 | ||
|
43 | ||
|
44 | if sys.platform == 'win32': | |
|
45 | def _find_cmd(cmd): | |
|
46 | """Find the full path to a .bat or .exe using the win32api module.""" | |
|
47 | try: | |
|
48 | from win32api import SearchPath | |
|
49 | except ImportError: | |
|
50 | raise ImportError('you need to have pywin32 installed for this to work') | |
|
51 | else: | |
|
52 | PATH = os.environ['PATH'] | |
|
53 | extensions = ['.exe', '.com', '.bat', '.py'] | |
|
54 | path = None | |
|
55 | for ext in extensions: | |
|
56 | try: | |
|
57 | path = SearchPath(PATH,cmd + ext)[0] | |
|
58 | except: | |
|
59 | pass | |
|
60 | if path is None: | |
|
61 | raise OSError("command %r not found" % cmd) | |
|
62 | else: | |
|
63 | return path | |
|
64 | ||
|
65 | ||
|
66 | 40 | def find_cmd(cmd): |
|
67 | 41 | """Find absolute path to executable cmd in a cross platform manner. |
|
68 | 42 | |
@@ -87,7 +61,7 b' def find_cmd(cmd):' | |||
|
87 | 61 | if cmd == 'python': |
|
88 | 62 | return os.path.abspath(sys.executable) |
|
89 | 63 | try: |
|
90 | path = _find_cmd(cmd) | |
|
64 | path = _find_cmd(cmd).rstrip() | |
|
91 | 65 | except OSError: |
|
92 | 66 | raise FindCmdError('command could not be found: %s' % cmd) |
|
93 | 67 | # which returns empty if not found |
@@ -147,28 +121,6 b' def arg_split(s, posix=False):' | |||
|
147 | 121 | return list(lex) |
|
148 | 122 | |
|
149 | 123 | |
|
150 | def system(cmd, verbose=0, debug=0, header=''): | |
|
151 | """Execute a system command, return its exit status. | |
|
152 | ||
|
153 | Options: | |
|
154 | ||
|
155 | - verbose (0): print the command to be executed. | |
|
156 | ||
|
157 | - debug (0): only print, do not actually execute. | |
|
158 | ||
|
159 | - header (''): Header to print on screen prior to the executed command (it | |
|
160 | is only prepended to the command, no newlines are added). | |
|
161 | ||
|
162 | Note: a stateful version of this function is available through the | |
|
163 | SystemExec class.""" | |
|
164 | ||
|
165 | stat = 0 | |
|
166 | if verbose or debug: print header+cmd | |
|
167 | sys.stdout.flush() | |
|
168 | if not debug: stat = os.system(cmd) | |
|
169 | return stat | |
|
170 | ||
|
171 | ||
|
172 | 124 | def abbrev_cwd(): |
|
173 | 125 | """ Return abbreviated version of cwd, e.g. d:mydir """ |
|
174 | 126 | cwd = os.getcwd().replace('\\','/') |
@@ -186,182 +138,3 b' def abbrev_cwd():' | |||
|
186 | 138 | |
|
187 | 139 | return (drivepart + ( |
|
188 | 140 | cwd == '/' and '/' or tail)) |
|
189 | ||
|
190 | ||
|
191 | # This function is used by ipython in a lot of places to make system calls. | |
|
192 | # We need it to be slightly different under win32, due to the vagaries of | |
|
193 | # 'network shares'. A win32 override is below. | |
|
194 | ||
|
195 | def shell(cmd, verbose=0, debug=0, header=''): | |
|
196 | """Execute a command in the system shell, always return None. | |
|
197 | ||
|
198 | Options: | |
|
199 | ||
|
200 | - verbose (0): print the command to be executed. | |
|
201 | ||
|
202 | - debug (0): only print, do not actually execute. | |
|
203 | ||
|
204 | - header (''): Header to print on screen prior to the executed command (it | |
|
205 | is only prepended to the command, no newlines are added). | |
|
206 | ||
|
207 | Note: this is similar to system(), but it returns None so it can | |
|
208 | be conveniently used in interactive loops without getting the return value | |
|
209 | (typically 0) printed many times.""" | |
|
210 | ||
|
211 | stat = 0 | |
|
212 | if verbose or debug: print header+cmd | |
|
213 | # flush stdout so we don't mangle python's buffering | |
|
214 | sys.stdout.flush() | |
|
215 | ||
|
216 | if not debug: | |
|
217 | set_term_title("IPy " + cmd) | |
|
218 | os.system(cmd) | |
|
219 | set_term_title("IPy " + abbrev_cwd()) | |
|
220 | ||
|
221 | # override shell() for win32 to deal with network shares | |
|
222 | if os.name in ('nt','dos'): | |
|
223 | ||
|
224 | shell_ori = shell | |
|
225 | ||
|
226 | def shell(cmd, verbose=0, debug=0, header=''): | |
|
227 | if os.getcwd().startswith(r"\\"): | |
|
228 | path = os.getcwd() | |
|
229 | # change to c drive (cannot be on UNC-share when issuing os.system, | |
|
230 | # as cmd.exe cannot handle UNC addresses) | |
|
231 | os.chdir("c:") | |
|
232 | # issue pushd to the UNC-share and then run the command | |
|
233 | try: | |
|
234 | shell_ori('"pushd %s&&"'%path+cmd,verbose,debug,header) | |
|
235 | finally: | |
|
236 | os.chdir(path) | |
|
237 | else: | |
|
238 | shell_ori(cmd,verbose,debug,header) | |
|
239 | ||
|
240 | shell.__doc__ = shell_ori.__doc__ | |
|
241 | ||
|
242 | ||
|
243 | def getoutput(cmd, verbose=0, debug=0, header='', split=0): | |
|
244 | """Dummy substitute for perl's backquotes. | |
|
245 | ||
|
246 | Executes a command and returns the output. | |
|
247 | ||
|
248 | Accepts the same arguments as system(), plus: | |
|
249 | ||
|
250 | - split(0): if true, the output is returned as a list split on newlines. | |
|
251 | ||
|
252 | Note: a stateful version of this function is available through the | |
|
253 | SystemExec class. | |
|
254 | ||
|
255 | This is pretty much deprecated and rarely used, getoutputerror may be | |
|
256 | what you need. | |
|
257 | ||
|
258 | """ | |
|
259 | ||
|
260 | if verbose or debug: print header+cmd | |
|
261 | if not debug: | |
|
262 | pipe = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout | |
|
263 | output = pipe.read() | |
|
264 | # stipping last \n is here for backwards compat. | |
|
265 | if output.endswith('\n'): | |
|
266 | output = output[:-1] | |
|
267 | if split: | |
|
268 | return output.split('\n') | |
|
269 | else: | |
|
270 | return output | |
|
271 | ||
|
272 | ||
|
273 | # for compatibility with older naming conventions | |
|
274 | xsys = system | |
|
275 | ||
|
276 | ||
|
277 | def getoutputerror(cmd, verbose=0, debug=0, header='', split=0): | |
|
278 | """Return (standard output,standard error) of executing cmd in a shell. | |
|
279 | ||
|
280 | Accepts the same arguments as system(), plus: | |
|
281 | ||
|
282 | - split(0): if true, each of stdout/err is returned as a list split on | |
|
283 | newlines. | |
|
284 | ||
|
285 | Note: a stateful version of this function is available through the | |
|
286 | SystemExec class.""" | |
|
287 | ||
|
288 | if verbose or debug: print header+cmd | |
|
289 | if not cmd: | |
|
290 | if split: | |
|
291 | return [],[] | |
|
292 | else: | |
|
293 | return '','' | |
|
294 | if not debug: | |
|
295 | p = subprocess.Popen(cmd, shell=True, | |
|
296 | stdin=subprocess.PIPE, | |
|
297 | stdout=subprocess.PIPE, | |
|
298 | stderr=subprocess.PIPE, | |
|
299 | close_fds=True) | |
|
300 | pin, pout, perr = (p.stdin, p.stdout, p.stderr) | |
|
301 | ||
|
302 | tout = pout.read().rstrip() | |
|
303 | terr = perr.read().rstrip() | |
|
304 | pin.close() | |
|
305 | pout.close() | |
|
306 | perr.close() | |
|
307 | if split: | |
|
308 | return tout.split('\n'),terr.split('\n') | |
|
309 | else: | |
|
310 | return tout,terr | |
|
311 | ||
|
312 | ||
|
313 | class SystemExec: | |
|
314 | """Access the system and getoutput functions through a stateful interface. | |
|
315 | ||
|
316 | Note: here we refer to the system and getoutput functions from this | |
|
317 | library, not the ones from the standard python library. | |
|
318 | ||
|
319 | This class offers the system and getoutput functions as methods, but the | |
|
320 | verbose, debug and header parameters can be set for the instance (at | |
|
321 | creation time or later) so that they don't need to be specified on each | |
|
322 | call. | |
|
323 | ||
|
324 | For efficiency reasons, there's no way to override the parameters on a | |
|
325 | per-call basis other than by setting instance attributes. If you need | |
|
326 | local overrides, it's best to directly call system() or getoutput(). | |
|
327 | ||
|
328 | The following names are provided as alternate options: | |
|
329 | - xsys: alias to system | |
|
330 | - bq: alias to getoutput | |
|
331 | ||
|
332 | An instance can then be created as: | |
|
333 | >>> sysexec = SystemExec(verbose=1,debug=0,header='Calling: ') | |
|
334 | """ | |
|
335 | ||
|
336 | def __init__(self, verbose=0, debug=0, header='', split=0): | |
|
337 | """Specify the instance's values for verbose, debug and header.""" | |
|
338 | self.verbose = verbose | |
|
339 | self.debug = debug | |
|
340 | self.header = header | |
|
341 | self.split = split | |
|
342 | ||
|
343 | def system(self, cmd): | |
|
344 | """Stateful interface to system(), with the same keyword parameters.""" | |
|
345 | ||
|
346 | system(cmd, self.verbose, self.debug, self.header) | |
|
347 | ||
|
348 | def shell(self, cmd): | |
|
349 | """Stateful interface to shell(), with the same keyword parameters.""" | |
|
350 | ||
|
351 | shell(cmd, self.verbose, self.debug, self.header) | |
|
352 | ||
|
353 | xsys = system # alias | |
|
354 | ||
|
355 | def getoutput(self, cmd): | |
|
356 | """Stateful interface to getoutput().""" | |
|
357 | ||
|
358 | return getoutput(cmd, self.verbose, self.debug, self.header, self.split) | |
|
359 | ||
|
360 | def getoutputerror(self, cmd): | |
|
361 | """Stateful interface to getoutputerror().""" | |
|
362 | ||
|
363 | return getoutputerror(cmd, self.verbose, self.debug, self.header, self.split) | |
|
364 | ||
|
365 | bq = getoutput # alias | |
|
366 | ||
|
367 |
@@ -15,11 +15,14 b' Tests for platutils.py' | |||
|
15 | 15 | #----------------------------------------------------------------------------- |
|
16 | 16 | |
|
17 | 17 | import sys |
|
18 | from unittest import TestCase | |
|
18 | 19 | |
|
19 | 20 | import nose.tools as nt |
|
20 | 21 | |
|
21 | from IPython.utils.process import find_cmd, FindCmdError, arg_split | |
|
22 | from IPython.utils.process import (find_cmd, FindCmdError, arg_split, | |
|
23 | system, getoutput, getoutputerror) | |
|
22 | 24 | from IPython.testing import decorators as dec |
|
25 | from IPython.testing import tools as tt | |
|
23 | 26 | |
|
24 | 27 | #----------------------------------------------------------------------------- |
|
25 | 28 | # Tests |
@@ -66,3 +69,27 b' def test_arg_split():' | |||
|
66 | 69 | ] |
|
67 | 70 | for argstr, argv in tests: |
|
68 | 71 | nt.assert_equal(arg_split(argstr), argv) |
|
72 | ||
|
73 | ||
|
74 | class SubProcessTestCase(TestCase, tt.TempFileMixin): | |
|
75 | def setUp(self): | |
|
76 | """Make a valid python temp file.""" | |
|
77 | lines = ["from __future__ import print_function", | |
|
78 | "import sys", | |
|
79 | "print('on stdout', end='', file=sys.stdout)", | |
|
80 | "print('on stderr', end='', file=sys.stderr)", | |
|
81 | "sys.stdout.flush()", | |
|
82 | "sys.stderr.flush()"] | |
|
83 | self.mktmp('\n'.join(lines)) | |
|
84 | ||
|
85 | def test_system(self): | |
|
86 | system('python "%s"' % self.fname) | |
|
87 | ||
|
88 | def test_getoutput(self): | |
|
89 | out = getoutput('python "%s"' % self.fname) | |
|
90 | self.assertEquals(out, 'on stdout') | |
|
91 | ||
|
92 | def test_getoutput(self): | |
|
93 | out, err = getoutputerror('python "%s"' % self.fname) | |
|
94 | self.assertEquals(out, 'on stdout') | |
|
95 | self.assertEquals(err, 'on stderr') |
@@ -19,9 +19,6 b' from __future__ import print_function' | |||
|
19 | 19 | import inspect |
|
20 | 20 | import os |
|
21 | 21 | import re |
|
22 | import sys | |
|
23 | ||
|
24 | from subprocess import Popen, PIPE | |
|
25 | 22 | |
|
26 | 23 | # Our own |
|
27 | 24 | from IPython.core.interactiveshell import ( |
@@ -83,26 +80,6 b' class ZMQInteractiveShell(InteractiveShell):' | |||
|
83 | 80 | |
|
84 | 81 | displayhook_class = Type(ZMQDisplayHook) |
|
85 | 82 | |
|
86 | def system(self, cmd): | |
|
87 | cmd = self.var_expand(cmd, depth=2).strip() | |
|
88 | ||
|
89 | # Runnning a bacgkrounded process from within the gui isn't supported | |
|
90 | # because we do p.wait() at the end. So instead of silently blocking | |
|
91 | # we simply refuse to run in this mode, to avoid surprising the user. | |
|
92 | if cmd.endswith('&'): | |
|
93 | raise OSError("Background processes not supported.") | |
|
94 | ||
|
95 | sys.stdout.flush() | |
|
96 | sys.stderr.flush() | |
|
97 | p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE) | |
|
98 | for line in p.stdout.read().split('\n'): | |
|
99 | if len(line) > 0: | |
|
100 | print(line) | |
|
101 | for line in p.stderr.read().split('\n'): | |
|
102 | if len(line) > 0: | |
|
103 | print(line, file=sys.stderr) | |
|
104 | p.wait() | |
|
105 | ||
|
106 | 83 | def init_io(self): |
|
107 | 84 | # This will just use sys.stdout and sys.stderr. If you want to |
|
108 | 85 | # override sys.stdout and sys.stderr themselves, you need to do that |
General Comments 0
You need to be logged in to leave comments.
Login now