Ticket #1361: sugar-1361.3.patch

File sugar-1361.3.patch, 15.7 KB (added by alsroot, 15 years ago)
  • extensions/cpsection/updater/backends/aslo.py

    From 02f3d8aab3d0110163470dc4cb151d2f472eb52f Mon Sep 17 00:00:00 2001
    From: Aleksey Lim <alsroot@member.fsf.org>
    Date: Sat, 19 Sep 2009 17:49:13 +0000
    Subject: Cancel updating process in Update control panel component #1361
    
    ---
     extensions/cpsection/updater/backends/aslo.py |   49 ++++++---------
     extensions/cpsection/updater/model.py         |   81 +++++++++++++++++++++----
     extensions/cpsection/updater/view.py          |   78 ++++++++++++++++++------
     3 files changed, 147 insertions(+), 61 deletions(-)
    
    diff --git a/extensions/cpsection/updater/backends/aslo.py b/extensions/cpsection/updater/backends/aslo.py
    index 5f257f9..79162fc 100644
    a b _FIND_SIZE = './/{http://www.mozilla.org/2004/em-rdf#}updateSize' 
    6464
    6565_UPDATE_PATH = 'http://activities.sugarlabs.org/services/update-aslo.php'
    6666
    67 _fetcher = None
    6867
    69 
    70 class _UpdateFetcher(object):
     68class Checker(object):
    7169
    7270    _CHUNK_SIZE = 10240
    7371
    class _UpdateFetcher(object): 
    8684        self._stream = None
    8785        self._xml_data = ''
    8886        self._bundle = bundle
     87        self.cancellable = gio.Cancellable()
    8988
    90         self._file.read_async(self.__file_read_async_cb)
     89        self._file.read_async(
     90                callback=self.__file_read_async_cb,
     91                cancellable=self.cancellable)
    9192
    9293    def __file_read_async_cb(self, gfile, result):
    9394        try:
    9495            self._stream = self._file.read_finish(result)
    9596        except:
    96             global _fetcher
    97             _fetcher = None
    9897            self._completion_cb(None, None, None, None, traceback.format_exc())
    9998            return
    10099
    101         self._stream.read_async(self._CHUNK_SIZE, self.__stream_read_async_cb)
     100        self._stream.read_async(
     101                count=self._CHUNK_SIZE,
     102                callback=self.__stream_read_async_cb,
     103                cancellable=self.cancellable)
    102104
    103105    def __stream_read_async_cb(self, stream, result):
     106        if self.cancellable.is_cancelled():
     107            self._completion_cb(self._bundle, None, None, None,
     108                    'Fetching was canceled')
     109            return
     110
    104111        xml_data = self._stream.read_finish(result)
    105112        if xml_data is None:
    106             global _fetcher
    107             _fetcher = None
    108113            self._completion_cb(self._bundle, None, None, None,
    109114                    'Error reading update information for %s from '
    110115                    'server.' % self._bundle.get_bundle_id())
    class _UpdateFetcher(object): 
    113118            self._process_result()
    114119        else:
    115120            self._xml_data += xml_data
    116             self._stream.read_async(self._CHUNK_SIZE,
    117                                     self.__stream_read_async_cb)
     121            self._stream.read_async(
     122                    count=self._CHUNK_SIZE,
     123                    callback=self.__stream_read_async_cb,
     124                    cancellable=self.cancellable)
    118125
    119126    def _process_result(self):
    120127        document = XML(self._xml_data)
    class _UpdateFetcher(object): 
    140147                logging.error(traceback.format_exc())
    141148                size = 0
    142149
    143         global _fetcher
    144         _fetcher = None
    145         self._completion_cb(self._bundle, version, link, size, None)
    146 
    147 
    148 def fetch_update_info(bundle, completion_cb):
    149     '''Queries the server for a newer version of the ActivityBundle.
    150 
    151        completion_cb receives bundle, version, link, size and possibly an error
    152        message:
    153        
    154        def completion_cb(bundle, version, link, size, error_message):
    155     '''
    156     global _fetcher
    157 
    158     if _fetcher is not None:
    159         raise RuntimeError('Multiple simultaneous requests are not supported')
    160 
    161     _fetcher = _UpdateFetcher(bundle, completion_cb)
     150        self._completion_cb(self._bundle, version, link, size, False)
  • extensions/cpsection/updater/model.py

    diff --git a/extensions/cpsection/updater/model.py b/extensions/cpsection/updater/model.py
    index 102edea..b3b150b 100755
    a b class UpdateModel(gobject.GObject): 
    5959        self._bundles_to_check = None
    6060        self._bundles_to_update = None
    6161        self._total_bundles_to_update = 0
     62        self._bundles_updated = 0
    6263        self._downloader = None
     64        self._checker = None
     65
     66    def cancel(self):
     67        logging.debug('Cancel updating')
     68
     69        if self._checker is not None:
     70            self._checker.cancellable.cancel()
     71        if self._downloader is not None:
     72            self._downloader.cancellable.cancel()
    6373
    6474    def check_updates(self):
    6575        self.updates = []
    class UpdateModel(gobject.GObject): 
    6878        self._check_next_update()
    6979
    7080    def _check_next_update(self):
     81        assert(self._checker is None)
     82
    7183        total = len(bundleregistry.get_registry())
    7284        current = total - len(self._bundles_to_check)
    7385
    class UpdateModel(gobject.GObject): 
    7587        self.emit('progress', UpdateModel.ACTION_CHECKING, bundle.get_name(),
    7688                  current, total)
    7789
    78         aslo.fetch_update_info(bundle, self.__check_completed_cb)
     90        self._checker = aslo.Checker(bundle, self.__check_completed_cb)
    7991
    8092    def __check_completed_cb(self, bundle, version, link, size, error_message):
    8193        if error_message is not None:
    8294            logging.error('Error getting update information from server:\n'
    8395                          '%s' % error_message)
    8496
     97        try :
     98            if self._checker.cancellable.is_cancelled():
     99                self.emit('progress', UpdateModel.ACTION_CHECKING, None, 0, 0)
     100                return
     101        finally:
     102            self._checker = None
     103
    85104        if version is not None and version > bundle.get_activity_version():
    86105            self.updates.append(BundleUpdate(bundle, version, link, size))
    87106
    class UpdateModel(gobject.GObject): 
    103122                self._bundles_to_update.append(bundle_update)
    104123
    105124        self._total_bundles_to_update = len(self._bundles_to_update)
     125        self._bundles_updated = 0
     126
    106127        self._download_next_update()
    107128
    108129    def _download_next_update(self):
     130        assert(self._downloader is None)
     131
    109132        bundle_update = self._bundles_to_update.pop()
    110133
    111134        total = self._total_bundles_to_update * 2
    class UpdateModel(gobject.GObject): 
    133156            self._downloader = None
    134157
    135158    def __downloader_error_cb(self, downloader, error_message):
     159        try :
     160            if self._downloader.cancellable.is_cancelled():
     161                self.emit('progress', UpdateModel.ACTION_DOWNLOADING,
     162                        None, 0, 0)
     163                return
     164        finally:
     165            if os.path.isfile(self._downloader.get_local_file_path()):
     166                os.unlink(self._downloader.get_local_file_path())
     167            self._downloader = None
     168
    136169        logging.error('Error downloading update:\n%s', error_message)
    137170
    138171        total = self._total_bundles_to_update * 2
    class UpdateModel(gobject.GObject): 
    145178
    146179    def _install_update(self, bundle_update, local_file_path):
    147180
     181        self._bundles_updated += 1
    148182        total = self._total_bundles_to_update * 2
    149183        current = total - len(self._bundles_to_update) * 2 - 1
    150184
    class UpdateModel(gobject.GObject): 
    175209            gobject.idle_add(self._download_next_update)
    176210
    177211    def get_total_bundles_to_update(self):
    178         return self._total_bundles_to_update
     212        return self._bundles_updated
    179213
    180214
    181215class BundleUpdate(object):
    class _Downloader(gobject.GObject): 
    208242        self._input_file = gio.File(bundle_update.link)
    209243        self._output_file = None
    210244        self._downloaded_size = 0
     245        self.cancellable = gio.Cancellable()
    211246
    212         self._input_file.read_async(self.__file_read_async_cb)
     247        self._input_file.read_async(
     248                callback=self.__file_read_async_cb,
     249                cancellable=self.cancellable)
    213250
    214251    def __file_read_async_cb(self, gfile, result):
    215252        try:
    class _Downloader(gobject.GObject): 
    217254        except:
    218255            self.emit('error', traceback.format_exc())
    219256            return
    220            
     257
    221258        temp_file_path = self._get_temp_file_path(self.bundle_update.link)
    222259        self._output_file = gio.File(temp_file_path)
    223260        self._output_stream = self._output_file.create()
    224261
    225         self._input_stream.read_async(self._CHUNK_SIZE, self.__read_async_cb,
    226                                       gobject.PRIORITY_LOW)
     262        self._input_stream.read_async(
     263                count=self._CHUNK_SIZE,
     264                callback=self.__read_async_cb,
     265                io_priority=gobject.PRIORITY_LOW,
     266                cancellable=self.cancellable)
    227267
    228268    def __read_async_cb(self, input_stream, result):
     269        if self.cancellable.is_cancelled():
     270            self._input_stream.close()
     271            self._output_stream.close()
     272            self.emit('error', 'downloading canceled')
     273            return
     274
    229275        data = input_stream.read_finish(result)
    230276
    231277        if data is None:
    class _Downloader(gobject.GObject): 
    237283            self._check_if_finished_writing()
    238284        else:
    239285            self._pending_buffers.append(data)
    240             self._input_stream.read_async(self._CHUNK_SIZE,
    241                                           self.__read_async_cb,
    242                                           gobject.PRIORITY_LOW)
     286            self._input_stream.read_async(
     287                    count=self._CHUNK_SIZE,
     288                    callback=self.__read_async_cb,
     289                    io_priority=gobject.PRIORITY_LOW,
     290                    cancellable=self.cancellable)
    243291
    244292        self._write_next_buffer()
    245293
    246294    def __write_async_cb(self, output_stream, result, user_data):
     295        if self.cancellable.is_cancelled():
     296            return
     297
    247298        count = output_stream.write_finish(result)
    248299
    249300        self._downloaded_size += count
    class _Downloader(gobject.GObject): 
    260311            data = self._pending_buffers.pop(0)
    261312            # TODO: we pass the buffer as user_data because of
    262313            # http://bugzilla.gnome.org/show_bug.cgi?id=564102
    263             self._output_stream.write_async(data, self.__write_async_cb,
    264                                             gobject.PRIORITY_LOW,
    265                                             user_data=data)
     314            self._output_stream.write_async(
     315                    buffer=data,
     316                    callback=self.__write_async_cb,
     317                    io_priority=gobject.PRIORITY_LOW,
     318                    cancellable=self.cancellable,
     319                    user_data=data)
    266320
    267321    def _get_temp_file_path(self, uri):
    268322        # TODO: Should we use the HTTP headers for the file name?
    class _Downloader(gobject.GObject): 
    270324                urlparse(uri)
    271325        path = os.path.basename(path)
    272326
     327        if not os.path.exists(env.get_user_activities_path()):
     328            os.makedirs(env.get_user_activities_path())
     329
    273330        base_name, extension_ = os.path.splitext(path)
    274331        fd, file_path = tempfile.mkstemp(dir=env.get_user_activities_path(),
    275332                prefix=base_name, suffix='.xo')
  • extensions/cpsection/updater/view.py

    diff --git a/extensions/cpsection/updater/view.py b/extensions/cpsection/updater/view.py
    index 9a77743..5e6a33a 100644
    a b class ActivityUpdater(SectionView): 
    6666
    6767        self._update_box = None
    6868        self._progress_pane = None
     69        self._finish_box = None
    6970
    7071        self._refresh()
    7172
    72     def _switch_to_update_box(self):
    73         if self._update_box in self.get_children():
    74             return
     73    def undo(self):
     74        self._model.cancel()
    7575
     76    def _clear_center(self):
    7677        if self._progress_pane in self.get_children():
    7778            self.remove(self._progress_pane)
    7879            self._progress_pane = None
    7980
     81        if self._update_box in self.get_children():
     82            self.remove(self._update_box)
     83            self._update_box = None
     84
     85        if self._finish_box in self.get_children():
     86            self.remove(self._finish_box)
     87            self._finish_box = None
     88
     89    def _switch_to_update_box(self):
     90        if self._update_box in self.get_children():
     91            return
     92
     93        self._clear_center()
     94
    8095        if self._update_box is None:
    8196            self._update_box = UpdateBox(self._model)
    8297            self._update_box.refresh_button.connect('clicked',
    class ActivityUpdater(SectionView): 
    91106        if self._progress_pane in self.get_children():
    92107            return
    93108
    94         if self._update_box in self.get_children():
    95             self.remove(self._update_box)
    96             self._update_box = None
     109        self._clear_center()
    97110
    98111        if self._progress_pane is None:
    99112            self._progress_pane = ProgressPane()
     113            self._progress_pane.cancel_button.connect('clicked',
     114                    self.__cancel_button_clicked_cb)
    100115
    101116        self.pack_start(self._progress_pane, expand=True, fill=False)
    102117        self._progress_pane.show()
    103118
    104     def _clear_center(self):
    105         if self._progress_pane in self.get_children():
    106             self.remove(self._progress_pane)
    107             self._progress_pane = None
     119    def __cancel_button_clicked_cb(self, button):
     120        self._model.cancel()
    108121
    109         if self._update_box in self.get_children():
    110             self.remove(self._update_box)
    111             self._update_box = None
     122    def _switch_to_finish_box(self):
     123        if self._finish_box in self.get_children():
     124            return
     125
     126        self._clear_center()
     127
     128        if self._finish_box is None:
     129            self._finish_box = FinishBox()
     130            self._finish_box.refresh_button.connect('clicked',
     131                    self.__refresh_button_clicked_cb)
     132
     133        self.pack_start(self._finish_box, expand=True, fill=True)
     134        self._finish_box.show()
    112135
    113136    def __progress_cb(self, model, action, bundle_name, current, total):
    114137        if current == total and action == UpdateModel.ACTION_CHECKING:
    class ActivityUpdater(SectionView): 
    141164            top_message = gobject.markup_escape_text(top_message)
    142165
    143166        self._top_label.set_markup('<big>%s</big>' % top_message)
    144        
     167
    145168        if not available_updates:
    146             self._clear_center()
     169            self._switch_to_finish_box()
    147170        else:
    148171            self._switch_to_update_box()
    149172            self._update_box.refresh()
    class ActivityUpdater(SectionView): 
    167190        top_message = top_message % installed_updates
    168191        top_message = gobject.markup_escape_text(top_message)
    169192        self._top_label.set_markup('<big>%s</big>' % top_message)
    170         self._clear_center()
     193        self._switch_to_finish_box()
    171194
    172195
    173196class ProgressPane(gtk.VBox):
    class ProgressPane(gtk.VBox): 
    195218        self.pack_start(alignment_box)
    196219        alignment_box.show()
    197220
    198         cancel_button = gtk.Button(stock=gtk.STOCK_CANCEL)
    199         alignment_box.add(cancel_button)
    200         cancel_button.show()
     221        self.cancel_button = gtk.Button(stock=gtk.STOCK_CANCEL)
     222        alignment_box.add(self.cancel_button)
     223        self.cancel_button.show()
    201224
    202225    def set_message(self, message):
    203226        self._label.set_text(message)
    class UpdateBox(gtk.VBox): 
    280303        return bundles_to_update
    281304
    282305
     306class FinishBox(gtk.VBox):
     307
     308    def __init__(self):
     309        gtk.VBox.__init__(self)
     310
     311        self.set_spacing(style.DEFAULT_PADDING)
     312
     313        bottom_box = gtk.HBox()
     314        bottom_box.set_spacing(style.DEFAULT_SPACING)
     315        self.pack_end(bottom_box, expand=False)
     316        bottom_box.show()
     317
     318        self.refresh_button = gtk.Button(stock=gtk.STOCK_REFRESH)
     319        bottom_box.pack_end(self.refresh_button, expand=False)
     320        self.refresh_button.show()
     321
     322
    283323class UpdateList(gtk.TreeView):
    284324
    285325    def __init__(self, model):