~juliank/software-center/debian-next

« back to all changes in this revision

Viewing changes to softwarecenter/backend/aptd.py

  • Committer: Julian Andres Klode
  • Date: 2010-08-02 15:20:09 UTC
  • mfrom: (429.62.230 trunk)
  • Revision ID: jak@debian.org-20100802152009-r5s64zd6rb43g7pa
* Merge with Ubuntu's 2.1 series (2.1.7 + bzr), remaining differences:
  - po/cs.po: Updated.
  - softwarecenter/app.py: Remove views menu when not on Ubuntu.
  - setup.py: Support + and - in version numbers.
  - setup.py, setup.cfg: Use unbranded stuff.
  - debian/control:
    - Update Standards-Version
    - Change Vcs-Bzr, Maintainer to Debian.
    - Do not depend on humanity-icon-theme, but gnome-icon-theme.
    - Remove Ubuntu from package description-
  - debian/rules:
    - Drop the old hack for handling non-Ubuntu systems.
    - Run build_i18n in clean.

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
# this program; if not, write to the Free Software Foundation, Inc.,
17
17
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
18
 
 
19
import aptsources.sourceslist
19
20
import dbus
20
21
import gobject
 
22
import logging
21
23
import os
22
 
import logging
23
24
import subprocess
 
25
import sys
 
26
from softwarecenter.utils import *
24
27
 
25
28
from aptdaemon import client
26
29
from aptdaemon import enums
27
30
from aptdaemon.gtkwidgets import AptMediumRequiredDialog, \
28
31
                                 AptConfigFileConflictDialog
 
32
try:
 
33
    from aptdaemon.defer import inline_callbacks
 
34
except ImportError:
 
35
    logging.getLogger("softwarecenter.backend").exception("aptdaemon import failed")
 
36
    print 'Need the latest aptdaemon, try "sudo apt-add-repository ppa:software-store-developers/ppa" to get the PPA'
 
37
    sys.exit(1)
 
38
 
29
39
import gtk
30
40
 
31
41
from softwarecenter.backend.transactionswatcher import TransactionsWatcher
64
74
        self.aptd_client = client.AptClient()
65
75
        self.pending_transactions = {}
66
76
        self._progress_signal = None
 
77
        self._logger = logging.getLogger("softwarecenter.backend")
67
78
    
68
79
    def _axi_finished(self, res):
69
80
        self.emit("channels-changed", res)
70
81
 
71
82
    # public methods
72
83
    def update_xapian_index(self):
73
 
        logging.debug("update_xapian_index")
 
84
        self._logger.debug("update_xapian_index")
74
85
        system_bus = dbus.SystemBus()
