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