~qbzr-dev/qbzr/trunk2a

390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
1
# -*- coding: utf-8 -*-
2
#
3
# QBzr - Qt frontend to Bazaar commands
4
# Copyright (C) 2006 Lukáš Lalinský <lalinsky@gmail.com>
5
# Copyright (C) 2008 Gary van der Merwe <garyvdm@gmail.com>
572.1.2 by Alexander Belchenko
fixed copyrights
6
# Copyright (C) 2009 Alexander Belchenko
1190.7.9 by Alexander Belchenko
Fixed qrun --execute mode. (Simon Kersey)
7
# Copyright (C) 2010 QBzr Developers
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
8
#
9
# This program is free software; you can redistribute it and/or
10
# modify it under the terms of the GNU General Public License
11
# as published by the Free Software Foundation; either version 2
12
# of the License, or (at your option) any later version.
13
#
14
# This program is distributed in the hope that it will be useful,
15
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
# GNU General Public License for more details.
18
#
19
# You should have received a copy of the GNU General Public License
20
# along with this program; if not, write to the Free Software
21
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22
575 by Alexander Belchenko
[win32] pass very long args string (>31K) to qsubprocess via temp file to avoid hitting the command-line length limit (~32K).
23
import os
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
24
import sys
575 by Alexander Belchenko
[win32] pass very long args string (>31K) to qsubprocess via temp file to avoid hitting the command-line length limit (~32K).
25
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
26
from PyQt4 import QtCore, QtGui
1470.1.2 by IWATA Hidetaka
SubProcessWindow: Show resolve button after conflict
27
from contextlib import contextmanager
487 by Alexander Belchenko
subprocess.py: data read from subprocess should be decoded to unicode with user_encoding, and write to terminal in terminal_encoding.
28
570 by Alexander Belchenko
Don't use os.kill on Windows
29
from bzrlib.plugins.qbzr.lib import MS_WINDOWS
1470.1.1 by IWATA Hidetaka
Separate code about InfoWidget from SubProcessWindow class.
30
from bzrlib.plugins.qbzr.lib.i18n import gettext, N_
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
31
from bzrlib.plugins.qbzr.lib.util import (
32
    BTN_CANCEL,
755 by Alexander Belchenko
progress bar messages should be unicode strings (to use in PyQt)
33
    BTN_CLOSE,
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
34
    BTN_OK,
755 by Alexander Belchenko
progress bar messages should be unicode strings (to use in PyQt)
35
    QBzrDialog,
416.2.2 by Gary van der Merwe
Refactor SubProcessWindow
36
    QBzrWindow,
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
37
    StandardButton,
755 by Alexander Belchenko
progress bar messages should be unicode strings (to use in PyQt)
38
    ensure_unicode,
1233.1.3 by Gary van der Merwe
SubProcessDialog: Show commit/revert options for UncommittedChanges error.
39
    InfoWidget,
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
40
    )
1233.1.2 by Gary van der Merwe
SubProcess: lazy imports.
41
42
from bzrlib.ui.text import TextProgressView, TextUIFactory
1283 by Gary van der Merwe
Don't lazy import bzrlib.plugins.qbzr.lib.trace.
43
from bzrlib.plugins.qbzr.lib.trace import (
44
   report_exception,
45
   SUB_LOAD_METHOD)
1233.1.3 by Gary van der Merwe
SubProcessDialog: Show commit/revert options for UncommittedChanges error.
46
1233.1.2 by Gary van der Merwe
SubProcess: lazy imports.
47
from bzrlib.lazy_import import lazy_import
48
lazy_import(globals(), '''
49
import codecs
50
import re
51
import shlex
52
import signal
53
import tempfile
54
import thread
55
56
from bzrlib import (
57
    bencode,
58
    commands,
1267.1.5 by Martin
Change of approach, just serialise needed attributes rather than introspecting exception instances
59
    errors,
1233.1.2 by Gary van der Merwe
SubProcess: lazy imports.
60
    osutils,
61
    ui,
62
    )
63
1233.1.3 by Gary van der Merwe
SubProcessDialog: Show commit/revert options for UncommittedChanges error.
64
from bzrlib.bzrdir import BzrDir
65
66
from bzrlib.plugins.qbzr.lib.commit import CommitWindow
67
from bzrlib.plugins.qbzr.lib.revert import RevertWindow
1470.1.5 by IWATA Hidetaka
Show Shelve button on SubProcessWidget after UncommittedChanges error detected.
68
from bzrlib.plugins.qbzr.lib.shelvewindow import ShelveWindow
1470.1.2 by IWATA Hidetaka
SubProcessWindow: Show resolve button after conflict
69
from bzrlib.plugins.qbzr.lib.conflicts import ConflictsWindow
1233.1.2 by Gary van der Merwe
SubProcess: lazy imports.
70
''')
787 by Gary van der Merwe
Remove a bunch of SubprocessUIFactory compatibility code.
71
582 by Gary van der Merwe
Use CLIUIFactory rather than TextUIFactory.
72
1091 by Alexander Belchenko
fixed problem with get_boolean when prompt is multiline string (with \n inside).
73
# Subprocess service messages markers
74
SUB_PROGRESS = "qbzr:PROGRESS:"
75
SUB_GETPASS = "qbzr:GETPASS:"
76
SUB_GETUSER = "qbzr:GETUSER:"
77
SUB_GETBOOL = "qbzr:GETBOOL:"
1435.2.1 by Benoît Pierre
Add support for UIFactory.choose.
78
SUB_CHOOSE = "qbzr:CHOOSE:"
1206 by Gary van der Merwe
Subprocess: Make it possible to detect the type of error from the subprocess.
79
SUB_ERROR = "qbzr:ERROR:"
1470.1.2 by IWATA Hidetaka
SubProcessWindow: Show resolve button after conflict
80
SUB_NOTIFY = "qbzr:NOTIFY:"
81
82
NOTIFY_CONFLICT = "conflict:"
1091 by Alexander Belchenko
fixed problem with get_boolean when prompt is multiline string (with \n inside).
83
84
1470.1.1 by IWATA Hidetaka
Separate code about InfoWidget from SubProcessWindow class.
85
class WarningInfoWidget(InfoWidget):
86
    def __init__(self, parent):
87
        InfoWidget.__init__(self, parent)
1470.1.7 by IWATA Hidetaka
SubProcessWidget: Change button layout of InfoWidget.
88
        layout = QtGui.QVBoxLayout(self)
89
        label_layout = QtGui.QHBoxLayout()
1470.1.1 by IWATA Hidetaka
Separate code about InfoWidget from SubProcessWindow class.
90
        
91
        icon = QtGui.QLabel()
92
        icon.setPixmap(self.style().standardPixmap(
93
                       QtGui.QStyle.SP_MessageBoxWarning))
1470.1.7 by IWATA Hidetaka
SubProcessWidget: Change button layout of InfoWidget.
94
        label_layout.addWidget(icon)
1470.1.1 by IWATA Hidetaka
Separate code about InfoWidget from SubProcessWindow class.
95
        self.label = QtGui.QLabel()
1470.1.7 by IWATA Hidetaka
SubProcessWidget: Change button layout of InfoWidget.
96
        label_layout.addWidget(self.label, 2)
97
        layout.addLayout(label_layout)
98
        self.button_layout = QtGui.QHBoxLayout()
99
        self.button_layout.addStretch(1)
1470.1.1 by IWATA Hidetaka
Separate code about InfoWidget from SubProcessWindow class.
100
        layout.addLayout(self.button_layout)
101
102
        self.buttons = []
103
104
    def add_button(self, text, on_click):
105
        button = QtGui.QPushButton(gettext(text))
106
        self.connect(button, QtCore.SIGNAL("clicked(bool)"), on_click)
107
        self.button_layout.addWidget(button)
108
        self.buttons.append((button, on_click))
109
110
    def remove_all_buttons(self):
111
        for button, on_click in self.buttons:
112
            self.disconnect(button, QtCore.SIGNAL("clicked(bool)"), on_click)
113
            self.button_layout.removeWidget(button)
114
            button.close()
115
        del(self.buttons[:])
116
117
    def set_label(self, text):
118
        self.label.setText(gettext(text))
119
1470.1.5 by IWATA Hidetaka
Show Shelve button on SubProcessWidget after UncommittedChanges error detected.
120
    def setup_for_uncommitted(self, on_commit, on_revert, on_shelve):
1470.1.1 by IWATA Hidetaka
Separate code about InfoWidget from SubProcessWindow class.
121
        self.remove_all_buttons()
122
        self.set_label(N_('Working tree has uncommitted changes.'))
123
        self.add_button(N_('Commit'), on_commit)
124
        self.add_button(N_('Revert'), on_revert)
1470.1.5 by IWATA Hidetaka
Show Shelve button on SubProcessWidget after UncommittedChanges error detected.
125
        self.add_button(N_('Shelve'), on_shelve)
1470.1.1 by IWATA Hidetaka
Separate code about InfoWidget from SubProcessWindow class.
126
1470.1.2 by IWATA Hidetaka
SubProcessWindow: Show resolve button after conflict
127
    def setup_for_conflicted(self, on_conflict, on_revert):
128
        self.remove_all_buttons()
129
        self.set_label(N_('Working tree has conflicts.'))
130
        self.add_button(N_('Resolve'), on_conflict)
131
        self.add_button(N_('Revert'), on_revert)
132
1482.1.2 by IWATA Hidetaka
Show Lock error more gracefully.
133
134
    def setup_for_locked(self, on_retry):
135
        self.remove_all_buttons()
136
        self.set_label(N_('Could not acquire lock. Please retry later.'))
137
        self.add_button(N_('Retry'), on_retry)
138
139
931.1.34 by Alexander Belchenko
get rid of old-style classes to make PyQt 4.6 happy.
140
class SubProcessWindowBase(object):
473 by Alexander Belchenko
window title refactoring: now title can be list/tuple or string or None -- no need to create fake one-item-list for strings.
141
419.2.1 by Gary van der Merwe
qcommit: Reimplement revert as a subprocess which was mistakenly lost at rev 392.
142
    def __init_internal__(self, title,
143
                          name="genericsubprocess",
144
                          args=None,
419.2.7 by Gary van der Merwe
Commands that we pass file paths to should be run from the tree.basedir
145
                          dir=None,
419.2.1 by Gary van der Merwe
qcommit: Reimplement revert as a subprocess which was mistakenly lost at rev 392.
146
                          default_size=None,
147
                          ui_mode=True,
148
                          dialog=True,
491 by Alexander Belchenko
SubProcessWidget has new parameter: hide_progress to hide progress bar by default.
149
                          parent=None,
1259.1.1 by Luis Arias
Implement a --immediate option for qupdate so that it can be launched immediately from bzr explorer.
150
                          hide_progress=False,
151
                          immediate=False):
419.2.1 by Gary van der Merwe
qcommit: Reimplement revert as a subprocess which was mistakenly lost at rev 392.
152
        self.restoreSize(name, default_size)
481.1.2 by Alexander Belchenko
using _check_args validation method only in SubProcessWindowBase.start()
153
        self._name = name
550 by Lukáš Lalinský
Fix restoring the size of designer-based subprocess dialogs
154
        self._default_size = default_size
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
155
        self.args = args
419.2.7 by Gary van der Merwe
Commands that we pass file paths to should be run from the tree.basedir
156
        self.dir = dir
416.2.1 by Gary van der Merwe
Implement ui_mode
157
        self.ui_mode = ui_mode
410 by Lukáš Lalinský
Fix the layout of qpull
158
900 by Alexander Belchenko
Teach most of q-commands about return code: 0 if operation finished successfully, 1 if dialog was cancelled by user.
159
        self.return_code = 1
