gcmd.py 24 KB

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