gcmd.py 24 KB

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