160
416.2.2 by Gary van der Merwe
Refactor SubProcessWindow
161
        if dialog:
162
            flags = (self.windowFlags() & ~QtCore.Qt.Window) | QtCore.Qt.Dialog
163
            self.setWindowFlags(flags)
410 by Lukáš Lalinský
Fix the layout of qpull
164
491 by Alexander Belchenko
SubProcessWidget has new parameter: hide_progress to hide progress bar by default.
165
        self.process_widget = SubProcessWidget(self.ui_mode, self, hide_progress)
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
166
        self.connect(self.process_widget,
167
            QtCore.SIGNAL("finished()"),
799.3.1 by Naoki INADA
Add do_/on_ prefix.
168
            self.on_finished)
390.3.2 by Gary van der Merwe
Change pull, push, branch, and merge to use SubProcessDialog.
169
        self.connect(self.process_widget,
1206 by Gary van der Merwe
Subprocess: Make it possible to detect the type of error from the subprocess.
170
            QtCore.SIGNAL("failed(QString)"),
799.3.1 by Naoki INADA
Add do_/on_ prefix.
171
            self.on_failed)
689.1.5 by Gary van der Merwe
Fix to get the subprocess dialog to show if there was an error showing the ext diff.
172
        self.connect(self.process_widget,
173
            QtCore.SIGNAL("error()"),
799.3.1 by Naoki INADA
Add do_/on_ prefix.
174
            self.on_error)
1470.1.2 by IWATA Hidetaka
SubProcessWindow: Show resolve button after conflict
175
        self.connect(self.process_widget,
176
            QtCore.SIGNAL("conflicted(QString)"),
177
            self.on_conflicted)
410 by Lukáš Lalinský
Fix the layout of qpull
178
1309.2.3 by Simon Kersey
Make subprocess buttons public
179
        self.closeButton = StandardButton(BTN_CLOSE)
180
        self.okButton = StandardButton(BTN_OK)
181
        self.cancelButton = StandardButton(BTN_CANCEL)
1190.7.9 by Alexander Belchenko
Fixed qrun --execute mode. (Simon Kersey)
182
689.1.5 by Gary van der Merwe
Fix to get the subprocess dialog to show if there was an error showing the ext diff.
183
        # ok button gets disabled when we start.
429.1.3 by Mark Hammond
Define subprocessStarted(bool) and subprocessFinished(bool) signals and connect them to the widget slots to auto disable the widgets while the subprocess is running. This means code never explicitly needs to enable/disable widgets and also means subprocess.py doesn't need to keep as many widgets in self.
184
        QtCore.QObject.connect(self,
185
                               QtCore.SIGNAL("subprocessStarted(bool)"),
1309.2.3 by Simon Kersey
Make subprocess buttons public
186
                               self.okButton,
429.1.3 by Mark Hammond
Define subprocessStarted(bool) and subprocessFinished(bool) signals and connect them to the widget slots to auto disable the widgets while the subprocess is running. This means code never explicitly needs to enable/disable widgets and also means subprocess.py doesn't need to keep as many widgets in self.
187
                               QtCore.SLOT("setDisabled(bool)"))
188
189
        # ok button gets hidden when we finish.
190
        QtCore.QObject.connect(self,
191
                               QtCore.SIGNAL("subprocessFinished(bool)"),
1309.2.3 by Simon Kersey
Make subprocess buttons public
192
                               self.okButton,
429.1.3 by Mark Hammond
Define subprocessStarted(bool) and subprocessFinished(bool) signals and connect them to the widget slots to auto disable the widgets while the subprocess is running. This means code never explicitly needs to enable/disable widgets and also means subprocess.py doesn't need to keep as many widgets in self.
193
                               QtCore.SLOT("setHidden(bool)"))
194
195
        # close button gets shown when we finish.
196
        QtCore.QObject.connect(self,
197
                               QtCore.SIGNAL("subprocessFinished(bool)"),
1309.2.3 by Simon Kersey
Make subprocess buttons public
198
                               self.closeButton,
1172.2.4 by Gary van der Merwe
Revert retry button behavior.
199
                               QtCore.SLOT("setShown(bool)"))
200
201
        # cancel button gets disabled when finished.
202
        QtCore.QObject.connect(self,
203
                               QtCore.SIGNAL("subprocessFinished(bool)"),
1309.2.3 by Simon Kersey
Make subprocess buttons public
204
                               self.cancelButton,
1172.2.4 by Gary van der Merwe
Revert retry button behavior.
205
                               QtCore.SLOT("setDisabled(bool)"))
206
207
        # ok button gets enabled when we fail.
208
        QtCore.QObject.connect(self,
209
                               QtCore.SIGNAL("subprocessFailed(bool)"),
1309.2.3 by Simon Kersey
Make subprocess buttons public
210
                               self.okButton,
1172.2.4 by Gary van der Merwe
Revert retry button behavior.
211
                               QtCore.SLOT("setDisabled(bool)"))
689.1.5 by Gary van der Merwe
Fix to get the subprocess dialog to show if there was an error showing the ext diff.
212
1172.2.5 by Gary van der Merwe
Implement the retry button by changing the text of the ok button.
213
        # Change the ok button to 'retry' if we fail.
214
        QtCore.QObject.connect(self,
215
                               QtCore.SIGNAL("subprocessFailed(bool)"),
1309.2.3 by Simon Kersey
Make subprocess buttons public
216
                               lambda failed: self.okButton.setText(
217
                                              gettext('&Retry')))
1172.2.5 by Gary van der Merwe
Implement the retry button by changing the text of the ok button.
218
429.1.1 by Mark Hammond
First cut at changes which give control of the layout back to children and allows us to fully utilize dialogs from QtDesigner. Kill the 'centralwidget' for dialogs, use QtDesigner to hookup slots for enabling/disabling widgets, remove tons of .py code and other goodness.
219
        self.buttonbox = QtGui.QDialogButtonBox(self)
1309.2.3 by Simon Kersey
Make subprocess buttons public
220
        self.buttonbox.addButton(self.okButton,
221
            QtGui.QDialogButtonBox.AcceptRole)
222
        self.buttonbox.addButton(self.closeButton,
223
            QtGui.QDialogButtonBox.AcceptRole)
224
        self.buttonbox.addButton(self.cancelButton,
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
225
            QtGui.QDialogButtonBox.RejectRole)
799.3.1 by Naoki INADA
Add do_/on_ prefix.
226
        self.connect(self.buttonbox, QtCore.SIGNAL("accepted()"), self.do_accept)
227
        self.connect(self.buttonbox, QtCore.SIGNAL("rejected()"), self.do_reject)
1309.2.3 by Simon Kersey
Make subprocess buttons public
228
        self.closeButton.setHidden(True) # but 'close' starts as hidden.
429.1.1 by Mark Hammond
First cut at changes which give control of the layout back to children and allows us to fully utilize dialogs from QtDesigner. Kill the 'centralwidget' for dialogs, use QtDesigner to hookup slots for enabling/disabling widgets, remove tons of .py code and other goodness.
229
1470.1.1 by IWATA Hidetaka
Separate code about InfoWidget from SubProcessWindow class.
230
        self.infowidget = WarningInfoWidget(self)
231
        self.infowidget.hide()
1470.1.2 by IWATA Hidetaka
SubProcessWindow: Show resolve button after conflict
232
        QtCore.QObject.connect(self,
233
                               QtCore.SIGNAL("subprocessStarted(bool)"),
234
                               self.infowidget,
235
                               QtCore.SLOT("setHidden(bool)"))
236
1259.1.1 by Luis Arias
Implement a --immediate option for qupdate so that it can be launched immediately from bzr explorer.
237
        if immediate:
238
            self.do_accept()
1233.1.3 by Gary van der Merwe
SubProcessDialog: Show commit/revert options for UncommittedChanges error.
239
492 by Alexander Belchenko
AddWindow uses vertical splitter (splitter sizes remembered in qbzr.conf)
240
    def make_default_status_box(self):
1482.1.1 by IWATA Hidetaka
Layout InfoWidget panel to all SubProcessDialog-based dialogs.
241
        panel = QtGui.QWidget()
242
        vbox = QtGui.QVBoxLayout(panel)
243
        vbox.setContentsMargins(0, 0, 0, 0)
244
        vbox.addWidget(self.infowidget)
492 by Alexander Belchenko
AddWindow uses vertical splitter (splitter sizes remembered in qbzr.conf)
245
        status_group_box = QtGui.QGroupBox(gettext("Status"))
429.1.1 by Mark Hammond
First cut at changes which give control of the layout back to children and allows us to fully utilize dialogs from QtDesigner. Kill the 'centralwidget' for dialogs, use QtDesigner to hookup slots for enabling/disabling widgets, remove tons of .py code and other goodness.
246
        status_layout = QtGui.QVBoxLayout(status_group_box)
551 by Lukáš Lalinský
Get rid of the extra padding in subprocess dialogs
247
        status_layout.setContentsMargins(0, 0, 0, 0)
429.1.1 by Mark Hammond
First cut at changes which give control of the layout back to children and allows us to fully utilize dialogs from QtDesigner. Kill the 'centralwidget' for dialogs, use QtDesigner to hookup slots for enabling/disabling widgets, remove tons of .py code and other goodness.
248
        status_layout.addWidget(self.process_widget)
1482.1.1 by IWATA Hidetaka
Layout InfoWidget panel to all SubProcessDialog-based dialogs.
249
        vbox.addWidget(status_group_box)
250
        return panel
251
252
    def make_process_panel(self):
253
        panel = QtGui.QWidget()
254
        vbox = QtGui.QVBoxLayout(panel)
255
        vbox.setContentsMargins(0, 0, 0, 0)
256
        vbox.addWidget(self.infowidget)
257
        vbox.addWidget(self.process_widget)
258
        return panel
938.1.12 by Alexander Belchenko
SubProcessWindowBase.make_default_layout_widgets: added docstring.
259
492 by Alexander Belchenko
AddWindow uses vertical splitter (splitter sizes remembered in qbzr.conf)
260
    def make_default_layout_widgets(self):
938.1.12 by Alexander Belchenko
SubProcessWindowBase.make_default_layout_widgets: added docstring.
261
        """Yields widgets to add to main dialog layout: status and button boxes.
262
        Status box has progress bar and console area.
263
        Button box has 2 buttons: OK and Cancel (after successfull command 
264
        execution there will be Close and Cancel).
265
        """
492 by Alexander Belchenko
AddWindow uses vertical splitter (splitter sizes remembered in qbzr.conf)
266
        yield self.make_default_status_box()
429.1.1 by Mark Hammond
First cut at changes which give control of the layout back to children and allows us to fully utilize dialogs from QtDesigner. Kill the 'centralwidget' for dialogs, use QtDesigner to hookup slots for enabling/disabling widgets, remove tons of .py code and other goodness.
267
        yield self.buttonbox
268
481.1.1 by Alexander Belchenko
added validation methods for SubProcessWindowBase that will be called in accept method.
269
    def validate(self):
270
        """Override this method in your class and do any validation there.
271
        Return True if all parameters is OK and subprocess can be started.
272
        """
273
        return True
274
275
    def _check_args(self):
276
        """Check that self.args is not None and return True.
277
        Otherwise show error dialog to the user and return False.
278
        """
799.1.8 by Gary van der Merwe
Cater for errors being raised in SubProcessDialog.validate.
279
        if self.args is None:
280
            raise RuntimeError('Subprocess action "%s" cannot be started\n'
281
                               'because self.args is None.' % self._name)
822 by Gary van der Merwe
SubProcessDialog._check_args must return True. Otherwise SubProcessDialogs that don't overwrite start don't run.
282
        return True
