~kelemeng/aptdaemon/bug640972

« back to all changes in this revision

Viewing changes to .pc/01_vendor-keys-download.patch/aptdaemon/client.py

  • Committer: Bazaar Package Importer
  • Author(s): Michael Vogt
  • Date: 2010-07-02 10:28:33 UTC
  • mfrom: (1.1.23 upstream)
  • Revision ID: james.westby@ubuntu.com-20100702102833-3iqalyretgyej9o2
Tags: 0.31+bzr416-0ubuntu1
* updated to current bzr
  - fixes STAT_FAILED vs STAT_ERROR crash
* debian/patches/01_vendor-keys-download.patch:
  - add changes from lp:~mvo/aptdaemon/download-vendor-keys
    to support downloading repository vendor keys via https
* debian/patches/03_auth_me_less.patch: 
  - updated to current code
* debian/control:
  - use DH_CENTRAL=include-links

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
"""
 
3
The module provides a client to the PackageKit DBus interface. It allows to
 
4
perform basic package manipulation tasks in a cross distribution way, e.g.
 
5
to search for packages, install packages or codecs.
 
6
"""
 
7
# Copyright (C) 2008 Canonical Ltd.
 
8
# Copyright (C) 2008 Aidan Skinner <aidan@skinner.me.uk>
 
9
# Copyright (C) 2008 Martin Pitt <martin.pitt@ubuntu.com>
 
10
# Copyright (C) 2008 Tim Lauridsen <timlau@fedoraproject.org>
 
11
# Copyright (C) 2008-2009 Sebastian Heinlein <devel@glatzor.de>
 
12
#
 
13
# Licensed under the GNU General Public License Version 2
 
14
#
 
15
# This program is free software; you can redistribute it and/or modify
 
16
# it under the terms of the GNU General Public License as published by
 
17
# the Free Software Foundation; either version 2 of the License, or
 
18
# (at your option) any later version.
 
19
#
 
20
# This program is distributed in the hope that it will be useful,
 
21
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
22
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
23
# GNU General Public License for more details.
 
24
#
 
25
# You should have received a copy of the GNU General Public License
 
26
# along with this program; if not, write to the Free Software
 
27
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
28
 
 
29
import locale
 
30
import weakref
 
31
 
 
32
import dbus
 
33
import dbus.glib
 
34
import dbus.mainloop.glib
 
35
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
 
36
import gobject
 
37
 
 
38
import enums
 
39
import debconf
 
40
import defer
 
41
from errors import convert_dbus_exception, TransactionFailed
 
42
 
 
43
__all__ = ("AptTransaction", "AptClient", "get_transaction", "get_daemon")
 
44
 
 
45
 
 
46
class MemoizedTransaction(type):
 
47
 
 
48
    """Metaclass to cache transactions."""
 
49
 
 
50
    cache = weakref.WeakValueDictionary()
 
51
 
 
52
    def __call__(mcs, *args):
 
53
        tid = args[0]
 
54
        try:
 
55
            return mcs.cache[tid]
 
56
        except KeyError:
 
57
            mcs.cache[tid] = value = \
 
58
                    super(MemoizedTransaction, mcs).__call__(*args)
 
59
            return value
 
60
 
 
61
 
 
62
class MemoizedMixIn(MemoizedTransaction, gobject.GObjectMeta):
 
63
 
 
64
    """Helper meta class for merging"""
 
65
 
 
66
 
 
67
class AptTransaction(gobject.GObject):
 
68
 
 
69
    """Represents an aptdaemon transaction. It allows asynchronous and
 
70
    synchronous processing.
 
71
    """
 
72
 
 
73
    __metaclass__ = MemoizedMixIn
 
