Merge lp:~mvo/software-center/size-calculation-via-aptdaemon into lp:software-center

Proposed by Michael Vogt
Status: Merged
Merged at revision: 3114
Proposed branch: lp:~mvo/software-center/size-calculation-via-aptdaemon
Merge into: lp:software-center
Diff against target: 391 lines (+127/-105)
6 files modified
softwarecenter/db/pkginfo.py (+10/-3)
softwarecenter/db/pkginfo_impl/aptcache.py (+52/-77)
softwarecenter/db/pkginfo_impl/packagekit.py (+8/-4)
softwarecenter/ui/gtk3/views/appdetailsview.py (+18/-14)
tests/test_pkginfo.py (+36/-4)
tests/utils.py (+3/-3)
To merge this branch: bzr merge lp:~mvo/software-center/size-calculation-via-aptdaemon
Reviewer Review Type Date Requested Status
James Westby (community) Approve
software-store-developers Pending
Review via email: mp+120359@code.launchpad.net

Description of the change

This moves the size calculation code outside of software-center into aptdaemon. This improves
responsiveness and fixes the issues described in bug #988854.

To post a comment you must log in.
Revision history for this message
James Westby (james-w) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'softwarecenter/db/pkginfo.py'
--- softwarecenter/db/pkginfo.py 2012-03-15 03:52:56 +0000
+++ softwarecenter/db/pkginfo.py 2012-08-20 10:09:26 +0000
@@ -121,6 +121,10 @@
121 'cache-broken': (GObject.SIGNAL_RUN_FIRST,121 'cache-broken': (GObject.SIGNAL_RUN_FIRST,
122 GObject.TYPE_NONE,122 GObject.TYPE_NONE,
123 ()),123 ()),
124 'query-total-size-on-install-done': (
125 GObject.SIGNAL_RUN_FIRST,
126 GObject.TYPE_NONE,
127 (int, int)),
124 }128 }
125129
126 def __getitem__(self, k):130 def __getitem__(self, k):
@@ -201,15 +205,18 @@
201 which will be removed if the package is installed."""205 which will be removed if the package is installed."""
202 return []206 return []
203207
204 def get_total_size_on_install(self, pkgname,208 def query_total_size_on_install(self, pkgname,
205 addons_install=None, addons_remove=None,209 addons_install=None, addons_remove=None,
206 archive_suite=None):210 archive_suite=None):
207 """ Returns a tuple (download_size, installed_size)211 """ Query for download and installed size
208 with disk size in KB calculated for pkgname installation212 with disk size in KB calculated for pkgname installation
209 plus addons change and a (optional) archive_suite that the213 plus addons change and a (optional) archive_suite that the
210 package comes from214 package comes from
215
216 This will emit a "query-total-size-on-install-done" signal
217 with the parameters (download_size, required_space_on_disk)
211 """218 """
212 return (0, 0)219 self.emit("query-total-size-on-install-done", 0, 0)
213220
214 def open(self):221 def open(self):
215 """222 """
216223
=== modified file 'softwarecenter/db/pkginfo_impl/aptcache.py'
--- softwarecenter/db/pkginfo_impl/aptcache.py 2012-08-07 13:15:08 +0000
+++ softwarecenter/db/pkginfo_impl/aptcache.py 2012-08-20 10:09:26 +0000
@@ -26,6 +26,9 @@
26from gi.repository import GObject26from gi.repository import GObject
27from gi.repository import Gio27from gi.repository import Gio
2828
29# we need this to get the size calculation done asyncronously and reliable
30from aptdaemon.client import AptClient
31
29from softwarecenter.db.pkginfo import PackageInfo, _Version32from softwarecenter.db.pkginfo import PackageInfo, _Version
30from softwarecenter.enums import PkgStates33from softwarecenter.enums import PkgStates
31from softwarecenter.utils import ExecutionTime34from softwarecenter.utils import ExecutionTime
@@ -135,6 +138,9 @@
135 "changed", self._on_apt_finished_stamp_changed)138 "changed", self._on_apt_finished_stamp_changed)
136 # this is fast, so ok139 # this is fast, so ok
137 self._language_packages = self._read_language_pkgs()140 self._language_packages = self._read_language_pkgs()
141 # query the totalize on install using aptdaemon
142 self.aptd_client = AptClient()
143 self._aptd_trans = None
138144
139 @staticmethod145 @staticmethod
140 def version_compare(a, b):146 def version_compare(a, b):
@@ -569,84 +575,53 @@
569 pkg._pkg, version._cand, archive_suite)575 pkg._pkg, version._cand, archive_suite)
570 return res576 return res
571577
572 def get_total_size_on_install(self, pkgname,578 # space calulcation stuff
573 addons_install=None, addons_remove=None,579 def _on_total_size_calculation_done(self, trans, space):
574 archive_suite=None):580 self.emit(
575 pkgs_to_install = []581 "query-total-size-on-install-done", trans.download, trans.space)
576 pkgs_to_remove = []582
577 total_download_size = 0 # in kB583 def _on_trans_simulate_error(self, error):
578 total_install_size = 0 # in kB584 LOG.exception("simulate failed")
579585
586 def _on_trans_commit_packages_ready(self, trans):
587 trans.connect("space-changed", self._on_total_size_calculation_done)
588 try:
589 trans.simulate(reply_handler=lambda: True,
590 error_handler=self._on_trans_simulate_error)
591 except:
592 LOG.exception("simulate failed")
593
594 def query_total_size_on_install(self, pkgname,
595 addons_install=[], addons_remove=[],
596 archive_suite=""):
580 if not pkgname in self._cache:597 if not pkgname in self._cache:
581 return (0, 0)598 self.emit("query-total-size-on-install-done", 0, 0)
582599
583 pkg = self._cache[pkgname]600 # ensure the syntax is right
584 version = pkg.installed601 if archive_suite:
585602 pkgname = pkgname + "/" + archive_suite
586 all_install = []603
587 if addons_install is not None:604 # and simulate the install/remove via aptdaemon
588 all_install += addons_install605 install = [pkgname] + addons_install
589606 remove = addons_remove
590 if version is None:607 reinstall = purge = upgrade = downgrade = []
591 # its important that its the first pkg as the depcache will608
592 # get cleared for each pkg and that will means that the609 if self._aptd_trans:
593 # set_candidate_release is lost again610 self._aptd_trans.cancel()
594 all_install.append(pkgname)611 self._aptd_trans = None
595612
596 for p in all_install:613 # do this async
597 # ensure that the archive_suite is set if needed, this needs to614 try:
598 # be in the loop as the cache is cleared in each loop iteration615 self.aptd_client.commit_packages(
599 if archive_suite:616 install, reinstall, remove, purge, upgrade, downgrade,
600 self._set_candidate_release(pkg, archive_suite)617 # wait
601 # now get the right version618 False,
602 version = self._cache[p].candidate619 # reply and error handlers
603 # this can happen on e.g. deb packages that are not in the cache620 self._on_trans_commit_packages_ready,
604 # testcase: software-center google-chrome-stable_current_amd64.deb621 self._on_trans_simulate_error)
605 if not version:622 except:
606 continue623 LOG.exception(
607 pkgs_to_install.append(version)624 "getting commit_packages trans failed for '%s'" % pkgname)
608 # now do it
609 deps_inst = self._try_install_and_get_all_deps_installed(
610 self._cache[p])
611 for dep in deps_inst:
612 if self._cache[dep].installed is None:
613 dep_version = self._cache[dep].candidate
614 pkgs_to_install.append(dep_version)
615 deps_remove = self._try_install_and_get_all_deps_removed(
616 self._cache[p])
617 for dep in deps_remove:
618 if self._cache[dep].is_installed:
619 dep_version = self._cache[dep].installed
620 pkgs_to_remove.append(dep_version)
621
622 all_remove = [] if addons_remove is None else addons_remove
623 for p in all_remove:
624 version = self._cache[p].installed
625 pkgs_to_remove.append(version)
626 deps_inst = self._try_install_and_get_all_deps_installed(
627 self._cache[p])
628 for dep in deps_inst:
629 if self._cache[dep].installed is None:
630 version = self._cache[dep].candidate
631 pkgs_to_install.append(version)
632 deps_remove = self._try_install_and_get_all_deps_removed(
633 self._cache[p])
634 for dep in deps_remove:
635 if self._cache[dep].installed is not None:
636 version = self._cache[dep].installed
637 pkgs_to_remove.append(version)
638
639 pkgs_to_install = list(set(pkgs_to_install))
640 pkgs_to_remove = list(set(pkgs_to_remove))
641
642 for pkg in pkgs_to_install:
643 if not pkg_downloaded(pkg) and not pkg.package.installed:
644 total_download_size += pkg.size
645 total_install_size += pkg.installed_size
646 for pkg in pkgs_to_remove:
647 total_install_size -= pkg.installed_size
648
649 return (total_download_size, total_install_size)
650625
651 def get_all_deps_upgrading(self, pkg):626 def get_all_deps_upgrading(self, pkg):
652 # note: this seems not to be used anywhere627 # note: this seems not to be used anywhere
653628
=== modified file 'softwarecenter/db/pkginfo_impl/packagekit.py'
--- softwarecenter/db/pkginfo_impl/packagekit.py 2012-03-07 20:08:53 +0000
+++ softwarecenter/db/pkginfo_impl/packagekit.py 2012-08-20 10:09:26 +0000
@@ -264,20 +264,24 @@
264 if (p.get_name() != pkg.name)264 if (p.get_name() != pkg.name)
265 and p.get_info() == packagekit.InfoEnum.INSTALLED]265 and p.get_info() == packagekit.InfoEnum.INSTALLED]
266266
267 def get_total_size_on_install(self, pkgname,267 def query_total_size_on_install(self, pkgname,
268 addons_install=None, addons_remove=None,268 addons_install=None, addons_remove=None,
269 archive_suite=None):269 archive_suite=None):
270 """ Returns a tuple (download_size, installed_size)270 """ emit query-total-size-on-install-done signal
271 with disk size in KB calculated for pkgname installation271 with disk size in KB calculated for pkgname installation
272 plus addons change.272 plus addons change.
273 """273 """
274 # FIXME: support archive_suite here too274 # FIXME: support archive_suite here too
275275
276 # FIXME: PackageKit reports only one size at a time276 # FIXME: PackageKit reports only one size at a time
277 download_size = 0
278 install_size = 0
277 if self.is_installed(pkgname):279 if self.is_installed(pkgname):
278 return (0, self.get_size(pkgname))280 install_size = self.get_size(pkgname)
279 else:281 else:
280 return (self.get_size(pkgname), 0)282 download_size = self.get_size(pkgname)
283 self.emit(
284 "query-total-size-on-install-done", download_size, install_size)
281285
282 @property286 @property
283 def ready(self):287 def ready(self):
284288
=== modified file 'softwarecenter/ui/gtk3/views/appdetailsview.py'
--- softwarecenter/ui/gtk3/views/appdetailsview.py 2012-08-17 08:56:26 +0000
+++ softwarecenter/ui/gtk3/views/appdetailsview.py 2012-08-20 10:09:26 +0000
@@ -762,8 +762,7 @@
762 if addon in self.addons_to_install:762 if addon in self.addons_to_install:
763 self.addons_to_install.remove(addon)763 self.addons_to_install.remove(addon)
764 self.status_bar.configure()764 self.status_bar.configure()
765 GObject.idle_add(self.view.update_totalsize,765 self.view.update_totalsize()
766 priority=GObject.PRIORITY_LOW)
767766
768 def configure(self, pkgname, update_addons=True):767 def configure(self, pkgname, update_addons=True):
769 self.addons_to_install = []768 self.addons_to_install = []
@@ -780,8 +779,7 @@
780 self.addons_to_install = []779 self.addons_to_install = []
781 self.addons_to_remove = []780 self.addons_to_remove = []
782 self.configure(self.view.app.pkgname)781 self.configure(self.view.app.pkgname)
783 GObject.idle_add(self.view.update_totalsize,782 self.view.update_totalsize()
784 priority=GObject.PRIORITY_LOW)
785783
786784
787_asset_cache = {}785_asset_cache = {}
@@ -814,6 +812,8 @@
814 self.distro = distro812 self.distro = distro
815 self.icons = icons813 self.icons = icons
816 self.cache = cache814 self.cache = cache
815 self.cache.connect("query-total-size-on-install-done",
816 self._on_query_total_size_on_install_done)
817 self.backend = get_install_backend()817 self.backend = get_install_backend()
818 self.cache.connect("cache-ready", self._on_cache_ready)818 self.cache.connect("cache-ready", self._on_cache_ready)
819 self.connect("destroy", self._on_destroy)819 self.connect("destroy", self._on_destroy)
@@ -1496,8 +1496,7 @@
1496 self.addons_manager.configure(app_details.pkgname)1496 self.addons_manager.configure(app_details.pkgname)
14971497
1498 # Update total size label1498 # Update total size label
1499 self.totalsize_info.set_value(_("Calculating..."))1499 self.update_totalsize()
1500 GObject.timeout_add(500, self.update_totalsize)
15011500
1502 # Update addons state bar1501 # Update addons state bar
1503 self.addons_statusbar.configure()1502 self.addons_statusbar.configure()
@@ -1545,9 +1544,7 @@
1545 self.addon_view.hide()1544 self.addon_view.hide()
1546 if self.addon_view.get_parent():1545 if self.addon_view.get_parent():
1547 self.info_vb.remove(self.addon_view)1546 self.info_vb.remove(self.addon_view)
1548 self.totalsize_info.set_value(_("Calculating..."))1547 self.update_totalsize()
1549 GObject.idle_add(self.update_totalsize,
1550 priority=GObject.PRIORITY_LOW)
1551 self._update_recommendations(app_details.pkgname)1548 self._update_recommendations(app_details.pkgname)
1552 self._update_reviews(app_details)1549 self._update_reviews(app_details)
15531550
@@ -2004,18 +2001,25 @@
2004 if not self.totalsize_info.get_property('visible'):2001 if not self.totalsize_info.get_property('visible'):
2005 return False2002 return False
20062003
2004 self.totalsize_info.set_value(_("Calculating..."))
2005
2007 while Gtk.events_pending():2006 while Gtk.events_pending():
2008 Gtk.main_iteration()2007 Gtk.main_iteration()
20092008
2010 label_string = ""2009 self.cache.query_total_size_on_install(
2011
2012 res = self.cache.get_total_size_on_install(
2013 self.app_details.pkgname,2010 self.app_details.pkgname,
2014 self.addons_manager.addons_to_install,2011 self.addons_manager.addons_to_install,
2015 self.addons_manager.addons_to_remove,2012 self.addons_manager.addons_to_remove,
2016 self.app.archive_suite)2013 self.app.archive_suite)
2017 total_download_size, total_install_size = res2014
2018 if res == (0, 0) and isinstance(self.app, DebFileApplication):2015 def _on_query_total_size_on_install_done(self,
2016 pkginfo,
2017 total_download_size,
2018 total_install_size):
2019 label_string = ""
2020 if (total_download_size == 0 and
2021 total_install_size == 0 and
2022 isinstance(self.app, DebFileApplication)):
2019 total_install_size = self.app_details.installed_size2023 total_install_size = self.app_details.installed_size
2020 if total_download_size > 0:2024 if total_download_size > 0:
2021 download_size = GLib.format_size(total_download_size)2025 download_size = GLib.format_size(total_download_size)
20222026
=== modified file 'tests/test_pkginfo.py'
--- tests/test_pkginfo.py 2012-05-30 18:39:55 +0000
+++ tests/test_pkginfo.py 2012-08-20 10:09:26 +0000
@@ -6,6 +6,7 @@
6from mock import patch6from mock import patch
77
8from tests.utils import (8from tests.utils import (
9 get_test_pkg_info,
9 setup_test_env,10 setup_test_env,
10)11)
11setup_test_env()12setup_test_env()
@@ -35,7 +36,36 @@
35 self.cache = apt.Cache(memonly=True)36 self.cache = apt.Cache(memonly=True)
3637
37 def test_get_total_size(self):38 def test_get_total_size(self):
38 # get a cache39 def _on_query_total_size_on_install_done(pkginfo, download, space):
40 self.need_download = download
41 self.need_space = space
42 loop.quit()
43 TEST_PKG = "casper"
44 ADDONS_TO_INSTALL = [ "lupin-casper" ]
45 ADDONS_TO_REMOVE = []
46 loop = GObject.MainLoop(GObject.main_context_default())
47 cache = get_test_pkg_info()
48 cache.connect(
49 "query-total-size-on-install-done",
50 _on_query_total_size_on_install_done)
51 cache.query_total_size_on_install(
52 TEST_PKG, ADDONS_TO_INSTALL, ADDONS_TO_REMOVE)
53 loop.run()
54 # work out the numbers that we at least need to get (this will
55 # not include dependencies so it is probably lower)
56 need_at_least_download = (
57 cache[TEST_PKG].candidate.size +
58 sum([cache[pkg].candidate.size for pkg in ADDONS_TO_INSTALL]))
59 need_at_least_installed = (
60 cache[TEST_PKG].candidate.installed_size +
61 sum([cache[pkg].candidate.installed_size for pkg in ADDONS_TO_INSTALL]))
62 self.assertTrue(self.need_download >= need_at_least_download)
63 self.assertTrue(self.need_space >= need_at_least_installed)
64 del self.need_download
65 del self.need_space
66
67 def test_get_total_size_with_mock(self):
68 # get a cache
39 cache = get_pkg_info()69 cache = get_pkg_info()
40 cache.open()70 cache.open()
41 # pick first uninstalled pkg71 # pick first uninstalled pkg
@@ -45,11 +75,13 @@
45 # prepare args75 # prepare args
46 addons_to_install = addons_to_remove = []76 addons_to_install = addons_to_remove = []
47 archive_suite = "foo"77 archive_suite = "foo"
48 with patch.object(cache, "_set_candidate_release") as f_mock:78 with patch.object(cache.aptd_client, "commit_packages") as f_mock:
49 cache.get_total_size_on_install(79 cache.query_total_size_on_install(
50 pkg.name, addons_to_install, addons_to_remove, archive_suite)80 pkg.name, addons_to_install, addons_to_remove, archive_suite)
51 # ensure it got called with the right arguments81 # ensure it got called with the right arguments
52 f_mock.assert_called_with(pkg, archive_suite)82 args, kwargs = f_mock.call_args
83 to_install = args[0]
84 self.assertTrue(to_install[0].endswith("/%s" % archive_suite))
5385
5486
55if __name__ == "__main__":87if __name__ == "__main__":
5688
=== modified file 'tests/utils.py'
--- tests/utils.py 2012-05-31 12:50:01 +0000
+++ tests/utils.py 2012-08-20 10:09:26 +0000
@@ -395,7 +395,7 @@
395 """Return (recommended, suggested) addons for 'pkgname'."""395 """Return (recommended, suggested) addons for 'pkgname'."""
396 return ([], [])396 return ([], [])
397397
398 def get_total_size_on_install(self, pkgname, addons_to_install,398 def query_total_size_on_install(self, pkgname, addons_to_install,
399 addons_to_remove, archive_suite):399 addons_to_remove, archive_suite):
400 """Return a fake (total_download_size, total_install_size) result."""400 """Emit a fake signal "query-total-size-on-install-done" """
401 return (0, 0)401 self.emit("query-total-size-on-install-done", (0, 0))

Subscribers

People subscribed via source and target branches