4 @brief wxGUI command interface
11 - Popen (from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440554)
18 (C) 2007-2008, 2010-2011 by the GRASS Development Team
19 This program is free software under the GNU General Public
20 License (>=v2). Read the file COPYING that comes with GRASS
23 @author Jachym Cepicky
24 @author Martin Landa <landa.martin gmail.com>
41 compatPath = os.path.join(globalvar.ETCWXDIR,
"compat")
42 sys.path.append(compatPath)
44 if subprocess.mswindows:
45 from win32file
import ReadFile, WriteFile
46 from win32pipe
import PeekNamedPipe
51 from threading
import Thread
54 grassPath = os.path.join(globalvar.ETCDIR,
"python")
55 sys.path.append(grassPath)
59 from debug
import Debug
as Debug
62 def __init__(self, message, parent = None, caption = None, showTraceback = True):
65 style = wx.OK | wx.ICON_ERROR | wx.CENTRE
66 exc_type, exc_value, exc_traceback = sys.exc_info()
68 exception = traceback.format_exc()
69 reason = exception.splitlines()[-1].
split(
':', 1)[-1].strip()
71 if Debug.GetLevel() > 0
and exc_traceback:
72 sys.stderr.write(exception)
74 if showTraceback
and exc_traceback:
75 wx.MessageBox(parent = parent,
76 message = message +
'\n\n%s: %s\n\n%s' % \
82 wx.MessageBox(parent = parent,
89 caption = _(
'Warning')
90 style = wx.OK | wx.ICON_WARNING | wx.CENTRE
91 wx.MessageBox(parent = parent,
98 caption = _(
'Message')
99 style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE
100 wx.MessageBox(parent = parent,
110 return str(self.
value)
113 """!Subclass subprocess.Popen"""
115 if subprocess.mswindows:
117 kwargs[
'args'] =
map(utils.EncodeString, kwargs[
'args'])
121 targs[0] =
map(utils.EncodeString, args[0])
124 subprocess.Popen.__init__(self, *args, **kwargs)
126 def recv(self, maxsize = None):
127 return self.
_recv(
'stdout', maxsize)
130 return self.
_recv(
'stderr', maxsize)
140 return getattr(self, which), maxsize
142 def _close(self, which):
143 getattr(self, which).close()
144 setattr(self, which,
None)
147 """!Try to kill running process"""
148 if subprocess.mswindows:
150 handle = win32api.OpenProcess(1, 0, self.pid)
151 return (0 != win32api.TerminateProcess(handle, 0))
154 os.kill(-self.pid, signal.SIGTERM)
158 if subprocess.mswindows:
164 x = msvcrt.get_osfhandle(self.stdin.fileno())
165 (errCode, written) = WriteFile(x, input)
167 return self.
_close(
'stdin')
168 except (subprocess.pywintypes.error, Exception), why:
169 if why[0]
in (109, errno.ESHUTDOWN):
170 return self.
_close(
'stdin')
175 def _recv(self, which, maxsize):
181 x = msvcrt.get_osfhandle(conn.fileno())
182 (read, nAvail, nMessage) = PeekNamedPipe(x, 0)
186 (errCode, read) = ReadFile(x, nAvail,
None)
189 except (subprocess.pywintypes.error, Exception), why:
190 if why[0]
in (109, errno.ESHUTDOWN):
194 if self.universal_newlines:
195 read = self._translate_newlines(read)
203 if not select.select([], [self.stdin], [], 0)[1]:
207 written = os.write(self.stdin.fileno(), input)
209 if why[0] == errno.EPIPE:
210 return self.
_close(
'stdin')
215 def _recv(self, which, maxsize):
220 flags = fcntl.fcntl(conn, fcntl.F_GETFL)
222 fcntl.fcntl(conn, fcntl.F_SETFL, flags| os.O_NONBLOCK)
225 if not select.select([conn], [], [], 0)[0]:
228 r = conn.read(maxsize)
233 if self.universal_newlines:
234 r = self._translate_newlines(r)
238 fcntl.fcntl(conn, fcntl.F_SETFL, flags)
240 message =
"Other end disconnected!"
251 while time.time() < x
or r:
255 raise Exception(message)
261 time.sleep(
max((x-time.time())/tr, 0))
268 raise Exception(message)
269 data = buffer(data, sent)
272 """!Run command in separate thread. Used for commands launched
275 If stdout/err is redirected, write() method is required for the
279 cmd = Command(cmd=['d.rast', 'elevation.dem'], verbose=3, wait=True)
281 if cmd.returncode == None:
283 elif cmd.returncode == 0:
286 print 'FAILURE (%d)' % cmd.returncode
289 @param cmd command given as list
290 @param stdin standard input stream
291 @param verbose verbose level [0, 3] (--q, --v)
292 @param wait wait for child execution terminated
293 @param rerr error handling (when CmdError raised).
294 True for redirection to stderr, False for GUI dialog,
295 None for no operation (quiet mode)
296 @param stdout redirect standard output or None
297 @param stderr redirect standard error output or None
299 def __init__ (self, cmd, stdin = None,
300 verbose =
None, wait =
True, rerr =
False,
301 stdout =
None, stderr =
None):
302 Debug.msg(1,
"gcmd.Command(): %s" %
' '.join(cmd))
310 if (
'--q' not in self.
cmd and '--quiet' not in self.
cmd)
and \
311 (
'--v' not in self.
cmd and '--verbose' not in self.
cmd):
312 if verbose
is not None:
314 self.cmd.append(
'--quiet')
316 self.cmd.append(
'--verbose')
318 verbose_orig = os.getenv(
"GRASS_VERBOSE")
319 os.environ[
"GRASS_VERBOSE"] = str(verbose)
326 self.cmdThread.start()
329 self.cmdThread.join()
330 if self.cmdThread.module:
331 self.cmdThread.module.wait()
336 self.cmdThread.join(0.5)
340 Debug.msg (3,
"Command(): cmd='%s', wait=%s, returncode=%d, alive=%s" % \
341 (
' '.join(cmd), wait, self.
returncode, self.cmdThread.isAlive()))
345 (_(
"Execution failed:"),
347 os.linesep, os.linesep,
351 elif rerr == sys.stderr:
352 stderr.write(
"Execution failed: '%s'" % (
' '.join(self.
cmd)))
353 stderr.write(
"%sDetails:%s%s" % (os.linesep,
359 Debug.msg (3,
"Command(): cmd='%s', wait=%s, returncode=?, alive=%s" % \
360 (
' '.join(cmd), wait, self.cmdThread.isAlive()))
363 os.environ[
"GRASS_VERBOSE"] = verbose_orig
364 elif "GRASS_VERBOSE" in os.environ:
365 del os.environ[
"GRASS_VERBOSE"]
367 def __ReadOutput(self, stream):
368 """!Read stream and return list of lines
370 @param stream stream to be read
378 line = stream.readline()
381 line = line.replace(
'%s' % os.linesep,
'').strip()
382 lineList.append(line)
386 def __ReadErrOutput(self):
387 """!Read standard error output and return list of lines"""
390 def __ProcessStdErr(self):
392 Read messages/warnings/errors from stderr
394 @return list of (type, message)
399 lines = self.cmdThread.error.strip(
'%s' % os.linesep). \
400 split(
'%s' % os.linesep)
410 if 'GRASS_INFO_WARNING' in line:
412 elif 'GRASS_INFO_ERROR' in line:
414 elif 'GRASS_INFO_END':
415 msg.append((type, content))
420 content += line.split(
':', 1)[1].strip()
422 msg.append((
None, line.strip()))
426 def __GetError(self):
427 """!Get error message or ''"""
428 if not self.cmdThread.module:
429 return _(
"Unable to exectute command: '%s'") %
' '.join(self.
cmd)
433 enc = locale.getdefaultlocale()[1]
435 return unicode(msg, enc)
442 """!Create separate thread for command. Used for commands launched
443 on the background."""
444 def __init__ (self, cmd, stdin = None,
445 stdout = sys.stdout, stderr = sys.stderr):
447 @param cmd command (given as list)
448 @param stdin standard input stream
449 @param stdout redirect standard output or None
450 @param stderr redirect standard error output or None
452 Thread.__init__(self)
469 os.environ[
"GRASS_MESSAGE_FORMAT"] =
"gui"
475 del os.environ[
"GRASS_MESSAGE_FORMAT"]
479 if len(self.
cmd) == 0:
482 Debug.msg(1,
"gcmd.CommandThread(): %s" %
' '.join(self.
cmd))
488 if sys.platform ==
'win32' and os.path.splitext(self.
cmd[0])[1] ==
'.py':
489 os.chdir(os.path.join(os.getenv(
'GISBASE'),
'etc',
'gui',
'scripts'))
490 args = [sys.executable, self.
cmd[0]] + self.
cmd[1:]
494 stdin = subprocess.PIPE,
495 stdout = subprocess.PIPE,
496 stderr = subprocess.PIPE,
497 shell = sys.platform ==
"win32")
500 print >> sys.stderr, e
504 self.module.stdin.write(self.
stdin)
505 self.module.stdin.close()
510 def _redirect_stream(self):
511 """!Redirect stream"""
514 out_fileno = self.module.stdout.fileno()
515 if not subprocess.mswindows:
516 flags = fcntl.fcntl(out_fileno, fcntl.F_GETFL)
517 fcntl.fcntl(out_fileno, fcntl.F_SETFL, flags| os.O_NONBLOCK)
521 out_fileno = self.module.stderr.fileno()
522 if not subprocess.mswindows:
523 flags = fcntl.fcntl(out_fileno, fcntl.F_GETFL)
524 fcntl.fcntl(out_fileno, fcntl.F_SETFL, flags| os.O_NONBLOCK)
527 while self.module.poll()
is None:
534 self.stdout.write(line)
537 self.stderr.write(line)
544 self.stdout.write(line)
547 self.stderr.write(line)
552 """!Abort running process, used by main thread to signal an abort"""
555 def _formatMsg(text):
556 """!Format error messages for dialogs
559 for line
in text.splitlines():
562 elif 'GRASS_INFO_MESSAGE' in line:
563 message += line.split(
':', 1)[1].strip() +
'\n'
564 elif 'GRASS_INFO_WARNING' in line:
565 message += line.split(
':', 1)[1].strip() +
'\n'
566 elif 'GRASS_INFO_ERROR' in line:
567 message += line.split(
':', 1)[1].strip() +
'\n'
568 elif 'GRASS_INFO_END' in line:
571 message += line.strip() +
'\n'
575 def RunCommand(prog, flags = "", overwrite = False, quiet = False, verbose = False,
576 parent =
None, read =
False, stdin =
None, getErrorMsg =
False, **kwargs):
577 """!Run GRASS command
579 @param prog program to run
580 @param flags flags given as a string
581 @param overwrite, quiet, verbose flags
582 @param parent parent window for error messages
583 @param read fetch stdout
584 @param stdin stdin or None
585 @param getErrorMsg get error messages on failure
586 @param kwargs program parameters
588 @return returncode (read == False and getErrorMsg == False)
589 @return returncode, messages (read == False and getErrorMsg == True)
590 @return stdout (read == True and getErrorMsg == False)
591 @return returncode, stdout, messages (read == True and getErrorMsg == True)
592 @return stdout, stderr
594 cmdString =
' '.join(grass.make_command(prog, flags, overwrite,
595 quiet, verbose, **kwargs))
597 Debug.msg(1,
"gcmd.RunCommand(): %s" % cmdString)
599 kwargs[
'stderr'] = subprocess.PIPE
602 kwargs[
'stdout'] = subprocess.PIPE
605 kwargs[
'stdin'] = subprocess.PIPE
607 ps = grass.start_command(prog, flags, overwrite, quiet, verbose, **kwargs)
609 Debug.msg(2,
"gcmd.RunCommand(): command started")
612 ps.stdin.write(stdin)
616 Debug.msg(3,
"gcmd.RunCommand(): decoding string")
617 stdout, stderr =
map(utils.DecodeString, ps.communicate())
620 Debug.msg(1,
"gcmd.RunCommand(): get return code %d" % ret)
622 Debug.msg(3,
"gcmd.RunCommand(): print error")
623 if ret != 0
and parent:
624 Debug.msg(2,
"gcmd.RunCommand(): error %s" % stderr)
626 Debug.msg(2,
"gcmd.RunCommand(): nothing to print ???")
631 Debug.msg(3,
"gcmd.RunCommand(): print read error")
636 return ret, _formatMsg(stderr)
639 Debug.msg(2,
"gcmd.RunCommand(): return stdout\n'%s'" % stdout)
641 Debug.msg(2,
"gcmd.RunCommand(): return stdout = None")
645 Debug.msg(2,
"gcmd.RunCommand(): return ret, stdout")
646 if read
and getErrorMsg:
647 return ret, stdout, _formatMsg(stderr)
649 Debug.msg(2,
"gcmd.RunCommand(): return result")
650 return stdout, _formatMsg(stderr)