74
 
 
75
    __gsignals__ = {"finished": (gobject.SIGNAL_RUN_FIRST,
 
76
                                 gobject.TYPE_NONE,
 
77
                                 (gobject.TYPE_STRING,)),
 
78
                    "dependencies-changed": (gobject.SIGNAL_RUN_FIRST,
 
79
                                             gobject.TYPE_NONE,
 
80
                                             (gobject.TYPE_PYOBJECT,
 
81
                                              gobject.TYPE_PYOBJECT,
 
82
                                              gobject.TYPE_PYOBJECT,
 
83
                                              gobject.TYPE_PYOBJECT,
 
84
                                              gobject.TYPE_PYOBJECT,
 
85
                                              gobject.TYPE_PYOBJECT,
 
86
                                              gobject.TYPE_PYOBJECT)),
 
87
                    "download-changed": (gobject.SIGNAL_RUN_FIRST,
 
88
                                         gobject.TYPE_NONE,
 
89
                                         (gobject.TYPE_INT,)),
 
90
                    "space-changed": (gobject.SIGNAL_RUN_FIRST,
 
91
                                      gobject.TYPE_NONE,
 
92
                                      (gobject.TYPE_INT,)),
 
93
                    "error": (gobject.SIGNAL_RUN_FIRST,
 
94
                              gobject.TYPE_NONE,
 
95
                              (gobject.TYPE_STRING, gobject.TYPE_STRING)),
 
96
                    "role-changed": (gobject.SIGNAL_RUN_FIRST,
 
97
                                     gobject.TYPE_NONE,
 
98
                                     (gobject.TYPE_STRING,)),
 
99
                    "terminal-attached-changed": (gobject.SIGNAL_RUN_FIRST,
 
100
                                                    gobject.TYPE_NONE,
 
101
                                                    (gobject.TYPE_BOOLEAN,)),
 
102
                    "cancellable-changed": (gobject.SIGNAL_RUN_FIRST,
 
103
                                            gobject.TYPE_NONE,
 
104
                                            (gobject.TYPE_BOOLEAN,)),
 
105
                    "meta-data-changed": (gobject.SIGNAL_RUN_FIRST,
 
106
                                          gobject.TYPE_NONE,
 
107
                                          (gobject.TYPE_PYOBJECT,)),
 
108
                    "status-changed": (gobject.SIGNAL_RUN_FIRST,
 
109
                                       gobject.TYPE_NONE,
 
110
                                       (gobject.TYPE_STRING,)),
 
111
                    "status-details-changed": (gobject.SIGNAL_RUN_FIRST,
 
112
                                               gobject.TYPE_NONE,
 
113
                                               (gobject.TYPE_STRING,)),
 
114
                    "progress-changed": (gobject.SIGNAL_RUN_FIRST,
 
115
                                 gobject.TYPE_NONE,
 
116
                                 (gobject.TYPE_INT,)),
 
117
                    "progress-details-changed": (gobject.SIGNAL_RUN_FIRST,
 
118
                                         gobject.TYPE_NONE,
 
119
                                         (gobject.TYPE_INT, gobject.TYPE_INT,
 
120
                                          gobject.TYPE_INT, gobject.TYPE_INT,
 
121
                                          gobject.TYPE_INT, gobject.TYPE_INT)),
 
122
                    "progress-download-changed": (gobject.SIGNAL_RUN_FIRST,
 
123
                                         gobject.TYPE_NONE,
 
124
                                         (gobject.TYPE_STRING,
 
125
                                          gobject.TYPE_STRING,
 
126
                                          gobject.TYPE_STRING,
 
127
                                          gobject.TYPE_INT,
 
128
                                          gobject.TYPE_INT,
 
129
                                          gobject.TYPE_STRING)),
 
130
                    "packages-changed": (gobject.SIGNAL_RUN_FIRST,
 
131
                                         gobject.TYPE_NONE,
 
132
                                         (gobject.TYPE_PYOBJECT,
 
133
                                          gobject.TYPE_PYOBJECT,
 
134
                                          gobject.TYPE_PYOBJECT,
 
135
                                          gobject.TYPE_PYOBJECT,
 
136
                                          gobject.TYPE_PYOBJECT)),
 
137
                    "paused": (gobject.SIGNAL_RUN_FIRST,
 
138
                               gobject.TYPE_NONE,
 
139
                               ()),
 
140
                    "resumed": (gobject.SIGNAL_RUN_FIRST,
 
141
                                gobject.TYPE_NONE,
 
142
                                ()),
 
143
                    "allow-unauthenticated-changed": (gobject.SIGNAL_RUN_FIRST,
 
144
                                                      gobject.TYPE_NONE,
 
145
                                                      (gobject.TYPE_BOOLEAN,)),
 
146
                    "remove-obsoleted-depends-changed": (gobject.SIGNAL_RUN_FIRST,
 
147
                                                 gobject.TYPE_NONE,
 
148
                                                 (gobject.TYPE_BOOLEAN,)),
 
149
                    "locale-changed": (gobject.SIGNAL_RUN_FIRST,
 
150
                                       gobject.TYPE_NONE,
 
151
                                       (gobject.TYPE_STRING,)),
 
152
                    "terminal-changed": (gobject.SIGNAL_RUN_FIRST,
 
153
                                         gobject.TYPE_NONE,
 
154
                                         (gobject.TYPE_STRING,)),
 
155
                    "debconf-socket-changed": (gobject.SIGNAL_RUN_FIRST,
 
156
                                               gobject.TYPE_NONE,
 
157
                                               (gobject.TYPE_STRING,)),
 
158
                    "http-proxy-changed": (gobject.SIGNAL_RUN_FIRST,
 
159
                                           gobject.TYPE_NONE,
 
160
                                           (gobject.TYPE_STRING,)),
 
161
                    "medium-required": (gobject.SIGNAL_RUN_FIRST,
 
162
                                        gobject.TYPE_NONE,
 
163
                                        (gobject.TYPE_STRING,
 
164
                                         gobject.TYPE_STRING)),
 
165
                    "config-file-conflict": (gobject.SIGNAL_RUN_FIRST,
 
166
                                             gobject.TYPE_NONE,
 
167
                                             (gobject.TYPE_STRING,
 
168
                                              gobject.TYPE_STRING)),
 
169
                    }
 
170
 
 
171
    def __init__(self, tid, bus=None):
 
172
        gobject.GObject.__init__(self)
 
173
        self.tid = tid
 
174
        self.role = enums.ROLE_UNSET
 
175
        self.error = None
 
176
        self.error_code = None
 
177
        self.error_details = None
 
178
        self.exit = enums.EXIT_UNFINISHED
 
179
        self.cancellable = False
 
180
        self.term_attached = False
 
181
        self.required_medium = None
 
182
        self.config_file_conflict = None
 
183
        self.status = None
 
184
        self.status_details = ""
 
185
        self.progress = 0
 
186
        self.paused = False
 
187
        self.http_proxy = None
 
188
        self.dependencies = [[], [], [], [], [], [], []]
 
189
        self.packages = [[], [], [], [], []]
 
190
        self.meta_data = {}
 
191
        self.remove_obsoleted_depends = False
 
192
        self.download = 0
 
193
        self.downloads = {}
 
194
        self.space = 0
 
195
        self._locale = ""
 
196
        self._method = None
 
197
        self._args = []
 
198
        self._debconf_helper = None
 
199
        # Connect the signal handlers to the DBus iface
 
200
        if not bus:
 
201
            bus = dbus.SystemBus()
 
202
        self._proxy = bus.get_object("org.debian.apt", tid)
 
203
        self._iface = dbus.Interface(self._proxy, "org.debian.apt.transaction")
 
204
        # Watch for a crashed daemon which orphaned the dbus object
 
205
        self._owner_watcher = bus.watch_name_owner("org.debian.apt",
 
206
                                                   self._on_name_owner_changed)
 
207
        # main signals
 