481.1.1 by Alexander Belchenko
added validation methods for SubProcessWindowBase that will be called in accept method.
283
799.3.1 by Naoki INADA
Add do_/on_ prefix.
284
    def do_accept(self):
390.3.2 by Gary van der Merwe
Change pull, push, branch, and merge to use SubProcessDialog.
285
        if self.process_widget.finished:
286
            self.close()
287
        else:
799.1.8 by Gary van der Merwe
Cater for errors being raised in SubProcessDialog.validate.
288
            try:
289
                if not self.validate():
290
                    return
291
            except:
292
                report_exception(type=SUB_LOAD_METHOD, window=self.window())
481.1.1 by Alexander Belchenko
added validation methods for SubProcessWindowBase that will be called in accept method.
293
                return
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
294
429.1.3 by Mark Hammond
Define subprocessStarted(bool) and subprocessFinished(bool) signals and connect them to the widget slots to auto disable the widgets while the subprocess is running. This means code never explicitly needs to enable/disable widgets and also means subprocess.py doesn't need to keep as many widgets in self.
295
            self.emit(QtCore.SIGNAL("subprocessStarted(bool)"), True)
858 by Alexander Belchenko
slots/signals refactoring: use new signal "disableUi(bool)" instead of "subprocessStarted(bool)" to disable/enable controls in dialogs.
296
            self.emit(QtCore.SIGNAL("disableUi(bool)"), True)
799.3.1 by Naoki INADA
Add do_/on_ prefix.
297
            self.do_start()
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
298
799.3.1 by Naoki INADA
Add do_/on_ prefix.
299
    def do_start(self):
481.1.2 by Alexander Belchenko
using _check_args validation method only in SubProcessWindowBase.start()
300
        if self._check_args():
799.3.2 by Naoki INADA
Replace some method's names.
301
            self.process_widget.do_start(self.dir, *self.args)
481.1.2 by Alexander Belchenko
using _check_args validation method only in SubProcessWindowBase.start()
302
        else:
1206 by Gary van der Merwe
Subprocess: Make it possible to detect the type of error from the subprocess.
303
            self.on_failed('CheckArgsFailed')
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
304
799.3.1 by Naoki INADA
Add do_/on_ prefix.
305
    def do_reject(self):
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
306
        if self.process_widget.is_running():
307
            self.process_widget.abort()
308
        else:
414 by Lukáš Lalinský
Fix various problems in the subprocess dialog and layouts in qgetnew/qgetupdates
309
            self.close()
310
799.3.1 by Naoki INADA
Add do_/on_ prefix.
311
    def on_finished(self):
419.2.5 by Gary van der Merwe
SubProcessDialog should return Accepted if successful.
312
        if hasattr(self, 'setResult'):
313
            self.setResult(QtGui.QDialog.Accepted)
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
314
429.1.3 by Mark Hammond
Define subprocessStarted(bool) and subprocessFinished(bool) signals and connect them to the widget slots to auto disable the widgets while the subprocess is running. This means code never explicitly needs to enable/disable widgets and also means subprocess.py doesn't need to keep as many widgets in self.
315
        self.emit(QtCore.SIGNAL("subprocessFinished(bool)"), True)
858 by Alexander Belchenko
slots/signals refactoring: use new signal "disableUi(bool)" instead of "subprocessStarted(bool)" to disable/enable controls in dialogs.
316
        self.emit(QtCore.SIGNAL("disableUi(bool)"), False)
429.1.3 by Mark Hammond
Define subprocessStarted(bool) and subprocessFinished(bool) signals and connect them to the widget slots to auto disable the widgets while the subprocess is running. This means code never explicitly needs to enable/disable widgets and also means subprocess.py doesn't need to keep as many widgets in self.
317
900 by Alexander Belchenko
Teach most of q-commands about return code: 0 if operation finished successfully, 1 if dialog was cancelled by user.
318
        self.return_code = 0
319
1470.1.2 by IWATA Hidetaka
SubProcessWindow: Show resolve button after conflict
320
        if not self.ui_mode and not self.infowidget.isVisible():
416.2.1 by Gary van der Merwe
Implement ui_mode
321
            self.close()
429.1.1 by Mark Hammond
First cut at changes which give control of the layout back to children and allows us to fully utilize dialogs from QtDesigner. Kill the 'centralwidget' for dialogs, use QtDesigner to hookup slots for enabling/disabling widgets, remove tons of .py code and other goodness.
322
1470.1.2 by IWATA Hidetaka
SubProcessWindow: Show resolve button after conflict
323
    def on_conflicted(self, tree_path):
324
        if tree_path:
325
            self.action_url = unicode(tree_path) # QString -> unicode
326
            self.infowidget.setup_for_conflicted(self.open_conflicts_win,
327
                                                 self.open_revert_win)
328
            self.infowidget.show()
329
1206 by Gary van der Merwe
Subprocess: Make it possible to detect the type of error from the subprocess.
330
    def on_failed(self, error):
689.1.5 by Gary van der Merwe
Fix to get the subprocess dialog to show if there was an error showing the ext diff.
331
        self.emit(QtCore.SIGNAL("subprocessFailed(bool)"), False)
858 by Alexander Belchenko
slots/signals refactoring: use new signal "disableUi(bool)" instead of "subprocessStarted(bool)" to disable/enable controls in dialogs.
332
        self.emit(QtCore.SIGNAL("disableUi(bool)"), False)
1233.1.3 by Gary van der Merwe
SubProcessDialog: Show commit/revert options for UncommittedChanges error.
333
        
334
        if error=='UncommittedChanges':
335
            self.action_url = self.process_widget.error_data['display_url']
1470.1.2 by IWATA Hidetaka
SubProcessWindow: Show resolve button after conflict
336
            self.infowidget.setup_for_uncommitted(self.open_commit_win, 
1470.1.5 by IWATA Hidetaka
Show Shelve button on SubProcessWidget after UncommittedChanges error detected.
337
                                                  self.open_revert_win,
338
                                                  self.open_shelve_win)
1470.1.1 by IWATA Hidetaka
Separate code about InfoWidget from SubProcessWindow class.
339
            self.infowidget.show()
858 by Alexander Belchenko
slots/signals refactoring: use new signal "disableUi(bool)" instead of "subprocessStarted(bool)" to disable/enable controls in dialogs.
340
1482.1.2 by IWATA Hidetaka
Show Lock error more gracefully.
341
        elif error=='LockContention':
342
            self.infowidget.setup_for_locked(self.do_accept)
343
            self.infowidget.show()
344
799.3.1 by Naoki INADA
Add do_/on_ prefix.
345
    def on_error(self):
689.1.5 by Gary van der Merwe
Fix to get the subprocess dialog to show if there was an error showing the ext diff.
346
        self.emit(QtCore.SIGNAL("subprocessError(bool)"), False)
858 by Alexander Belchenko
slots/signals refactoring: use new signal "disableUi(bool)" instead of "subprocessStarted(bool)" to disable/enable controls in dialogs.
347
550 by Lukáš Lalinský
Fix restoring the size of designer-based subprocess dialogs
348
    def setupUi(self, ui):
349
        ui.setupUi(self)
350
        if self._restore_size:
351
            self.resize(self._restore_size)
352
1233.1.3 by Gary van der Merwe
SubProcessDialog: Show commit/revert options for UncommittedChanges error.
353
    def open_commit_win(self, b):
354
        # XXX refactor so that the tree can be opened by the window
355
        tree, branch = BzrDir.open_tree_or_branch(self.action_url)
1470.1.4 by IWATA Hidetaka
SubProcessDialog: Make possible to show sub window even if SubProcessDialog is modal.
356
        commit_window = CommitWindow(tree, None, parent=self)
1233.1.3 by Gary van der Merwe
SubProcessDialog: Show commit/revert options for UncommittedChanges error.
357
        self.windows.append(commit_window)
358
        commit_window.show()
359
    
360
    def open_revert_win(self, b):
361
        # XXX refactor so that the tree can be opened by the window
362
        tree, branch = BzrDir.open_tree_or_branch(self.action_url)
1470.1.4 by IWATA Hidetaka
SubProcessDialog: Make possible to show sub window even if SubProcessDialog is modal.
363
        revert_window = RevertWindow(tree, None, parent=self)
1233.1.3 by Gary van der Merwe
SubProcessDialog: Show commit/revert options for UncommittedChanges error.
364
        self.windows.append(revert_window)
365
        revert_window.show()
473 by Alexander Belchenko
window title refactoring: now title can be list/tuple or string or None -- no need to create fake one-item-list for strings.
366
1470.1.5 by IWATA Hidetaka
Show Shelve button on SubProcessWidget after UncommittedChanges error detected.
367
    def open_shelve_win(self, b):
368
        shelve_window = ShelveWindow(directory=self.action_url, parent=self)
369
        self.windows.append(shelve_window)
370
        shelve_window.show()
371
1470.1.2 by IWATA Hidetaka
SubProcessWindow: Show resolve button after conflict
372
    def open_conflicts_win(self, b):
1470.1.4 by IWATA Hidetaka
SubProcessDialog: Make possible to show sub window even if SubProcessDialog is modal.
373
        window = ConflictsWindow(self.action_url, parent=self)
1470.1.2 by IWATA Hidetaka
SubProcessWindow: Show resolve button after conflict
374
        self.windows.append(window)
375
        window.show()
1470.1.3 by IWATA Hidetaka
SubProcessWindow: Close InfoWidget after all conflicts are resolved.
376
        QtCore.QObject.connect(window,
377
                               QtCore.SIGNAL("allResolved(bool)"),
378
                               self.infowidget,
379
                               QtCore.SLOT("setHidden(bool)")) 
1470.1.2 by IWATA Hidetaka
SubProcessWindow: Show resolve button after conflict
380
799.3.5 by Naoki INADA
Reverse mro.
381
class SubProcessWindow(SubProcessWindowBase, QBzrWindow):
419.2.1 by Gary van der Merwe
qcommit: Reimplement revert as a subprocess which was mistakenly lost at rev 392.
382
383
    def __init__(self, title,
384
                 name="genericsubprocess",
385
                 args=None,
419.2.7 by Gary van der Merwe
Commands that we pass file paths to should be run from the tree.basedir
386
                 dir=None,
419.2.1 by Gary van der Merwe
qcommit: Reimplement revert as a subprocess which was mistakenly lost at rev 392.
387
                 default_size=None,
388
                 ui_mode=True,
389
                 dialog=True,
491 by Alexander Belchenko
SubProcessWidget has new parameter: hide_progress to hide progress bar by default.
390
                 parent=None,
391
                 hide_progress=False):
473 by Alexander Belchenko
window title refactoring: now title can be list/tuple or string or None -- no need to create fake one-item-list for strings.
392
        QBzrWindow.__init__(self, title, parent)
419.2.1 by Gary van der Merwe
qcommit: Reimplement revert as a subprocess which was mistakenly lost at rev 392.
393
        self.__init_internal__(title,
394
                               name=name,
395
                               args=args,
419.2.7 by Gary van der Merwe
Commands that we pass file paths to should be run from the tree.basedir
396
                               dir=dir,
419.2.1 by Gary van der Merwe
qcommit: Reimplement revert as a subprocess which was mistakenly lost at rev 392.
397
                               default_size=default_size,
398
                               ui_mode=ui_mode,
399
                               dialog=dialog,
491 by Alexander Belchenko
SubProcessWidget has new parameter: hide_progress to hide progress bar by default.
400
                               parent=parent,
401
                               hide_progress=hide_progress)
419.2.1 by Gary van der Merwe
qcommit: Reimplement revert as a subprocess which was mistakenly lost at rev 392.
402
799.3.5 by Naoki INADA
Reverse mro.
403
    def closeEvent(self, event):
