Merge lp:~mvo/software-center/size-calculation-via-aptdaemon into lp:software-center
- size-calculation-via-aptdaemon
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
James Westby (community) | Approve | ||
software-store-developers | Pending | ||
Review via email: mp+120359@code.launchpad.net |
Commit message
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
1 | === modified file 'softwarecenter/db/pkginfo.py' |
2 | --- softwarecenter/db/pkginfo.py 2012-03-15 03:52:56 +0000 |
3 | +++ softwarecenter/db/pkginfo.py 2012-08-20 10:09:26 +0000 |
4 | @@ -121,6 +121,10 @@ |
5 | 'cache-broken': (GObject.SIGNAL_RUN_FIRST, |
6 | GObject.TYPE_NONE, |
7 | ()), |
8 | + 'query-total-size-on-install-done': ( |
9 | + GObject.SIGNAL_RUN_FIRST, |
10 | + GObject.TYPE_NONE, |
11 | + (int, int)), |
12 | } |
13 | |
14 | def __getitem__(self, k): |
15 | @@ -201,15 +205,18 @@ |
16 | which will be removed if the package is installed.""" |
17 | return [] |
18 | |
19 | - def get_total_size_on_install(self, pkgname, |
20 | + def query_total_size_on_install(self, pkgname, |
21 | addons_install=None, addons_remove=None, |
22 | archive_suite=None): |
23 | - """ Returns a tuple (download_size, installed_size) |
24 | + """ Query for download and installed size |
25 | with disk size in KB calculated for pkgname installation |
26 | plus addons change and a (optional) archive_suite that the |
27 | package comes from |
28 | + |
29 | + This will emit a "query-total-size-on-install-done" signal |
30 | + with the parameters (download_size, required_space_on_disk) |
31 | """ |
32 | - return (0, 0) |
33 | + self.emit("query-total-size-on-install-done", 0, 0) |
34 | |
35 | def open(self): |
36 | """ |
37 | |
38 | === modified file 'softwarecenter/db/pkginfo_impl/aptcache.py' |
39 | --- softwarecenter/db/pkginfo_impl/aptcache.py 2012-08-07 13:15:08 +0000 |
40 | +++ softwarecenter/db/pkginfo_impl/aptcache.py 2012-08-20 10:09:26 +0000 |
41 | @@ -26,6 +26,9 @@ |
42 | from gi.repository import GObject |
43 | from gi.repository import Gio |
44 | |
45 | +# we need this to get the size calculation done asyncronously and reliable |
46 | +from aptdaemon.client import AptClient |
47 | + |
48 | from softwarecenter.db.pkginfo import PackageInfo, _Version |
49 | from softwarecenter.enums import PkgStates |
50 | from softwarecenter.utils import ExecutionTime |
51 | @@ -135,6 +138,9 @@ |
52 | "changed", self._on_apt_finished_stamp_changed) |
53 | # this is fast, so ok |
54 | self._language_packages = self._read_language_pkgs() |
55 | + # query the totalize on install using aptdaemon |
56 | + self.aptd_client = AptClient() |
57 | + self._aptd_trans = None |
58 | |
59 | @staticmethod |
60 | def version_compare(a, b): |
61 | @@ -569,84 +575,53 @@ |
62 | pkg._pkg, version._cand, archive_suite) |
63 | return res |
64 | |
65 | - def get_total_size_on_install(self, pkgname, |
66 | - addons_install=None, addons_remove=None, |
67 | - archive_suite=None): |
68 | - pkgs_to_install = [] |
69 | - pkgs_to_remove = [] |
70 | - total_download_size = 0 # in kB |
71 | - total_install_size = 0 # in kB |
72 | - |
73 | + # space calulcation stuff |
74 | + def _on_total_size_calculation_done(self, trans, space): |
75 | + self.emit( |
76 | + "query-total-size-on-install-done", trans.download, trans.space) |
77 | + |
78 | + def _on_trans_simulate_error(self, error): |
79 | + LOG.exception("simulate failed") |
80 | + |
81 | + def _on_trans_commit_packages_ready(self, trans): |
82 | + trans.connect("space-changed", self._on_total_size_calculation_done) |
83 | + try: |
84 | + trans.simulate(reply_handler=lambda: True, |
85 | + error_handler=self._on_trans_simulate_error) |
86 | + except: |
87 | + LOG.exception("simulate failed") |
88 | + |
89 | + def query_total_size_on_install(self, pkgname, |
90 | + addons_install=[], addons_remove=[], |
91 | + archive_suite=""): |
92 | if not pkgname in self._cache: |
93 | - return (0, 0) |
94 | - |
95 | - pkg = self._cache[pkgname] |
96 | - version = pkg.installed |
97 | - |
98 | - all_install = [] |
99 | - if addons_install is not None: |
100 | - all_install += addons_install |
101 | - |
102 | - if version is None: |
103 | - # its important that its the first pkg as the depcache will |
104 | - # get cleared for each pkg and that will means that the |
105 | - # set_candidate_release is lost again |
106 | - all_install.append(pkgname) |
107 | - |
108 | - for p in all_install: |
109 | - # ensure that the archive_suite is set if needed, this needs to |
110 | - # be in the loop as the cache is cleared in each loop iteration |
111 | - if archive_suite: |
112 | - self._set_candidate_release(pkg, archive_suite) |
113 | - # now get the right version |
114 | - version = self._cache[p].candidate |
115 | - # this can happen on e.g. deb packages that are not in the cache |
116 | - # testcase: software-center google-chrome-stable_current_amd64.deb |
117 | - if not version: |
118 | - continue |
119 | - pkgs_to_install.append(version) |
120 | - # now do it |
121 | - deps_inst = self._try_install_and_get_all_deps_installed( |
122 | - self._cache[p]) |
123 | - for dep in deps_inst: |
124 | - if self._cache[dep].installed is None: |
125 | - dep_version = self._cache[dep].candidate |
126 | - pkgs_to_install.append(dep_version) |
127 | - deps_remove = self._try_install_and_get_all_deps_removed( |
128 | - self._cache[p]) |
129 | - for dep in deps_remove: |
130 | - if self._cache[dep].is_installed: |
131 | - dep_version = self._cache[dep].installed |
132 | - pkgs_to_remove.append(dep_version) |
133 | - |
134 | - all_remove = [] if addons_remove is None else addons_remove |
135 | - for p in all_remove: |
136 | - version = self._cache[p].installed |
137 | - pkgs_to_remove.append(version) |
138 | - deps_inst = self._try_install_and_get_all_deps_installed( |
139 | - self._cache[p]) |
140 | - for dep in deps_inst: |
141 | - if self._cache[dep].installed is None: |
142 | - version = self._cache[dep].candidate |
143 | - pkgs_to_install.append(version) |
144 | - deps_remove = self._try_install_and_get_all_deps_removed( |
145 | - self._cache[p]) |
146 | - for dep in deps_remove: |
147 | - if self._cache[dep].installed is not None: |
148 | - version = self._cache[dep].installed |
149 | - pkgs_to_remove.append(version) |
150 | - |
151 | - pkgs_to_install = list(set(pkgs_to_install)) |
152 | - pkgs_to_remove = list(set(pkgs_to_remove)) |
153 | - |
154 | - for pkg in pkgs_to_install: |
155 | - if not pkg_downloaded(pkg) and not pkg.package.installed: |
156 | - total_download_size += pkg.size |
157 | - total_install_size += pkg.installed_size |
158 | - for pkg in pkgs_to_remove: |
159 | - total_install_size -= pkg.installed_size |
160 | - |
161 | - return (total_download_size, total_install_size) |
162 | + self.emit("query-total-size-on-install-done", 0, 0) |
163 | + |
164 | + # ensure the syntax is right |
165 | + if archive_suite: |
166 | + pkgname = pkgname + "/" + archive_suite |
167 | + |
168 | + # and simulate the install/remove via aptdaemon |
169 | + install = [pkgname] + addons_install |
170 | + remove = addons_remove |
171 | + reinstall = purge = upgrade = downgrade = [] |
172 | + |
173 | + if self._aptd_trans: |
174 | + self._aptd_trans.cancel() |
175 | + self._aptd_trans = None |
176 | + |
177 | + # do this async |
178 | + try: |
179 | + self.aptd_client.commit_packages( |
180 | + install, reinstall, remove, purge, upgrade, downgrade, |
181 | + # wait |
182 | + False, |
183 | + # reply and error handlers |
184 | + self._on_trans_commit_packages_ready, |
185 | + self._on_trans_simulate_error) |
186 | + except: |
187 | + LOG.exception( |
188 | + "getting commit_packages trans failed for '%s'" % pkgname) |
189 | |
190 | def get_all_deps_upgrading(self, pkg): |
191 | # note: this seems not to be used anywhere |
192 | |
193 | === modified file 'softwarecenter/db/pkginfo_impl/packagekit.py' |
194 | --- softwarecenter/db/pkginfo_impl/packagekit.py 2012-03-07 20:08:53 +0000 |
195 | +++ softwarecenter/db/pkginfo_impl/packagekit.py 2012-08-20 10:09:26 +0000 |
196 | @@ -264,20 +264,24 @@ |
197 | if (p.get_name() != pkg.name) |
198 | and p.get_info() == packagekit.InfoEnum.INSTALLED] |
199 | |
200 | - def get_total_size_on_install(self, pkgname, |
201 | + def query_total_size_on_install(self, pkgname, |
202 | addons_install=None, addons_remove=None, |
203 | archive_suite=None): |
204 | - """ Returns a tuple (download_size, installed_size) |
205 | + """ emit query-total-size-on-install-done signal |
206 | with disk size in KB calculated for pkgname installation |
207 | plus addons change. |
208 | """ |
209 | # FIXME: support archive_suite here too |
210 | |
211 | # FIXME: PackageKit reports only one size at a time |
212 | + download_size = 0 |
213 | + install_size = 0 |
214 | if self.is_installed(pkgname): |
215 | - return (0, self.get_size(pkgname)) |
216 | + install_size = self.get_size(pkgname) |
217 | else: |
218 | - return (self.get_size(pkgname), 0) |
219 | + download_size = self.get_size(pkgname) |
220 | + self.emit( |
221 | + "query-total-size-on-install-done", download_size, install_size) |
222 | |
223 | @property |
224 | def ready(self): |
225 | |
226 | === modified file 'softwarecenter/ui/gtk3/views/appdetailsview.py' |
227 | --- softwarecenter/ui/gtk3/views/appdetailsview.py 2012-08-17 08:56:26 +0000 |
228 | +++ softwarecenter/ui/gtk3/views/appdetailsview.py 2012-08-20 10:09:26 +0000 |
229 | @@ -762,8 +762,7 @@ |
230 | if addon in self.addons_to_install: |
231 | self.addons_to_install.remove(addon) |
232 | self.status_bar.configure() |
233 | - GObject.idle_add(self.view.update_totalsize, |
234 | - priority=GObject.PRIORITY_LOW) |
235 | + self.view.update_totalsize() |
236 | |
237 | def configure(self, pkgname, update_addons=True): |
238 | self.addons_to_install = [] |
239 | @@ -780,8 +779,7 @@ |
240 | self.addons_to_install = [] |
241 | self.addons_to_remove = [] |
242 | self.configure(self.view.app.pkgname) |
243 | - GObject.idle_add(self.view.update_totalsize, |
244 | - priority=GObject.PRIORITY_LOW) |
245 | + self.view.update_totalsize() |
246 | |
247 | |
248 | _asset_cache = {} |
249 | @@ -814,6 +812,8 @@ |
250 | self.distro = distro |
251 | self.icons = icons |
252 | self.cache = cache |
253 | + self.cache.connect("query-total-size-on-install-done", |
254 | + self._on_query_total_size_on_install_done) |
255 | self.backend = get_install_backend() |
256 | self.cache.connect("cache-ready", self._on_cache_ready) |
257 | self.connect("destroy", self._on_destroy) |
258 | @@ -1496,8 +1496,7 @@ |
259 | self.addons_manager.configure(app_details.pkgname) |
260 | |
261 | # Update total size label |
262 | - self.totalsize_info.set_value(_("Calculating...")) |
263 | - GObject.timeout_add(500, self.update_totalsize) |
264 | + self.update_totalsize() |
265 | |
266 | # Update addons state bar |
267 | self.addons_statusbar.configure() |
268 | @@ -1545,9 +1544,7 @@ |
269 | self.addon_view.hide() |
270 | if self.addon_view.get_parent(): |
271 | self.info_vb.remove(self.addon_view) |
272 | - self.totalsize_info.set_value(_("Calculating...")) |
273 | - GObject.idle_add(self.update_totalsize, |
274 | - priority=GObject.PRIORITY_LOW) |
275 | + self.update_totalsize() |
276 | self._update_recommendations(app_details.pkgname) |
277 | self._update_reviews(app_details) |
278 | |
279 | @@ -2004,18 +2001,25 @@ |
280 | if not self.totalsize_info.get_property('visible'): |
281 | return False |
282 | |
283 | + self.totalsize_info.set_value(_("Calculating...")) |
284 | + |
285 | while Gtk.events_pending(): |
286 | Gtk.main_iteration() |
287 | |
288 | - label_string = "" |
289 | - |
290 | - res = self.cache.get_total_size_on_install( |
291 | + self.cache.query_total_size_on_install( |
292 | self.app_details.pkgname, |
293 | self.addons_manager.addons_to_install, |
294 | self.addons_manager.addons_to_remove, |
295 | self.app.archive_suite) |
296 | - total_download_size, total_install_size = res |
297 | - if res == (0, 0) and isinstance(self.app, DebFileApplication): |
298 | + |
299 | + def _on_query_total_size_on_install_done(self, |
300 | + pkginfo, |
301 | + total_download_size, |
302 | + total_install_size): |
303 | + label_string = "" |
304 | + if (total_download_size == 0 and |
305 | + total_install_size == 0 and |
306 | + isinstance(self.app, DebFileApplication)): |
307 | total_install_size = self.app_details.installed_size |
308 | if total_download_size > 0: |
309 | download_size = GLib.format_size(total_download_size) |
310 | |
311 | === modified file 'tests/test_pkginfo.py' |
312 | --- tests/test_pkginfo.py 2012-05-30 18:39:55 +0000 |
313 | +++ tests/test_pkginfo.py 2012-08-20 10:09:26 +0000 |
314 | @@ -6,6 +6,7 @@ |
315 | from mock import patch |
316 | |
317 | from tests.utils import ( |
318 | + get_test_pkg_info, |
319 | setup_test_env, |
320 | ) |
321 | setup_test_env() |
322 | @@ -35,7 +36,36 @@ |
323 | self.cache = apt.Cache(memonly=True) |
324 | |
325 | def test_get_total_size(self): |
326 | - # get a cache |
327 | + def _on_query_total_size_on_install_done(pkginfo, download, space): |
328 | + self.need_download = download |
329 | + self.need_space = space |
330 | + loop.quit() |
331 | + TEST_PKG = "casper" |
332 | + ADDONS_TO_INSTALL = [ "lupin-casper" ] |
333 | + ADDONS_TO_REMOVE = [] |
334 | + loop = GObject.MainLoop(GObject.main_context_default()) |
335 | + cache = get_test_pkg_info() |
336 | + cache.connect( |
337 | + "query-total-size-on-install-done", |
338 | + _on_query_total_size_on_install_done) |
339 | + cache.query_total_size_on_install( |
340 | + TEST_PKG, ADDONS_TO_INSTALL, ADDONS_TO_REMOVE) |
341 | + loop.run() |
342 | + # work out the numbers that we at least need to get (this will |
343 | + # not include dependencies so it is probably lower) |
344 | + need_at_least_download = ( |
345 | + cache[TEST_PKG].candidate.size + |
346 | + sum([cache[pkg].candidate.size for pkg in ADDONS_TO_INSTALL])) |
347 | + need_at_least_installed = ( |
348 | + cache[TEST_PKG].candidate.installed_size + |
349 | + sum([cache[pkg].candidate.installed_size for pkg in ADDONS_TO_INSTALL])) |
350 | + self.assertTrue(self.need_download >= need_at_least_download) |
351 | + self.assertTrue(self.need_space >= need_at_least_installed) |
352 | + del self.need_download |
353 | + del self.need_space |
354 | + |
355 | + def test_get_total_size_with_mock(self): |
356 | + # get a cache |
357 | cache = get_pkg_info() |
358 | cache.open() |
359 | # pick first uninstalled pkg |
360 | @@ -45,11 +75,13 @@ |
361 | # prepare args |
362 | addons_to_install = addons_to_remove = [] |
363 | archive_suite = "foo" |
364 | - with patch.object(cache, "_set_candidate_release") as f_mock: |
365 | - cache.get_total_size_on_install( |
366 | + with patch.object(cache.aptd_client, "commit_packages") as f_mock: |
367 | + cache.query_total_size_on_install( |
368 | pkg.name, addons_to_install, addons_to_remove, archive_suite) |
369 | # ensure it got called with the right arguments |
370 | - f_mock.assert_called_with(pkg, archive_suite) |
371 | + args, kwargs = f_mock.call_args |
372 | + to_install = args[0] |
373 | + self.assertTrue(to_install[0].endswith("/%s" % archive_suite)) |
374 | |
375 | |
376 | if __name__ == "__main__": |
377 | |
378 | === modified file 'tests/utils.py' |
379 | --- tests/utils.py 2012-05-31 12:50:01 +0000 |
380 | +++ tests/utils.py 2012-08-20 10:09:26 +0000 |
381 | @@ -395,7 +395,7 @@ |
382 | """Return (recommended, suggested) addons for 'pkgname'.""" |
383 | return ([], []) |
384 | |
385 | - def get_total_size_on_install(self, pkgname, addons_to_install, |
386 | + def query_total_size_on_install(self, pkgname, addons_to_install, |
387 | addons_to_remove, archive_suite): |
388 | - """Return a fake (total_download_size, total_install_size) result.""" |
389 | - return (0, 0) |
390 | + """Emit a fake signal "query-total-size-on-install-done" """ |
391 | + self.emit("query-total-size-on-install-done", (0, 0)) |