208
        self._signal_matcher = \
 
209
            self._iface.connect_to_signal("PropertyChanged",
 
210
                                          self._on_property_changed,
 
211
                                          utf8_strings=True)
 
212
 
 
213
    def _on_name_owner_changed(self, connection):
 
214
        """Fail the transaction if the daemon died."""
 
215
        if connection == "" and self.exit == enums.EXIT_UNFINISHED:
 
216
            self._on_property_changed("Error", (enums.ERROR_DAEMON_DIED,
 
217
                                                "It seems that the daemon "
 
218
                                                "died."))
 
219
            self._on_property_changed("Cancellable", False)
 
220
            self._on_property_changed("TerminalAttached", False)
 
221
            self._on_property_changed("ExitState", enums.EXIT_FAILED)
 
222
 
 
223
    def _on_property_changed(self, property_name, value):
 
224
        """Callback for the PropertyChanged signal."""
 
225
        if property_name == "TerminalAttached":
 
226
            self.term_attached = value
 
227
            self.emit("terminal-attached-changed", value)
 
228
        elif property_name == "Cancellable":
 
229
            self.cancellable = value
 
230
            self.emit("cancellable-changed", value)
 
231
        elif property_name == "DebconfSocket":
 
232
            self.emit("debconf-socket-changed", value)
 
233
        elif property_name == "RemoveObsoletedDepends":
 
234
            self.emit("remove-obsoleted-depends-changed", value)
 
235
            self.remove_obsoleted_depends = value
 
236
        elif property_name == "AllowUnauthenticated":
 
237
            self.emit("allow-unauthenticated-changed", value)
 
238
        elif property_name == "Terminal":
 
239
            self.emit("terminal-changed", value)
 
240
        elif property_name == "Dependencies":
 
241
            self.dependencies = value
 
242
            self.emit("dependencies-changed", *value)
 
243
        elif property_name == "Packages":
 
244
            self.packages = value
 
245
            self.emit("packages-changed", *value)
 
246
        elif property_name == "Locale":
 
247
            self.locale = value
 
248
            self.emit("locale-changed", value)
 
249
        elif property_name == "Role":
 
250
            self.role = value
 
251
            self.emit("role-changed", value)
 
252
        elif property_name == "Status":
 
253
            self.status = value
 
254
            self.emit("status-changed", value)
 
255
        elif property_name == "StatusDetails":
 
256
            self.status_details = value
 
257
            self.emit("status-details-changed", value)
 
258
        elif property_name == "ProgressDownload":
 
259
            uri, status, desc, size, download, msg = value
 
260
            if uri:
 
261
                self.downloads[uri] = (status, desc, size, download, msg)
 
262
                self.emit("progress-download-changed", *value)
 
263
        elif property_name == "Progress":
 
264
            self.progress = value
 
265
            self.emit("progress-changed", value)
 
266
        elif property_name == "ConfigFileConflict":
 
267
            self.config_file_conflict = value
 
268
            if value != ("", ""):
 
269
                self.emit("config-file-conflict", *value)
 
270
        elif property_name == "MetaData":
 
271
            self.meta_data = value
 
272
            self.emit("meta-data-changed", value)
 
273
        elif property_name == "Paused":
 
274
            self.paused = value
 
275
            if value:
 
276
                self.emit("paused")
 
277
            else:
 
278
                self.emit("resumed")
 
279
        elif property_name == "RequiredMedium":
 
280
            self.required_medium = value
 
281
            if value != ("", ""):
 
282
                self.emit("medium-required", *value)
 
283
        elif property_name == "ProgressDetails":
 
284
            self.progress_details = value
 
285
            self.emit("progress-details-changed", *value)
 
286
        elif property_name == "Download":
 
287
            self.download = value
 
288
            self.emit("download-changed", value)
 
289
        elif property_name == "Space":
 
290
            self.space = value
 
291
            self.emit("space-changed", value)
 
292
        elif property_name == "HttpProxy":
 
293
            self.http_proxy = value
 
294
            self.emit("http-proxy-changed", value)
 
295
        elif property_name == "Error":
 
296
            self.error_code, self.error_details = value
 
297
            if self.error_code != -1:
 
298
                self.error = TransactionFailed(self.error_code,
 
299
                                               self.error_details)
 
300
                self.emit("error", *value)
 
301
        elif property_name == "ExitState":
 
302
            self.exit = value
 
303
            if value != enums.EXIT_UNFINISHED:
 
304
                self.emit("finished", value)
 
305
                self._owner_watcher.cancel()
 
306
                if self._debconf_helper:
 
307
                    self._debconf_helper.stop()
 
308
                self.disconnect()
 
309
 
 
310
    @defer.deferable_function
 
311
    @convert_dbus_exception
 
312
    def sync(self, reply_handler=None, error_handler=None):
 
313
        """Sync the client transaction properites with the backend one.
 
314
 
 
315
        Keyword arguments:
 
316
        reply_handler - callback function. If specified in combination with
 
317
            error_handler the method will be called asynchrounsouly.
 
318
        error_handler - in case of an error the given callback gets the
 
319
            corresponding DBus exception instance
 
320
        """
 
321
        def sync_properties(prop_dict):
 
322
            for property_name, value in prop_dict.iteritems():
 
323
                self._on_property_changed(property_name, value)
 
324
            if reply_handler:
 
325
                reply_handler(self)
 
326
        if reply_handler and error_handler:
 
327
            self._proxy.GetAll("org.debian.apt.transaction",
 
328
                               dbus_interface=dbus.PROPERTIES_IFACE,
 
329
                               reply_handler=sync_properties,
 
330
                               error_handler=error_handler)
 
331
        else:
 
332
            properties = self._proxy.GetAll("org.debian.apt.transaction",
 
333
                                           dbus_interface=dbus.PROPERTIES_IFACE)
 