404
        if not self.process_widget.is_running():
405
            QBzrWindow.closeEvent(self, event)
406
        else:
407
            self.process_widget.abort()
408
            event.ignore()
409
410
411
class SubProcessDialog(SubProcessWindowBase, QBzrDialog):
429.1.9 by Mark Hammond
Clarify some comments and move to docstrings.
412
    """An abstract base-class for all subprocess related dialogs.
419.2.1 by Gary van der Merwe
qcommit: Reimplement revert as a subprocess which was mistakenly lost at rev 392.
413
429.1.9 by Mark Hammond
Clarify some comments and move to docstrings.
414
    It is expected that sub-classes of this will create their own UI, and while
415
    doing so, will add the widgets returned by
416
    self.make_default_layout_widgets()
417
    """
473 by Alexander Belchenko
window title refactoring: now title can be list/tuple or string or None -- no need to create fake one-item-list for strings.
418
429.1.1 by Mark Hammond
First cut at changes which give control of the layout back to children and allows us to fully utilize dialogs from QtDesigner. Kill the 'centralwidget' for dialogs, use QtDesigner to hookup slots for enabling/disabling widgets, remove tons of .py code and other goodness.
419
    def __init__(self, title=None,
419.2.1 by Gary van der Merwe
qcommit: Reimplement revert as a subprocess which was mistakenly lost at rev 392.
420
                 name="genericsubprocess",
421
                 args=None,
419.2.7 by Gary van der Merwe
Commands that we pass file paths to should be run from the tree.basedir
422
                 dir=None,
419.2.1 by Gary van der Merwe
qcommit: Reimplement revert as a subprocess which was mistakenly lost at rev 392.
423
                 default_size=None,
424
                 ui_mode=True,
425
                 dialog=True,
491 by Alexander Belchenko
SubProcessWidget has new parameter: hide_progress to hide progress bar by default.
426
                 parent=None,
1259.1.1 by Luis Arias
Implement a --immediate option for qupdate so that it can be launched immediately from bzr explorer.
427
                 hide_progress=False,
428
                 immediate=False):
429.1.1 by Mark Hammond
First cut at changes which give control of the layout back to children and allows us to fully utilize dialogs from QtDesigner. Kill the 'centralwidget' for dialogs, use QtDesigner to hookup slots for enabling/disabling widgets, remove tons of .py code and other goodness.
429
        QBzrDialog.__init__(self, title, parent)
419.2.1 by Gary van der Merwe
qcommit: Reimplement revert as a subprocess which was mistakenly lost at rev 392.
430
        self.__init_internal__(title,
431
                               name=name,
432
                               args=args,
419.2.7 by Gary van der Merwe
Commands that we pass file paths to should be run from the tree.basedir
433
                               dir=dir,
419.2.1 by Gary van der Merwe
qcommit: Reimplement revert as a subprocess which was mistakenly lost at rev 392.
434
                               default_size=default_size,
435
                               ui_mode=ui_mode,
436
                               dialog=dialog,
491 by Alexander Belchenko
SubProcessWidget has new parameter: hide_progress to hide progress bar by default.
437
                               parent=parent,
1259.1.1 by Luis Arias
Implement a --immediate option for qupdate so that it can be launched immediately from bzr explorer.
438
                               hide_progress=hide_progress,
439
                               immediate=immediate)
419.2.1 by Gary van der Merwe
qcommit: Reimplement revert as a subprocess which was mistakenly lost at rev 392.
440
799.3.5 by Naoki INADA
Reverse mro.
441
    def closeEvent(self, event):
442
        if not self.process_widget.is_running():
443
            QBzrDialog.closeEvent(self, event)
444
        else:
445
            self.process_widget.abort()
446
            event.ignore()
447
448
429.1.1 by Mark Hammond
First cut at changes which give control of the layout back to children and allows us to fully utilize dialogs from QtDesigner. Kill the 'centralwidget' for dialogs, use QtDesigner to hookup slots for enabling/disabling widgets, remove tons of .py code and other goodness.
449
class SimpleSubProcessDialog(SubProcessDialog):
429.1.9 by Mark Hammond
Clarify some comments and move to docstrings.
450
    """A concrete helper class of SubProcessDialog, which has a single label
451
    widget for displaying a simple description before executing a subprocess.
452
    """
473 by Alexander Belchenko
window title refactoring: now title can be list/tuple or string or None -- no need to create fake one-item-list for strings.
453
429.1.1 by Mark Hammond
First cut at changes which give control of the layout back to children and allows us to fully utilize dialogs from QtDesigner. Kill the 'centralwidget' for dialogs, use QtDesigner to hookup slots for enabling/disabling widgets, remove tons of .py code and other goodness.
454
    def __init__(self, title, desc,
455
                 name="genericsubprocess",
456
                 args=None,
457
                 dir=None,
458
                 default_size=None,
459
                 ui_mode=True,
460
                 dialog=True,
419.4.11 by Gary van der Merwe
Merge Trunk
461
                 hide_progress=False,
419.4.7 by Gary van der Merwe
Merge Trunk.
462
                 auto_start_show_on_failed=False,
491 by Alexander Belchenko
SubProcessWidget has new parameter: hide_progress to hide progress bar by default.
463
                 parent=None,
1259.1.1 by Luis Arias
Implement a --immediate option for qupdate so that it can be launched immediately from bzr explorer.
464
                 immediate=False
419.4.11 by Gary van der Merwe
Merge Trunk
465
                 ):
429.1.1 by Mark Hammond
First cut at changes which give control of the layout back to children and allows us to fully utilize dialogs from QtDesigner. Kill the 'centralwidget' for dialogs, use QtDesigner to hookup slots for enabling/disabling widgets, remove tons of .py code and other goodness.
466
        super(SimpleSubProcessDialog, self).__init__(
467
                               title,
468
                               name=name,
469
                               args=args,
470
                               dir=dir,
471
                               default_size=default_size,
472
                               ui_mode=ui_mode,
473
                               dialog=dialog,
491 by Alexander Belchenko
SubProcessWidget has new parameter: hide_progress to hide progress bar by default.
474
                               parent=parent,
1259.1.1 by Luis Arias
Implement a --immediate option for qupdate so that it can be launched immediately from bzr explorer.
475
                               hide_progress=hide_progress,
476
                               immediate=immediate)
429.1.1 by Mark Hammond
First cut at changes which give control of the layout back to children and allows us to fully utilize dialogs from QtDesigner. Kill the 'centralwidget' for dialogs, use QtDesigner to hookup slots for enabling/disabling widgets, remove tons of .py code and other goodness.
477
        self.desc = desc
478
        # create a layout to hold our one label and the subprocess widgets.
479
        layout = QtGui.QVBoxLayout(self)
496 by Alexander Belchenko
SimpleSubProcessDialog: put description into groupbox.
480
        groupbox = QtGui.QGroupBox(gettext('Description'))
481
        v = QtGui.QVBoxLayout(groupbox)
482
        label = QtGui.QLabel(self.desc)
429.1.1 by Mark Hammond
First cut at changes which give control of the layout back to children and allows us to fully utilize dialogs from QtDesigner. Kill the 'centralwidget' for dialogs, use QtDesigner to hookup slots for enabling/disabling widgets, remove tons of .py code and other goodness.
483
        label.font().setBold(True)
496 by Alexander Belchenko
SimpleSubProcessDialog: put description into groupbox.
484
        v.addWidget(label)
485
        layout.addWidget(groupbox)
429.1.1 by Mark Hammond
First cut at changes which give control of the layout back to children and allows us to fully utilize dialogs from QtDesigner. Kill the 'centralwidget' for dialogs, use QtDesigner to hookup slots for enabling/disabling widgets, remove tons of .py code and other goodness.
486
        # and add the subprocess widgets.
487
        for w in self.make_default_layout_widgets():
488
            layout.addWidget(w)
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
489
419.4.10 by Gary van der Merwe
auto_start_show_on_failed only needs to be in SimpleSubProcessDialog. Use SIGNAL/SLOT design to show the SimpleSubProcessDialog on failed.
490
        self.auto_start_show_on_failed = auto_start_show_on_failed
689.1.5 by Gary van der Merwe
Fix to get the subprocess dialog to show if there was an error showing the ext diff.
491
        QtCore.QTimer.singleShot(1, self.auto_start)
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
492
689.1.5 by Gary van der Merwe
Fix to get the subprocess dialog to show if there was an error showing the ext diff.
493
    def auto_start(self):
419.4.10 by Gary van der Merwe
auto_start_show_on_failed only needs to be in SimpleSubProcessDialog. Use SIGNAL/SLOT design to show the SimpleSubProcessDialog on failed.
494
        if self.auto_start_show_on_failed:
495
            QtCore.QObject.connect(self,
689.1.5 by Gary van der Merwe
Fix to get the subprocess dialog to show if there was an error showing the ext diff.
496
                                   QtCore.SIGNAL("subprocessFailed(bool)"),
497
                                   self,
498
                                   QtCore.SLOT("setHidden(bool)"))
499
            QtCore.QObject.connect(self,
500
                                   QtCore.SIGNAL("subprocessError(bool)"),
419.4.10 by Gary van der Merwe
auto_start_show_on_failed only needs to be in SimpleSubProcessDialog. Use SIGNAL/SLOT design to show the SimpleSubProcessDialog on failed.
501
                                   self,
502
                                   QtCore.SLOT("setHidden(bool)"))
799.3.1 by Naoki INADA
Add do_/on_ prefix.
503
            self.do_start()
487 by Alexander Belchenko
subprocess.py: data read from subprocess should be decoded to unicode with user_encoding, and write to terminal in terminal_encoding.
504
779.1.1 by Alexander Belchenko
qupdate: simple dialog to update working tree in the branch.
505
416.2.2 by Gary van der Merwe
Refactor SubProcessWindow
506
class SubProcessWidget(QtGui.QWidget):
410 by Lukáš Lalinský
Fix the layout of qpull
507
491 by Alexander Belchenko
SubProcessWidget has new parameter: hide_progress to hide progress bar by default.
508
    def __init__(self, ui_mode, parent=None, hide_progress=False):
416.2.2 by Gary van der Merwe
Refactor SubProcessWindow
509
        QtGui.QGroupBox.__init__(self, parent)
416.2.1 by Gary van der Merwe
Implement ui_mode
510
        self.ui_mode = ui_mode
410 by Lukáš Lalinský
Fix the layout of qpull
511
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
512
        layout = QtGui.QVBoxLayout(self)
410 by Lukáš Lalinský
Fix the layout of qpull
513
571.1.3 by Gary van der Merwe
Show transport activity in new label.
514
        message_layout = QtGui.QHBoxLayout()
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
515
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
516
        self.progressMessage = QtGui.QLabel(self)
410 by Lukáš Lalinský
Fix the layout of qpull
517
        #self.progressMessage.setWordWrap(True) -- this breaks minimal window size hint
861 by Alexander Belchenko
status & progress bar window: initial status message is "Ready" (instead of "Stopped")
518
        self.progressMessage.setText(gettext("Ready"))
571.1.3 by Gary van der Merwe
Show transport activity in new label.
519
        message_layout.addWidget(self.progressMessage, 1)
520
521
        self.transportActivity = QtGui.QLabel(self)
522
        message_layout.addWidget(self.transportActivity)
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
523
571.1.3 by Gary van der Merwe
Show transport activity in new label.
524
        layout.addLayout(message_layout)
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
525
526
        self.progressBar = QtGui.QProgressBar(self)
527
        self.progressBar.setMaximum(1000000)
528
        layout.addWidget(self.progressBar)
529
530
        self.console = QtGui.QTextBrowser(self)
