sources for cmdexec.py [rev. unknown]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
"""
module defining basic hook for executing commands
in a - as much as possible - platform independent way.
Current list:
    exec_cmd(cmd)       executes the given command and returns output
                        or ExecutionFailed exception (if exit status!=0)
"""
import os, sys
import py
#-----------------------------------------------------------
# posix external command execution
#-----------------------------------------------------------
def posix_exec_cmd(cmd, debug=False):
    """ return output of executing 'cmd'.
    raise ExecutionFailed exeception if the command failed.
    the exception will provide an 'err' attribute containing
    the error-output from the command.
    """
    __tracebackhide__ = True
    import popen2
    import errno
    #print "execing", cmd
    child = popen2.Popen3(cmd, 1)
    stdin, stdout, stderr = child.tochild, child.fromchild, child.childerr
    stdin.close()
    # XXX sometimes we get a blocked r.read() call (see below)
    #     although select told us there is something to read.
    #     only the next three lines appear to prevent
    #     the read call from blocking infinitely.
    import fcntl
    def set_non_block(fd):
        flags = fcntl.fcntl(fd, fcntl.F_GETFL)
        flags = flags | os.O_NONBLOCK
        fcntl.fcntl(fd, fcntl.F_SETFL, flags)
    set_non_block(stdout.fileno())
    set_non_block(stderr.fileno())
    #fcntl.fcntl(stdout, fcntl.F_SETFL, os.O_NONBLOCK)
    #fcntl.fcntl(stderr, fcntl.F_SETFL, os.O_NONBLOCK)
    import select
    out, err = [], []
    while 1:
        r_list = filter(lambda x: x and not x.closed, [stdout, stderr])
        if not r_list:
            break
        try:
            r_list = select.select(r_list, [], [])[0]
        except (select.error, IOError), se:
            if se.args[0] == errno.EINTR:
                continue
            else:
                raise
        for r  in r_list:
            try:
                data = r.read()   # XXX see XXX above
            except IOError, io:
                if io.args[0] == errno.EAGAIN:
                    continue
                # Connection Lost
                raise
            except OSError, ose:
                if ose.errno == errno.EPIPE:
                    # Connection Lost
                    raise
                if ose.errno == errno.EAGAIN: # MacOS-X does this
                    continue
                raise
            if not data:
                r.close()
                continue
            if debug:
                sys.stderr.write(data)
            if r is stdout:
                out.append(data)
            else:
                err.append(data)
    pid, systemstatus = os.waitpid(child.pid, 0)
    if pid != child.pid:
        raise ExecutionFailed, "child process disappeared during: "+ cmd
    if systemstatus:
        if os.WIFSIGNALED(systemstatus):
            status = os.WTERMSIG(systemstatus) + 128
        else:
            status = os.WEXITSTATUS(systemstatus)
        raise ExecutionFailed(status, systemstatus, cmd,
                              ''.join(out), ''.join(err))
    return "".join(out)
#-----------------------------------------------------------
# simple win32 external command execution
#-----------------------------------------------------------
def win32_exec_cmd(cmd):
    """ return output of executing 'cmd'.
    raise ExecutionFailed exeception if the command failed.
    the exception will provide an 'err' attribute containing
    the error-output from the command.
    Note that this method can currently deadlock because
    we don't have WaitForMultipleObjects in the std-python api.
    Further note that the rules for quoting are very special
    under Windows. Do a HELP CMD in a shell, and tell me if
    you understand this. For now, I try to do a fix.
    """
    #print "*****", cmd
    # the following quoting is only valid for CMD.EXE, not COMMAND.COM
    cmd_quoting = True
    try:
        if os.environ['COMSPEC'].upper().endswith('COMMAND.COM'):
            cmd_quoting = False
    except KeyError:
        pass
    if cmd_quoting:
        if '"' in cmd and not cmd.startswith('""'):
            cmd = '"%s"' % cmd
    return popen3_exec_cmd(cmd)
def popen3_exec_cmd(cmd):
    stdin, stdout, stderr = os.popen3(cmd)
    out = stdout.read()
    err = stderr.read()
    stdout.close()
    stderr.close()
    status = stdin.close()
    if status:
        raise ExecutionFailed(status, status, cmd, out, err)
    return out
def pypy_exec_cmd(cmd):
    return popen3_exec_cmd(cmd)
class ExecutionFailed(py.error.Error):
    def __init__(self, status, systemstatus, cmd, out, err):
        Exception.__init__(self)
        self.status = status
        self.systemstatus = systemstatus
        self.cmd = cmd
        self.err = err
        self.out = out
    def __str__(self):
        return "ExecutionFailed: %d  %s\n%s" %(self.status, self.cmd, self.err)
#
# choose correct platform-version
#
if sys.platform == 'win32':
    cmdexec = win32_exec_cmd
elif hasattr(sys, 'pypy') or hasattr(sys, 'pypy_objspaceclass'):
    cmdexec = popen3_exec_cmd
else:
    cmdexec = posix_exec_cmd
# export the exception under the name 'py.process.cmdexec.Error'
cmdexec.Error = ExecutionFailed
try:
    ExecutionFailed.__module__ = 'py.process.cmdexec'
    ExecutionFailed.__name__ = 'Error'
except (AttributeError, TypeError):
    pass