334
            sync_properties(properties)
 
335
 
 
336
 
 
337
    @defer.deferable_function
 
338
    @convert_dbus_exception
 
339
    def run(self, reply_handler=None, error_handler=None):
 
340
        """Start processing the transaction.
 
341
 
 
342
        Keyword arguments:
 
343
        reply_handler - callback function. If specified in combination with
 
344
            error_handler the method will be called asynchrounsouly.
 
345
        error_handler - in case of an error the given callback gets the
 
346
            corresponding DBus exception instance
 
347
        """
 
348
        try:
 
349
            return self._iface.Run(error_handler=error_handler,
 
350
                                   reply_handler=reply_handler)
 
351
        except Exception, error:
 
352
            if error_handler:
 
353
                error_handler(error)
 
354
            else:
 
355
                raise
 
356
 
 
357
    @defer.deferable_function
 
358
    @convert_dbus_exception
 
359
    def simulate(self, reply_handler=None, error_handler=None):
 
360
        """Simulate the transaction to calculate the dependencies, the
 
361
        required download size and the required disk space.
 
362
 
 
363
        The corresponding properties of the transaction will be updated.
 
364
 
 
365
        Also TransactionFailed exceptions could be raised, if e.g. a
 
366
        requested package could not be installed or the cache is currently
 
367
        broken.
 
368
 
 
369
        Keyword arguments:
 
370
        reply_handler - callback function. If specified in combination with
 
371
            error_handler the method will be called asynchrounsouly.
 
372
        error_handler - in case of an error the given callback gets the
 
373
            corresponding DBus exception instance
 
374
        """
 
375
        self._iface.Simulate(reply_handler=reply_handler,
 
376
                             error_handler=error_handler)
 
377
 
 
378
    @defer.deferable_function
 
379
    @convert_dbus_exception
 
380
    def cancel(self, reply_handler=None, error_handler=None):
 
381
        """Cancel the running transaction.
 
382
 
 
383
        Keyword arguments:
 
384
        reply_handler - callback function. If specified in combination with
 
385
            error_handler the method will be called asynchrounsouly.
 
386
        error_handler - in case of an error the given callback gets the
 
387
            corresponding DBus exception instance
 
388
        """
 
389
        self._iface.Cancel(reply_handler=reply_handler,
 
390
                           error_handler=error_handler)
 
391
 
 
392
    @defer.deferable_function
 
393
    @convert_dbus_exception
 
394
    def set_http_proxy(self, proxy, reply_handler=None, error_handler=None):
 
395
        """Set the HttpProxy property of the transaction.
 
396
 
 
397
        Keyword arguments:
 
398
        proxy - the URL of the proxy server, e.g. "http://proxy:8080"
 
399
        reply_handler - callback function. If specified in combination with
 
400
            error_handler the method will be called asynchrounsouly.
 
401
        error_handler - in case of an error the given callback gets the
 
402
            corresponding DBus exception instance
 
403
        """
 
404
        if reply_handler:
 
405
            _reply_handler = lambda: reply_handler(self)
 
406
        else:
 
407
            _reply_handler = None
 
408
        self._proxy.Set("org.debian.apt.transaction", "HttpProxy", proxy,
 
409
                        dbus_interface=dbus.PROPERTIES_IFACE,
 
410
                        reply_handler=_reply_handler,
 
411
                        error_handler=error_handler)
 
412
 
 
413
    @defer.deferable_function
 
414
    @convert_dbus_exception
 
415
    def set_remove_obsoleted_depends(self, remove_obsoleted_depends,
 
416
                                     reply_handler=None, error_handler=None):
 
417
        """Set the RemoveObsoletedDepends protperty of the transaction.
 
418
 
 
419
        Keyword arguments:
 
420
        remove_obsoleted_depends - if True also remove no longer required
 
421
            dependencies which have been installed automatically before
 
422
        reply_handler - callback function. If specified in combination with
 
423
            error_handler the method will be called asynchrounsouly.
 
424
        error_handler - in case of an error the given callback gets the
 
425
            corresponding DBus exception instance
 
426
        """
 
427
        if reply_handler:
 
428
            _reply_handler = lambda: reply_handler(self)
 
429
        else:
 
430
            _reply_handler = None
 
431
        self._proxy.Set("org.debian.apt.transaction", 
 
432
                        "RemoveObsoletedDepends", remove_obsoleted_depends,
 
433
                        dbus_interface=dbus.PROPERTIES_IFACE,
 
434
                        reply_handler=_reply_handler,
 
435
                        error_handler=error_handler)
 
436
 
 
437
    @defer.deferable_function
 
438
    @convert_dbus_exception
 
439
    def set_allow_unauthenticated(self, allow_unauthenticated, 
 
440
                            reply_handler=None, error_handler=None):
 
441
        """Set AllowUnauthencitaed property of the transaction.
 
442
 
 
443
        Keyword arguments:
 
444
        allow_unauthenticated - if True the installation of packages which
 
445
            have not been signed by a trusted software vendor are allowed
 
446
            to be installed. Defaults to False.
 
447
        reply_handler - callback function. If specified in combination with
 
448
            error_handler the method will be called asynchrounsouly.
 
449
        error_handler - in case of an error the given callback gets the
 
450
            corresponding DBus exception instance
 
451
        """
 
452
        if reply_handler:
 
453
            _reply_handler = lambda: reply_handler(self)
 
454
        else:
 
455
            _reply_handler = None
 
456
        self._proxy.Set("org.debian.apt.transaction", 
 
457
                        "AllowUnauthenticated", allow_unauthenticated,
 
458
                        dbus_interface=dbus.PROPERTIES_IFACE,
 
459
                        reply_handler=_reply_handler,
 
460
                        error_handler=error_handler)
 
461
 
 
462
    @defer.deferable_function
 
