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 abort(self)
Abort running process, used by main thread to signal an abort.
Subclass subprocess.Popen.
def __ReadErrOutput(self)
Read standard error output and return list of lines.
Create separate thread for command.
def _redirect_stream(self)
Redirect stream.
def __GetError(self)
Get error message or ''.
def split(s)
Platform spefic shlex.split.
def __ProcessStdErr(self)
def _recv(self, which, maxsize)
Run command in separate thread.
def GetRealCmd(cmd)
Return real command name - only for MS Windows.
def GetDefaultEncoding
Get default system encoding.
def DecodeString(string)
Decode string using system encoding.
def get_conn_maxsize(self, which, maxsize)
def EncodeString(string)
Return encoded string using system locales.
def __init__(self, args, kwargs)
def RunCommand(prog, flags="", overwrite=False, quiet=False, verbose=False, parent=None, read=False, stdin=None, getErrorMsg=False, kwargs)
Run GRASS command.
def run(self)
Run command.
def __ReadOutput(self, stream)
Read stream and return list of lines.
def kill(self)
Try to kill running process.