~ubuntu-branches/debian/sid/aptdaemon/sid

« back to all changes in this revision

Viewing changes to aptdaemon/progress.py

  • Committer: Package Import Robot
  • Author(s): Julian Andres Klode
  • Date: 2012-03-05 16:03:14 UTC
  • mfrom: (18.1.73 precise)
  • Revision ID: package-import@ubuntu.com-20120305160314-zhwgowgtnu14tlc2
Tags: 0.43+bzr769-1
* New upstream snapshot
  - Works without pkcompat (Closes: #652644)
* Merge with Ubuntu; remaining changes:
  - debian/control: Changes of Vcs, Maintainer stuff
  - Do not use dh-translations

Show diffs side-by-side

added added

removed removed

Lines of Context:
30
30
import apt_pkg
31
31
import apt.progress.base
32
32
import apt.debfile
33
 
import gobject
 
33
from gi.repository import GObject
34
34
 
35
 
import enums
36
 
import lock
37
 
from loop import mainloop
 
35
from . import enums
 
36
from . import lock
 
37
from .loop import mainloop
 
38
from .utils import IsoCodes
38
39
 
39
40
# Required to get translatable strings extraced by xgettext
40
41
_ = lambda s: s
110
111
        except:
111
112
            log.warning("An additional step to open the cache is required")
112
113
 
 
114
 
113
115
class DaemonAcquireProgress(apt.progress.base.AcquireProgress):
114
116
    '''
115
117
    Handle the package download process
150
152
                                             current_size | item.owner.partialsize, \
151
153
                                             msg
152
154
 
 
155
    def _emit_status_details(self, items):
 
156
        """Emit the transaction status details."""
 
157
        names = set()
 
158
        for item in items:
 
159
            if item.owner.id:
 
160
                names.add(item.owner.id)
 
161
            else:
 
162
                names.add(item.shortdesc)
 
163
        if names:
 
164
            #TRANSLATORS: %s is a list of package names
 
165
            msg = self.transaction.ngettext("Downloading %(files)s",
 
166
                                            "Downloading %(files)s",
 
167
                                            len(items)) % {"files":
 
168
                                                           " ".join(names)}
 
169
            self.transaction.status_details = msg
 
170
 
153
171
    def done(self, item):
154
172
        """Invoked when an item is successfully and completely fetched."""
155
173
        self._emit_acquire_item(item)
193
211
            self.transaction.progress = progress
194
212
            self.progress = progress
195
213
        # Show all currently downloaded files
196
 
        items = set()
 
214
        items = []
197
215
        for worker in owner.workers:
198
216
            if not worker.current_item:
199
217
                continue
200
218
            self._emit_acquire_item(worker.current_item,
201
219
                                    worker.total_size,
202
220
                                    worker.current_size)
203
 
            if worker.current_item.owner.id:
204
 
                items.add(worker.current_item.owner.id)
205
 
            else:
206
 
                items.add(worker.current_item.shortdesc)
207
 
        if items:
208
 
            #TRANSLATORS: %s is a list of package names
209
 
            msg = self.transaction.ngettext("Downloading %(files)s",
210
 
                                            "Downloading %(files)s",
211
 
                                            len(items)) % {"files":
212
 
                                                           " ".join(items)}
213
 
            self.transaction.status_details = msg
214
 
 
215
 
        while gobject.main_context_default().pending():
216
 
            gobject.main_context_default().iteration()
 
221
            items.append(worker.current_item)
 
222
        self._emit_status_details(items)
 
223
        while GObject.main_context_default().pending():
 
224
            GObject.main_context_default().iteration()
217
225
        return True
218
226
 
219
227
    def start(self):
234
242
        self.transaction.paused = True
235
243
        self.transaction.status = enums.STATUS_WAITING_MEDIUM
236
244
        while self.transaction.paused:
237
 
            gobject.main_context_default().iteration()
 
245
            GObject.main_context_default().iteration()
238
246
        self.transaction.status = enums.STATUS_DOWNLOADING
239
247
        if self.transaction.cancelled:
240
248
            return False
241
249
        return True
242
250
 
243
251
 
 
252
class DaemonAcquireRepoProgress(DaemonAcquireProgress):
 
253
 
 
254
    """Handle the repository information download"""
 
255
 
 
256
    def __init__(self, transaction, begin=0, end=100):
 
257
        DaemonAcquireProgress.__init__(self, transaction, begin, end)
 
258
        self.languages = IsoCodes("iso_639", "iso_639_1_code")
 
259
        self.countries = IsoCodes("iso_3166", "alpha_2_code")
 
260
        self.progress = 101
 
261
 
 
262
    def start(self):
 
263
        """Callback at the beginning of the operation"""
 
264
        self.transaction.status = enums.STATUS_DOWNLOADING_REPO
 
265
        self.transaction.cancellable = True
 
266
 
 
267
    def _emit_status_details(self, items):
 
268
        """Emit the transaction status details."""
 
269
        repos = set()
 
270
        for item in items:
 
271
            # We are only interested in the hostname currently
 
272
            try:
 
273
                repos.add(item.description.split()[0].split("://")[-1])
 
274
            except IndexError:
 
275
                #TRANSLATORS: the string is used as a fallback if we cannot
 
276
                #             get the URI of a local repository
 
277
                repos.add(self.transaction.gettext("local repository"))
 
278
        if repos:
 
279
            #TRANSLATORS: %s is a list of repository names
 
280
            msg = self.transaction.ngettext("Downloading from %s",
 
281
                                            "Downloading from %s",
 
282
                                            len(repos)) % " ".join(repos)
 
283
            self.transaction.status_details = msg
 
284
 
 
285
    def _emit_acquire_item(self, item, total_size=0, current_size=0):
 
286
        if item.owner.status == apt_pkg.AcquireItem.STAT_DONE:
 
287
            status = enums.DOWNLOAD_DONE
 
288
            # Workaround for a bug in python-apt, see lp: #581886
 
289
            current_size = item.owner.filesize
 
290
        elif item.owner.status == apt_pkg.AcquireItem.STAT_AUTH_ERROR:
 
291
            status = enums.DOWNLOAD_AUTH_ERROR
 
292
        elif item.owner.status == apt_pkg.AcquireItem.STAT_FETCHING:
 
293
            status = enums.DOWNLOAD_FETCHING
 
294
        elif item.owner.status == apt_pkg.AcquireItem.STAT_ERROR:
 
295
            status = enums.DOWNLOAD_ERROR
 
296
        elif item.owner.status == apt_pkg.AcquireItem.STAT_IDLE:
 
297
            status = enums.DOWNLOAD_IDLE
 
298
        else:
 
299
            # Workaround: The StatTransientNetworkError status isn't mapped
 
300
            # by python-apt, see LP #602578
 
301
            status = enums.DOWNLOAD_NETWORK_ERROR
 
302
        if item.owner.status != apt_pkg.AcquireItem.STAT_DONE and \
 
303
           item.owner.error_text:
 
304
            msg = item.owner.error_text
 
305
        elif item.owner.mode:
 
306
            msg = item.owner.mode
 
307
        else:
 
308
            msg = ""
 
309
        # Get a better description than e.g. Packages or Sources
 
310
        host, dist = item.description.split()[0:2]
 
311
        try:
 
312
            host = host.split("://")[1]
 
313
        except IndexError:
 
314
            #TRANSLATORS: the string is used as a fallback if we cannot
 
315
            #             get the URI of a local repository
 
316
            desc = self.transaction.gettext("local repository")
 
317
        repo = "%s %s" % (host, dist)
 
318
        if item.shortdesc == "InRelease":
 
319
            #TRANSLATORS: repo is the name of a repository
 
320
            desc = self.transaction.gettext("Structure of %s") % repo
 
321
        elif item.shortdesc == "Release":
 
322
            #TRANSLATORS: repo is the name of a repository
 
323
            desc = self.transaction.gettext("Description of %s") % repo
 
324
        elif item.shortdesc == "Release.gpg":
 
325
            #TRANSLATORS: repo is the name of a repository
 
326
            desc = self.transaction.gettext("Description signature "
 
327
                                            "of %s") % repo
 
328
        elif item.shortdesc.startswith("Packages"):
 
329
            #TRANSLATORS: repo is the name of a repository
 
330
            desc = self.transaction.gettext("Available packages from %s") % repo
 
331
        elif item.shortdesc.startswith("Sources"):
 
332
            #TRANSLATORS: repo is the name of a repository
 
333
            desc = self.transaction.gettext("Available sources from %s") % repo
 
334
        elif item.shortdesc == "TranslationIndex":
 
335
            #TRANSLATORS: repo is the name of a repository
 
336
            desc = self.transaction.gettext("Available translations from "
 
337
                                            "%s") % repo
 
338
        elif item.shortdesc.startswith("Translation-"):
 
339
            lang = item.shortdesc.split("-", 1)[-1]
 
340
            try:
 
341
                lang, country = lang.split("_")
 
342
            except ValueError:
 
343
                country = None
 
344
            if country:
 
345
                #TRANSLATORS: The first %s is the name of a language. The second
 
346
                #             one the name of the country. The third %s is the
 
347
                #             name of a repository
 
348
                desc = self.transaction.gettext("Translations for %s (%s) from "
 
349
                                                "%s") % \
 
350
                        (self.languages.get_localised_name(lang,
 
351
                                                       self.transaction.locale),
 
352
                         self.countries.get_localised_name(country,
 
353
                                                       self.transaction.locale),
 
354
                         repo)
 
355
            else:
 
356
                #TRANSLATORS: %s is the name of a language. The second one is
 
357
                #             the name of the repository
 
358
                desc = self.transaction.gettext("Translations for %s from "
 
359
                                                "%s") % \
 
360
                        (self.languages.get_localised_name(lang,
 
361
                                                       self.transaction.locale),
 
362
                         repo)
 
363
        else:
 
364
            desc = item.shortdesc
 
365
        self.transaction.progress_download = item.uri, status, desc, \
 
366
                                             total_size | item.owner.filesize, \
 
367
                                             current_size | item.owner.partialsize, \
 
368
                                             msg
 
369
 
 
370
 
244
371
class DaemonInstallProgress(object):
245
372
 
246
373
    def __init__(self, transaction, begin=50, end=100):
270
397
        lock.wait_for_lock(self.transaction, lock.status_lock)
271
398
 
272
399
    def _child(self, pm):
273
 
        # force terminal messages in dpkg to be untranslated, the
274
 
        # status-fd or debconf prompts will not be affected
275
 
        os.environ["DPKG_UNTRANSLATED_MESSAGES"] = "1"
276
400
        try:
277
401
            res = pm.do_install(self.status_child_fd)
278
402
        except:
300
424
            try:
301
425
                self._setup_child()
302
426
                self._child(*args, **kwargs)
303
 
            except Exception, error:
 
427
            except Exception as error:
304
428
                traceback.print_exc()
305
429
            finally:
306
430
                # Give the parent process enough time to catch the output
314
438
            os.close(self.status_child_fd)
315
439
        log.debug("Child pid: %s", pid)
316
440
        watchers = []
317
 
        flags = gobject.IO_IN | gobject.IO_ERR | gobject.IO_HUP
 
441
        flags = GObject.IO_IN | GObject.IO_ERR | GObject.IO_HUP
318
442
        if self.transaction.terminal:
319
443
            # Setup copying of i/o between the controlling terminals
320
 
            watchers.append(gobject.io_add_watch(terminal_fd, flags,
 
444
            watchers.append(GObject.io_add_watch(terminal_fd, flags,
321
445
                                                 self._copy_io))
322
 
        watchers.append(gobject.io_add_watch(self.master_fd, flags,
 
446
        watchers.append(GObject.io_add_watch(self.master_fd, flags,
323
447
                                             self._copy_io_master, terminal_fd))
324
448
        # Monitor the child process
325
 
        watchers.append(gobject.child_watch_add(pid, self._on_child_exit))
 
449
        watchers.append(GObject.child_watch_add(pid, self._on_child_exit))
326
450
        # Watch for status updates
327
 
        watchers.append(gobject.io_add_watch(self.status_parent_fd,
328
 
                                             gobject.IO_IN,
 
451
        watchers.append(GObject.io_add_watch(self.status_parent_fd,
 
452
                                             GObject.IO_IN,
329
453
                                             self._on_status_update))
330
454
        while self._child_exit == -1:
331
 
            gobject.main_context_default().iteration()
 
455
            GObject.main_context_default().iteration()
332
456
        for id in watchers:
333
 
            gobject.source_remove(id)
 
457
            GObject.source_remove(id)
334
458
        # Restore the settings of the transaction terminal
335
459
        if terminal_fd:
336
460
            try:
469
593
        apt_pkg.config.set("CommandLine::AsString", cmd)
470
594
 
471
595
    def _copy_io_master(self, source, condition, target):
472
 
        if condition == gobject.IO_IN:
 
596
        if condition == GObject.IO_IN:
473
597
            self.last_activity = time.time()
474
598
            try:
475
599
                char = os.read(source, 1)
500
624
        return False
501
625
 
502
626
    def _copy_io(self, source, condition):
503
 
        if condition == gobject.IO_IN:
 
627
        if condition == GObject.IO_IN:
504
628
            char = os.read(source, 1)
505
629
            # Detect config file prompt answers on the console
506
630
            # FIXME: Perhaps should only set the
541
665
        self.transaction.paused = True
542
666
        self.transaction.status = enums.STATUS_WAITING_CONFIG_FILE_PROMPT
543
667
        while self.transaction.paused:
544
 
            gobject.main_context_default().iteration()
 
668
            GObject.main_context_default().iteration()
545
669
        log.debug("Sending config file answer: %s",
546
670
                  self.transaction.config_file_conflict_resolution)
547
671
        if self.transaction.config_file_conflict_resolution == "replace":