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
|
# Copyright 2016 Free Software Foundation, Inc.
# This file is part of GNU Radio
#
# GNU Radio Companion is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# GNU Radio Companion is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
import gobject
import os
import threading
import shlex
import subprocess
import sys
import re
from distutils.spawn import find_executable
from ..core import Messages
class ExecFlowGraphThread(threading.Thread):
"""Execute the flow graph as a new process and wait on it to finish."""
def __init__(self, flow_graph_page, xterm_executable, callback):
"""
ExecFlowGraphThread constructor.
Args:
action_handler: an instance of an ActionHandler
"""
threading.Thread.__init__(self)
self.page = flow_graph_page # store page and dont use main window calls in run
self.xterm_executable = xterm_executable
self.update_callback = callback
try:
self.process = self._popen()
self.page.set_proc(self.process)
self.update_callback()
self.start()
except Exception as e:
Messages.send_verbose_exec(str(e))
Messages.send_end_exec()
def _popen(self):
"""
Execute this python flow graph.
"""
run_command = self.page.get_flow_graph().get_option('run_command')
generator = self.page.get_generator()
try:
run_command = run_command.format(
python=shlex_quote(sys.executable),
filename=shlex_quote(generator.file_path))
run_command_args = shlex.split(run_command)
except Exception as e:
raise ValueError("Can't parse run command {!r}: {0}".format(run_command, e))
# When in no gui mode on linux, use a graphical terminal (looks nice)
xterm_executable = find_executable(self.xterm_executable)
if generator.generate_options == 'no_gui' and xterm_executable:
run_command_args = [xterm_executable, '-e', run_command]
# this does not reproduce a shell executable command string, if a graphical
# terminal is used. Passing run_command though shlex_quote would do it but
# it looks really ugly and confusing in the console panel.
Messages.send_start_exec(' '.join(run_command_args))
return subprocess.Popen(
args=run_command_args,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
shell=False, universal_newlines=True
)
def run(self):
"""
Wait on the executing process by reading from its stdout.
Use gobject.idle_add when calling functions that modify gtk objects.
"""
# handle completion
r = "\n"
while r:
gobject.idle_add(Messages.send_verbose_exec, r)
r = os.read(self.process.stdout.fileno(), 1024)
self.process.poll()
gobject.idle_add(self.done)
def done(self):
"""Perform end of execution tasks."""
Messages.send_end_exec(self.process.returncode)
self.page.set_proc(None)
self.update_callback()
###########################################################
# back-port from python3
###########################################################
_find_unsafe = re.compile(r'[^\w@%+=:,./-]').search
def shlex_quote(s):
"""Return a shell-escaped version of the string *s*."""
if not s:
return "''"
if _find_unsafe(s) is None:
return s
# use single quotes, and put single quotes into double quotes
# the string $'b is then quoted as '$'"'"'b'
return "'" + s.replace("'", "'\"'\"'") + "'"
|