Internet radio browser GUI for music/video streams from various directory services.

โŒˆโŒ‹ โŽ‡ branch:  streamtuner2


Check-in [81a043699c]

Overview
Comment:Fix orgi action.run reference; implemented .quote override
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 81a043699c560df7fe28db57c74748df25a04d3e
User & Date: mario on 2017-10-14 18:18:00
Other Links: manifest | tags
Context
2017-10-14
22:31
OggIcon plugin was misplaced check-in: ac3d7b7757 user: mario tags: trunk
18:18
Fix orgi action.run reference; implemented .quote override check-in: 81a043699c user: mario tags: trunk
11:42
Fix/merge popen and shell variant check-in: 7aa0a47c0b user: mario tags: trunk
Changes

Modified contrib/st2subprocess.py from [357a6531ee] to [dfd2e2745d].

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)