aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/mtn2git/mtn/utility.py
blob: c7345c5d1ea8ef72785b087b1fda06958948090b (plain)
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
import popen2
import select
import fcntl
import os

def set_nonblocking(fd):
    fl = fcntl.fcntl(fd, fcntl.F_GETFL)
    fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NDELAY)

def run_command(command, timeout=None, to_child=None):
    "returns a tuple of (was_timeout, exit_code, data_read)"
    p = popen2.Popen3(command, capturestderr=True)
    set_nonblocking(p.fromchild)
    set_nonblocking(p.childerr)
    fromchild_read = ""
    childerr_read = ""
    was_timeout = False
    if to_child != None:
        p.tochild.write(to_child)
    p.tochild.close()
    while 1:
        ro, rw, re = select.select([p.fromchild], [], [p.childerr], timeout)
        if not ro and not rw and not re:
            was_timeout = True
            break
        if p.fromchild in ro:
            recv = p.fromchild.read()
            if recv == "": break
            fromchild_read += recv
        if p.childerr in re:
            recv = p.childerr.read()
            if recv == "": break
            childerr_read += recv
    if not was_timeout:
        # check for any data we might have missed (due to a premature break)
        # (if there isn't anything we just get a IOError, which we don't mind
        try: fromchild_read += p.fromchild.read()
        except IOError: pass
        try: childerr_read += p.childerr.read()
        except IOError: pass
    p.fromchild.close()
    # if there wasn't a timeout, the program should have exited; in which case we should wait() for it
    # otherwise, it might be hung, so the parent should wait for it.
    # (wrap in a try: except: just in case some other thread happens to wait() and grab ours; god wrapping 
    # python around UNIX is horrible sometimes)
    exitcode = None
    try: 
        if not was_timeout: exitcode = p.wait() >> 8
    except: pass
    return { 'run_command' : command,
         'timeout' : was_timeout, 
         'exitcode' : exitcode, 
         'fromchild' : fromchild_read, 
         'childerr' : childerr_read }

def iter_command(command, timeout=None):
    p = popen2.Popen3(command, capturestderr=True)
    set_nonblocking(p.fromchild)
    set_nonblocking(p.childerr)
    fromchild_read = ""
    childerr_read = ""
    was_timeout = False
    while 1:
        ro, rw, re = select.select([p.fromchild], [], [p.childerr], timeout)
        if not ro and not rw and not re:
            was_timeout = True
            break
        if p.fromchild in ro:
            recv = p.fromchild.read()
            if recv == "": break
            fromchild_read += recv
            while 1:
                nl = fromchild_read.find('\n')
                if nl == -1: break
                yield fromchild_read[:nl]
                fromchild_read = fromchild_read[nl+1:]
        if p.childerr in re:
            recv = p.childerr.read()
            if recv == "": break
            childerr_read += recv
    if not was_timeout:
        # check for any data we might have missed (due to a premature break)
        # (if there isn't anything we just get a IOError, which we don't mind
        try: fromchild_read += p.fromchild.read()
        except IOError: pass
        try: childerr_read += p.childerr.read()
        except IOError: pass
    p.fromchild.close()
    p.tochild.close()
    # yield anything left over
    to_yield = fromchild_read.split('\n')
    while len(to_yield): yield to_yield.pop()
    # call wait()
    try:
        if not was_timeout: p.wait()
    except: pass
    if len(childerr_read): raise Exception("data on stderr (command is %s)" % command, childerr_read)
    if was_timeout: raise Exception("command timeout")