463
    @convert_dbus_exception
 
464
    def set_debconf_frontend(self, frontend, reply_handler=None,
 
465
                             error_handler=None):
 
466
        """Set the DebconfSocket property of the transaction.
 
467
 
 
468
        Keyword arguments:
 
469
        debconf_socket - a socket to which the debconf proxy frontend should
 
470
            be attached. A debconf frontend running in the user's session
 
471
            should listen to the socket.
 
472
        reply_handler - callback function. If specified in combination with
 
473
            error_handler the method will be called asynchrounsouly.
 
474
        error_handler - in case of an error the given callback gets the
 
475
            corresponding DBus exception instance
 
476
        """
 
477
        if reply_handler:
 
478
            _reply_handler = lambda: reply_handler(self)
 
479
        else:
 
480
            _reply_handler = None
 
481
        self._debconf_helper = debconf.DebconfProxy(frontend)
 
482
        self._proxy.Set("org.debian.apt.transaction", "DebconfSocket",
 
483
                        self._debconf_helper.socket_path,
 
484
                        dbus_interface=dbus.PROPERTIES_IFACE,
 
485
                        reply_handler=_reply_handler,
 
486
                        error_handler=error_handler)
 
487
        self._debconf_helper.start()
 
488
 
 
489
    @defer.deferable_function
 
490
    @convert_dbus_exception
 
491
    def set_meta_data(self, **kwargs):
 
492
        """Store additional meta information of the transaction in the
 
493
        MetaData property of the transaction.
 
494
 
 
495
        The method accepts of key=value pairs. The key has to be prefixed with
 
496
        an underscore separated identifier of the client application.
 
497
 
 
498
        In the following example Software-Center sets an application name
 
499
        and icon:
 
500
 
 
501
        >>> Transaction.set_meta_data(sc_icon="shiny", sc_app="xterm")
 
502
 
 
503
        Special keyword arguments:
 
504
        reply_handler - callback function. If specified in combination with
 
505
            error_handler the method will be called asynchrounsouly.
 
506
        error_handler - in case of an error the given callback gets the
 
507
            corresponding DBus exception instance
 
508
        """
 
509
        reply_handler = kwargs.pop("reply_handler", None)
 
510
        error_handler = kwargs.pop("error_handler", None)
 
511
        if reply_handler:
 
512
            _reply_handler = lambda: reply_handler(self)
 
513
        else:
 
514
            _reply_handler = None
 
515
        self._proxy.Set("org.debian.apt.transaction", "MetaData", kwargs,
 
516
                        dbus_interface=dbus.PROPERTIES_IFACE,
 
517
                        reply_handler=_reply_handler,
 
518
                        error_handler=error_handler)
 
519
 
 
520
    @defer.deferable_function
 
521
    @convert_dbus_exception
 
522
    def set_terminal(self, ttyname, reply_handler=None, error_handler=None):
 
523
        """Set the Terminal property of the transaction to attach a
 
524
        controlling terminal to the dpkg call.
 
525
 
 
526
        Keyword arguments:
 
527
        ttyname - the path to the master tty
 
528
        reply_handler - callback function. If specified in combination with
 
529
            error_handler the method will be called asynchrounsouly.
 
530
        error_handler - in case of an error the given callback gets the
 
531
            corresponding DBus exception instance
 
532
        """
 
533
        if reply_handler:
 
534
            _reply_handler = lambda: reply_handler(self)
 
535
        else:
 
536
            _reply_handler = None
 
537
        self._proxy.Set("org.debian.apt.transaction", "Terminal", ttyname,
 
538
                        dbus_interface=dbus.PROPERTIES_IFACE,
 
539
                        reply_handler=_reply_handler,
 
540
                        error_handler=error_handler)
 
541
 
 
542
    def disconnect(self):
 
543
        """Stop monitoring the progress of the transaction."""
 
544
        self._signal_matcher.remove()
 
545
        del self._signal_matcher
 
546
 
 
547
    @defer.deferable_function
 
548
    @convert_dbus_exception
 
549
    def set_locale(self, locale_name, reply_handler=None, error_handler=None):
 
550
        """Set the language for status and error messages.
 
551
 
 
552
        Keyword arguments:
 
553
        locale_name - a supported locale, e.g. "de_DE@UTF-8"
 
554
        reply_handler - callback function. If specified in combination with
 
555
            error_handler the method will be called asynchrounsouly.
 
556
        error_handler - in case of an error the given callback gets the
 
557
            corresponding DBus exception instance
 
558
        """
 
559
        if reply_handler:
 
560
            _reply_handler = lambda: reply_handler(self)
 
561
        else:
 
562
            _reply_handler = None
 
563
        self._proxy.Set("org.debian.apt.transaction", "Locale", locale_name,
 
564
                        dbus_interface=dbus.PROPERTIES_IFACE,
 
565
                        reply_handler=_reply_handler,
 
566
                        error_handler=error_handler)
 
567
 
 
568
    @defer.deferable_function
 
569
    @convert_dbus_exception
 
570
    def provide_medium(self, medium, reply_handler=None, error_handler=None):
 
571
        """Continue a paused transaction which waited for a medium to install
 
572
        from.
 
573
 
 
574
        Keyword arguments:
 
575
        medium - the name of the provided medium
 
576
        reply_handler - callback function. If specified in combination with
 
577
            error_handler the method will be called asynchrounsouly.
 
578
        error_handler - in case of an error the given callback gets the
 
579
            corresponding DBus exception instance
 
580
        """
 
581
        self._iface.ProvideMedium(medium, reply_handler=reply_handler,
 
582
                                  error_handler=error_handler)
 
583
 
 
584
    @defer.deferable_function
 
585
    @convert_dbus_exception
 
586
    def resolve_config_file_conflict(self, config, answer, reply_handler=None,
 
587
                                     error_handler=None):
 
