Bug 48904

Summary: switching from pygobject2 to pygi in 1.0 was an incompatible change
Product: dbus Reporter: Alan <alanjas>
Component: pythonAssignee: Simon McVittie <smcv>
Status: RESOLVED FIXED QA Contact: John (J5) Palmieri <johnp>
Severity: normal    
Priority: medium CC: barry, sklist
Version: 1.0.x   
Hardware: x86 (IA32)   
OS: Linux (All)   
Whiteboard:
i915 platform: i915 features:
Attachments: Log of the error

Description Alan 2012-04-18 19:02:38 UTC
Created attachment 60290 [details]
Log of the error

When I use sugar-emulator, activities that uses Dbus give errors like this:


Traceback (most recent call last):
  File "/usr/bin/sugar-activity", line 21, in <module>
    main.main()
  File "/usr/lib/python2.7/dist-packages/sugar/activity/main.py", line 121, in main
    module = __import__(module_name)
  File "/home/alan/Activities/TurtleBots.activity/TurtleArtActivity.py", line 68, in <module>
    from TurtleArt.tacollaboration import Collaboration
  File "/home/alan/Activities/TurtleBots.activity/TurtleArt/tacollaboration.py", line 23, in <module>
    from dbus.gobject_service import ExportedGObject
  File "/usr/lib/python2.7/dist-packages/dbus/gobject_service.py", line 27, in <module>
    from gi.repository import GObject as gobject
  File "/usr/lib/python2.7/dist-packages/gi/__init__.py", line 23, in <module>
    from ._gi import _API, Repository
