4 @brief wxGUI command interface
11 - gcmd::Popen (from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440554)
19 (C) 2007-2008, 2010-2011 by the GRASS Development Team
21 This program is free software under the GNU General Public License
22 (>=v2). Read the file COPYING that comes with GRASS for details.
24 @author Jachym Cepicky
25 @author Martin Landa <landa.martin gmail.com>
42 compatPath = os.path.join(globalvar.ETCWXDIR,
"compat")
43 sys.path.append(compatPath)
45 if subprocess.mswindows:
46 from win32file
import ReadFile, WriteFile
47 from win32pipe
import PeekNamedPipe
52 from threading
import Thread
56 from core
import globalvar
60 """!Return real command name - only for MS Windows
62 if sys.platform ==
'win32':
63 for ext
in globalvar.grassScripts.keys():
64 if cmd
in globalvar.grassScripts[ext]:
70 """!Decode string using system encoding
72 @param string string to be decoded
74 @return decoded string
80 Debug.msg(5,
"DecodeString(): enc=%s" % _enc)
81 return string.decode(_enc)
86 """!Return encoded string using system locales
88 @param string string to be encoded
90 @return encoded string
96 Debug.msg(5,
"EncodeString(): enc=%s" % _enc)
97 return string.encode(_enc)
102 def __init__(self, message, parent = None, caption = None, showTraceback = True):
105 style = wx.OK | wx.ICON_ERROR | wx.CENTRE
106 exc_type, exc_value, exc_traceback = sys.exc_info()
108 exception = traceback.format_exc()
109 reason = exception.splitlines()[-1].
split(
':', 1)[-1].strip()
111 if Debug.GetLevel() > 0
and exc_traceback:
112 sys.stderr.write(exception)
114 if showTraceback
and exc_traceback:
115 wx.MessageBox(parent = parent,
116 message = message +
'\n\n%s: %s\n\n%s' % \
122 wx.MessageBox(parent = parent,
129 caption = _(
'Warning')
130 style = wx.OK | wx.ICON_WARNING | wx.CENTRE
131 wx.MessageBox(parent = parent,
138 caption = _(
'Message')
139 style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE
140 wx.MessageBox(parent = parent,
153 """!Subclass subprocess.Popen"""
155 if subprocess.mswindows:
156 args = map(EncodeString, args)
158 subprocess.Popen.__init__(self, args, **kwargs)
160 def recv(self, maxsize = None):
161 return self.
_recv(
'stdout', maxsize)
164 return self.
_recv(
'stderr', maxsize)
174 return getattr(self, which), maxsize
176 def _close(self, which):
177 getattr(self, which).close()
178 setattr(self, which,
None)
181 """!Try to kill running process"""
182 if subprocess.mswindows:
184 handle = win32api.OpenProcess(1, 0, self.pid)
185 return (0 != win32api.TerminateProcess(handle, 0))
188 os.kill(-self.pid, signal.SIGTERM)
192 if subprocess.mswindows:
198 x = msvcrt.get_osfhandle(self.stdin.fileno())
199 (errCode, written) = WriteFile(x, input)
201 return self.
_close(
'stdin')
202 except (subprocess.pywintypes.error, Exception), why:
203 if why[0]
in (109, errno.ESHUTDOWN):
204 return self.
_close(
'stdin')
209 def _recv(self, which, maxsize):
215 x = msvcrt.get_osfhandle(conn.fileno())
216 (read, nAvail, nMessage) = PeekNamedPipe(x, 0)
220 (errCode, read) = ReadFile(x, nAvail,
None)
223 except (subprocess.pywintypes.error, Exception), why:
224 if why[0]
in (109, errno.ESHUTDOWN):
228 if self.universal_newlines:
229 read = self._translate_newlines(read)
237 if not select.select([], [self.stdin], [], 0)[1]:
241 written = os.write(self.stdin.fileno(), input)
243 if why[0] == errno.EPIPE:
244 return self.
_close(
'stdin')
249 def _recv(self, which, maxsize):
254 flags = fcntl.fcntl(conn, fcntl.F_GETFL)
256 fcntl.fcntl(conn, fcntl.F_SETFL, flags| os.O_NONBLOCK)
259 if not select.select([conn], [], [], 0)[0]:
262 r = conn.read(maxsize)
267 if self.universal_newlines:
268 r = self._translate_newlines(r)
272 fcntl.fcntl(conn, fcntl.F_SETFL, flags)
274 message =
"Other end disconnected!"
285 while time.time() < x
or r:
289 raise Exception(message)
295 time.sleep(
max((x-time.time())/tr, 0))
302 raise Exception(message)
303 data = buffer(data, sent)
306 """!Run command in separate thread. Used for commands launched
309 If stdout/err is redirected, write() method is required for the
313 cmd = Command(cmd=['d.rast', 'elevation.dem'], verbose=3, wait=True)
315 if cmd.returncode == None:
317 elif cmd.returncode == 0:
320 print 'FAILURE (%d)' % cmd.returncode
323 @param cmd command given as list
324 @param stdin standard input stream
325 @param verbose verbose level [0, 3] (--q, --v)
326 @param wait wait for child execution terminated
327 @param rerr error handling (when CmdError raised).
328 True for redirection to stderr, False for GUI dialog,
329 None for no operation (quiet mode)
330 @param stdout redirect standard output or None
331 @param stderr redirect standard error output or None
333 def __init__ (self, cmd, stdin = None,
334 verbose =
None, wait =
True, rerr =
False,
335 stdout =
None, stderr =
None):
336 Debug.msg(1,
"gcmd.Command(): %s" %
' '.join(cmd))
344 if (
'--q' not in self.
cmd and '--quiet' not in self.
cmd)
and \
345 (
'--v' not in self.
cmd and '--verbose' not in self.
cmd):
346 if verbose
is not None:
348 self.cmd.append(
'--quiet')
350 self.cmd.append(
'--verbose')
352 verbose_orig = os.getenv(
"GRASS_VERBOSE")
353 os.environ[
"GRASS_VERBOSE"] = str(verbose)
360 self.cmdThread.start()
363 self.cmdThread.join()
364 if self.cmdThread.module:
365 self.cmdThread.module.wait()
370 self.cmdThread.join(0.5)
374 Debug.msg (3,
"Command(): cmd='%s', wait=%s, returncode=%d, alive=%s" % \
375 (
' '.join(cmd), wait, self.
returncode, self.cmdThread.isAlive()))
379 (_(
"Execution failed:"),
381 os.linesep, os.linesep,
385 elif rerr == sys.stderr:
386 stderr.write(
"Execution failed: '%s'" % (
' '.join(self.
cmd)))
387 stderr.write(
"%sDetails:%s%s" % (os.linesep,
393 Debug.msg (3,
"Command(): cmd='%s', wait=%s, returncode=?, alive=%s" % \
394 (
' '.join(cmd), wait, self.cmdThread.isAlive()))
397 os.environ[
"GRASS_VERBOSE"] = verbose_orig
398 elif "GRASS_VERBOSE" in os.environ:
399 del os.environ[
"GRASS_VERBOSE"]
401 def __ReadOutput(self, stream):
402 """!Read stream and return list of lines
404 @param stream stream to be read
412 line = stream.readline()
415 line = line.replace(
'%s' % os.linesep,
'').strip()
416 lineList.append(line)
420 def __ReadErrOutput(self):
421 """!Read standard error output and return list of lines"""
424 def __ProcessStdErr(self):
426 Read messages/warnings/errors from stderr
428 @return list of (type, message)
433 lines = self.cmdThread.error.strip(
'%s' % os.linesep). \
434 split(
'%s' % os.linesep)
444 if 'GRASS_INFO_WARNING' in line:
446 elif 'GRASS_INFO_ERROR' in line:
448 elif 'GRASS_INFO_END':
449 msg.append((type, content))
454 content += line.split(
':', 1)[1].strip()
456 msg.append((
None, line.strip()))
460 def __GetError(self):
461 """!Get error message or ''"""
462 if not self.cmdThread.module:
463 return _(
"Unable to exectute command: '%s'") %
' '.join(self.
cmd)
468 return unicode(msg, _enc)
474 """!Create separate thread for command. Used for commands launched
475 on the background."""
476 def __init__ (self, cmd, env = None, stdin = None,
477 stdout = sys.stdout, stderr = sys.stderr):
479 @param cmd command (given as list)
480 @param env environmental variables
481 @param stdin standard input stream
482 @param stdout redirect standard output or None
483 @param stderr redirect standard error output or None
485 Thread.__init__(self)
503 os.environ[
"GRASS_MESSAGE_FORMAT"] =
"gui"
509 del os.environ[
"GRASS_MESSAGE_FORMAT"]
513 if len(self.
cmd) == 0:
516 Debug.msg(1,
"gcmd.CommandThread(): %s" %
' '.join(self.
cmd))
522 if sys.platform ==
'win32' and os.path.splitext(self.
cmd[0])[1] ==
'.py':
524 os.chdir(os.path.join(os.getenv(
'GISBASE'),
'etc',
'gui',
'scripts'))
525 args = [sys.executable, self.
cmd[0]] + self.
cmd[1:]
526 if sys.platform ==
'win32' and \
527 self.
cmd[0]
in globalvar.grassScripts[globalvar.SCT_EXT]:
528 args[0] = self.
cmd[0] + globalvar.SCT_EXT
529 env = copy.deepcopy(self.
env)
533 scriptdir = os.path.join(os.getenv(
'GISBASE').replace(
'/',
'\\'),
'scripts')
534 if scriptdir
not in sys.path:
535 sys.path.append(scriptdir)
541 stdin = subprocess.PIPE,
542 stdout = subprocess.PIPE,
543 stderr = subprocess.PIPE,
544 shell = sys.platform ==
"win32",
549 print >> sys.stderr, e
553 self.module.stdin.write(self.
stdin)
554 self.module.stdin.close()
559 def _redirect_stream(self):
560 """!Redirect stream"""
563 out_fileno = self.module.stdout.fileno()
564 if not subprocess.mswindows:
565 flags = fcntl.fcntl(out_fileno, fcntl.F_GETFL)
566 fcntl.fcntl(out_fileno, fcntl.F_SETFL, flags| os.O_NONBLOCK)
570 out_fileno = self.module.stderr.fileno()
571 if not subprocess.mswindows:
572 flags = fcntl.fcntl(out_fileno, fcntl.F_GETFL)
573 fcntl.fcntl(out_fileno, fcntl.F_SETFL, flags| os.O_NONBLOCK)
576 while self.module.poll()
is None:
583 self.stdout.write(line)
586 self.stderr.write(line)
593 self.stdout.write(line)
596 self.stderr.write(line)
601 """!Abort running process, used by main thread to signal an abort"""
604 def _formatMsg(text):
605 """!Format error messages for dialogs
608 for line
in text.splitlines():
611 elif 'GRASS_INFO_MESSAGE' in line:
612 message += line.split(
':', 1)[1].strip() +
'\n'
613 elif 'GRASS_INFO_WARNING' in line:
614 message += line.split(
':', 1)[1].strip() +
'\n'
615 elif 'GRASS_INFO_ERROR' in line:
616 message += line.split(
':', 1)[1].strip() +
'\n'
617 elif 'GRASS_INFO_END' in line:
620 message += line.strip() +
'\n'
624 def RunCommand(prog, flags = "", overwrite = False, quiet = False, verbose = False,
625 parent =
None, read =
False, stdin =
None, getErrorMsg =
False, **kwargs):
626 """!Run GRASS command
628 @param prog program to run
629 @param flags flags given as a string
630 @param overwrite, quiet, verbose flags
631 @param parent parent window for error messages
632 @param read fetch stdout
633 @param stdin stdin or None
634 @param getErrorMsg get error messages on failure
635 @param kwargs program parameters
637 @return returncode (read == False and getErrorMsg == False)
638 @return returncode, messages (read == False and getErrorMsg == True)
639 @return stdout (read == True and getErrorMsg == False)
640 @return returncode, stdout, messages (read == True and getErrorMsg == True)
641 @return stdout, stderr
643 cmdString =
' '.join(grass.make_command(prog, flags, overwrite,
644 quiet, verbose, **kwargs))
646 Debug.msg(1,
"gcmd.RunCommand(): %s" % cmdString)
648 kwargs[
'stderr'] = subprocess.PIPE
651 kwargs[
'stdout'] = subprocess.PIPE
654 kwargs[
'stdin'] = subprocess.PIPE
656 ps = grass.start_command(
GetRealCmd(prog), flags, overwrite, quiet, verbose, **kwargs)
658 Debug.msg(2,
"gcmd.RunCommand(): command started")
661 ps.stdin.write(stdin)
665 Debug.msg(3,
"gcmd.RunCommand(): decoding string")
666 stdout, stderr = map(DecodeString, ps.communicate())
669 Debug.msg(1,
"gcmd.RunCommand(): get return code %d" % ret)
671 Debug.msg(3,
"gcmd.RunCommand(): print error")
672 if ret != 0
and parent:
673 Debug.msg(2,
"gcmd.RunCommand(): error %s" % stderr)
675 Debug.msg(2,
"gcmd.RunCommand(): nothing to print ???")
680 Debug.msg(3,
"gcmd.RunCommand(): print read error")
685 return ret, _formatMsg(stderr)
688 Debug.msg(2,
"gcmd.RunCommand(): return stdout\n'%s'" % stdout)
690 Debug.msg(2,
"gcmd.RunCommand(): return stdout = None")
694 Debug.msg(2,
"gcmd.RunCommand(): return ret, stdout")
695 if read
and getErrorMsg:
696 return ret, stdout, _formatMsg(stderr)
698 Debug.msg(2,
"gcmd.RunCommand(): return result")
699 return stdout, _formatMsg(stderr)
702 """!Get default system encoding
704 @param forceUTF8 force 'UTF-8' if encoding is not defined
706 @return system encoding (can be None)
708 enc = locale.getdefaultlocale()[1]
709 if forceUTF8
and (enc
is None or enc ==
'UTF8'):
712 Debug.msg(1,
"GetSystemEncoding(): %s" % enc)
def __GetError
Get error message or ''.
def EncodeString
Return encoded string using system locales.
def DecodeString
Decode string using system encoding.
def abort
Abort running process, used by main thread to signal an abort.
Subclass subprocess.Popen.
Create separate thread for command.
def GetRealCmd
Return real command name - only for MS Windows.
def split
Platform spefic shlex.split.
Run command in separate thread.
def GetDefaultEncoding
Get default system encoding.
def kill
Try to kill running process.
def __ReadOutput
Read stream and return list of lines.
def __ReadErrOutput
Read standard error output and return list of lines.
def _redirect_stream
Redirect stream.
def RunCommand
Run GRASS command.