588
        """Continue a paused transaction that waits for the resolution of a
 
589
        configuration file conflict.
 
590
 
 
591
        Keyword arguments:
 
592
        config - the path of the corresponding config file
 
593
        answer - can be either "keep" or "replace"
 
594
        reply_handler - callback function. If specified in combination with
 
595
            error_handler the method will be called asynchrounsouly.
 
596
        error_handler - in case of an error the given callback gets the
 
597
            corresponding DBus exception instance
 
598
        """
 
599
        self._iface.ResolveConfigFileConflict(config, answer,
 
600
                                              reply_handler=reply_handler,
 
601
                                              error_handler=error_handler)
 
602
 
 
603
 
 
604
class AptClient:
 
605
 
 
606
    """Provides a complete client for aptdaemon."""
 
607
 
 
608
    def __init__(self):
 
609
        """Return a new AptClient instance."""
 
610
        self.bus = dbus.SystemBus()
 
611
        # Catch an invalid locale
 
612
        try:
 
613
            self._locale = "%s.%s" % locale.getdefaultlocale()
 
614
        except ValueError:
 
615
            self._locale = None
 
616
        self.terminal = None
 
617
 
 
618
    @convert_dbus_exception
 
619
    def get_trusted_vendor_keys(self, reply_handler=None, error_handler=None):
 
620
        """Return the list of trusted vendors.
 
621
 
 
622
        Keyword arguments:
 
623
        reply_handler - callback function. If specified in combination with
 
624
            error_handler the method will be called asynchrounsouly.
 
625
        error_handler - in case of an error the given callback gets the
 
626
            corresponding DBus exception instance
 
627
        """
 
628
        daemon = get_aptdaemon()
 
629
        keys = daemon.GetTrustedVendorKeys(reply_handler=reply_handler,
 
630
                                           error_handler=error_handler)
 
631
        return keys
 
632
 
 
633
    @defer.deferable_function
 
634
    @convert_dbus_exception
 
635
    def upgrade_system(self, safe_mode=True, wait=False, reply_handler=None,
 
636
                       error_handler=None):
 
637
        """Return a transaction which upgrades the software installed on
 
638
        the system.
 
639
 
 
640
        Keyword arguments:
 
641
        safe_mode - if True skip upgrades which require the installation or
 
642
            removal of other packages. Defaults to True.
 
643
        wait - if True run the transaction immediately and return its exit
 
644
            state instead of the transaction itself.
 
645
        reply_handler - callback function. If specified in combination with
 
646
            error_handler the method will be called asynchrounsouly.
 
647
        error_handler - in case of an error the given callback gets the
 
648
            corresponding DBus exception instance
 
649
        """
 
650
        return self._run_transaction("UpgradeSystem", [safe_mode],
 
651
                                     wait, reply_handler, error_handler)
 
652
 
 
653
    @defer.deferable_function
 
654
    @convert_dbus_exception
 
655
    def install_packages(self, package_names, wait=False, reply_handler=None,
 
656
                         error_handler=None):
 
657
        """Return a transaction which will the install the given packages.
 
658
 
 
659
        Keyword arguments:
 
660
        package_names - a list of package names
 
661
        wait - if True run the transaction immediately and return its exit
 
662
            state instead of the transaction itself.
 
663
        reply_handler - callback function. If specified in combination with
 
664
            error_handler the method will be called asynchrounsouly.
 
665
        error_handler - in case of an error the given callback gets the
 
666
            corresponding DBus exception instance
 
667
        """
 
668
        return self._run_transaction("InstallPackages", [package_names],
 
669
                                     wait, reply_handler, error_handler)
 
670
 
 
671
    @defer.deferable_function
 
672
    @convert_dbus_exception
 
673
    def add_repository(self, src_type, uri, dist, comps=[], comment="", 
 
674
                       sourcesfile="", reply_handler=None, error_handler=None):
 
675
        """Add repository to the sources list.
 
676
 
 
677
        Keyword arguments:
 
678
        src_type -- the type of the entry (deb, deb-src)
 
679
        uri -- the main repository uri (e.g. http://archive.ubuntu.com/ubuntu)
 
680
        dist -- the distribution to use (e.g. karmic, "/")
 
681
        comps -- a (possible empty) list of components (main, restricted)
 
682
        comment -- an (optional) comment
 
683
        sourcesfile -- an (optinal) filename in sources.list.d 
 
684
        reply_handler - callback function. If specified in combination with
 
685
            error_handler the method will be called asynchrounsouly.
 
686
        error_handler - in case of an error the given callback gets the
 
687
            corresponding DBus exception instance
 
688
         """
 
689
        daemon = get_aptdaemon()
 
690
        # dbus can not deal with empty lists and will error
 
691
        if comps == []:
 
692
            comps = [""]
 
693
        return daemon.AddRepository(src_type, uri, dist, comps, comment,
 
694
                                    sourcesfile, reply_handler=reply_handler,
 
695
                                    error_handler=error_handler)
 
696
 
 
697
    @defer.deferable_function
 
698
    @convert_dbus_exception
 
699
    def add_vendor_key_from_file(self, path, wait=False, reply_handler=None,
 
700
                                 error_handler=None):
 
701
        """Return a transaction which add the GPG key of a software vendor
 
702
        to the list of trusted ones.
 
703
 
 
704
        Keyword arguments:
 
705
        path - location of the key file
 
706
        wait - if True run the transaction immediately and return its exit
 
707
            state instead of the transaction itself.
 
708
        reply_handler - callback function. If specified in combination with
 
709
            error_handler the method will be called asynchrounsouly.
 
710
        error_handler - in case of an error the given callback gets the
 
711
            corresponding DBus exception instance
 
712
        """
 
