gcmd.py 23 KB

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