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 enc = locale.getdefaultlocale()[1]
82 sys.stderr.write(_(
"ERROR: %s\n") % str(e))
86 Debug.msg(5,
"DecodeString(): enc=%s" % enc)
87 return string.decode(enc)
92 """!Return encoded string using system locales
94 @param string string to be encoded
96 @return encoded string
100 enc = locale.getdefaultlocale()[1]
102 Debug.msg(5,
"EncodeString(): enc=%s" % enc)
103 return string.encode(enc)
108 def __init__(self, message, parent = None, caption = None, showTraceback = True):
111 style = wx.OK | wx.ICON_ERROR | wx.CENTRE
112 exc_type, exc_value, exc_traceback = sys.exc_info()
114 exception = traceback.format_exc()
115 reason = exception.splitlines()[-1].
split(
':', 1)[-1].strip()
117 if Debug.GetLevel() > 0
and exc_traceback:
118 sys.stderr.write(exception)
120 if showTraceback
and exc_traceback:
121 wx.MessageBox(parent = parent,
122 message = message +
'\n\n%s: %s\n\n%s' % \
128 wx.MessageBox(parent = parent,
135 caption = _(
'Warning')
136 style = wx.OK | wx.ICON_WARNING | wx.CENTRE
137 wx.MessageBox(parent = parent,
144 caption = _(
'Message')
145 style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE
146 wx.MessageBox(parent = parent,
159 """!Subclass subprocess.Popen"""
161 if subprocess.mswindows:
162 args = map(EncodeString, args)
164 subprocess.Popen.__init__(self, args, **kwargs)
166 def recv(self, maxsize = None):
167 return self.
_recv(
'stdout', maxsize)
170 return self.
_recv(
'stderr', maxsize)
180 return getattr(self, which), maxsize
182 def _close(self, which):
183 getattr(self, which).close()
184 setattr(self, which,
None)
187 """!Try to kill running process"""
188 if subprocess.mswindows:
190 handle = win32api.OpenProcess(1, 0, self.pid)
191 return (0 != win32api.TerminateProcess(handle, 0))
194 os.kill(-self.pid, signal.SIGTERM)
198 if subprocess.mswindows:
204 x = msvcrt.get_osfhandle(self.stdin.fileno())
205 (errCode, written) = WriteFile(x, input)
207 return self.
_close(
'stdin')
208 except (subprocess.pywintypes.error, Exception), why:
209 if why[0]
in (109, errno.ESHUTDOWN):
210 return self.
_close(
'stdin')
215 def _recv(self, which, maxsize):
221 x = msvcrt.get_osfhandle(conn.fileno())
222 (read, nAvail, nMessage) = PeekNamedPipe(x, 0)
226 (errCode, read) = ReadFile(x, nAvail,
None)
229 except (subprocess.pywintypes.error, Exception), why:
230 if why[0]
in (109, errno.ESHUTDOWN):
234 if self.universal_newlines:
235 read = self._translate_newlines(read)
243 if not select.select([], [self.stdin], [], 0)[1]:
247 written = os.write(self.stdin.fileno(), input)
249 if why[0] == errno.EPIPE:
250 return self.
_close(
'stdin')
255 def _recv(self, which, maxsize):
260 flags = fcntl.fcntl(conn, fcntl.F_GETFL)
262 fcntl.fcntl(conn, fcntl.F_SETFL, flags| os.O_NONBLOCK)
265 if not select.select([conn], [], [], 0)[0]:
268 r = conn.read(maxsize)
273 if self.universal_newlines:
274 r = self._translate_newlines(r)
278 fcntl.fcntl(conn, fcntl.F_SETFL, flags)
280 message =
"Other end disconnected!"
291 while time.time() < x
or r:
295 raise Exception(message)
301 time.sleep(
max((x-time.time())/tr, 0))
308 raise Exception(message)
309 data = buffer(data, sent)
312 """!Run command in separate thread. Used for commands launched
315 If stdout/err is redirected, write() method is required for the
319 cmd = Command(cmd=['d.rast', 'elevation.dem'], verbose=3, wait=True)
321 if cmd.returncode == None:
323 elif cmd.returncode == 0:
326 print 'FAILURE (%d)' % cmd.returncode
329 @param cmd command given as list
330 @param stdin standard input stream
331 @param verbose verbose level [0, 3] (--q, --v)
332 @param wait wait for child execution terminated
333 @param rerr error handling (when CmdError raised).
334 True for redirection to stderr, False for GUI dialog,
335 None for no operation (quiet mode)
336 @param stdout redirect standard output or None
337 @param stderr redirect standard error output or None
339 def __init__ (self, cmd, stdin = None,
340 verbose =
None, wait =
True, rerr =
False,
341 stdout =
None, stderr =
None):
342 Debug.msg(1,
"gcmd.Command(): %s" %
' '.join(cmd))
350 if (
'--q' not in self.
cmd and '--quiet' not in self.
cmd)
and \
351 (
'--v' not in self.
cmd and '--verbose' not in self.
cmd):
352 if verbose
is not None:
354 self.cmd.append(
'--quiet')
356 self.cmd.append(
'--verbose')
358 verbose_orig = os.getenv(
"GRASS_VERBOSE")
359 os.environ[
"GRASS_VERBOSE"] = str(verbose)
366 self.cmdThread.start()
369 self.cmdThread.join()
370 if self.cmdThread.module:
371 self.cmdThread.module.wait()
376 self.cmdThread.join(0.5)
380 Debug.msg (3,
"Command(): cmd='%s', wait=%s, returncode=%d, alive=%s" % \
381 (
' '.join(cmd), wait, self.
returncode, self.cmdThread.isAlive()))
385 (_(
"Execution failed:"),
387 os.linesep, os.linesep,
391 elif rerr == sys.stderr:
392 stderr.write(
"Execution failed: '%s'" % (
' '.join(self.
cmd)))
393 stderr.write(
"%sDetails:%s%s" % (os.linesep,
399 Debug.msg (3,
"Command(): cmd='%s', wait=%s, returncode=?, alive=%s" % \
400 (
' '.join(cmd), wait, self.cmdThread.isAlive()))
403 os.environ[
"GRASS_VERBOSE"] = verbose_orig
404 elif "GRASS_VERBOSE" in os.environ:
405 del os.environ[
"GRASS_VERBOSE"]
407 def __ReadOutput(self, stream):
408 """!Read stream and return list of lines
410 @param stream stream to be read
418 line = stream.readline()
421 line = line.replace(
'%s' % os.linesep,
'').strip()
422 lineList.append(line)
426 def __ReadErrOutput(self):
427 """!Read standard error output and return list of lines"""
430 def __ProcessStdErr(self):
432 Read messages/warnings/errors from stderr
434 @return list of (type, message)
439 lines = self.cmdThread.error.strip(
'%s' % os.linesep). \
440 split(
'%s' % os.linesep)
450 if 'GRASS_INFO_WARNING' in line:
452 elif 'GRASS_INFO_ERROR' in line:
454 elif 'GRASS_INFO_END':
455 msg.append((type, content))
460 content += line.split(
':', 1)[1].strip()
462 msg.append((
None, line.strip()))
466 def __GetError(self):
467 """!Get error message or ''"""
468 if not self.cmdThread.module:
469 return _(
"Unable to exectute command: '%s'") %
' '.join(self.
cmd)
473 enc = locale.getdefaultlocale()[1]
475 return unicode(msg, enc)
482 """!Create separate thread for command. Used for commands launched
483 on the background."""
484 def __init__ (self, cmd, env = None, stdin = None,
485 stdout = sys.stdout, stderr = sys.stderr):
487 @param cmd command (given as list)
488 @param env environmental variables
489 @param stdin standard input stream
490 @param stdout redirect standard output or None
491 @param stderr redirect standard error output or None
493 Thread.__init__(self)
511 os.environ[
"GRASS_MESSAGE_FORMAT"] =
"gui"
517 del os.environ[
"GRASS_MESSAGE_FORMAT"]
521 if len(self.
cmd) == 0:
524 Debug.msg(1,
"gcmd.CommandThread(): %s" %
' '.join(self.
cmd))
530 if sys.platform ==
'win32' and os.path.splitext(self.
cmd[0])[1] ==
'.py':
532 os.chdir(os.path.join(os.getenv(
'GISBASE'),
'etc',
'gui',
'scripts'))
533 args = [sys.executable, self.
cmd[0]] + self.
cmd[1:]
534 if sys.platform ==
'win32' and \
535 self.
cmd[0]
in globalvar.grassScripts[globalvar.SCT_EXT]:
536 args[0] = self.
cmd[0] + globalvar.SCT_EXT
537 env = copy.deepcopy(self.
env)
541 scriptdir = os.path.join(os.getenv(
'GISBASE').replace(
'/',
'\\'),
'scripts')
542 if scriptdir
not in sys.path:
543 sys.path.append(scriptdir)
549 stdin = subprocess.PIPE,
550 stdout = subprocess.PIPE,
551 stderr = subprocess.PIPE,
552 shell = sys.platform ==
"win32",
557 print >> sys.stderr, e
561 self.module.stdin.write(self.
stdin)
562 self.module.stdin.close()
567 def _redirect_stream(self):
568 """!Redirect stream"""
571 out_fileno = self.module.stdout.fileno()
572 if not subprocess.mswindows:
573 flags = fcntl.fcntl(out_fileno, fcntl.F_GETFL)
574 fcntl.fcntl(out_fileno, fcntl.F_SETFL, flags| os.O_NONBLOCK)
578 out_fileno = self.module.stderr.fileno()
579 if not subprocess.mswindows:
580 flags = fcntl.fcntl(out_fileno, fcntl.F_GETFL)
581 fcntl.fcntl(out_fileno, fcntl.F_SETFL, flags| os.O_NONBLOCK)
584 while self.module.poll()
is None:
591 self.stdout.write(line)
594 self.stderr.write(line)
601 self.stdout.write(line)
604 self.stderr.write(line)
609 """!Abort running process, used by main thread to signal an abort"""
612 def _formatMsg(text):
613 """!Format error messages for dialogs
616 for line
in text.splitlines():
619 elif 'GRASS_INFO_MESSAGE' in line:
620 message += line.split(
':', 1)[1].strip() +
'\n'
621 elif 'GRASS_INFO_WARNING' in line:
622 message += line.split(
':', 1)[1].strip() +
'\n'
623 elif 'GRASS_INFO_ERROR' in line:
624 message += line.split(
':', 1)[1].strip() +
'\n'
625 elif 'GRASS_INFO_END' in line:
628 message += line.strip() +
'\n'
632 def RunCommand(prog, flags = "", overwrite = False, quiet = False, verbose = False,
633 parent =
None, read =
False, stdin =
None, getErrorMsg =
False, **kwargs):
634 """!Run GRASS command
636 @param prog program to run
637 @param flags flags given as a string
638 @param overwrite, quiet, verbose flags
639 @param parent parent window for error messages
640 @param read fetch stdout
641 @param stdin stdin or None
642 @param getErrorMsg get error messages on failure
643 @param kwargs program parameters
645 @return returncode (read == False and getErrorMsg == False)
646 @return returncode, messages (read == False and getErrorMsg == True)
647 @return stdout (read == True and getErrorMsg == False)
648 @return returncode, stdout, messages (read == True and getErrorMsg == True)
649 @return stdout, stderr
651 cmdString =
' '.join(grass.make_command(prog, flags, overwrite,
652 quiet, verbose, **kwargs))
654 Debug.msg(1,
"gcmd.RunCommand(): %s" % cmdString)
656 kwargs[
'stderr'] = subprocess.PIPE
659 kwargs[
'stdout'] = subprocess.PIPE
662 kwargs[
'stdin'] = subprocess.PIPE
664 ps = grass.start_command(
GetRealCmd(prog), flags, overwrite, quiet, verbose, **kwargs)
666 Debug.msg(2,
"gcmd.RunCommand(): command started")
669 ps.stdin.write(stdin)
673 Debug.msg(3,
"gcmd.RunCommand(): decoding string")
674 stdout, stderr = map(DecodeString, ps.communicate())
677 Debug.msg(1,
"gcmd.RunCommand(): get return code %d" % ret)
679 Debug.msg(3,
"gcmd.RunCommand(): print error")
680 if ret != 0
and parent:
681 Debug.msg(2,
"gcmd.RunCommand(): error %s" % stderr)
683 Debug.msg(2,
"gcmd.RunCommand(): nothing to print ???")
688 Debug.msg(3,
"gcmd.RunCommand(): print read error")
693 return ret, _formatMsg(stderr)
696 Debug.msg(2,
"gcmd.RunCommand(): return stdout\n'%s'" % stdout)
698 Debug.msg(2,
"gcmd.RunCommand(): return stdout = None")
702 Debug.msg(2,
"gcmd.RunCommand(): return ret, stdout")
703 if read
and getErrorMsg:
704 return ret, stdout, _formatMsg(stderr)
706 Debug.msg(2,
"gcmd.RunCommand(): return result")
707 return stdout, _formatMsg(stderr)
710 """!Get default system encoding
712 @param forceUTF8 force 'UTF-8' if encoding is not defined
714 @return system encoding (can be None)
716 enc = locale.getdefaultlocale()[1]
717 if forceUTF8
and (enc
is None or enc ==
'UTF8'):
720 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.