Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
corpnewt
GitHub Repository: corpnewt/gibMacOS
Path: blob/master/Scripts/run.py
175 views
1
import sys, subprocess, time, threading, shlex
2
try:
3
from Queue import Queue, Empty
4
except:
5
from queue import Queue, Empty
6
7
ON_POSIX = 'posix' in sys.builtin_module_names
8
9
class Run:
10
11
def __init__(self):
12
return
13
14
def _read_output(self, pipe, q):
15
try:
16
for line in iter(lambda: pipe.read(1), b''):
17
q.put(line)
18
except ValueError:
19
pass
20
pipe.close()
21
22
def _create_thread(self, output):
23
# Creates a new queue and thread object to watch based on the output pipe sent
24
q = Queue()
25
t = threading.Thread(target=self._read_output, args=(output, q))
26
t.daemon = True
27
return (q,t)
28
29
def _stream_output(self, comm, shell = False):
30
output = error = ""
31
p = None
32
try:
33
if shell and type(comm) is list:
34
comm = " ".join(shlex.quote(x) for x in comm)
35
if not shell and type(comm) is str:
36
comm = shlex.split(comm)
37
p = subprocess.Popen(comm, shell=shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0, universal_newlines=True, close_fds=ON_POSIX)
38
# Setup the stdout thread/queue
39
q,t = self._create_thread(p.stdout)
40
qe,te = self._create_thread(p.stderr)
41
# Start both threads
42
t.start()
43
te.start()
44
45
while True:
46
c = z = ""
47
try: c = q.get_nowait()
48
except Empty: pass
49
else:
50
sys.stdout.write(c)
51
output += c
52
sys.stdout.flush()
53
try: z = qe.get_nowait()
54
except Empty: pass
55
else:
56
sys.stderr.write(z)
57
error += z
58
sys.stderr.flush()
59
if not c==z=="": continue # Keep going until empty
60
# No output - see if still running
61
p.poll()
62
if p.returncode != None:
63
# Subprocess ended
64
break
65
# No output, but subprocess still running - stall for 20ms
66
time.sleep(0.02)
67
68
o, e = p.communicate()
69
return (output+o, error+e, p.returncode)
70
except:
71
if p:
72
try: o, e = p.communicate()
73
except: o = e = ""
74
return (output+o, error+e, p.returncode)
75
return ("", "Command not found!", 1)
76
77
def _decode(self, value, encoding="utf-8", errors="ignore"):
78
# Helper method to only decode if bytes type
79
if sys.version_info >= (3,0) and isinstance(value, bytes):
80
return value.decode(encoding,errors)
81
return value
82
83
def _run_command(self, comm, shell = False):
84
c = None
85
try:
86
if shell and type(comm) is list:
87
comm = " ".join(shlex.quote(x) for x in comm)
88
if not shell and type(comm) is str:
89
comm = shlex.split(comm)
90
p = subprocess.Popen(comm, shell=shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
91
c = p.communicate()
92
except:
93
if c == None:
94
return ("", "Command not found!", 1)
95
return (self._decode(c[0]), self._decode(c[1]), p.returncode)
96
97
def run(self, command_list, leave_on_fail = False):
98
# Command list should be an array of dicts
99
if type(command_list) is dict:
100
# We only have one command
101
command_list = [command_list]
102
output_list = []
103
for comm in command_list:
104
args = comm.get("args", [])
105
shell = comm.get("shell", False)
106
stream = comm.get("stream", False)
107
sudo = comm.get("sudo", False)
108
stdout = comm.get("stdout", False)
109
stderr = comm.get("stderr", False)
110
mess = comm.get("message", None)
111
show = comm.get("show", False)
112
113
if not mess == None:
114
print(mess)
115
116
if not len(args):
117
# nothing to process
118
continue
119
if sudo:
120
# Check if we have sudo
121
out = self._run_command(["which", "sudo"])
122
if "sudo" in out[0]:
123
# Can sudo
124
if type(args) is list:
125
args.insert(0, out[0].replace("\n", "")) # add to start of list
126
elif type(args) is str:
127
args = out[0].replace("\n", "") + " " + args # add to start of string
128
129
if show:
130
print(" ".join(args))
131
132
if stream:
133
# Stream it!
134
out = self._stream_output(args, shell)
135
else:
136
# Just run and gather output
137
out = self._run_command(args, shell)
138
if stdout and len(out[0]):
139
print(out[0])
140
if stderr and len(out[1]):
141
print(out[1])
142
# Append output
143
output_list.append(out)
144
# Check for errors
145
if leave_on_fail and out[2] != 0:
146
# Got an error - leave
147
break
148
if len(output_list) == 1:
149
# We only ran one command - just return that output
150
return output_list[0]
151
return output_list
152
153