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 | 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 |
-
+
-
+
-
+
| # encoding: utf-8
# api: streamtuner2
# title: win32/subprocess
# description: Utilizes subprocess spawning or win32 API instead of os.system
# version: 0.3.1
# version: 0.4
# depends: streamtuner2 > 2.2.0, python >= 2.7
# priority: optional
# config:
# { name: cmd_spawn, type: select, select: "popen|shell|call|execv|spawnv|pywin32|win32api|system", value: popen, description: Spawn method }
# { name: cmd_flags, type: select, select: "none|nowait|detached|nowaito|all|sw_hide||sw_minimize|sw_show", value: nowait, description: Process creation flags (win32) }
# { name: cmd_flags, type: select, select: "none|nowait|detached|nowaito|all|sw_hide|sw_maximize|sw_minimize|sw_show", value: nowait, description: Process creation flags (win32) }
# type: handler
# category: io
#
# Overrides the action.run method with subprocess.Popen() and a bit
# cmdline parsing. Which mostly makes sense on Windows to avoid some
# `start` bugs, such as "http://.." arguments getting misinterpreted.
# Also works on Linux, though with few advantages and some gotchas.
#
# +------------------+-----+------+---------+-------+----------------------+
# | VARIANT | SYS | ARGS | FLAGS | PATHS | NOTES |
# +------------------+-----+------+---------+-------+----------------------+
# | subprocess.popen | * | [] | all | base | Most compatible |
# | subprocess.shell | lnx | str | all | base | Linux only? |
# | subprocess.call | * | [] | all | base& | May block Gtk thread |
# | os.execv | w32 | s/[] | - | full& | fork+exec |
# | os.spawnv | lnx | s/[] | nowait | full& | ? |
# | pywin32.CreatePr | w32 | str | detached| full | Few parameters used |
# | win32api.WinExec | w32 | str | sw_show | base | Mostly like `start`? |
# | win32api.WinExec | w32 | str | sw_* | base | Mostly like `start`? |
# | system/default | * | str | - | base | normal action.run() |
# +------------------+-----+------+---------+-------+----------------------+
#
# · The flags are only supported on Windows. The FLAGS column just lists
# recommended defaults. Will implicitly be `none` for Linux.
# · Note that for Linux, you should decorate player commands with "&" and
# use absolute paths for call/exec/spawn to work as expected. |
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 | 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 |
+
-
+
+
+
+
+
+
-
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
-
+
+
-
+
+
+
+
+
-
-
+
+
+
-
-
+
| from channels import FeaturePlugin
import action
try:
import win32process
import win32api
except Exception as e:
log.ERR("st2subprocess:", e)
log.ERR("pywin32/win32api not available", e)
# references to original action.* methods
orig_run = action.run
orig_quote = action.quote
# hook action.run
class st2subprocess (FeaturePlugin):
# option strings to creationflags
flagmap = {
"nowait": os.P_NOWAIT,
"detached": 0x00000008, # https://stackoverflow.com/a/13593257/345031
"nowait0": os.P_NOWAITO,
"nowaito": os.P_NOWAITO,
"all": 8 | os.P_NOWAIT | os.P_NOWAITO,
"wait": os.P_WAIT,
"none": 0,
"sw_hide": 0, # https://docs.python.org/2.7/library/subprocess.html#subprocess.STARTUPINFO
"sw_minimize": 2, # or 6
"sw_maximize": 3,
"sw_show": 5, # https://msdn.microsoft.com/en-us/library/windows/desktop/ms633548(v=vs.85).aspx
}
# swap out action.run()
def init2(self, parent, *k, **kw):
self.osrun = action.run
action.run = self.action_run
# override for exec method
def action_run(self, cmd):
action.run = self.run
if conf.windows:
action.quote = self.quote
# override for action.quote
def quote(self, ins):
# use default for string-style exec methods / or array arg
if conf.cmd_spawn in ("system", "default", "win32api") or type(ins) is list:
return orig_quote(ins)
# only Posix-style shell quoting
return pipes.quote(ins)
# override for action.run (os.system exec method)
def run(self, cmd):
# blacklist
if re.search("streamripper|cmd\.exe", cmd):
return self.osrun(cmd)
return orig_run(cmd)
# split args
args = shlex.split(cmd)
# undo win32 quoting damage
if conf.windows and re.search('\^', cmd):
if conf.windows and re.search('\^', cmd): #and action.quote != self.quote:
args = [re.sub(r'\^(?=[()<>"&^])', '', s) for s in args]
# flags
if conf.windows and conf.cmd_flags in self.flagmap:
flags = self.flagmap[conf.cmd_flags] if conf.windows and conf.cmd_flags in self.flagmap else 0
flags = self.flagmap[conf.cmd_flags]
else:
flags = 0
# variant
v = conf.cmd_spawn
# debug
log.EXEC("st2subprocess:", args, "creationflags=%s"%flags)
log.EXEC("st2subprocess/%s:" % v, args, "creationflags=%s"%flags)
#-- Popen → https://docs.python.org/2/library/subprocess.html#popen-constructor
v = conf.cmd_spawn
if v in ("popen", "shell"):
#-- Popen w/ shell=True and string cmd
if (v=="shell"):
args = [cmd]
log.POPEN(
subprocess.Popen(args, shell=(v=="shell"), creationflags=flags).__dict__
)
#-- call → https://docs.python.org/2/library/subprocess.html#replacing-os-system
elif v == "call":
log.CALL(
subprocess.call(args, creationflags=flags).__dict__
subprocess.call(args, creationflags=flags)
)
#-- execv → https://docs.python.org/2/library/os.html#os.execv
elif v == "execv":
log.OS_EXECV(
os.execv(args[0], args) if os.fork() == 0 else "..."
)
#-- spawnv → https://docs.python.org/2/library/os.html#os.spawnv |
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145 | 153
154
155
156
157
158
159
160
161
162
163
164
165
166
167 |
-
+
-
-
+
| None, cmd, None, None, 0, flags,
None, None, win32process.STARTUPINFO()
)
)
#-- win32api
elif conf.cmd_spawn == "win32api":
log.WIN32API_WinExec(
win32api.WinExec(cmd, flags)
win32api.WinExec(cmd, flags) # should only use SW_* flags
)
# fallback
else:
return self.osrun(cmd)
return orig_run(cmd)
|