475.2.13 by Alexander Belchenko
subprocess.py: console in SubProcessWidget does not take focus via Tab
531
        self.console.setFocusPolicy(QtCore.Qt.ClickFocus)
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
532
        layout.addWidget(self.console)
410 by Lukáš Lalinský
Fix the layout of qpull
533
487 by Alexander Belchenko
subprocess.py: data read from subprocess should be decoded to unicode with user_encoding, and write to terminal in terminal_encoding.
534
        self.encoding = osutils.get_user_encoding()
535
        self.stdout = None
536
        self.stderr = None
537
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
538
        self.process = QtCore.QProcess()
539
        self.connect(self.process,
540
            QtCore.SIGNAL("readyReadStandardOutput()"),
541
            self.readStdout)
542
        self.connect(self.process,
543
            QtCore.SIGNAL("readyReadStandardError()"),
544
            self.readStderr)
545
        self.connect(self.process,
546
            QtCore.SIGNAL("error(QProcess::ProcessError)"),
547
            self.reportProcessError)
548
        self.connect(self.process,
549
            QtCore.SIGNAL("finished(int, QProcess::ExitStatus)"),
550
            self.onFinished)
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
551
419.2.7 by Gary van der Merwe
Commands that we pass file paths to should be run from the tree.basedir
552
        self.defaultWorkingDir = self.process.workingDirectory ()
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
553
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
554
        self.finished = False
555
        self.aborting = False
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
556
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
557
        self.messageFormat = QtGui.QTextCharFormat()
558
        self.errorFormat = QtGui.QTextCharFormat()
559
        self.errorFormat.setForeground(QtGui.QColor('red'))
938.1.10 by Alexander Belchenko
SubProcessWidget.logMessageEx(): extended method to show more kind of messages in console area. Now supported 3 kinds: plain, error (red text), cmdline (blue text).
560
        self.cmdlineFormat = QtGui.QTextCharFormat()
561
        self.cmdlineFormat.setForeground(QtGui.QColor('blue'))
491 by Alexander Belchenko
SubProcessWidget has new parameter: hide_progress to hide progress bar by default.
562
563
        if hide_progress:
564
            self.hide_progress()
565
1457.2.14 by Alexander Belchenko
create a command-line for bzr; force passing command line via temporary file because of bug 936587
566
        self.force_passing_args_via_file = False
575 by Alexander Belchenko
[win32] pass very long args string (>31K) to qsubprocess via temp file to avoid hitting the command-line length limit (~32K).
567
        self._args_file = None  # temp file to pass arguments to qsubprocess
1206 by Gary van der Merwe
Subprocess: Make it possible to detect the type of error from the subprocess.
568
        self.error_class = ''
1233.1.1 by Gary van der Merwe
SubProcess: Transmit the error dict.
569
        self.error_data = {}
1470.1.2 by IWATA Hidetaka
SubProcessWindow: Show resolve button after conflict
570
        self.conflicted = False
575 by Alexander Belchenko
[win32] pass very long args string (>31K) to qsubprocess via temp file to avoid hitting the command-line length limit (~32K).
571
416.2.6 by Gary van der Merwe
qadd, qrevert: Use SubProcessWindow.
572
    def hide_progress(self):
573
        self.progressMessage.setHidden(True)
574
        self.progressBar.setHidden(True)
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
575
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
576
    def is_running(self):
577
        return self.process.state() == QtCore.QProcess.Running or\
578
               self.process.state() == QtCore.QProcess.Starting
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
579
894 by Alexander Belchenko
SubProcessWidget.do_start: added docstring
580
    def do_start(self, workdir, *args):
581
        """Launch one bzr command.
582
        @param  workdir:    working directory for command.
583
                            Could be None to use self.defaultWorkingDir
584
        @param  args:   bzr command and its arguments
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
585
        @type   args:   all arguments should be unicode strings (or ascii-only).
894 by Alexander Belchenko
SubProcessWidget.do_start: added docstring
586
        """
429.1.1 by Mark Hammond
First cut at changes which give control of the layout back to children and allows us to fully utilize dialogs from QtDesigner. Kill the 'centralwidget' for dialogs, use QtDesigner to hookup slots for enabling/disabling widgets, remove tons of .py code and other goodness.
587
        QtGui.QApplication.processEvents() # make sure ui has caught up
894 by Alexander Belchenko
SubProcessWidget.do_start: added docstring
588
        self.start_multi(((workdir, args),))
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
589
416.2.5 by Gary van der Merwe
qcommit: Get add working.
590
    def start_multi(self, commands):
571.1.3 by Gary van der Merwe
Show transport activity in new label.
591
        self.setProgress(0, [gettext("Starting...")], "")
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
592
        self.console.setFocus(QtCore.Qt.OtherFocusReason)
416.2.5 by Gary van der Merwe
qcommit: Get add working.
593
        self.commands = list(commands)
594
        self._start_next()
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
595
416.2.5 by Gary van der Merwe
qcommit: Get add working.
596
    def _start_next(self):
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
597
        """Start first command from self.commands queue."""
1143 by Alexander Belchenko
subprocess.py: logMessageEx can echo messages to real terminal in non --ui-mode.
598
        self._setup_stdout_stderr()
575 by Alexander Belchenko
[win32] pass very long args string (>31K) to qsubprocess via temp file to avoid hitting the command-line length limit (~32K).
599
        self._delete_args_file()
419.2.7 by Gary van der Merwe
Commands that we pass file paths to should be run from the tree.basedir
600
        dir, args = self.commands.pop(0)
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
601
1065 by Alexander Belchenko
log invoked command-line in subprocess widget
602
        # Log the command we about to execute
603
        def format_args_for_log(args):
604
            r = ['bzr']
605
            for a in args:
606
                a = unicode(a).translate({
607
                        ord(u'\n'): u'\\n',
608
                        ord(u'\r'): u'\\r',
609
                        ord(u'\t'): u'\\t',
610
                        })
611
                if " " in a:
612
                    r.append('"%s"' % a)
613
                else:
614
                    r.append(a)
1071 by Alexander Belchenko
shrink long logged command line to avoid flood in the status view.
615
            s = ' '.join(r)
616
            if len(s) > 128:  # XXX make it configurable?
1150 by Alexander Belchenko
subprocess.py: minor fix in "Run command" report truncation with ellipsis.
617
                s = s[:128] + '...'
1071 by Alexander Belchenko
shrink long logged command line to avoid flood in the status view.
618
            return s
1143 by Alexander Belchenko
subprocess.py: logMessageEx can echo messages to real terminal in non --ui-mode.
619
        self.logMessageEx("Run command: "+format_args_for_log(args), "cmdline", self.stderr)
1065 by Alexander Belchenko
log invoked command-line in subprocess widget
620
939.1.4 by Alexander Belchenko
using special helper functon bencode_unicode() in SubProcessWidget._start_next().
621
        args = bencode_unicode(args)
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
622
942 by Alexander Belchenko
run qsubprocess: if command-line for qsubprocess becomes very long we need to pass it via temp file regardless of platform.
623
        # win32 has command-line length limit about 32K, but it seems 
624
        # problems with command-line buffer limit occurs not only on windows.
625
        # see bug https://bugs.launchpad.net/qbzr/+bug/396165
1190.1.8 by Alexander Belchenko
qcommit: multiline comments passed to subprocess via temporary file.
626
        # on Linux I believe command-line is in utf-8,
627
        # so we need to have some extra space
628
        # when converting unicode -> utf8
1190.1.40 by Alexander Belchenko
qsubprocess: pass command line with double and multiple backslashes (e.g. UNC paths like \\server\path\to\branch) via temporary file.
629
        if (len(args) > 10000       # XXX make the threshold configurable in qbzr.conf?
630
            or re.search(r"(?:"
631
                r"\n|\r"            # workaround for bug #517420
632
                r"|\\\\"            # workaround for bug #528944
1457.2.14 by Alexander Belchenko
create a command-line for bzr; force passing command line via temporary file because of bug 936587
633
                r")", args) is not None
634
            or self.force_passing_args_via_file     # workaround for bug #936587
635
            ):
942 by Alexander Belchenko
run qsubprocess: if command-line for qsubprocess becomes very long we need to pass it via temp file regardless of platform.
636
            # save the args to the file
637
            fname = self._create_args_file(args)
638
            args = "@" + fname.replace('\\', '/')
575 by Alexander Belchenko
[win32] pass very long args string (>31K) to qsubprocess via temp file to avoid hitting the command-line length limit (~32K).
639
419.2.7 by Gary van der Merwe
Commands that we pass file paths to should be run from the tree.basedir
640
        if dir is None:
641
            dir = self.defaultWorkingDir
1457.2.14 by Alexander Belchenko
create a command-line for bzr; force passing command line via temporary file because of bug 936587
642
1206 by Gary van der Merwe
Subprocess: Make it possible to detect the type of error from the subprocess.
643
        self.error_class = ''
1233.1.1 by Gary van der Merwe
SubProcess: Transmit the error dict.
644
        self.error_data = {}
1457.2.14 by Alexander Belchenko
create a command-line for bzr; force passing command line via temporary file because of bug 936587
645
1143 by Alexander Belchenko
subprocess.py: logMessageEx can echo messages to real terminal in non --ui-mode.
646
        self.process.setWorkingDirectory(dir)
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
647
        if getattr(sys, "frozen", None) is not None:
1305.1.13 by Gary van der Merwe
Fix finding bzr.exe when running from bzrw.exe with out full path in the command line.
648
            bzr_exe = sys.executable
1305.1.14 by Gary van der Merwe
SubProcess: Make sure we are allways running bzr, so that tortoisebzr dose not have to modify sys.executable/sys.argv.
649
            if os.path.basename(bzr_exe) != "bzr.exe":
650
                # Was run from bzrw.exe or tbzrcommand.
651
                bzr_exe = os.path.join(os.path.dirname(sys.executable), "bzr.exe")
652
                if not os.path.isfile(bzr_exe):
653
                    self.reportProcessError(
654
                        None, gettext('Could not locate "bzr.exe".'))
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
655
            self.process.start(
953 by Alexander Belchenko
qsubprocess: when launching qsubprocess we should use bzr.exe in the case of windows GUI (bzrw.exe).
656
                bzr_exe, ['qsubprocess', '--bencode', args])
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
657
        else:
1305.1.14 by Gary van der Merwe
SubProcess: Make sure we are allways running bzr, so that tortoisebzr dose not have to modify sys.executable/sys.argv.
658
            # otherwise running as python script.
659
            # ensure run from bzr, and not others, e.g. tbzrcommand.py
660
            script = sys.argv[0]
661
            # make absolute, because we may be running in a different
662
            # dir.
663
            script = os.path.abspath(script)
664
            if os.path.basename(script) != "bzr":
665
                import bzrlib
666
                # are we running directly from a bzr directory?
667
                script = os.path.join(bzrlib.__path__[0], "..", "bzr")
668
                if not os.path.isfile(script):
669
                    # maybe from an installed bzr?
670
                    script = os.path.join(sys.prefix, "scripts", "bzr")
671
                if not os.path.isfile(script):
672
                    self.reportProcessError(
673
                        None, gettext('Could not locate "bzr" script.'))
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
674
            self.process.start(
1305.1.14 by Gary van der Merwe
SubProcess: Make sure we are allways running bzr, so that tortoisebzr dose not have to modify sys.executable/sys.argv.
675
                sys.executable, [script, 'qsubprocess', '--bencode', args])
487 by Alexander Belchenko
subprocess.py: data read from subprocess should be decoded to unicode with user_encoding, and write to terminal in terminal_encoding.
676
677
    def _setup_stdout_stderr(self):
678
        if self.stdout is None:
679
            writer = codecs.getwriter(osutils.get_terminal_encoding())
