forked from markbirbeck/sublime-text-shell-command
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathOsShell.py
137 lines (109 loc) · 4.5 KB
/
OsShell.py
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
import os
import shlex
import subprocess
import threading
import select
import sublime
from . import SublimeHelper as SH
def process(commands, callback=None, settings=None, working_dir=None, wait_for_completion=None, **kwargs):
# If there's no callback method then just return the output as
# a string:
#
if callback is None:
return _process(commands, settings=settings, working_dir=working_dir, wait_for_completion=wait_for_completion, **kwargs)
# If there is a callback then run this asynchronously:
#
else:
thread = threading.Thread(target=_process, kwargs={
'commands': commands,
'callback': callback,
'settings': settings,
'working_dir': working_dir,
'wait_for_completion': wait_for_completion
})
thread.start()
def _process(commands, callback=None, settings=None, working_dir=None, wait_for_completion=None, **kwargs):
'''Process one or more OS commands.'''
if wait_for_completion is None:
wait_for_completion = False
# We're expecting a list of commands, so if we only have one, convert
# it to a list:
#
if isinstance(commands, str):
commands = [commands]
results = []
# Windows needs STARTF_USESHOWWINDOW in order to start the process with a
# hidden window.
#
# See:
#
# http://stackoverflow.com/questions/1016384/cross-platform-subprocess-with-hidden-window
#
startupinfo = None
if os.name == 'nt':
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
# Now we can execute each command:
#
for command in commands:
# See if there are any interactive shell settings that we could use:
#
bash_env = None
if settings is not None and settings.has('shell_configuration_file'):
bash_env = settings.get('shell_configuration_file')
else:
bash_env = os.getenv('ENV')
if bash_env is not None:
command = '. {} && {}'.format(bash_env, command)
try:
proc = subprocess.Popen(command,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
shell=True,
cwd=working_dir,
startupinfo=startupinfo)
# We're going to keep polling the command and either:
#
# 1. we get None to tell us that the command is still running, or;
# 2. we get a return code to indicate that the command has finished.
#
return_code = None
while return_code is None:
return_code = proc.poll()
# If there's no error then see what we got from the command:
#
if return_code is None or return_code == 0:
r, _, _ = select.select([proc.stdout], [], [])
if r:
# Process whatever output we can get:
#
output = True
while output:
output = proc.stdout.readline().decode()
# If the caller wants everything in one go, or
# there is no callback function, then batch up
# the output. Otherwise pass it back to the
# caller as it becomes available:
#
if wait_for_completion is True or callback is None:
results += output
else:
SH.main_thread(callback, output, **kwargs)
except subprocess.CalledProcessError as e:
SH.main_thread(callback, e.returncode)
except OSError as e:
if e.errno == 2:
sublime.message_dialog('Command not found\n\nCommand is: %s' % command)
else:
raise e
# Concatenate all of the results and return the value. If we've been
# using the callback then just make one last call with 'None' to indicate
# that we're finished:
#
result = ''.join(results)
if callback is None:
return result
if wait_for_completion is True:
SH.main_thread(callback, result, **kwargs)
SH.main_thread(callback, None, **kwargs)