75
86
        axi = dbus.Interface(
76
87
            system_bus.get_object("org.debian.AptXapianIndex","/"),
81
92
        # first arg is force, second update_only
82
93
        axi.update_async(True, True)
83
94
 
 
95
    @inline_callbacks
 
96
    def fix_broken_depends(self):
 
97
        self.emit("transaction-started")
 
98
        try:
 
99
            trans = yield self.aptd_client.fix_broken_depends(defer=True)
 
100
            yield self._run_transaction(trans, None, None, None)
 
101
        except Exception, error:
 
102
            self._on_trans_error(error)
 
103
 
 
104
    @inline_callbacks
84
105
    def upgrade(self, pkgname, appname, iconname):
85
106
        """ upgrade a single package """
86
107
        self.emit("transaction-started")
87
 
        reply_handler = lambda trans: self._run_transaction(trans, pkgname,
88
 
                                                            appname, iconname)
89
 
        self.aptd_client.upgrade_packages([pkgname],
90
 
                                          reply_handler=reply_handler,
91
 
                                          error_handler=self._on_trans_error)
 
108
        try:
 
109
            trans = yield self.aptd_client.upgrade_packages([pkgname],
 
110
                                                            defer=True)
 
111
            yield self._run_transaction(trans, pkgname, appname, iconname)
 
112
        except Exception, error:
 
113
            self._on_trans_error(error)
92
114
 
 
115
    @inline_callbacks
93
116
    def remove(self, pkgname, appname, iconname):
94
117
        """ remove a single package """
95
118
        self.emit("transaction-started")
96
 
        reply_handler = lambda trans: self._run_transaction(trans, pkgname,
97
 
                                                            appname, iconname)
98
 
        self.aptd_client.remove_packages([pkgname], wait=False, 
99
 
                                         reply_handler=reply_handler,
100
 
                                         error_handler=self._on_trans_error)
101
 
 
 
119
        try:
 
120
            trans = yield self.aptd_client.remove_packages([pkgname],
 
121
                                                           defer=True)
 
122
            yield self._run_transaction(trans, pkgname, appname, iconname)
 
123
        except Exception, error:
 
124
            self._on_trans_error(error)
 
125
 
 
126
    @inline_callbacks
 
127
    def remove_multiple(self, pkgnames, appnames, iconnames):
 
128
        """ queue a list of packages for removal  """
 
129
        for pkgname, appname, iconname in zip(pkgnames, appnames, iconnames):
 
130
            yield self.remove(pkgname, appname, iconname)
 
131
 
 
132
    @inline_callbacks
102
133
    def install(self, pkgname, appname, iconname):
103
134
        """ install a single package """
104
135
        self.emit("transaction-started")
105
 
        reply_handler = lambda trans: self._run_transaction(trans, pkgname,
106
 
                                                            appname, iconname)
107
 
        self.aptd_client.install_packages([pkgname],
108
 
                                          reply_handler=reply_handler,
109
 
                                          error_handler=self._on_trans_error)
110
 
 
 
136
        try:
 
137
            trans = yield self.aptd_client.install_packages([pkgname],
 
138
                                                            defer=True)
 
139
            yield self._run_transaction(trans, pkgname, appname, iconname)
 
140
        except Exception, error:
 
141
            self._on_trans_error(error)
 
142
 
 
143
    @inline_callbacks
 
144
    def install_multiple(self, pkgnames, appnames, iconnames):
 
145
        """ queue a list of packages for install  """
 
146
        for pkgname, appname, iconname in zip(pkgnames, appnames, iconnames):
 
147
            yield self.install(pkgname, appname, iconname)
 
148
 
 
149
    @inline_callbacks
111
150
    def reload(self):
112
151
        """ reload package list """
113
 
        reply_handler = lambda trans: self._run_transaction(trans, None, None,
114
 
                                                            None)
115
 
        self.aptd_client.update_cache(reply_handler=reply_handler,
116
 
                                      error_handler=self._on_trans_error)
 
152
        try:
 
153
            trans = yield self.aptd_client.update_cache(defer=True)
 
154
            yield self._run_transaction(trans, None, None, None)
 
155
        except Exception, error:
 
156
            self._on_trans_error(error)
117
157
 
 
158
    @inline_callbacks
118
159
    def enable_component(self, component):
119
 
        logging.debug("enable_component: %s" % component)
 
160
        self._logger.debug("enable_component: %s" % component)
120
161
        try:
121
 
            self.aptd_client.enable_distro_component(component)
122
 
        except dbus.exceptions.DBusException, e:
123
 
            if e._dbus_error_name == "org.freedesktop.PolicyKit.Error.NotAuthorized":
124
 
                logging.error("enable_component: '%s'" % e)
 
162
            yield self.aptd_client.enable_distro_component(component, defer=True)
 
163
        except dbus.DBusException, err:
 
164
            if err.get_dbus_name() == "org.freedesktop.PolicyKit.Error.NotAuthorized":
 
165
                self._logger.error("enable_component: '%s'" % err)
125
166
                return
126
167
            raise
127
168
        # now update the cache
128
 
        self.reload()
 
169
        yield self.reload()
129
170
 
 
171
    @inline_callbacks
130
172
    def enable_channel(self, channelfile):
131
 
        import aptsources.sourceslist
132
 
 
133
173
        # read channel file and add all relevant lines
134
174
        for line in open(channelfile):
135
175
            line = line.strip()
139
179
            if entry.invalid:
140
180
                continue
141
181
            sourcepart = os.path.basename(channelfile)
142
 
            try:
143
 
                self.aptd_client.add_repository(
144
 
                    entry.type, entry.uri, entry.dist, entry.comps,
145
 
                    "Added by software-center", sourcepart)
146
 
            except dbus.exceptions.DBusException, e:
147
 
                if e._dbus_error_name == "org.freedesktop.PolicyKit.Error.NotAuthorized":
148
 
                    logging.error("add_repository: '%s'" % e)
149
 
                    return
150
 
        # now update the cache
151
 
        self.reload()
 
182
            yield self.add_sources_list_entry(entry, sourcepart)
 
183
        yield self.reload()
 
184
 
 
185
    @inline_callbacks
 
186
    def add_sources_list_entry(self, source_entry, sourcepart=None):
 
187
        if isinstance(source_entry, basestring):
 
188
            entry = SourceEntry(source_entry)
 
189
        elif isinstance(source_entry, aptsources.sourceslist.SourceEntry):
 
190
            entry = source_entry
 
191
        else:
 
192
            raise ValueError, "Unsupported entry type %s" % type(source_entry)
 
193
 
 
194
        if not sourcepart:
 
195
            sourcepart = sources_filename_from_ppa_entry(entry)
 
196
 
 
197
        args = (entry.type, entry.uri, entry.dist, entry.comps,
 
198
                "Added by software-center", sourcepart)
 
199
        try:
 
200
            yield self.aptd_client.add_repository(*args)
 
201
        except dbus.DBusException, err:
 
202
            if err.get_dbus_name() == "org.freedesktop.PolicyKit.Error.NotAuthorized":
 
203
                self._logger.error("add_repository: '%s'" % err)
 
204
                return
152
205
 
153
206
    # internal helpers
154
207
    def on_transactions_changed(self, current, pending):
191
244
        except KeyError:
192
245
            pass
193
246
 
194
 
    def _on_trans_reply(self):
195
 
        # dummy callback for now, but its required, otherwise the aptdaemon
196
 
        # client blocks the UI and keeps gtk from refreshing
197
 
        logging.debug("_on_trans_reply")
198
 
 
199
 
    def _on_trans_error(self, error):
200
 
        logging.warn("_on_trans_error: %s" % error)
201
 
        # re-enable the action button again if anything went wrong
202
 
        if (error._dbus_error_name == "org.freedesktop.PolicyKit.Error.NotAuthorized" or
203
 
            error._dbus_error_name == "org.freedesktop.DBus.Error.NoReply"):
204
 
            pass
205
 
        else:
206
 
            raise error
207
 
        self.emit("transaction-stopped")
208
 
 
209
247
    def _on_trans_finished(self, trans, enum):
210
248
        """callback when a aptdaemon transaction finished"""
211
249
        if enum == enums.EXIT_FAILED:
213
251
            # cancel handling in aptdaemon (LP: #440941)
214
252
            # FIXME: this is not a proper fix, just a workaround
215
253
            if trans.error_code == enums.ERROR_DAEMON_DIED:
216
 
                logging.warn("daemon dies, ignoring: %s" % excep)
 
254
                self._logger.warn("daemon dies, ignoring: %s" % excep)
217
255
            else:
218
256
                msg = "%s: %s\n%s\n\n%s" % (
219
257
                    _("Error"),
220
258
                    enums.get_error_string_from_enum(trans.error_code),
221
259
                    enums.get_error_description_from_enum(trans.error_code),
222
260
                    trans.error_details)
223
 
                logging.error("error in _on_trans_finished '%s'" % msg)
 
261
                self._logger.error("error in _on_trans_finished '%s'" % msg)
224
262
                # show dialog to the user and exit (no need to reopen
225
263
                # the cache)
226
264
                dialogs.error(
262
300
        else:
263
301
            transaction.cancel()
264
302
 
265
 
    def set_http_proxy(self, trans):
266
 
        """ set http proxy based on gconf and attach it to a transaction """
267
 
        http_proxy = get_http_proxy_string_from_gconf()
268
 
        if http_proxy:
269
 
            trans.set_http_proxy(http_proxy, reply_handler=lambda t: True,
270
 
                                 error_handler=self._on_trans_error)
271
 
 
 
303
    @inline_callbacks
272
304
    def _run_transaction(self, trans, pkgname, appname, iconname):
273
305
        # connect signals
274
306
        trans.connect("config-file-conflict", self._config_file_conflict)
275
307
        trans.connect("medium-required", self._medium_required)
276
308
        trans.connect("finished", self._on_trans_finished)
277
 
        # set appname/iconname/pkgname only if we actually have one
278
 
        if appname:
279
 
            trans.set_meta_data(sc_appname=appname, 
280
 
                                reply_handler=lambda t: True,
281
 
                                error_handler=self._on_trans_error)
282
 
        if iconname:
283
 
            trans.set_meta_data(sc_iconname=iconname,
284
 
                                reply_handler=lambda t: True,
285
 
                                error_handler=self._on_trans_error)
286
 
        # we do not always have a pkgname, e.g. "cache_update" does not
287
 
        if pkgname:
288
 
            trans.set_meta_data(sc_pkgname=pkgname,
289
 
                                reply_handler=lambda t: True,
290
 
                                error_handler=self._on_trans_error)
291
 
            # setup debconf only if we have a pkg
292
 
            trans.set_debconf_frontend("gnome", reply_handler=lambda t: True,
293
 
                                       error_handler=self._on_trans_error)
294
 
            # set this once the new aptdaemon 0.2.x API can be used
295
 
            trans.set_remove_obsoleted_depends(True, 
296
 
                                               reply_handler=lambda t: True,
297
 
                                               error_handler=self._on_trans_error)
298
 
            
299
 
        # set proxy and run
300
 
        self.set_http_proxy(trans)
301
 
        trans.run(error_handler=self._on_trans_error,
302
 
                  reply_handler=self._on_trans_reply)
 
309
        try:
 
310
            # set appname/iconname/pkgname only if we actually have one
 
311
            if appname:
 
312
                yield trans.set_meta_data(sc_appname=appname, defer=True)
 
313
            if iconname:
 
314
                yield trans.set_meta_data(sc_iconname=iconname, defer=True)
 
315
            # we do not always have a pkgname, e.g. "cache_update" does not
 
316
            if pkgname:
 
317
                yield trans.set_meta_data(sc_pkgname=pkgname, defer=True)
 
318
                # setup debconf only if we have a pkg
 
319
                yield trans.set_debconf_frontend("gnome", defer=True)
 
320
                # set this once the new aptdaemon 0.2.x API can be used
 
321
                trans.set_remove_obsoleted_depends(True, defer=True)
 
322
            # set proxy and run
 
323
            http_proxy = get_http_proxy_string_from_gconf()
 
324
            if http_proxy:
 
325
                trans.set_http_proxy(http_proxy, defer=True)
 
326
            yield trans.run(defer=True)
 
327
        except Exception, error:
 
328
            self._on_trans_error(error)
 
329
 
 
330
    def _on_trans_error(self, error):
 
331
        self._logger.warn("_on_trans_error: %s", error)
 
332
        # re-enable the action button again if anything went wrong
 
333
        self.emit("transaction-stopped")
 
334
        if isinstance(error, dbus.DBusException):
 
335
            name = error.get_dbus_name()
 
336
            if name in ["org.freedesktop.PolicyKit.Error.NotAuthorized",
 
337
                        "org.freedesktop.DBus.Error.NoReply"]:
 
338
                pass
 
339
        else:
 
340
            raise error
 
341
 
303
342
 
304
343
if __name__ == "__main__":
305
344
    #c = client.AptClient()