713
        return self._run_transaction("AddVendorKeyFromFile", [path],
 
714
                                     wait, reply_handler, error_handler)
 
715
 
 
716
    @defer.deferable_function
 
717
    @convert_dbus_exception
 
718
    def remove_vendor_key(self, fingerprint, wait=False, reply_handler=None,
 
719
                          error_handler=None):
 
720
        """Return a transaction which will remove the GPG of a software vendor
 
721
        from the list of trusted ones.
 
722
 
 
723
        Keyword arguments:
 
724
        fingerprint - identifier of the GPG key
 
725
        wait - if True run the transaction immediately and return its exit
 
726
            state instead of the transaction itself.
 
727
        reply_handler - callback function. If specified in combination with
 
728
            error_handler the method will be called asynchrounsouly.
 
729
        error_handler - in case of an error the given callback gets the
 
730
            corresponding DBus exception instance
 
731
        """
 
732
        return self._run_transaction("RemoveVendorKey", [fingerprint],
 
733
                                     wait, reply_handler, error_handler)
 
734
 
 
735
    @defer.deferable_function
 
736
    @convert_dbus_exception
 
737
    def install_file(self, path, wait=False, reply_handler=None,
 
738
                     error_handler=None):
 
739
        """Return a transaction which installs a local package file.
 
740
 
 
741
        Keyword arguments:
 
742
        path - location of the package file
 
743
        wait - if True run the transaction immediately and return its exit
 
744
            state instead of the transaction itself.
 
745
        reply_handler - callback function. If specified in combination with
 
746
            error_handler the method will be called asynchrounsouly.
 
747
        error_handler - in case of an error the given callback gets the
 
748
            corresponding DBus exception instance
 
749
        """
 
750
        return self._run_transaction("InstallFile", [path],
 
751
                                     wait, reply_handler, error_handler)
 
752
 
 
753
    @defer.deferable_function
 
754
    @convert_dbus_exception
 
755
    def upgrade_packages(self, package_names, wait=False, reply_handler=None,
 
756
                         error_handler=None):
 
757
        """Return a transaction which upgrades the given packages.
 
758
 
 
759
        Keyword arguments:
 
760
        package_names - list of package names which should be upgraded
 
761
        wait - if True run the transaction immediately and return its exit
 
762
            state instead of the transaction itself.
 
763
        reply_handler - callback function. If specified in combination with
 
764
            error_handler the method will be called asynchrounsouly.
 
765
        error_handler - in case of an error the given callback gets the
 
766
            corresponding DBus exception instance
 
767
        """
 
768
        return self._run_transaction("UpgradePackages", [package_names],
 
769
                                     wait, reply_handler, error_handler)
 
770
 
 
771
    @defer.deferable_function
 
772
    @convert_dbus_exception
 
773
    def remove_packages(self, package_names, wait=False,
 
774
                        reply_handler=None, error_handler=None):
 
775
        """Return a transaction which removes the given packages.
 
776
 
 
777
        Keyword arguments:
 
778
        package_names - list of package names which should be removed
 
779
        wait - if True run the transaction immediately and return its exit
 
780
            state instead of the transaction itself.
 
781
        reply_handler - callback function. If specified in combination with
 
782
            error_handler the method will be called asynchrounsouly.
 
783
        error_handler - in case of an error the given callback gets the
 
784
            corresponding DBus exception instance
 
785
        """
 
786
        return self._run_transaction("RemovePackages", [package_names],
 
787
                                     wait, reply_handler, error_handler)
 
788
 
 
789
    @defer.deferable_function
 
790
    @convert_dbus_exception
 
791
    def commit_packages(self, install, reinstall, remove, purge, upgrade,
 
792
                        wait=False, reply_handler=None, error_handler=None):
 
793
        """Return a transaction which performs a complex package managagement
 
794
        task which consits of installing, reinstalling, removing,
 
795
        purging and upgrading packages at the same time.
 
796
 
 
797
        Keyword arguments:
 
798
        install - list of package names to install
 
799
        reinstall - list of package names to reinstall
 
800
        remove - list of package names to remove
 
801
        purge - list of package names to purge
 
802
        upgrade - list of package names to upgrade
 
803
        wait - if True run the transaction immediately and return its exit
 
804
            state instead of the transaction itself.
 
805
        reply_handler - callback function. If specified in combination with
 
806
            error_handler the method will be called asynchrounsouly.
 
807
        error_handler - in case of an error the given callback gets the
 
808
            corresponding DBus exception instance
 
809
        """
 
810
        def check_empty_list(lst):
 
811
            if not lst:
 
812
                return [""]
 
813
            else:
 
814
                return lst
 
815
        pkgs = [check_empty_list(lst) for lst in [install, reinstall, remove,
 
816
                                                  purge, upgrade]]
 
817
        return self._run_transaction("CommitPackages", pkgs,
 
818
                                     wait, reply_handler, error_handler)
 
819
 
 
820
    @defer.deferable_function
 
821
    @convert_dbus_exception
 
822
    def fix_broken_depends(self, wait=False, reply_handler=None,
 
823
                           error_handler=None):
 
824
        """Return a transacation which tries to fix broken dependencies.
 
825
        Use with care since currently there isn't any preview of the
 
826
        changes.
 
827
 
 
828
        Keyword arguments:
 
829
        wait - if True run the transaction immediately and return its exit
 
830
            state instead of the transaction itself.
 
831
        reply_handler - callback function. If specified in combination with
 
832
            error_handler the method will be called asynchrounsouly.
 
833
        error_handler - in case of an error the given callback gets the
 
834
            corresponding DBus exception instance
 
835
        """
 
836
        return self._run_transaction("FixBrokenDepends", [],
 
837
                                     wait, reply_handler, error_handler)
 
838
 
 
839
    @defer.deferable_function
 
840
    @convert_dbus_exception
 