680
            self.stdout = writer(sys.stdout, errors='replace')
681
            self.stderr = writer(sys.stderr, errors='replace')
575 by Alexander Belchenko
[win32] pass very long args string (>31K) to qsubprocess via temp file to avoid hitting the command-line length limit (~32K).
682
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
683
    def abort(self):
684
        if self.is_running():
598 by Gary van der Merwe
If the user cancels a password request, interupt from in the sub process, rather than from the main process.
685
            self.abort_futher_processes()
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
686
            if not self.aborting:
557 by Gary van der Merwe
Send, and correctly handle SIGINT to abort subprocess.
687
                self.aborting = True
570 by Alexander Belchenko
Don't use os.kill on Windows
688
                if MS_WINDOWS:
572.1.1 by Alexander Belchenko
[win32] using win32event to send signal to child subprocess asking to stop. qsubprocess then uses thread.interrupt_main() to emulate Ctrl+C.
689
                    # trying to send signal to our subprocess
690
                    signal_event(get_child_pid(self.process.pid()))
570 by Alexander Belchenko
Don't use os.kill on Windows
691
                else:
692
                    # be nice and try to use ^C
693
                    os.kill(self.process.pid(), signal.SIGINT) 
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
694
                self.setProgress(None, [gettext("Aborting...")])
695
            else:
696
                self.process.terminate()
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
697
598 by Gary van der Merwe
If the user cancels a password request, interupt from in the sub process, rather than from the main process.
698
    def abort_futher_processes(self):
699
        self.commands = []
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
700
571.1.3 by Gary van der Merwe
Show transport activity in new label.
701
    def setProgress(self, progress, messages, transport_activity=None):
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
702
        if progress is not None:
703
            self.progressBar.setValue(progress)
704
        if progress == 1000000 and not messages:
705
            text = gettext("Finished!")
706
        else:
1435.1.3 by Martin Packman
Rework how progress updates are passed over the subprocess link and allow unicode text
707
            text = " / ".join(messages)
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
708
        self.progressMessage.setText(text)
571.1.3 by Gary van der Merwe
Show transport activity in new label.
709
        if transport_activity is not None:
710
            self.transportActivity.setText(transport_activity)
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
711
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
712
    def readStdout(self):
754 by Alexander Belchenko
compatibility with new bencode.bdecode: we need to pass plain string, not [ascii] unicode
713
        # ensure we read from subprocess plain string
714
        data = str(self.process.readAllStandardOutput())
715
        # we need unicode for all strings except bencoded streams
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
716
        for line in data.splitlines():
1091 by Alexander Belchenko
fixed problem with get_boolean when prompt is multiline string (with \n inside).
717
            if line.startswith(SUB_PROGRESS):
1141 by Alexander Belchenko
qsubprocess: Catch possible errors from bencode decoder is we got malformed string from child process. Show the error nicely.
718
                try:
1435.1.3 by Martin Packman
Rework how progress updates are passed over the subprocess link and allow unicode text
719
                    progress, transport_activity, task_info = bencode.bdecode(
720
                        line[len(SUB_PROGRESS):])
721
                    messages = [b.decode("utf-8") for b in task_info]
1141 by Alexander Belchenko
qsubprocess: Catch possible errors from bencode decoder is we got malformed string from child process. Show the error nicely.
722
                except ValueError, e:
723
                    # we got malformed data from qsubprocess (bencode failed to decode)
724
                    # so just show it in the status console
1143 by Alexander Belchenko
subprocess.py: logMessageEx can echo messages to real terminal in non --ui-mode.
725
                    self.logMessageEx("qsubprocess error: "+str(e), "error", self.stderr)
726
                    self.logMessageEx(line.decode(self.encoding), "error", self.stderr)
1141 by Alexander Belchenko
qsubprocess: Catch possible errors from bencode decoder is we got malformed string from child process. Show the error nicely.
727
                else:
728
                    self.setProgress(progress, messages, transport_activity)
1091 by Alexander Belchenko
fixed problem with get_boolean when prompt is multiline string (with \n inside).
729
            elif line.startswith(SUB_GETPASS):
730
                prompt = bdecode_prompt(line[len(SUB_GETPASS):])
579 by Gary van der Merwe
Pressing cancel on password dialog for a sub process should abort, so that we don't try running further processes in the queue.
731
                passwd, ok = QtGui.QInputDialog.getText(self,
732
                                                        gettext("Enter Password"),
733
                                                        prompt,
734
                                                        QtGui.QLineEdit.Password)
596 by Alexander Belchenko
bencode bool as int
735
                data = unicode(passwd).encode('utf-8'), int(ok)
1091 by Alexander Belchenko
fixed problem with get_boolean when prompt is multiline string (with \n inside).
736
                self.process.write(SUB_GETPASS + bencode.bencode(data) + "\n")
595 by Gary van der Merwe
If we cancel a subprocess password request, we should still write to the console, because abort does not work on windows if we are waiting for stdin.readline() .
737
                if not ok:
598 by Gary van der Merwe
If the user cancels a password request, interupt from in the sub process, rather than from the main process.
738
                    self.abort_futher_processes()
1091 by Alexander Belchenko
fixed problem with get_boolean when prompt is multiline string (with \n inside).
739
            elif line.startswith(SUB_GETUSER):
740
                prompt = bdecode_prompt(line[len(SUB_GETUSER):])
1016.1.3 by Gary van der Merwe
SubprocessUIFactory: add get_username and get_boolean.
741
                passwd, ok = QtGui.QInputDialog.getText(self,
742
                                                        gettext("Enter Username"),
743
                                                        prompt)
744
                data = unicode(passwd).encode('utf-8'), int(ok)
1091 by Alexander Belchenko
fixed problem with get_boolean when prompt is multiline string (with \n inside).
745
                self.process.write(SUB_GETUSER + bencode.bencode(data) + "\n")
1016.1.3 by Gary van der Merwe
SubprocessUIFactory: add get_username and get_boolean.
746
                if not ok:
747
                    self.abort_futher_processes()
1091 by Alexander Belchenko
fixed problem with get_boolean when prompt is multiline string (with \n inside).
748
            elif line.startswith(SUB_GETBOOL):
749
                prompt = bdecode_prompt(line[len(SUB_GETBOOL):])
