Merge lp:~pitti/ubuntuone-control-panel/gi-fixes into lp:ubuntuone-control-panel

Proposed by Martin Pitt
Status: Merged
Approved by: Natalia Bidart
Approved revision: 206
Merged at revision: 204
Proposed branch: lp:~pitti/ubuntuone-control-panel/gi-fixes
Merge into: lp:ubuntuone-control-panel
Diff against target: 138 lines (+20/-16)
4 files modified
ubuntuone/controlpanel/dbus_service.py (+12/-4)
ubuntuone/controlpanel/dbustests/test_dbus_service.py (+1/-1)
ubuntuone/controlpanel/gui/gtk/gui.py (+2/-7)
ubuntuone/controlpanel/gui/gtk/tests/test_gui.py (+5/-4)
To merge this branch: bzr merge lp:~pitti/ubuntuone-control-panel/gi-fixes
Reviewer Review Type Date Requested Status
Natalia Bidart (community) Approve
Martin Pitt (community) Abstain
dobey (community) Abstain
Review via email: mp+72449@code.launchpad.net

Commit message

Make the library work with both GI and static bindings, and consistently use static bindings in the GTK UI. (LP: #829186)

Description of the change

pygobject >= 2.90 is now absolutely zero tolerant against importing both the
static and the GI version of a particular library. This was mostly the case
with 2.28 as well, but did work in some cases (like "import gobject; from
gi.repository import Gtk", in particular for "glib" and "gobject"). These now
cause errors as well.

The GTK frontend has a rather long dependency chain, and porting it to
GTK3/PyGI is going to take a while. It is too risky for Ubuntu Oneiric at this
point. (See lp:~pitti/+junk/u1-control-panel-pygi for my current progress with
this).

On the other hand, the backend already uses the Soup GNOME module, which does
not have an 1:1 equivalent with a static binding. So fix up the last remaining
bit to make this use the GI bindings consistently.

With the fix from this branch we can use the web client library from both
programs with static bindings (GTK frontend), as well as from programs which
use the gobject-introspection bindings (backend).

See bug 829186 for more details.

To post a comment you must log in.
Revision history for this message
Natalia Bidart (nataliabidart) wrote :

The branch looks great, but, instead of reimplementing the humanize function, please use the one defined in ubuntone.controlpanel.gui.

Thanks a lot for helping us!

review: Needs Fixing
Revision history for this message
Natalia Bidart (nataliabidart) wrote :

Very likely you will need to import it the humanize method, and make the callers do not use it like an instance method, but as a global function (ie remove the self.humanize calls and replace by calls to humanize directly, there is no point of having it as an instance method).

Revision history for this message
dobey (dobey) wrote :

12 +if 'gobject' in sys.modules:
13 + import gobject as GObject
14 +else:
15 + from gi.repository import GObject

I'm pretty sure this if statement will always fail, even if the GI version isn't available, as sys.modules doesn't list what is available, but rather what is actively loaded in the current python process? Or at least, looking at sys.modules in python shell without loading anything else, doesn't seem to give particularly useful results for performing this sort of check.

And it's not clear to me why the GLib.format_size_for_display() was removed. I suppose because you didn't convert it to use Gtk from gi.repository?

review: Needs Fixing
Revision history for this message
Martin Pitt (pitti) wrote :

@Rodney: The 'gobject' in sys.modules works very well, and it's indeed intended to check what is already loaded into the Python process; that's exactly what we need to know -- "has anything before us already imported gobject, or any other static binding depending on it?" (such as glib or gtk). This check has been applied to some 10 other library packages, and is also the one that upstream uses:

  http://git.gnome.org/browse/pygobject/commit/?id=65ac35cca8d24f4c1

> And it's not clear to me why the GLib.format_size_for_display() was removed.

Because we can't use GLib (it's the GI binding) when the program importing dbus_service already has loaded static bindings (gobject, glib). Unfortunately the static glib does not export this function, so we can't fallback to the static variant in this case.

I'll change the code to use humanize() instead, I wasn't aware of this.

204. By Martin Pitt

Entirely drop gui/gtk/gui.py's humanize() method, use the one from ubuntuone.controlpanel.gui instead

Revision history for this message
Martin Pitt (pitti) :
review: Needs Resubmitting
Revision history for this message
dobey (dobey) :
review: Abstain
Revision history for this message
Natalia Bidart (nataliabidart) wrote :

There is a failing test:

[ERROR]
Traceback (most recent call last):
  File "/usr/lib/python2.7/unittest/case.py", line 321, in run
    testMethod()
  File "/usr/lib/pymodules/python2.7/mocker.py", line 146, in test_method_wrapper
    result = test_method()
  File "/home/nessita/canonical/u1/controlpanel/review_gi-fixes/ubuntuone/controlpanel/dbustests/test_dbus_service.py", line 111, in test_dbus_service_main
    mainloop = self.mocker.replace(mainloop)
  File "/usr/lib/pymodules/python2.7/mocker.py", line 701, in replace
    mock = self.proxy(object, spec, type, name, count, passthrough)
  File "/usr/lib/pymodules/python2.7/mocker.py", line 658, in proxy
    object = getattr(object, attr)
exceptions.AttributeError: 'module' object has no attribute 'gobject'

ubuntuone.controlpanel.dbustests.test_dbus_service.DBusServiceMainTestCase.test_dbus_service_main

Fix for this would be:

=== modified file 'ubuntuone/controlpanel/dbustests/test_dbus_service.py'
--- ubuntuone/controlpanel/dbustests/test_dbus_service.py 2011-07-05 19:12:10 +0000
+++ ubuntuone/controlpanel/dbustests/test_dbus_service.py 2011-08-25 13:38:19 +0000
@@ -107,7 +107,7 @@
         rs()
         self.mocker.result(True)

- mainloop = "ubuntuone.controlpanel.dbus_service.gobject.MainLoop"
+ mainloop = "ubuntuone.controlpanel.dbus_service.GObject.MainLoop"
         mainloop = self.mocker.replace(mainloop)
         mainloop()
         loop = self.mocker.mock()

Please also run ./run-tests after applying the fix since there are lint issues:

ubuntuone/controlpanel/dbus_service.py:
    29: [E0611] No name 'GObject' in module 'gi.repository'
    29: [W0404] Reimport 'GObject' (imported line 25)

review: Needs Fixing
Revision history for this message
Martin Pitt (pitti) wrote :

Rodney explained what "Resubmit" really means; reverting my "Resubmit" then.

review: Abstain
205. By Martin Pitt

test_dbus_service.py: Fix mocker to use GObject, too

Revision history for this message
Martin Pitt (pitti) wrote :

I pushed the mocker and pylint fixes.

206. By Martin Pitt

quiesce false-positive pylint warnings

Revision history for this message
Natalia Bidart (nataliabidart) wrote :

Looks great!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'ubuntuone/controlpanel/dbus_service.py'
--- ubuntuone/controlpanel/dbus_service.py 2011-06-29 17:59:07 +0000
+++ ubuntuone/controlpanel/dbus_service.py 2011-08-25 16:11:27 +0000
@@ -20,9 +20,17 @@
20"""Export the control backend thru DBus."""20"""Export the control backend thru DBus."""
2121
22from functools import wraps22from functools import wraps
23import sys
2324
24import dbus.service25import dbus.service
25import gobject26# pylint: disable=E0611
27# pylint: disable=W0404
28if 'gobject' in sys.modules:
29 import gobject as GObject
30else:
31 from gi.repository import GObject
32# pylint: enable=W0404
33# pylint: enable=E0611
2634
27from dbus.mainloop.glib import DBusGMainLoop35from dbus.mainloop.glib import DBusGMainLoop
28from dbus.service import method, signal36from dbus.service import method, signal
@@ -608,9 +616,9 @@
608616
609617
610def run_mainloop(loop=None):618def run_mainloop(loop=None):
611 """Run the gobject main loop."""619 """Run the GObject main loop."""
612 if loop is None:620 if loop is None:
613 loop = gobject.MainLoop()621 loop = GObject.MainLoop()
614 loop.run()622 loop.run()
615623
616624
@@ -643,7 +651,7 @@
643 """Hook the DBus listeners and start the main loop."""651 """Hook the DBus listeners and start the main loop."""
644 init_mainloop()652 init_mainloop()
645 if register_service():653 if register_service():
646 loop = gobject.MainLoop()654 loop = GObject.MainLoop()
647 publish_backend(shutdown_func=loop.quit)655 publish_backend(shutdown_func=loop.quit)
648 run_mainloop(loop=loop)656 run_mainloop(loop=loop)
649 else:657 else:
650658
=== modified file 'ubuntuone/controlpanel/dbustests/test_dbus_service.py'
--- ubuntuone/controlpanel/dbustests/test_dbus_service.py 2011-07-05 19:12:10 +0000
+++ ubuntuone/controlpanel/dbustests/test_dbus_service.py 2011-08-25 16:11:27 +0000
@@ -107,7 +107,7 @@
107 rs()107 rs()
108 self.mocker.result(True)108 self.mocker.result(True)
109109
110 mainloop = "ubuntuone.controlpanel.dbus_service.gobject.MainLoop"110 mainloop = "ubuntuone.controlpanel.dbus_service.GObject.MainLoop"
111 mainloop = self.mocker.replace(mainloop)111 mainloop = self.mocker.replace(mainloop)
112 mainloop()112 mainloop()
113 loop = self.mocker.mock()113 loop = self.mocker.mock()
114114
=== modified file 'ubuntuone/controlpanel/gui/gtk/gui.py'
--- ubuntuone/controlpanel/gui/gtk/gui.py 2011-08-18 16:30:27 +0000
+++ ubuntuone/controlpanel/gui/gtk/gui.py 2011-08-25 16:11:27 +0000
@@ -32,7 +32,6 @@
32from dbus.mainloop.glib import DBusGMainLoop32from dbus.mainloop.glib import DBusGMainLoop
33from ubuntu_sso import networkstate33from ubuntu_sso import networkstate
34# pylint: disable=E0611,F040134# pylint: disable=E0611,F0401
35from gi.repository import GLib
36from ubuntuone.platform.credentials import (35from ubuntuone.platform.credentials import (
37 APP_NAME as U1_APP_NAME,36 APP_NAME as U1_APP_NAME,
38 CredentialsManagementTool,37 CredentialsManagementTool,
@@ -171,10 +170,6 @@
171170
172 logger.debug('%s: started.', self.__class__.__name__)171 logger.debug('%s: started.', self.__class__.__name__)
173172
174 def humanize(self, int_bytes):
175 """Return a human readble string of 'int_bytes'."""
176 return GLib.format_size_for_display(int_bytes)
177
178 def _set_warning(self, message, label):173 def _set_warning(self, message, label):
179 """Set 'message' as warning in 'label'."""174 """Set 'message' as warning in 'label'."""
180 label.set_markup(WARNING_MARKUP % message)175 label.set_markup(WARNING_MARKUP % message)
@@ -496,7 +491,7 @@
496 scroll_to_cell = True491 scroll_to_cell = True
497 else:492 else:
498 free_bytes_str = self.FREE_SPACE493 free_bytes_str = self.FREE_SPACE
499 free_bytes_args = {'free_space': self.humanize(free_bytes)}494 free_bytes_args = {'free_space': humanize(free_bytes)}
500 free_bytes = free_bytes_str % free_bytes_args495 free_bytes = free_bytes_str % free_bytes_args
501496
502 row = (self.ROW_HEADER % (name, free_bytes),497 row = (self.ROW_HEADER % (name, free_bytes),
@@ -1518,7 +1513,7 @@
1518 """Backend notifies of account info."""1513 """Backend notifies of account info."""
1519 used = int(info['quota_used'])1514 used = int(info['quota_used'])
1520 total = int(info['quota_total'])1515 total = int(info['quota_total'])
1521 data = {'used': self.humanize(used), 'total': self.humanize(total),1516 data = {'used': humanize(used), 'total': humanize(total),
1522 'percentage': (used / total) * 100}1517 'percentage': (used / total) * 100}
1523 self._update_quota(QUOTA_LABEL % data, data)1518 self._update_quota(QUOTA_LABEL % data, data)
15241519
15251520
=== modified file 'ubuntuone/controlpanel/gui/gtk/tests/test_gui.py'
--- ubuntuone/controlpanel/gui/gtk/tests/test_gui.py 2011-08-18 15:50:38 +0000
+++ ubuntuone/controlpanel/gui/gtk/tests/test_gui.py 2011-08-25 16:11:27 +0000
@@ -42,6 +42,7 @@
42)42)
43from ubuntuone.controlpanel.gui.gtk.tests.test_package_manager import (43from ubuntuone.controlpanel.gui.gtk.tests.test_package_manager import (
44 SUCCESS, FAILURE)44 SUCCESS, FAILURE)
45from ubuntuone.controlpanel.gui import humanize
4546
4647
47# Attribute 'yyy' defined outside __init__, access to a protected member48# Attribute 'yyy' defined outside __init__, access to a protected member
@@ -196,7 +197,7 @@
196 treeiter = self.ui.volumes_store.get_iter_root()197 treeiter = self.ui.volumes_store.get_iter_root()
197 for name, free_bytes, volumes in FAKE_VOLUMES_INFO:198 for name, free_bytes, volumes in FAKE_VOLUMES_INFO:
198 name = "%s's" % name if name else gui.MY_FOLDERS199 name = "%s's" % name if name else gui.MY_FOLDERS
199 free_bytes = self.ui.humanize(int(free_bytes))200 free_bytes = humanize(int(free_bytes))
200 header = (name, self.ui.FREE_SPACE % {'free_space': free_bytes})201 header = (name, self.ui.FREE_SPACE % {'free_space': free_bytes})
201202
202 # check parent row203 # check parent row
@@ -277,7 +278,7 @@
277 treeiter = self.ui.volumes_store.get_iter_root()278 treeiter = self.ui.volumes_store.get_iter_root()
278 for name, free_bytes, volumes in FAKE_VOLUMES_NO_FREE_SPACE_INFO:279 for name, free_bytes, volumes in FAKE_VOLUMES_NO_FREE_SPACE_INFO:
279 name = "%s's" % name if name else gui.MY_FOLDERS280 name = "%s's" % name if name else gui.MY_FOLDERS
280 free_bytes = self.ui.humanize(int(free_bytes))281 free_bytes = humanize(int(free_bytes))
281 free_bytes = self.ui.NO_FREE_SPACE % {'free_space': free_bytes}282 free_bytes = self.ui.NO_FREE_SPACE % {'free_space': free_bytes}
282283
283 # check parent row284 # check parent row
@@ -1912,8 +1913,8 @@
1912 used = int(info['quota_used'])1913 used = int(info['quota_used'])
1913 total = int(info['quota_total'])1914 total = int(info['quota_total'])
1914 percentage = round((used / total) * 100, 2)1915 percentage = round((used / total) * 100, 2)
1915 expected = {'used': self.ui.humanize(used),1916 expected = {'used': humanize(used),
1916 'total': self.ui.humanize(total),1917 'total': humanize(total),
1917 'percentage': percentage}1918 'percentage': percentage}
1918 msg = gui.QUOTA_LABEL % expected1919 msg = gui.QUOTA_LABEL % expected
1919 self.assertEqual(self.ui.quota_label.get_text(), msg)1920 self.assertEqual(self.ui.quota_label.get_text(), msg)

Subscribers

People subscribed via source and target branches