841
    def fix_incomplete_install(self, wait=False, reply_handler=None,
 
842
                               error_handler=None):
 
843
        """Return a transaction which tries to complete cancelled installations
 
844
        by calling dpkg --configure -a.
 
845
 
 
846
        Keyword arguments:
 
847
        wait - if True run the transaction immediately and return its exit
 
848
            state instead of the transaction itself.
 
849
        reply_handler - callback function. If specified in combination with
 
850
            error_handler the method will be called asynchrounsouly.
 
851
        error_handler - in case of an error the given callback gets the
 
852
            corresponding DBus exception instance
 
853
        """
 
854
        return self._run_transaction("FixIncompleteInstall", [], wait,
 
855
                                     reply_handler, error_handler)
 
856
 
 
857
    @defer.deferable_function
 
858
    @convert_dbus_exception
 
859
    def update_cache(self, wait=False, reply_handler=None, error_handler=None):
 
860
        """Return a transaction which queries the software sources
 
861
        (package repositories) for available packages.
 
862
 
 
863
        Keyword arguments:
 
864
        wait - if True run the transaction immediately and return its exit
 
865
            state instead of the transaction itself.
 
866
        reply_handler - callback function. If specified in combination with
 
867
            error_handler the method will be called asynchrounsouly.
 
868
        error_handler - in case of an error the given callback gets the
 
869
            corresponding DBus exception instance
 
870
        """
 
871
        return self._run_transaction("UpdateCache", [], wait, reply_handler,
 
872
                                     error_handler)
 
873
 
 
874
    @defer.deferable_function
 
875
    @convert_dbus_exception
 
876
    def enable_distro_component(self, component, reply_handler=None,
 
877
                                error_handler=None):
 
878
        """Add component to the sources list.
 
879
 
 
880
        Keyword arguments:
 
881
        component -- a components e.g. main or universe
 
882
        reply_handler - callback function. If specified in combination with
 
883
            error_handler the method will be called asynchrounsouly.
 
884
        error_handler - in case of an error the given callback gets the
 
885
            corresponding DBus exception instance
 
886
         """
 
887
        daemon = get_aptdaemon()
 
888
        return daemon.EnableDistroComponent(component,
 
889
                                            reply_handler=reply_handler,
 
890
                                            error_handler=error_handler)
 
891
 
 
892
    def _run_transaction(self, method_name, args, wait, reply_handler,
 
893
                         error_handler):
 
894
        async = reply_handler and error_handler
 
895
        try:
 
896
            deferred = self._run_transaction_helper(method_name, args, wait,
 
897
                                                    async)
 
898
        except Exception, error:
 
899
            if async:
 
900
                error_handler(error)
 
901
            else:
 
902
                raise
 
903
        if async:
 
904
            deferred.add_callbacks(reply_handler)
 
905
            deferred.add_errback(error_handler)
 
906
            return deferred
 
907
        elif isinstance(deferred.result, defer.DeferredException):
 
908
            raise deferred.result.value
 
909
        else:
 
910
            return deferred.result
 
911
 
 
912
    @defer.inline_callbacks
 
913
    def _run_transaction_helper(self, method_name, args, wait, async):
 
914
        daemon = get_aptdaemon()
 
915
        dbus_method = daemon.get_dbus_method(method_name)
 
916
        if async:
 
917
            deferred = defer.Deferred()
 
918
            dbus_method(reply_handler=deferred.callback,
 
919
                        error_handler=deferred.errback, *args)
 
920
            tid = yield deferred
 
921
        else:
 
922
            tid = dbus_method(*args)
 
923
        trans = AptTransaction(tid, self.bus)
 
924
        if self._locale:
 
925
            yield trans.set_locale(self._locale, defer=async)
 
926
        if self.terminal:
 
927
            yield trans.set_terminal(self.terminal, defer=async)
 
928
        yield trans.sync(defer=async)
 
929
        if wait and async:
 
930
            deferred_wait = defer.Deferred()
 
931
            trans.connect("finished",
 
932
                          lambda trans, exit: deferred_wait.callback(exit))
 
933
            yield trans.run(defer=True)
 
934
            yield deferred_wait
 
935
        elif wait:
 
936
            trans.run()
 
937
            while trans.exit == enums.EXIT_UNFINISHED:
 
938
                context = gobject.main_context_default()
 
939
                context.iteration(True)
 
940
            if trans.error:
 
941
                raise trans.error
 
942
        defer.return_value(trans)
 
943
 
 
944
 
 
945
@defer.deferable_function
 
946
@convert_dbus_exception
 
947
def get_transaction(tid, bus=None, reply_handler=None, error_handler=None):
 
948
    """Return an AptTransaction instance connected to the given transaction.
 
949
 
 
950
    Keyword arguments:
 
951
    tid -- the identifier of the transaction
 
952
    bus -- the D-Bus connection that should be used (defaults to the system bus)
 
953
    """
 
954
    if not bus:
 
955
        bus = dbus.SystemBus()
 
956
    trans = AptTransaction(tid, bus)
 
957
    if error_handler and reply_handler:
 
958
        trans.sync(reply_handler=reply_handler, error_handler=error_handler)
 
959
    else:
 
960
        trans.sync()
 
961
        return trans
 
962
 
 
963
def get_aptdaemon(bus=None):
 
964
    """Return the aptdaemon D-Bus interface.
 
965
 
 
966
    Keyword argument:
 
967
    bus -- the D-Bus connection that should be used (defaults to the system bus)
 
968
    """
 
969
    if not bus:
 
970
        bus = dbus.SystemBus()
 
971
    return dbus.Interface(bus.get_object("org.debian.apt",
 
972
                                         "/org/debian/apt",
 
973
                                         False),
 
974
                          "org.debian.apt")
 
975
 
 
976
# vim:ts=4:sw=4:et