ImportError: could not import gobject (error was: ImportError('When using gi.repository you must not import static modules like "gobject". Please change all occurrences of "import gobject" to "from gi.repository import GObject".',))
Comment 1 John (J5) Palmieri 2012-04-18 20:47:47 UTC
dbus-python and PyGObject introspection are not compatible with each other.  It looks like TurtleActivity needs to be ported.  PyGObject Introspection has its own dbus interface.  This bug should be filed on Sugar.  It is not PyGObject or dbus-python bug.
Comment 2 Simon McVittie 2012-04-19 02:20:53 UTC
(In reply to comment #1)
> dbus-python and PyGObject introspection are not compatible with each other.

Actually... Barry Warsaw ported dbus.gobject_service from gobject (PyGObject 2) to gi.repository.GObject (PyGObject 3) so that it could support Python 3.

I hadn't realised this would be an incompatible change for projects using gobject...

Barry, any ideas?
Comment 3 John (J5) Palmieri 2012-04-19 11:45:00 UTC
It is because it is two different python type systems clashing with each other over the same namespace.  So you are never sure if you are getting the static gobject or the dynamicly generated gobject for a given class name.
Comment 4 Alan 2012-04-19 11:49:53 UTC
(In reply to comment #3)
> It is because it is two different python type systems clashing with each other
> over the same namespace.  So you are never sure if you are getting the static
> gobject or the dynamicly generated gobject for a given class name.

The problem is the alias? (... as gobject)
Comment 5 John (J5) Palmieri 2012-04-19 11:51:55 UTC
You will need a ported version of Sugar.Activity.  I thought that was already ported and running in parallel.  As long as the GObject and gobject stuff isn't running in the same python interpreter they can happily run side by side in different apps.
Comment 6 John (J5) Palmieri 2012-04-19 11:56:05 UTC
Also to not surprise other apps using the dbus.gobject_service I would suggest changing the name so that you can keep the older apps working while you port everything over.
Comment 7 Simon McVittie 2012-04-20 02:26:42 UTC
(In reply to comment #6)
> Also to not surprise other apps using the dbus.gobject_service I would suggest
> changing the name so that you can keep the older apps working while you port
> everything over.

It's a bit late for that; there has been a release (1.0, back in January) in which d.g_s uses PyGI, so the only options seem to be:

* revert it (breaking any Python 2 PyGI apps that use d.g_s in dbus-python
  1.0), add a d.pygobject3_service which uses PyGI, and make d.g_s import
  that instead if run under Python 3

* declare that yes, it has changed incompatibly, and apps have to deal
  with it

Opinions?

I'd really like to hear Barry's thoughts on this, since he made this change in the first place.
Comment 8 Simon McVittie 2012-04-20 02:30:10 UTC
(In reply to comment #4)
> The problem is the alias? (... as gobject)

No, the problem is that if any module in an application imports gobject (PyGObject 2), that application must not also import gi.repository.GObject (PyGObject 3), and vice versa.

In dbus-python 1.0, dbus.gobject_service changed from using gobject to using gi.repository.GObject. I didn't realise at the time that this was an incompatible change.
Comment 9 John (J5) Palmieri 2012-04-20 08:21:09 UTC
The reason we changed was so that pygobject2 could be parallel installed with pygobject3 (for PyGTK+ support) and we could fix a slew of ref counting bugs (and others) that couldn't be fixed without breaking ABI (and breaking PyGTK+).
Comment 10 John (J5) Palmieri 2012-04-20 08:26:03 UTC
I suggest you just port them all. If they don't rely on PyGTK+ then all you have to do is search and replace all 'import gobject' with 'from gi.repository import GObject as gobject' (though we frown on aliasing to the lower case gobject).  If the app uses PyGTK+ you will have to either port it to PyGObject or remove any libs that import gi.repository.GObject
Comment 11 Barry Warsaw 2012-04-20 08:45:27 UTC
On Apr 20, 2012, at 09:30 AM, bugzilla-daemon@freedesktop.org wrote:

>https://bugs.freedesktop.org/show_bug.cgi?id=48904
>
>--- Comment #8 from Simon McVittie <simon.mcvittie@collabora.co.uk> 2012-04-20 02:30:10 PDT ---
>(In reply to comment #4)
>> The problem is the alias? (... as gobject)
>
>No, the problem is that if any module in an application imports gobject
>(PyGObject 2), that application must not also import gi.repository.GObject
>(PyGObject 3), and vice versa.
>
>In dbus-python 1.0, dbus.gobject_service changed from using gobject to using
>gi.repository.GObject. I didn't realise at the time that this was an
>incompatible change.

Yikes, this is an unfortunate backward incompatibility, sorry for that.

There are a couple of ways I see to resolve the problem.

1) Punt and require applications using dbus-python 1.0 to port to pygi.  This
   is the cleanest solution from dbus-python's perspective, and you can almost
   justify this since the change occurred in a major version bump, and from a
   logically "pre-release" version (i.e. 0 in the first digit) to a 1.0
   version.  OTOH, dbus-python 0.X has been effectively stable for ages, and
   it's probably not so nice to all the existing code out there. ;)

2) In dbus/gobject_service.py, check the Python version (via sys.hexversion)
   and in Python 2, import the real gobject instead of gi.repository.GObject
   (the alias is not the problem here).  I haven't tested it, but I think the
   rest of the code in the module should work fine.  In Python 3, you'd still
   import gi.repository.GObject as gobject.  Pro: only the first import line
   needs to be changed.  Con: I generally don't like version checks, but more
   importantly, this would prevent Python 2 applications from using pygi, and
   I think we should encourage everyone to port to pygi for both Python 2 and
   Python 3.

3) As Simon suggested, revert gobject_service.py and add a new module that gi
   applications would import instead.  I'd probably call it gi_service.py
   instead of pyobject3_service, since it better implies that both Python 2
   and Python 3 apps using pygi can benefit from this.

4) Similar to #3, leave gobject_service.py alone and reinstate the old version
   of the module as gobject2_service.py.  This would still require existing
   applications to change their code to use the old service, but wouldn't
   penalize new code which has already adapted to dbus-python 1.0.

Other thoughts I have just keep getting uglier so I'll keep them to myself. ;)

My preference in order would be #1, #4, #3, #2.
Comment 12 Simon McVittie 2012-04-20 10:55:37 UTC
This possible hack occurs to me:

if 'gobject' in sys.modules:
    import gobject
else:
    from gi.repository import GObject as gobject

or maybe a more conservative version:

if 'gi.repository.GObject' in sys.modules:
    from gi.repository import GObject as gobject
elif 'gobject' in sys.modules:
    import gobject
else:
    raise ImportError('must import either gi.repository.GObject or gobject before importing dbus.gobject_service')

Affected packages in Debian unstable (determined by grepping for gobject_service in packages that depend on python-dbus, so not necessarily 100% accurate):

dragbox
gst-qa-system
kupfer
sugar-browse-activity-0.84
sugar-browse-activity-0.86
sugar-presence-service-0.84
sugar-presence-service-0.86
sugar-presence-service-0.88
sugar-presence-service-0.90

(No, I don't know why we have four separate versions of the Presence Service. I'm off to file some bugs...)
Comment 13 Barry Warsaw 2012-04-20 11:17:50 UTC
On Apr 20, 2012, at 05:55 PM, bugzilla-daemon@freedesktop.org wrote:

>This possible hack occurs to me:
>
>if 'gobject' in sys.modules:
>    import gobject
>else:
>    from gi.repository import GObject as gobject
>
>or maybe a more conservative version:
>
>if 'gi.repository.GObject' in sys.modules:
>    from gi.repository import GObject as gobject
>elif 'gobject' in sys.modules:
>    import gobject
>else:
>    raise ImportError('must import either gi.repository.GObject or gobject
>before importing dbus.gobject_service')

It's a hack, but maybe not an unreasonable one.  If you go with the second
approach, why not just check for 'gi' in sys.modules?

The hack can probably be subverted, though I can't think of how that could be
done in a naive way (i.e. you'd probably have to deliberately do something
weird).

>Affected packages in Debian unstable (determined by grepping for
>gobject_service in packages that depend on python-dbus, so not necessarily
>100% accurate):
>
>dragbox
>gst-qa-system
>kupfer
>sugar-browse-activity-0.84
>sugar-browse-activity-0.86
>sugar-presence-service-0.84
>sugar-presence-service-0.86
>sugar-presence-service-0.88
>sugar-presence-service-0.90
>
>(No, I don't know why we have four separate versions of the Presence Service.
>I'm off to file some bugs...)

So, 5 packages then.  Of course that doesn't count anything that's not in
Debian, but it's probably a pretty good indication that you could get away
with #1 (tell them to port to pygi).
Comment 14 Barry Warsaw 2012-04-20 12:38:32 UTC
On Apr 20, 2012, at 02:14 PM, Barry Warsaw wrote:

>The hack can probably be subverted, though I can't think of how that could be
>done in a naive way (i.e. you'd probably have to deliberately do something
>weird).

Heh, one easy way is to import dbus.gobject_service before you import gi or
gobject.
Comment 15 John (J5) Palmieri 2012-04-23 08:56:30 UTC
That hack is generally frowned upon because the error is there to say you have explicitly done something that needs fixing.  Why not have the new code simply call a function at the top like sugar.enable_gi(True) or the old code - sugar.enable_gobject_deprecated(True).

In fact I like the the deprecated call as it allows authors to see that they are broken and either need to fix their app or acknowledge that they have seen the error and understand what they are doing may not work in future releases.
Comment 16 John (J5) Palmieri 2012-04-23 08:58:10 UTC
Oh and deprecated should print out a nasty warning stating that this is only a temporary fix.

Also with that code you could write a script that outputs a table of apps that have yet to be ported.
Comment 17 Simon McVittie 2012-04-26 03:29:25 UTC
(In reply to comment #14)
> Heh, one easy way is to import dbus.gobject_service before you import gi or
> gobject.

Right, that's a point in favour of the slightly more conservative version (require importing one of the others first).

(In reply to comment #15)
> That hack is generally frowned upon because the error is there to say you have
> explicitly done something that needs fixing.  Why not have the new code simply
> call a function at the top like sugar.enable_gi(True) or the old code -
> sugar.enable_gobject_deprecated(True).

Requiring enable_gi means code that worked correctly with dbus-python 1.0 no longer works (now it has to call that function).

Requiring enable_gobject_deprecated means code that worked correctly with dbus-python < 1.0 no longer works (now it has to call that function).

We lose either way; if we're going to break existing code, we might as well either revert the change or keep it, rather than adding complexity.
Comment 18 Barry Warsaw 2012-04-26 06:53:18 UTC
On Apr 26, 2012, at 10:29 AM, bugzilla-daemon@freedesktop.org wrote:

>https://bugs.freedesktop.org/show_bug.cgi?id=48904
>
>--- Comment #17 from Simon McVittie <simon.mcvittie@collabora.co.uk> 2012-04-26 03:29:25 PDT ---
>(In reply to comment #14)
>> Heh, one easy way is to import dbus.gobject_service before you import gi or
>> gobject.
>
>Right, that's a point in favour of the slightly more conservative version
>(require importing one of the others first).
>
>(In reply to comment #15)
>> That hack is generally frowned upon because the error is there to say you have
>> explicitly done something that needs fixing.  Why not have the new code simply
>> call a function at the top like sugar.enable_gi(True) or the old code -
>> sugar.enable_gobject_deprecated(True).
>
>Requiring enable_gi means code that worked correctly with dbus-python 1.0 no
>longer works (now it has to call that function).
>
>Requiring enable_gobject_deprecated means code that worked correctly with
>dbus-python < 1.0 no longer works (now it has to call that function).
>
>We lose either way; if we're going to break existing code, we might as well
>either revert the change or keep it, rather than adding complexity.

Ultimately, it's your call.  My preference would be to favor the gi+dbus1.0
code.  The hack seems reasonable though to help much of the existing
gobject+dbus<1.0 to get over the porting hump.
Comment 19 Simon McVittie 2012-05-02 03:14:33 UTC
Here is a proposal. I've pushed an implementation to freedesktop.org git and will release it if there are no objections.

In Python 2, we continue to provide dbus.gobject_service. If you have already imported 'gi' it uses gi.repository.GObject (matching 1.0); otherwise, it uses gobject (matching 0.84). Either way, there is a deprecation warning.

In Python 3, dbus.gobject_service just doesn't exist (a compatibility break between 1.0 and 1.1, but an acceptable one, IMO).

In both Python 2 and 3, we provide dbus.gi_service which is like Barry's version of gobject_service in 1.0, is not deprecated, and uses gi.repository.GObject.

----

From a quick look at the source code of the affected Debian packages (in Debian and, in most cases, upstream), none of them expect to use g-i, so using 1.0 breaks them all immediately, and mostly reverting to 0.84's behaviour seems wisest.
Comment 20 Barry Warsaw 2012-05-02 07:05:07 UTC
On May 02, 2012, at 10:14 AM, bugzilla-daemon@freedesktop.org wrote:

>Here is a proposal. I've pushed an implementation to freedesktop.org git and
>will release it if there are no objections.
>
>In Python 2, we continue to provide dbus.gobject_service. If you have already
>imported 'gi' it uses gi.repository.GObject (matching 1.0); otherwise, it uses
>gobject (matching 0.84). Either way, there is a deprecation warning.
>
>In Python 3, dbus.gobject_service just doesn't exist (a compatibility break
>between 1.0 and 1.1, but an acceptable one, IMO).
>
>In both Python 2 and 3, we provide dbus.gi_service which is like Barry's
>version of gobject_service in 1.0, is not deprecated, and uses
>gi.repository.GObject.
>
>----
>
>From a quick look at the source code of the affected Debian packages (in
>Debian and, in most cases, upstream), none of them expect to use g-i, so
>using 1.0 breaks them all immediately, and mostly reverting to 0.84's
>behaviour seems wisest.

+1

We'll just have to deal with any breakages from 1.0 to 1.1 as they come.
Comment 21 Scott Kitterman 2012-05-02 07:47:01 UTC
A couple of thoughts from reading over the bug...

From comment 9:

"The reason we changed was so ... we could fix a slew of ref counting bugs (and others) that couldn't be fixed without breaking ABI (and breaking PyGTK+)"

From comment 10:

"If they don't rely on PyGTK+ then all you have to do is search and replace all 'import gobject' with 'from gi.repository import GObject as gobject'"

Did anyone check if the handful of applications this affects rely on PyGTK+?  If they don't then it seems like keeping the current incompatible change provides a technically better solution and it's better to do the trivial porting that's needed (I'd be glad to help with that in Debian).
Comment 22 Simon McVittie 2012-05-03 09:22:57 UTC
(In reply to comment #21)
> Did anyone check if the handful of applications this affects rely on PyGTK+? 

Dragbox, Kupfer and Browse do.

(In reply to comment #21)
> If they don't then it seems like keeping the current incompatible change
> provides a technically better solution and it's better to do the trivial
> porting that's needed (I'd be glad to help with that in Debian).

dbus.gobject_service is going to be marked as deprecated in 1.1; when I've released that, you're welcome to port stuff to dbus.gi_service (same API, but not deprecated, and with gi.repository.GObject instead of gobject).
Comment 23 Scott Kitterman 2012-05-03 09:34:10 UTC
OK.  Thanks.  I guess that takes the easy and move forward option off the table.
Comment 24 Simon McVittie 2012-07-04 10:26:02 UTC
This was fixed in 1.1.0.

Use of freedesktop.org services, including Bugzilla, is subject to our Code of Conduct. How we collect and use information is described in our Privacy Policy.