PageRenderTime 37ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/tortoisehg/hgtk/hgthread.py

https://bitbucket.org/tortoisehg/hgtk/
Python | 205 lines | 187 code | 6 blank | 12 comment | 6 complexity | 82adf64b509d77942ebb202311c14d96 MD5 | raw file
Possible License(s): GPL-2.0
  1. # hgthread.py - Gtk UI class TortoiseHg
  2. #
  3. # Copyright 2009 Steve Borho <steve@borho.org>
  4. #
  5. # This software may be used and distributed according to the terms of the
  6. # GNU General Public License version 2, incorporated herein by reference.
  7. import gtk
  8. import Queue
  9. import time
  10. import urllib2
  11. from mercurial import ui, util, error
  12. from tortoisehg.util.i18n import _
  13. from tortoisehg.util import hglib, thread2
  14. from tortoisehg.hgtk import dialog, gdialog
  15. class GtkUi(ui.ui):
  16. '''
  17. PyGtk enabled mercurial.ui subclass. All this code will be running
  18. in a background thread, so it cannot directly call into Gtk.
  19. Instead, it places output and dialog requests onto queues for the
  20. main thread to pickup.
  21. '''
  22. def __init__(self, src=None, outputq=None, errorq=None, dialogq=None,
  23. responseq=None, progressq=None):
  24. super(GtkUi, self).__init__(src)
  25. if src:
  26. self.outputq = src.outputq
  27. self.errorq = src.errorq
  28. self.dialogq = src.dialogq
  29. self.responseq = src.responseq
  30. self.progressq = src.progressq
  31. else:
  32. self.outputq = outputq
  33. self.errorq = errorq
  34. self.dialogq = dialogq
  35. self.responseq = responseq
  36. self.progressq = progressq
  37. self.setconfig('ui', 'interactive', 'on')
  38. self.setconfig('progress', 'disable', 'True')
  39. def write(self, *args, **opts):
  40. if self._buffers:
  41. self._buffers[-1].extend([str(a) for a in args])
  42. else:
  43. for a in args:
  44. self.outputq.put((str(a), opts.get('label', '')))
  45. def write_err(self, *args, **opts):
  46. for a in args:
  47. self.errorq.put(str(a))
  48. def label(self, msg, label):
  49. return msg
  50. def flush(self):
  51. pass
  52. def prompt(self, msg, choices=None, default="y"):
  53. if not self.interactive(): return default
  54. try:
  55. # send request to main thread, await response
  56. self.dialogq.put( (msg, True, choices, None) )
  57. r = self.responseq.get(True)
  58. if r is None:
  59. raise EOFError
  60. if not r:
  61. return default
  62. if choices:
  63. # return char for Mercurial 1.3
  64. choice = choices[r]
  65. return choice[choice.index("&")+1].lower()
  66. return r
  67. except EOFError:
  68. raise util.Abort(_('response expected'))
  69. def promptchoice(self, msg, choices, default=0):
  70. if not self.interactive(): return default
  71. try:
  72. # send request to main thread, await response
  73. self.dialogq.put( (msg, True, choices, default) )
  74. r = self.responseq.get(True)
  75. if r is None:
  76. raise EOFError
  77. return r
  78. except EOFError:
  79. raise util.Abort(_('response expected'))
  80. def getpass(self, prompt=None, default=None):
  81. # send request to main thread, await response
  82. self.dialogq.put( (prompt or _('password: '), False, None, default) )
  83. r = self.responseq.get(True)
  84. if r is None:
  85. raise util.Abort(_('response expected'))
  86. return r
  87. def progress(self, topic, pos, item='', unit='', total=None):
  88. self.progressq.put( (topic, item, pos, total, unit) )
  89. class HgThread(thread2.Thread):
  90. '''
  91. Run an hg command in a background thread, implies output is being
  92. sent to a rendered text buffer interactively and requests for
  93. feedback from Mercurial can be handled by the user via dialog
  94. windows.
  95. '''
  96. def __init__(self, args=[], postfunc=None, parent=None):
  97. self.outputq = Queue.Queue()
  98. self.errorq = Queue.Queue()
  99. self.dialogq = Queue.Queue()
  100. self.responseq = Queue.Queue()
  101. self.progressq = Queue.Queue()
  102. self.ui = GtkUi(None, self.outputq, self.errorq, self.dialogq,
  103. self.responseq, self.progressq)
  104. self.args = args
  105. self.ret = None
  106. self.postfunc = postfunc
  107. self.parent = parent
  108. thread2.Thread.__init__(self)
  109. def getqueue(self):
  110. return self.outputq
  111. def geterrqueue(self):
  112. return self.errorq
  113. def getprogqueue(self):
  114. return self.progressq
  115. def return_code(self):
  116. '''
  117. None - command is incomplete, possibly exited with exception
  118. 0 - command returned successfully
  119. else an error was returned
  120. '''
  121. return self.ret
  122. def process_dialogs(self):
  123. '''Polled every 10ms to serve dialogs for the background thread'''
  124. try:
  125. (prompt, visible, choices, default) = self.dialogq.get_nowait()
  126. if choices:
  127. dlg = gdialog.CustomPrompt('Hg Prompt', prompt,
  128. self.parent, choices, default)
  129. dlg.connect('response', self.prompt_response)
  130. dlg.show_all()
  131. else:
  132. dlg = dialog.entry_dialog(self.parent, prompt,
  133. visible, default, self.dialog_response)
  134. except Queue.Empty:
  135. pass
  136. def prompt_response(self, dialog, response_id):
  137. dialog.destroy()
  138. if response_id == gtk.RESPONSE_DELETE_EVENT:
  139. self.responseq.put(None)
  140. else:
  141. self.responseq.put(response_id)
  142. def dialog_response(self, dialog, response_id):
  143. if response_id == gtk.RESPONSE_OK:
  144. text = dialog.entry.get_text()
  145. else:
  146. text = None
  147. dialog.destroy()
  148. self.responseq.put(text)
  149. def run(self):
  150. try:
  151. for k, v in self.ui.configitems('defaults'):
  152. self.ui.setconfig('defaults', k, '')
  153. l = 'control'
  154. ret = hglib.dispatch._dispatch(self.ui, self.args)
  155. if ret:
  156. self.ui.write(_('[command returned code %d ') % int(ret), label=l)
  157. else:
  158. self.ui.write(_('[command completed successfully '), label=l)
  159. self.ui.write(time.asctime() + ']\n', label=l)
  160. self.ret = ret or 0
  161. if self.postfunc:
  162. self.postfunc(ret)
  163. except util.Abort, e:
  164. self.ui.write_err(_('abort: ') + str(e) + '\n')
  165. except (error.RepoError, urllib2.HTTPError), e:
  166. self.ui.write_err(str(e) + '\n')
  167. except urllib2.URLError, e:
  168. import ssl
  169. err = str(e)
  170. if isinstance(e.args[0], ssl.SSLError):
  171. parts = e.args[0].strerror.split(':')
  172. if len(parts) == 7:
  173. file, line, level, errno, lib, func, reason = parts
  174. if func == 'SSL3_GET_SERVER_CERTIFICATE':
  175. err = local._('SSL: Server certificate verify failed')
  176. elif errno == '00000000':
  177. err = local._('SSL: unknown error %s:%s') % (file, line)
  178. else:
  179. err = local._('SSL error: %s') % reason
  180. self.ui.write_err(err + '\n')
  181. except (Exception, OSError, IOError), e:
  182. self.ui.write_err(str(e) + '\n')