gcmd.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791
  1. """
  2. @package core.gcmd
  3. @brief wxGUI command interface
  4. Classes:
  5. - gcmd::GError
  6. - gcmd::GWarning
  7. - gcmd::GMessage
  8. - gcmd::GException
  9. - gcmd::Popen (from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440554)
  10. - gcmd::Command
  11. - gcmd::CommandThread
  12. Functions:
  13. - RunCommand
  14. - GetDefaultEncoding
  15. (C) 2007-2008, 2010-2011 by the GRASS Development Team
  16. This program is free software under the GNU General Public License
  17. (>=v2). Read the file COPYING that comes with GRASS for details.
  18. @author Jachym Cepicky
  19. @author Martin Landa <landa.martin gmail.com>
  20. """
  21. from __future__ import print_function
  22. import os
  23. import sys
  24. import time
  25. import errno
  26. import signal
  27. import traceback
  28. import locale
  29. import subprocess
  30. from threading import Thread
  31. import wx
  32. is_mswindows = sys.platform == 'win32'
  33. if is_mswindows:
  34. from win32file import ReadFile, WriteFile
  35. from win32pipe import PeekNamedPipe
  36. import msvcrt
  37. else:
  38. import select
  39. import fcntl
  40. from grass.script import core as grass
  41. from core import globalvar
  42. from core.debug import Debug
  43. from grass.script.utils import decode
  44. # cannot import from the core.utils module to avoid cross dependencies
  45. try:
  46. # intended to be used also outside this module
  47. import gettext
  48. _ = gettext.translation(
  49. 'grasswxpy',
  50. os.path.join(
  51. os.getenv("GISBASE"),
  52. 'locale')).ugettext
  53. except IOError:
  54. # using no translation silently
  55. def null_gettext(string):
  56. return string
  57. _ = null_gettext
  58. if sys.version_info.major == 2:
  59. bytes = str
  60. def DecodeString(string):
  61. """Decode string using system encoding
  62. :param string: string to be decoded
  63. :return: decoded string
  64. """
  65. if not string:
  66. return string
  67. if _enc and isinstance(string, bytes):
  68. Debug.msg(5, "DecodeString(): enc=%s" % _enc)
  69. return string.decode(_enc)
  70. return string
  71. def EncodeString(string):
  72. """Return encoded string using system locales
  73. :param string: string to be encoded
  74. :return: encoded string
  75. """
  76. if not string:
  77. return string
  78. if _enc:
  79. Debug.msg(5, "EncodeString(): enc=%s" % _enc)
  80. return string.encode(_enc)
  81. return string
  82. class GError:
  83. def __init__(self, message, parent=None, caption=None, showTraceback=True):
  84. """Show error message window
  85. :param message: error message
  86. :param parent: centre window on parent if given
  87. :param caption: window caption (if not given "Error")
  88. :param showTraceback: True to show also Python traceback
  89. """
  90. if not caption:
  91. caption = _('Error')
  92. style = wx.OK | wx.ICON_ERROR | wx.CENTRE
  93. exc_type, exc_value, exc_traceback = sys.exc_info()
  94. if exc_traceback:
  95. exception = traceback.format_exc()
  96. reason = exception.splitlines()[-1].split(':', 1)[-1].strip()
  97. if Debug.GetLevel() > 0 and exc_traceback:
  98. sys.stderr.write(exception)
  99. if showTraceback and exc_traceback:
  100. wx.MessageBox(parent=parent,
  101. message=message + '\n\n%s: %s\n\n%s' %
  102. (_('Reason'),
  103. reason, exception),
  104. caption=caption,
  105. style=style)
  106. else:
  107. wx.MessageBox(parent=parent,
  108. message=message,
  109. caption=caption,
  110. style=style)
  111. class GWarning:
  112. def __init__(self, message, parent=None):
  113. caption = _('Warning')
  114. style = wx.OK | wx.ICON_WARNING | wx.CENTRE
  115. wx.MessageBox(parent=parent,
  116. message=message,
  117. caption=caption,
  118. style=style)
  119. class GMessage:
  120. def __init__(self, message, parent=None):
  121. caption = _('Message')
  122. style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE
  123. wx.MessageBox(parent=parent,
  124. message=message,
  125. caption=caption,
  126. style=style)
  127. class GException(Exception):
  128. def __init__(self, value=''):
  129. self.value = value
  130. def __str__(self):
  131. return self.value
  132. def __unicode__(self):
  133. return self.value
  134. class Popen(subprocess.Popen):
  135. """Subclass subprocess.Popen"""
  136. def __init__(self, args, **kwargs):
  137. if is_mswindows:
  138. args = map(EncodeString, args)
  139. # The Windows shell (cmd.exe) requires some special characters to
  140. # be escaped by preceding them with 3 carets (^^^). cmd.exe /?
  141. # mentions <space> and &()[]{}^=;!'+,`~. A quick test revealed that
  142. # only ^|&<> need to be escaped. A single quote can be escaped by
  143. # enclosing it with double quotes and vice versa.
  144. for i in range(2, len(args)):
  145. # "^" must be the first character in the list to avoid double
  146. # escaping.
  147. for c in ("^", "|", "&", "<", ">"):
  148. if c in args[i]:
  149. if "=" in args[i]:
  150. a = args[i].split("=")
  151. k = a[0] + "="
  152. v = "=".join(a[1:len(a)])
  153. else:
  154. k = ""
  155. v = args[i]
  156. # If there are spaces, the argument was already
  157. # esscaped with double quotes, so don't escape it
  158. # again.
  159. if c in v and not " " in v:
  160. # Here, we escape each ^ in ^^^ with ^^ and a
  161. # <special character> with ^ + <special character>,
  162. # so we need 7 carets.
  163. v = v.replace(c, "^^^^^^^" + c)
  164. args[i] = k + v
  165. subprocess.Popen.__init__(self, args, **kwargs)
  166. def recv(self, maxsize=None):
  167. return self._recv('stdout', maxsize)
  168. def recv_err(self, maxsize=None):
  169. return self._recv('stderr', maxsize)
  170. def send_recv(self, input='', maxsize=None):
  171. return self.send(input), self.recv(maxsize), self.recv_err(maxsize)
  172. def get_conn_maxsize(self, which, maxsize):
  173. if maxsize is None:
  174. maxsize = 1024
  175. elif maxsize < 1:
  176. maxsize = 1
  177. return getattr(self, which), maxsize
  178. def _close(self, which):
  179. getattr(self, which).close()
  180. setattr(self, which, None)
  181. def kill(self):
  182. """Try to kill running process"""
  183. if is_mswindows:
  184. import win32api
  185. handle = win32api.OpenProcess(1, 0, self.pid)
  186. return (0 != win32api.TerminateProcess(handle, 0))
  187. else:
  188. try:
  189. os.kill(-self.pid, signal.SIGTERM) # kill whole group
  190. except OSError:
  191. pass
  192. if sys.platform == 'win32':
  193. def send(self, input):
  194. if not self.stdin:
  195. return None
  196. try:
  197. x = msvcrt.get_osfhandle(self.stdin.fileno())
  198. (errCode, written) = WriteFile(x, input)
  199. except ValueError:
  200. return self._close('stdin')
  201. except (subprocess.pywintypes.error, Exception) as why:
  202. if why[0] in (109, errno.ESHUTDOWN):
  203. return self._close('stdin')
  204. raise
  205. return written
  206. def _recv(self, which, maxsize):
  207. conn, maxsize = self.get_conn_maxsize(which, maxsize)
  208. if conn is None:
  209. return None
  210. try:
  211. x = msvcrt.get_osfhandle(conn.fileno())
  212. (read, nAvail, nMessage) = PeekNamedPipe(x, 0)
  213. if maxsize < nAvail:
  214. nAvail = maxsize
  215. if nAvail > 0:
  216. (errCode, read) = ReadFile(x, nAvail, None)
  217. except ValueError:
  218. return self._close(which)
  219. except (subprocess.pywintypes.error, Exception) as why:
  220. if why[0] in (109, errno.ESHUTDOWN):
  221. return self._close(which)
  222. raise
  223. if self.universal_newlines:
  224. read = self._translate_newlines(read)
  225. return read
  226. else:
  227. def send(self, input):
  228. if not self.stdin:
  229. return None
  230. if not select.select([], [self.stdin], [], 0)[1]:
  231. return 0
  232. try:
  233. written = os.write(self.stdin.fileno(), input)
  234. except OSError as why:
  235. if why[0] == errno.EPIPE: # broken pipe
  236. return self._close('stdin')
  237. raise
  238. return written
  239. def _recv(self, which, maxsize):
  240. conn, maxsize = self.get_conn_maxsize(which, maxsize)
  241. if conn is None:
  242. return None
  243. flags = fcntl.fcntl(conn, fcntl.F_GETFL)
  244. if not conn.closed:
  245. fcntl.fcntl(conn, fcntl.F_SETFL, flags | os.O_NONBLOCK)
  246. try:
  247. if not select.select([conn], [], [], 0)[0]:
  248. return ''
  249. r = conn.read(maxsize)
  250. if not r:
  251. return self._close(which)
  252. if self.universal_newlines:
  253. r = self._translate_newlines(r)
  254. return r
  255. finally:
  256. if not conn.closed:
  257. fcntl.fcntl(conn, fcntl.F_SETFL, flags)
  258. message = "Other end disconnected!"
  259. def recv_some(p, t=.1, e=1, tr=5, stderr=0):
  260. if tr < 1:
  261. tr = 1
  262. x = time.time() + t
  263. y = []
  264. r = ''
  265. pr = p.recv
  266. if stderr:
  267. pr = p.recv_err
  268. while time.time() < x or r:
  269. r = pr()
  270. if r is None:
  271. if e:
  272. raise Exception(message)
  273. else:
  274. break
  275. elif r:
  276. y.append(decode(r))
  277. else:
  278. time.sleep(max((x - time.time()) / tr, 0))
  279. return ''.join(y)
  280. def send_all(p, data):
  281. while len(data):
  282. sent = p.send(data)
  283. if sent is None:
  284. raise Exception(message)
  285. data = buffer(data, sent)
  286. class Command:
  287. """Run command in separate thread. Used for commands launched
  288. on the background.
  289. If stdout/err is redirected, write() method is required for the
  290. given classes.
  291. cmd = Command(cmd=['d.rast', 'elevation.dem'], verbose=3, wait=True)
  292. if cmd.returncode == None:
  293. print 'RUNNING?'
  294. elif cmd.returncode == 0:
  295. print 'SUCCESS'
  296. else:
  297. print 'FAILURE (%d)' % cmd.returncode
  298. """
  299. def __init__(self, cmd, stdin=None,
  300. verbose=None, wait=True, rerr=False,
  301. stdout=None, stderr=None):
  302. """
  303. :param cmd: command given as list
  304. :param stdin: standard input stream
  305. :param verbose: verbose level [0, 3] (--q, --v)
  306. :param wait: wait for child execution terminated
  307. :param rerr: error handling (when GException raised).
  308. True for redirection to stderr, False for GUI
  309. dialog, None for no operation (quiet mode)
  310. :param stdout: redirect standard output or None
  311. :param stderr: redirect standard error output or None
  312. """
  313. Debug.msg(1, "gcmd.Command(): %s" % ' '.join(cmd))
  314. self.cmd = cmd
  315. self.stderr = stderr
  316. #
  317. # set verbosity level
  318. #
  319. verbose_orig = None
  320. if ('--q' not in self.cmd and '--quiet' not in self.cmd) and \
  321. ('--v' not in self.cmd and '--verbose' not in self.cmd):
  322. if verbose is not None:
  323. if verbose == 0:
  324. self.cmd.append('--quiet')
  325. elif verbose == 3:
  326. self.cmd.append('--verbose')
  327. else:
  328. verbose_orig = os.getenv("GRASS_VERBOSE")
  329. os.environ["GRASS_VERBOSE"] = str(verbose)
  330. #
  331. # create command thread
  332. #
  333. self.cmdThread = CommandThread(cmd, stdin,
  334. stdout, stderr)
  335. self.cmdThread.start()
  336. if wait:
  337. self.cmdThread.join()
  338. if self.cmdThread.module:
  339. self.cmdThread.module.wait()
  340. self.returncode = self.cmdThread.module.returncode
  341. else:
  342. self.returncode = 1
  343. else:
  344. self.cmdThread.join(0.5)
  345. self.returncode = None
  346. if self.returncode is not None:
  347. Debug.msg(
  348. 3, "Command(): cmd='%s', wait=%s, returncode=%d, alive=%s" %
  349. (' '.join(cmd), wait, self.returncode, self.cmdThread.isAlive()))
  350. if rerr is not None and self.returncode != 0:
  351. if rerr is False: # GUI dialog
  352. raise GException("%s '%s'%s%s%s %s%s" %
  353. (_("Execution failed:"),
  354. ' '.join(self.cmd),
  355. os.linesep, os.linesep,
  356. _("Details:"),
  357. os.linesep,
  358. _("Error: ") + self.__GetError()))
  359. elif rerr == sys.stderr: # redirect message to sys
  360. stderr.write("Execution failed: '%s'" %
  361. (' '.join(self.cmd)))
  362. stderr.write(
  363. "%sDetails:%s%s" %
  364. (os.linesep,
  365. _("Error: ") +
  366. self.__GetError(),
  367. os.linesep))
  368. else:
  369. pass # nop
  370. else:
  371. Debug.msg(
  372. 3, "Command(): cmd='%s', wait=%s, returncode=?, alive=%s" %
  373. (' '.join(cmd), wait, self.cmdThread.isAlive()))
  374. if verbose_orig:
  375. os.environ["GRASS_VERBOSE"] = verbose_orig
  376. elif "GRASS_VERBOSE" in os.environ:
  377. del os.environ["GRASS_VERBOSE"]
  378. def __ReadOutput(self, stream):
  379. """Read stream and return list of lines
  380. :param stream: stream to be read
  381. """
  382. lineList = []
  383. if stream is None:
  384. return lineList
  385. while True:
  386. line = stream.readline()
  387. if not line:
  388. break
  389. line = line.replace('%s' % os.linesep, '').strip()
  390. lineList.append(line)
  391. return lineList
  392. def __ReadErrOutput(self):
  393. """Read standard error output and return list of lines"""
  394. return self.__ReadOutput(self.cmdThread.module.stderr)
  395. def __ProcessStdErr(self):
  396. """
  397. Read messages/warnings/errors from stderr
  398. :return: list of (type, message)
  399. """
  400. if self.stderr is None:
  401. lines = self.__ReadErrOutput()
  402. else:
  403. lines = self.cmdThread.error.strip('%s' % os.linesep). \
  404. split('%s' % os.linesep)
  405. msg = []
  406. type = None
  407. content = ""
  408. for line in lines:
  409. if len(line) == 0:
  410. continue
  411. if 'GRASS_' in line: # error or warning
  412. if 'GRASS_INFO_WARNING' in line: # warning
  413. type = "WARNING"
  414. elif 'GRASS_INFO_ERROR' in line: # error
  415. type = "ERROR"
  416. elif 'GRASS_INFO_END': # end of message
  417. msg.append((type, content))
  418. type = None
  419. content = ""
  420. if type:
  421. content += line.split(':', 1)[1].strip()
  422. else: # stderr
  423. msg.append((None, line.strip()))
  424. return msg
  425. def __GetError(self):
  426. """Get error message or ''"""
  427. if not self.cmdThread.module:
  428. return _("Unable to exectute command: '%s'") % ' '.join(self.cmd)
  429. for type, msg in self.__ProcessStdErr():
  430. if type == 'ERROR':
  431. if _enc:
  432. return unicode(msg, _enc)
  433. return msg
  434. return ''
  435. class CommandThread(Thread):
  436. """Create separate thread for command. Used for commands launched
  437. on the background."""
  438. def __init__(self, cmd, env=None, stdin=None,
  439. stdout=sys.stdout, stderr=sys.stderr):
  440. """
  441. :param cmd: command (given as list)
  442. :param env: environmental variables
  443. :param stdin: standard input stream
  444. :param stdout: redirect standard output or None
  445. :param stderr: redirect standard error output or None
  446. """
  447. Thread.__init__(self)
  448. self.cmd = cmd
  449. self.stdin = stdin
  450. self.stdout = stdout
  451. self.stderr = stderr
  452. self.env = env
  453. self.module = None
  454. self.error = ''
  455. self._want_abort = False
  456. self.aborted = False
  457. self.setDaemon(True)
  458. # set message formatting
  459. self.message_format = os.getenv("GRASS_MESSAGE_FORMAT")
  460. os.environ["GRASS_MESSAGE_FORMAT"] = "gui"
  461. def __del__(self):
  462. if self.message_format:
  463. os.environ["GRASS_MESSAGE_FORMAT"] = self.message_format
  464. else:
  465. del os.environ["GRASS_MESSAGE_FORMAT"]
  466. def run(self):
  467. """Run command"""
  468. if len(self.cmd) == 0:
  469. return
  470. Debug.msg(1, "gcmd.CommandThread(): %s" % ' '.join(self.cmd))
  471. self.startTime = time.time()
  472. # TODO: replace ugly hack below
  473. # this cannot be replaced it can be only improved
  474. # also unifying this with 3 other places in code would be nice
  475. # changing from one chdir to get_real_command function
  476. args = self.cmd
  477. if sys.platform == 'win32':
  478. if os.path.splitext(args[0])[1] == globalvar.SCT_EXT:
  479. args[0] = args[0][:-3]
  480. # using Python executable to run the module if it is a script
  481. # expecting at least module name at first position
  482. # cannot use make_command for this now because it is used in GUI
  483. # The same code is in grass.script.core already twice.
  484. args[0] = grass.get_real_command(args[0])
  485. if args[0].endswith('.py'):
  486. args.insert(0, sys.executable)
  487. try:
  488. self.module = Popen(args,
  489. stdin=subprocess.PIPE,
  490. stdout=subprocess.PIPE,
  491. stderr=subprocess.PIPE,
  492. shell=sys.platform == "win32",
  493. env=self.env)
  494. except OSError as e:
  495. self.error = str(e)
  496. print(e, file=sys.stderr)
  497. return 1
  498. if self.stdin: # read stdin if requested ...
  499. self.module.stdin.write(self.stdin)
  500. self.module.stdin.close()
  501. # redirect standard outputs...
  502. self._redirect_stream()
  503. def _redirect_stream(self):
  504. """Redirect stream"""
  505. if self.stdout:
  506. # make module stdout/stderr non-blocking
  507. out_fileno = self.module.stdout.fileno()
  508. if not is_mswindows:
  509. flags = fcntl.fcntl(out_fileno, fcntl.F_GETFL)
  510. fcntl.fcntl(out_fileno, fcntl.F_SETFL, flags | os.O_NONBLOCK)
  511. if self.stderr:
  512. # make module stdout/stderr non-blocking
  513. out_fileno = self.module.stderr.fileno()
  514. if not is_mswindows:
  515. flags = fcntl.fcntl(out_fileno, fcntl.F_GETFL)
  516. fcntl.fcntl(out_fileno, fcntl.F_SETFL, flags | os.O_NONBLOCK)
  517. # wait for the process to end, sucking in stuff until it does end
  518. while self.module.poll() is None:
  519. if self._want_abort: # abort running process
  520. self.module.terminate()
  521. self.aborted = True
  522. return
  523. if self.stdout:
  524. line = recv_some(self.module, e=0, stderr=0)
  525. self.stdout.write(line)
  526. if self.stderr:
  527. line = recv_some(self.module, e=0, stderr=1)
  528. self.stderr.write(line)
  529. if len(line) > 0:
  530. self.error = line
  531. # get the last output
  532. if self.stdout:
  533. line = recv_some(self.module, e=0, stderr=0)
  534. self.stdout.write(line)
  535. if self.stderr:
  536. line = recv_some(self.module, e=0, stderr=1)
  537. self.stderr.write(line)
  538. if len(line) > 0:
  539. self.error = line
  540. def abort(self):
  541. """Abort running process, used by main thread to signal an abort"""
  542. self._want_abort = True
  543. def _formatMsg(text):
  544. """Format error messages for dialogs
  545. """
  546. message = ''
  547. for line in text.splitlines():
  548. if len(line) == 0:
  549. continue
  550. elif 'GRASS_INFO_MESSAGE' in line:
  551. message += line.split(':', 1)[1].strip() + '\n'
  552. elif 'GRASS_INFO_WARNING' in line:
  553. message += line.split(':', 1)[1].strip() + '\n'
  554. elif 'GRASS_INFO_ERROR' in line:
  555. message += line.split(':', 1)[1].strip() + '\n'
  556. elif 'GRASS_INFO_END' in line:
  557. return message
  558. else:
  559. message += line.strip() + '\n'
  560. return message
  561. def RunCommand(prog, flags="", overwrite=False, quiet=False,
  562. verbose=False, parent=None, read=False,
  563. parse=None, stdin=None, getErrorMsg=False, **kwargs):
  564. """Run GRASS command
  565. :param prog: program to run
  566. :param flags: flags given as a string
  567. :param overwrite, quiet, verbose: flags
  568. :param parent: parent window for error messages
  569. :param read: fetch stdout
  570. :param parse: fn to parse stdout (e.g. grass.parse_key_val) or None
  571. :param stdin: stdin or None
  572. :param getErrorMsg: get error messages on failure
  573. :param kwargs: program parameters
  574. :return: returncode (read == False and getErrorMsg == False)
  575. :return: returncode, messages (read == False and getErrorMsg == True)
  576. :return: stdout (read == True and getErrorMsg == False)
  577. :return: returncode, stdout, messages (read == True and getErrorMsg == True)
  578. :return: stdout, stderr
  579. """
  580. cmdString = b' '.join(grass.make_command(prog, flags, overwrite,
  581. quiet, verbose, **kwargs))
  582. Debug.msg(1, "gcmd.RunCommand(): %s" % cmdString)
  583. kwargs['stderr'] = subprocess.PIPE
  584. if read:
  585. kwargs['stdout'] = subprocess.PIPE
  586. if stdin:
  587. kwargs['stdin'] = subprocess.PIPE
  588. if parent:
  589. messageFormat = os.getenv('GRASS_MESSAGE_FORMAT', 'gui')
  590. os.environ['GRASS_MESSAGE_FORMAT'] = 'standard'
  591. start = time.time()
  592. ps = grass.start_command(prog, flags, overwrite, quiet, verbose, **kwargs)
  593. if stdin:
  594. ps.stdin.write(stdin)
  595. ps.stdin.close()
  596. ps.stdin = None
  597. stdout, stderr = list(map(DecodeString, ps.communicate()))
  598. if parent: # restore previous settings
  599. os.environ['GRASS_MESSAGE_FORMAT'] = messageFormat
  600. ret = ps.returncode
  601. Debug.msg(1, "gcmd.RunCommand(): get return code %d (%.6f sec)" %
  602. (ret, (time.time() - start)))
  603. if ret != 0:
  604. if stderr:
  605. Debug.msg(2, "gcmd.RunCommand(): error %s" % stderr)
  606. else:
  607. Debug.msg(2, "gcmd.RunCommand(): nothing to print ???")
  608. if parent:
  609. GError(parent=parent,
  610. caption=_("Error in %s") % prog,
  611. message=stderr)
  612. if not read:
  613. if not getErrorMsg:
  614. return ret
  615. else:
  616. return ret, _formatMsg(stderr)
  617. if stdout:
  618. Debug.msg(3, "gcmd.RunCommand(): return stdout\n'%s'" % stdout)
  619. else:
  620. Debug.msg(3, "gcmd.RunCommand(): return stdout = None")
  621. if parse:
  622. stdout = parse(stdout)
  623. if not getErrorMsg:
  624. return stdout
  625. if read and getErrorMsg:
  626. return ret, stdout, _formatMsg(stderr)
  627. return stdout, _formatMsg(stderr)
  628. def GetDefaultEncoding(forceUTF8=False):
  629. """Get default system encoding
  630. :param bool forceUTF8: force 'UTF-8' if encoding is not defined
  631. :return: system encoding (can be None)
  632. """
  633. enc = locale.getdefaultlocale()[1]
  634. if forceUTF8 and (enc is None or enc == 'UTF8'):
  635. return 'UTF-8'
  636. Debug.msg(1, "GetSystemEncoding(): %s" % enc)
  637. return enc
  638. _enc = GetDefaultEncoding() # define as global variable