1016.1.3 by Gary van der Merwe
SubprocessUIFactory: add get_username and get_boolean.
750
                button = QtGui.QMessageBox.question(
1091 by Alexander Belchenko
fixed problem with get_boolean when prompt is multiline string (with \n inside).
751
                    self, "Bazaar", prompt,
1016.1.3 by Gary van der Merwe
SubprocessUIFactory: add get_username and get_boolean.
752
                    QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
753
                
754
                data = (button == QtGui.QMessageBox.Yes)
1091 by Alexander Belchenko
fixed problem with get_boolean when prompt is multiline string (with \n inside).
755
                self.process.write(SUB_GETBOOL + bencode.bencode(data) + "\n")
1435.2.1 by Benoît Pierre
Add support for UIFactory.choose.
756
            elif line.startswith(SUB_CHOOSE):
757
                msg, choices, default = bdecode_choose_args(line[len(SUB_CHOOSE):])
758
                mbox = QtGui.QMessageBox(parent=self)
759
                mbox.setText(msg)
760
                mbox.setIcon(QtGui.QMessageBox.Question)
761
                choices = choices.split('\n')
762
                index = 0
763
                for c in choices:
764
                    button = mbox.addButton(c, QtGui.QMessageBox.AcceptRole)
765
                    if index == default:
766
                        mbox.setDefaultButton(button)
767
                    index += 1
768
                index = mbox.exec_()
769
                self.process.write(SUB_CHOOSE + bencode.bencode(index) + "\n")
1206 by Gary van der Merwe
Subprocess: Make it possible to detect the type of error from the subprocess.
770
            elif line.startswith(SUB_ERROR):
1267.1.1 by Martin
Bencode exception instances with dedicated function for better reliability
771
                self.error_class, self.error_data = bdecode_exception_instance(
772
                    line[len(SUB_ERROR):])
1470.1.2 by IWATA Hidetaka
SubProcessWindow: Show resolve button after conflict
773
            elif line.startswith(SUB_NOTIFY):
774
                msg = line[len(SUB_NOTIFY):]
775
                if msg.startswith(NOTIFY_CONFLICT):
776
                    self.conflicted = True
777
                    self.conflict_tree_path = bdecode_prompt(msg[len(NOTIFY_CONFLICT):])
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
778
            else:
1198 by INADA Naoki
Use errors='replace' when decoding stdout of subprocess.
779
                line = line.decode(self.encoding, 'replace')
1143 by Alexander Belchenko
subprocess.py: logMessageEx can echo messages to real terminal in non --ui-mode.
780
                self.logMessageEx(line, 'plain', self.stdout)
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
781
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
782
    def readStderr(self):
853 by Naoki INADA
Use error='replace' to decode subprocess's stderr.
783
        data = str(self.process.readAllStandardError()).decode(self.encoding, 'replace')
689.1.5 by Gary van der Merwe
Fix to get the subprocess dialog to show if there was an error showing the ext diff.
784
        if data:
785
            self.emit(QtCore.SIGNAL("error()"))
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
786
416.2.5 by Gary van der Merwe
qcommit: Get add working.
787
        for line in data.splitlines():
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
788
            error = line.startswith("bzr: ERROR:")
1143 by Alexander Belchenko
subprocess.py: logMessageEx can echo messages to real terminal in non --ui-mode.
789
            self.logMessage(line, error, self.stderr)
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
790
1143 by Alexander Belchenko
subprocess.py: logMessageEx can echo messages to real terminal in non --ui-mode.
791
    def logMessage(self, message, error=False, terminal_stream=None):
1142 by Alexander Belchenko
subprocess.py: logMessage is wrapper around logMessageEx.
792
        kind = 'plain'
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
793
        if error:
1142 by Alexander Belchenko
subprocess.py: logMessage is wrapper around logMessageEx.
794
            kind = 'error'
1143 by Alexander Belchenko
subprocess.py: logMessageEx can echo messages to real terminal in non --ui-mode.
795
        self.logMessageEx(message, kind, terminal_stream)
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
796
1143 by Alexander Belchenko
subprocess.py: logMessageEx can echo messages to real terminal in non --ui-mode.
797
    def logMessageEx(self, message, kind="plain", terminal_stream=None):
938.1.10 by Alexander Belchenko
SubProcessWidget.logMessageEx(): extended method to show more kind of messages in console area. Now supported 3 kinds: plain, error (red text), cmdline (blue text).
798
        """Write message to console area.
799
        @param kind: kind of message used for selecting style of formatting.
800
            Possible kind values:
801
                * plain = usual message, written in default style;
802
                * error = error message, written in red;
803
                * cmdline = show actual command-line, written in blue.
1143 by Alexander Belchenko
subprocess.py: logMessageEx can echo messages to real terminal in non --ui-mode.
804
        @param terminal_stream: if we working in non --ui-mode
805
            the message can be echoed to real terminal via specified
806
            terminal_stream (e.g. sys.stdout or sys.stderr)
938.1.10 by Alexander Belchenko
SubProcessWidget.logMessageEx(): extended method to show more kind of messages in console area. Now supported 3 kinds: plain, error (red text), cmdline (blue text).
807
        """
808
        if kind == 'error':
809
            format = self.errorFormat
810
        elif kind == 'cmdline':
811
            format = self.cmdlineFormat
812
        else:
813
            format = self.messageFormat
814
        self.console.setCurrentCharFormat(format)
815
        self.console.append(message)
816
        scrollbar = self.console.verticalScrollBar()
817
        scrollbar.setValue(scrollbar.maximum())
1143 by Alexander Belchenko
subprocess.py: logMessageEx can echo messages to real terminal in non --ui-mode.
818
        if not self.ui_mode and terminal_stream:
819
            terminal_stream.write(message)
820
            terminal_stream.write('\n')
938.1.10 by Alexander Belchenko
SubProcessWidget.logMessageEx(): extended method to show more kind of messages in console area. Now supported 3 kinds: plain, error (red text), cmdline (blue text).
821
1305.1.14 by Gary van der Merwe
SubProcess: Make sure we are allways running bzr, so that tortoisebzr dose not have to modify sys.executable/sys.argv.
822
    def reportProcessError(self, error, message=None):
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
823
        self.aborting = False
824
        self.setProgress(1000000, [gettext("Failed!")])
1305.1.14 by Gary van der Merwe
SubProcess: Make sure we are allways running bzr, so that tortoisebzr dose not have to modify sys.executable/sys.argv.
825
        if message is None:
826
            if error == QtCore.QProcess.FailedToStart:
827
                message = gettext("Failed to start bzr.")
828
            else:
829
                message = gettext("Error while running bzr. (error code: %d)" % error)
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
830
        self.logMessage(message, True)
1206 by Gary van der Merwe
Subprocess: Make it possible to detect the type of error from the subprocess.
831
        self.emit(QtCore.SIGNAL("failed(QString)"), self.error_class)
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
832
833
    def onFinished(self, exitCode, exitStatus):
575 by Alexander Belchenko
[win32] pass very long args string (>31K) to qsubprocess via temp file to avoid hitting the command-line length limit (~32K).
834
        self._delete_args_file()
557 by Gary van der Merwe
Send, and correctly handle SIGINT to abort subprocess.
835
        if self.aborting:
836
            self.aborting = False
837
            self.setProgress(1000000, [gettext("Aborted!")])
1206 by Gary van der Merwe
Subprocess: Make it possible to detect the type of error from the subprocess.
838
            self.emit(QtCore.SIGNAL("failed(QString)"), 'Aborted')
1233 by Gary van der Merwe
SubProcessWidget: Only bzr return codes >= 3 should be considered as errors.
839
        elif exitCode < 3:
557 by Gary van der Merwe
Send, and correctly handle SIGINT to abort subprocess.
840
            if self.commands and not self.aborting:
416.2.5 by Gary van der Merwe
qcommit: Get add working.
841
                self._start_next()
842
            else:
843
                self.finished = True
571.1.2 by Gary van der Merwe
Use new progress api to show transport activity.
844
                self.setProgress(1000000, [gettext("Finished!")])
1470.1.2 by IWATA Hidetaka
SubProcessWindow: Show resolve button after conflict
845
                if self.conflicted:
846
                    self.emit(QtCore.SIGNAL("conflicted(QString)"), 
847
                              self.conflict_tree_path)
416.2.5 by Gary van der Merwe
qcommit: Get add working.
848
                self.emit(QtCore.SIGNAL("finished()"))
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
849
        else:
850
            self.setProgress(1000000, [gettext("Failed!")])
1206 by Gary van der Merwe
Subprocess: Make it possible to detect the type of error from the subprocess.
851
            self.emit(QtCore.SIGNAL("failed(QString)"), self.error_class)
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
852
575 by Alexander Belchenko
[win32] pass very long args string (>31K) to qsubprocess via temp file to avoid hitting the command-line length limit (~32K).
853
    def _create_args_file(self, text):
830 by Alexander Belchenko
qsubprocess: when writing data to temp file encode it as utf-8 stream.
854
        """@param text: text to write into temp file,
855
                        it should be unicode string
856
        """
575 by Alexander Belchenko
[win32] pass very long args string (>31K) to qsubprocess via temp file to avoid hitting the command-line length limit (~32K).
857
        if self._args_file:
858
            self._delete_args_file()
859
        qdir = os.path.join(tempfile.gettempdir(), 'QBzr', 'qsubprocess')
860
        if not os.path.isdir(qdir):
861
            os.makedirs(qdir)
862
        fd, fname = tempfile.mkstemp(dir=qdir)
863
        f = os.fdopen(fd, "wb")
864
        try:
830 by Alexander Belchenko
qsubprocess: when writing data to temp file encode it as utf-8 stream.
865
            f.write(text.encode('utf8'))
575 by Alexander Belchenko
[win32] pass very long args string (>31K) to qsubprocess via temp file to avoid hitting the command-line length limit (~32K).
866
        finally:
867
            f.close()   # it closes fd as well
868
        self._args_file = fname
869
        return fname
870
871
    def _delete_args_file(self):
872
        if self._args_file:
873
            try:
874
                os.unlink(self._args_file)
1117.1.2 by Alexander Belchenko
some cleanups suggested by pyflakes
875
            except (IOError, OSError):
575 by Alexander Belchenko
[win32] pass very long args string (>31K) to qsubprocess via temp file to avoid hitting the command-line length limit (~32K).
876
                pass
877
            else:
878
                self._args_file = None
879
390.3.1 by Gary van der Merwe
Create Generic SubProcessDialog and get revert to use it.
880
787 by Gary van der Merwe
Remove a bunch of SubprocessUIFactory compatibility code.
881
class SubprocessProgressView (TextProgressView):
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
882
851 by Gary van der Merwe
SubprocessUIFactory fixes.
883
    def __init__(self, term_file):
884
        TextProgressView.__init__(self, term_file)
885
        # The TextProgressView does not show the transport activity untill
886
        # there was a progress update. This changed becuse showing the
887
        # transport activity before a progress update would cause artifactes to
888
        # remain on the screen. We don't have to worry about that
889
        self._have_output = True
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
890
787 by Gary van der Merwe
Remove a bunch of SubprocessUIFactory compatibility code.
891
    def _repaint(self):
892
        if self._last_task:
1435.1.3 by Martin Packman
Rework how progress updates are passed over the subprocess link and allow unicode text
893
            # Since bzr 2.2 _format_task returns a 2-tuple of unicode
894
            text, counter = self._format_task(self._last_task)
895
            task_info = (text.encode("utf-8"), counter.encode("utf-8"))
787 by Gary van der Merwe
Remove a bunch of SubprocessUIFactory compatibility code.
896
            progress_frac = self._last_task._overall_completion_fraction()
897
            if progress_frac is not None:
898
                progress = int(progress_frac * 1000000)
899
            else:
900
                progress = 1
901
        else:
1435.1.3 by Martin Packman
Rework how progress updates are passed over the subprocess link and allow unicode text
902
            task_info = ()
787 by Gary van der Merwe
Remove a bunch of SubprocessUIFactory compatibility code.
903
            progress = 0
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
904
787 by Gary van der Merwe
Remove a bunch of SubprocessUIFactory compatibility code.
905
        trans = self._last_transport_msg
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
906
1091 by Alexander Belchenko
fixed problem with get_boolean when prompt is multiline string (with \n inside).
907
        self._term_file.write(
1435.1.3 by Martin Packman
Rework how progress updates are passed over the subprocess link and allow unicode text
908
            SUB_PROGRESS + bencode.bencode((progress, trans, task_info)) + '\n')
787 by Gary van der Merwe
Remove a bunch of SubprocessUIFactory compatibility code.
909
        self._term_file.flush()
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
910
851 by Gary van der Merwe
SubprocessUIFactory fixes.
911
    def clear(self):
912
        pass
913
914
915
class SubprocessUIFactory(TextUIFactory):
571.1.1 by Gary van der Merwe
Move SubprocessUIFactory to lib/subprocess.py
916
851 by Gary van der Merwe
SubprocessUIFactory fixes.
917
    def make_progress_view(self):
918
        return SubprocessProgressView(self.stdout)
979 by Gary van der Merwe
Better SubprocessUIFactory backward compatibility.
919
    
920
    # This is to be compatabile with bzr < rev 4558
921
    _make_progress_view = make_progress_view
922
    
582 by Gary van der Merwe
Use CLIUIFactory rather than TextUIFactory.
923
    def clear_term(self):
924
        """Prepare the terminal for output.
925
926
        This will, for example, clear text progress bars, and leave the
927
        cursor at the leftmost position."""
928
        pass
571.1.1 by Gary van der Merwe
Move SubprocessUIFactory to lib/subprocess.py
929
1016.1.3 by Gary van der Merwe
SubprocessUIFactory: add get_username and get_boolean.
930
    def _get_answer_from_main(self, name, arg):
1091 by Alexander Belchenko
fixed problem with get_boolean when prompt is multiline string (with \n inside).
931
        self.stdout.write(name + bencode_prompt(arg) + '\n')
571.1.1 by Gary van der Merwe
Move SubprocessUIFactory to lib/subprocess.py
932
        self.stdout.flush()
933
        line = self.stdin.readline()
1016.1.3 by Gary van der Merwe
SubprocessUIFactory: add get_username and get_boolean.
934
        if line.startswith(name):
935
            return bencode.bdecode(line[len(name):].rstrip('\r\n'))
936
        raise Exception("Did not recive a answer from the main process.")
937
    
1435.2.1 by Benoît Pierre
Add support for UIFactory.choose.
938
    def _choose_from_main(self, msg, choices, default):
939
        name = SUB_CHOOSE
940
        self.stdout.write(name + bencode_choose_args(msg, choices, default) + '\n')
941
        self.stdout.flush()
942
        line = self.stdin.readline()
943
        if line.startswith(name):
944
            return bencode.bdecode(line[len(name):].rstrip('\r\n'))
945
        raise Exception("Did not recive a answer from the main process.")
946
    
1016.1.3 by Gary van der Merwe
SubprocessUIFactory: add get_username and get_boolean.
947
    def get_password(self, prompt='', **kwargs):
948
        prompt = prompt % kwargs
1091 by Alexander Belchenko
fixed problem with get_boolean when prompt is multiline string (with \n inside).
949
        passwd, accepted = self._get_answer_from_main(SUB_GETPASS, prompt)
1016.1.3 by Gary van der Merwe
SubprocessUIFactory: add get_username and get_boolean.
950
        if accepted:
951
            return passwd
952
        else:
953
            raise KeyboardInterrupt()
954
    
955
    def get_username(self, prompt='', **kwargs):
956
        prompt = prompt % kwargs
1091 by Alexander Belchenko
fixed problem with get_boolean when prompt is multiline string (with \n inside).
957
        username, accepted = self._get_answer_from_main(SUB_GETUSER, prompt)
1016.1.3 by Gary van der Merwe
SubprocessUIFactory: add get_username and get_boolean.
958
        if accepted:
959
            return username
960
        else:
961
            raise KeyboardInterrupt()
647 by Alexander Belchenko
qrevert: added Diff button to view changes in files selected to revert.
962
1016.1.3 by Gary van der Merwe
SubprocessUIFactory: add get_username and get_boolean.
963
    def get_boolean(self, prompt):
1091 by Alexander Belchenko
fixed problem with get_boolean when prompt is multiline string (with \n inside).
964
        return self._get_answer_from_main(SUB_GETBOOL, prompt+'?')
965
1435.2.1 by Benoît Pierre
Add support for UIFactory.choose.
966
    def choose(self, msg, choices, default=None):
967
        if default is None:
968
            default = -1
969
        index = self._choose_from_main(msg, choices, default)
970
        return index
971
572.1.1 by Alexander Belchenko
[win32] using win32event to send signal to child subprocess asking to stop. qsubprocess then uses thread.interrupt_main() to emulate Ctrl+C.
972
1190.1.4 by Alexander Belchenko
move qsubprocess command class back to commands.py, but body stays in subprocess.py.
973
# [bialix 2010/02/04] body of cmd_qsubprocess has moved from commands.py
1117.1.1 by Alexander Belchenko
cmd_qsubprocess moved from commands.py to subprocess.py
974
# to see annotation of cmd_qsubprocess before move use:
975
#     bzr qannotate commands.py -r1117
976
1470.1.2 by IWATA Hidetaka
SubProcessWindow: Show resolve button after conflict
977
@contextmanager
978
def watch_conflicts(on_conflicted):
979
    """
980
    Call on_conflicted when conflicts generated in the context.
981
    :on_conflicted: callable with 1 argument(abspath of wt).
982
    """
983
    from uuid import uuid1
984
    hook_name = uuid1().hex
985
986
    def post_merge(m):
987
        if len(m.cooked_conflicts) > 0:
988
            try:
989
                abspath = m.this_tree.abspath('')
990
            except AttributeError:
991
                abspath = ""
992
            on_conflicted(abspath)
993
994
    try:
995
        from bzrlib.merge import Merger
996
        Merger.hooks.install_named_hook('post_merge', post_merge, hook_name)
997
        yield
998
    finally:
999
        Merger.hooks.uninstall_named_hook('post_merge', hook_name)
1000
1190.1.4 by Alexander Belchenko
move qsubprocess command class back to commands.py, but body stays in subprocess.py.
1001
def run_subprocess_command(cmd, bencoded=False):
1002
    """The actual body of qsubprocess.
1003
    Running specified bzr command in the subprocess.
1004
    @param cmd: string with command line to run.
1005
    @param bencoded: either cmd_str is bencoded list or not.
1006
1007
    NOTE: if cmd starts with @ sign then it used as name of the file
1008
    where actual command line string is saved (utf-8 encoded).
1117.1.1 by Alexander Belchenko
cmd_qsubprocess moved from commands.py to subprocess.py
1009
    """
1010
    if MS_WINDOWS:
1190.1.4 by Alexander Belchenko
move qsubprocess command class back to commands.py, but body stays in subprocess.py.
1011
        thread.start_new_thread(windows_emulate_ctrl_c, ())
1012
    else:
1013
        signal.signal(signal.SIGINT, sigabrt_handler)
1014
    ui.ui_factory = SubprocessUIFactory(stdin=sys.stdin,
1015
                                        stdout=sys.stdout,
1016
                                        stderr=sys.stderr)
1017
    if cmd.startswith('@'):
1018
        fname = cmd[1:]
1019
        f = open(fname, 'rb')
1020
        try:
1021
            cmd_utf8 = f.read()
1022
        finally:
1023
            f.close()
1024
    else:
1025
        cmd_utf8 = cmd.encode('utf8')
1026
    if not bencoded:
1027
        argv = [unicode(p, 'utf-8') for p in shlex.split(cmd_utf8)]
1028
    else:
1029
        argv = [unicode(p, 'utf-8') for p in bencode.bdecode(cmd_utf8)]
1206 by Gary van der Merwe
Subprocess: Make it possible to detect the type of error from the subprocess.
1030
    try:
1470.1.2 by IWATA Hidetaka
SubProcessWindow: Show resolve button after conflict
1031
        def on_conflicted(wtpath):
1032
            print "%s%s%s" % (SUB_NOTIFY, NOTIFY_CONFLICT, bencode_prompt(wtpath))
1033
        with watch_conflicts(on_conflicted):
1034
            return commands.run_bzr(argv)
1267.1.2 by Martin
Add comment suggested by Alexander in review, and make original try/except Python 2.4 friendly too
1035
    except (KeyboardInterrupt, SystemExit):
1036
        raise
1206 by Gary van der Merwe
Subprocess: Make it possible to detect the type of error from the subprocess.
1037
    except Exception, e:
1267.1.1 by Martin
Bencode exception instances with dedicated function for better reliability
1038
        print "%s%s" % (SUB_ERROR, bencode_exception_instance(e))
1206 by Gary van der Merwe
Subprocess: Make it possible to detect the type of error from the subprocess.
1039
        raise
1117.1.1 by Alexander Belchenko
cmd_qsubprocess moved from commands.py to subprocess.py
1040
1041
1042
def sigabrt_handler(signum, frame):
1043
    raise KeyboardInterrupt()
1044
1045
572.1.1 by Alexander Belchenko
[win32] using win32event to send signal to child subprocess asking to stop. qsubprocess then uses thread.interrupt_main() to emulate Ctrl+C.
1046
if MS_WINDOWS:
1047
    import ctypes
572.1.3 by Alexander Belchenko
using getattr(sys, "frozen") instead of hasattr. added comment about ctypes package __path__
1048
    if getattr(sys, "frozen", None):
1049
        # this is needed for custom bzr.exe builds (without TortoiseBzr inside)
572.1.1 by Alexander Belchenko
[win32] using win32event to send signal to child subprocess asking to stop. qsubprocess then uses thread.interrupt_main() to emulate Ctrl+C.
1050
        ctypes.__path__.append(os.path.normpath(
1051
            os.path.join(os.path.dirname(__file__), '..', '_lib', 'ctypes')))
1052
    from ctypes import cast, POINTER, Structure
1053
    from ctypes.wintypes import DWORD, HANDLE
1054
1055
    class PROCESS_INFORMATION(Structure):
1056
        _fields_ = [("hProcess", HANDLE),
1057
                    ("hThread", HANDLE),
1058
                    ("dwProcessID", DWORD),
1059
                    ("dwThreadID", DWORD)]
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
1060
572.1.1 by Alexander Belchenko
[win32] using win32event to send signal to child subprocess asking to stop. qsubprocess then uses thread.interrupt_main() to emulate Ctrl+C.
1061
    LPPROCESS_INFORMATION = POINTER(PROCESS_INFORMATION)
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
1062
572.1.1 by Alexander Belchenko
[win32] using win32event to send signal to child subprocess asking to stop. qsubprocess then uses thread.interrupt_main() to emulate Ctrl+C.
1063
    def get_child_pid(voidptr):
1064
        lp = cast(int(voidptr), LPPROCESS_INFORMATION)
1065
        return lp.contents.dwProcessID
1066
1067
    def get_event_name(child_pid):
1068
        return 'qbzr-qsubprocess-%d' % child_pid
1069
1070
    def signal_event(child_pid):
1071
        import win32event
1072
        ev = win32event.CreateEvent(None, 0, 0, get_event_name(child_pid))
1073
        try:
1074
            win32event.SetEvent(ev)
1075
        finally:
1076
            ev.Close()
576 by Gary van der Merwe
Be compatabile with new progress api, and show transport activity.
1077
1190.1.4 by Alexander Belchenko
move qsubprocess command class back to commands.py, but body stays in subprocess.py.
1078
    def windows_emulate_ctrl_c():
1079
        """To emulate Ctrl+C on Windows we have to wait some global event,
1080
        and once it will trigger we will try to interrupt main thread.
1081
        IMPORTANT: This function should be invoked as separate thread!
1082
        """
1083
        import win32event
1084
        ev = win32event.CreateEvent(None, 0, 0, get_event_name(os.getpid()))
1085
        try:
1086
            win32event.WaitForSingleObject(ev, win32event.INFINITE)
1087
        finally:
1088
            ev.Close()
1089
        thread.interrupt_main()
1090
1091
939.1.1 by Alexander Belchenko
qsubprocess: new command-line option --bencode to use bencoded list of arguments.
1092
def bencode_unicode(args):
1093
    """Bencode list of unicode strings as list of utf-8 strings and converting
1094
    resulting string to unicode.
1095
    """
1096
    args_utf8 = bencode.bencode([unicode(a).encode('utf-8') for a in args])
1097
    return unicode(args_utf8, 'utf-8')
1091 by Alexander Belchenko
fixed problem with get_boolean when prompt is multiline string (with \n inside).
1098
1099
def bencode_prompt(arg):
1100
    return bencode.bencode(arg.encode('unicode-escape'))
1101
1102
def bdecode_prompt(s):
1103
    return bencode.bdecode(s).decode('unicode-escape')
1267 by Alexander Belchenko
encode strings in error objects with unicode-escape.
1104
1435.2.1 by Benoît Pierre
Add support for UIFactory.choose.
1105
def bencode_choose_args(msg, choices, default):
1106
    if default is None:
1107
        default = -1
1108
    return bencode.bencode([
1109
        msg.encode('unicode-escape'),
1110
        choices.encode('unicode-escape'),
1111
        default
1112
    ])
1113
1114
def bdecode_choose_args(s):
1115
    msg, choices, default = bencode.bdecode(s)
1116
    msg = msg.decode('unicode-escape')
1117
    choices = choices.decode('unicode-escape')
1118
    return msg, choices, default
1267.1.1 by Martin
Bencode exception instances with dedicated function for better reliability
1119
1120
def bencode_exception_instance(e):
1121
    """Serialise the main information about an exception instance with bencode
1122
1267.1.5 by Martin
Change of approach, just serialise needed attributes rather than introspecting exception instances
1123
    For now, nearly all exceptions just give the exception name as a string,
1124
    but a dictionary is also given that may contain unicode-escaped attributes.
1267.1.1 by Martin
Bencode exception instances with dedicated function for better reliability
1125
    """
1126
    # GZ 2011-04-15: Could use bzrlib.trace._qualified_exception_name in 2.4
1127
    ename = e.__class__.__name__
1128
    d = {}
1267.1.5 by Martin
Change of approach, just serialise needed attributes rather than introspecting exception instances
1129
    # For now be conservative and only serialise attributes that will get used
1130
    keys = []
1131
    if isinstance(e, errors.UncommittedChanges):
1132
        keys.append("display_url")
1133
    for key in keys:
1134
        # getattr and __repr__ can break in lots of ways, so catch everything
1135
        # but exceptions that occur as interrupts, allowing for Python 2.4
1136
        try:
1137
            val = getattr(e, key)
1267.1.1 by Martin
Bencode exception instances with dedicated function for better reliability
1138
            if not isinstance(val, unicode):
1267.1.5 by Martin
Change of approach, just serialise needed attributes rather than introspecting exception instances
1139
                if not isinstance(val, str):
1140
                    val = repr(val)
1141
                val = val.decode("ascii", "replace")
1142
        except (KeyboardInterrupt, SystemExit):
1143
            raise
1144
        except:
1145
            val = "[QBzr could not serialize this attribute]"
1146
        d[key] = val.encode("unicode-escape")
1267.1.1 by Martin
Bencode exception instances with dedicated function for better reliability
1147
    return bencode.bencode((ename, d))
1148
1149
1150
def bdecode_exception_instance(s):
1151
    """Deserialise information about an exception instance with bdecode"""
1152
    ename, d = bencode.bdecode(s)
1153
    for k in d:
1267.1.4 by Martin
As qbzr uses splitlines and bencode doesn't escape newlines do need unicode-escape
1154
        d[k] = d[k].decode("unicode-escape")
1267.1.1 by Martin
Bencode exception instances with dedicated function for better reliability
1155
    return ename, d
1156
1157
1158
# GZ 2011-04-15: Remove or deprecate these functions if they remain unused?
1267 by Alexander Belchenko
encode strings in error objects with unicode-escape.
1159
def encode_unicode_escape(obj):
1160
    if isinstance(obj, dict):
1161
        result = {}
1162
        for k,v in obj.iteritems():
1163
            result[k] = v.encode('unicode-escape')
1164
        return result
1165
    else:
1166
        raise TypeError('encode_unicode_escape: unsupported type: %r' % type(obj))
1167
1168
def decode_unicode_escape(obj):
1169
    if isinstance(obj, dict):
1170
        result = {}
1171
        for k,v in obj.iteritems():
1172
            result[k] = v.decode('unicode-escape')
1173
        return result
1174
    else:
1175
        raise TypeError('decode_unicode_escape: unsupported type: %r' % type(obj))