After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 315079 - super(PyGtkWidget, self).do_size_request(req) doesn't work
super(PyGtkWidget, self).do_size_request(req) doesn't work
Status: RESOLVED OBSOLETE
Product: pygobject
Classification: Bindings
Component: gobject
2.9.0
Other All
: Normal normal
: ---
Assigned To: John Ehresman
Python bindings maintainers
Depends on:
Blocks:
 
 
Reported: 2005-09-01 23:13 UTC by John Ehresman
Modified: 2012-04-21 14:14 UTC
See Also:
GNOME target: ---
GNOME version: ---



Description John Ehresman 2005-09-01 23:13:18 UTC
The use of super to chain up to the method in a superclass results in a not
enough args error.  This may be a result of the do_ C function being identified
as a class method, though I'm not sure.
Comment 1 Gustavo Carneiro 2005-10-29 13:43:40 UTC
I think super will not work because we override do_size_request as a normal
method, but the parent class has do_size_request as classmethod.

super has a special behaviour when it sees a classmethod, but in this case it
sees a regular method, and doesn't realize the same method in the parent class
is a classmethod.

In retrospect, maybe this was a design error, but at the time I couldn't find
any better alternative at the time, nor one was suggested to me.
Comment 2 John Ehresman 2005-11-07 18:22:05 UTC
Why is do_size_request defined as a classmethod in the parent class?  There's
probably a good reason for it, but I don't know what it is.
Comment 3 Gustavo Carneiro 2005-11-07 19:28:56 UTC
It needs the class as first argument.  Seemed natural...
Comment 4 Gustavo Carneiro 2005-12-18 12:35:07 UTC
Anyway, good or bad this can't be changed now..
Comment 5 Johan (not receiving bugmail) Dahlin 2005-12-19 04:25:13 UTC
Why can't it be changed? 
I consider super() support important. I'm not convinced it cannot be changed in an API-compatible way.
Comment 6 John Ehresman 2005-12-19 15:44:46 UTC
I think we said on irc that the current behavior (that the methods are class methods) is a bug and could be fixed in the future.  The argument is that since it was never documented, the fact that these were class methods wasn't part of the api.
Comment 7 Gustavo Carneiro 2005-12-19 16:01:56 UTC
It's not true that since it's not documented it is not part of the API.

If this can be fixed, i'm open to suggestions, because i don't know how.  Even if it is not perfect, it is much better than what we had before: nothing.
Comment 8 John Ehresman 2005-12-19 16:07:40 UTC
Why can't it be a normal method?  You'd get the class from the instance passed in and find the method in the superclass through super() or a similiar algorithm.  I consider the super() failure caused by the classmethod to be the bug, not the whole mechanism.
Comment 9 Gustavo Carneiro 2005-12-19 18:13:43 UTC
Hm.. I hadn't thought of that, it's too obvious :P
I guess that changing to regular method would work, even in an API compatible way.
Comment 10 Gustavo Carneiro 2006-01-14 17:02:39 UTC
I'd like to fix this, but codegen stayed in pygtk, which did not branch... maybe next time.
Comment 11 Gustavo Carneiro 2006-04-01 18:48:47 UTC
(In reply to comment #8)
> Why can't it be a normal method?  You'd get the class from the instance passed
> in and find the method in the superclass through super() or a similiar
> algorithm.

I've thought about this again and it doesn't work as regular method.  The problem is that currently you have something like this (mixed python/C):

class GtkEntry:
  @classmethod
  def do_insert_text(cls, self, text)
      GtkEntryClass *klass = g_class_ref(pyg_type_from_object(cls))
      klass->insert_text(text)

class MyEntry(GtkEntry):
  def do_insert_text(self, text):
      GtkEntry.do_insert_text(self, text)

To use regular method instead of classmethod we would have:

class GtkEntry:
  def do_insert_text(self, text)
      GtkEntryClass *klass = g_class_ref(pyg_type_from_object(self))
      klass->insert_text(text)

class MyEntry(GtkEntry):
  def do_insert_text(self, text):
      GtkEntry.do_insert_text(self, text) # or super...

In the second case this happens:
  1- MyEntry.do_insert_text calls GtkEntry.do_insert_text
  2- GtkEntry.do_insert_text does GtkEntryClass *klass = g_class_ref(pyg_type_from_object(self)), (which is the class of MyEntry!)
  3- GtkEntry.do_insert_text then calls klass->insert_text(text)
  4- klass->insert_text points to MyEntry.do_insert_text, so goto 1 -> (infinite recursion)

So, unless there are any better ideas (I'm always receptive to them), I really should close this NOTGNOME.  IMHO Python's super is buggy and the bug should be fixed in Python.
Comment 12 John Ehresman 2006-05-01 04:58:56 UTC
The trick is to bind the type into the method, using something like:

class Widget(GObject): # in C
  def do_size_request(self, req):
    klass = g_class_ref(GTK_WIDGET_TYPE)
    klass->do_size_request(self, req)

class Entry(Widget): # in C
  def do_size_request(self, req):
    klass = g_class_ref(GTK_ENTRY_TYPE)
    klass->do_size_request(self, req)

class PyEntry(Entry):
  def do_size_request(self, req):
    super(PyEntry, self)->do_size_request(req)

Either generate C code for each type, or use meta classes to do something like the following -- this is handwaving and may be a crazy idea

class Widget(GObject): # in C
  def do_size_request(self, req, **kw):
    klass = kw.get(' klass')
    if klass == None:
      klass = g_class_ref(GTK_WIDGET_TYPE)
    klass->do_size_request(self, req)

class GObjectMeta(type):
    "Metaclass for automatically registering GObject classes"
    def __init__(cls, name, bases, dict_):
        _add_do_virtuals(cls, bases) # or maybe it's a method on the meta-class
        type.__init__(cls, name, bases, dict_)
        cls._type_register(cls.__dict__)

def _add_do_virtuals(cls, bases):
    for b in bases:
        for name in _get_virtual_names(b):
            def func(self, *args, **kw):
                base_func = getattr('do_' + name)
                kw_copy = dict(kw)
                kw_copy[' klass'] = cls
                base_func(self, *args, **kw_copy)
            method = new.instancemethod(func, None, cls)
            setattr(cls, 'do_' + name, method)

Comment 13 Gustavo Carneiro 2006-05-01 12:55:32 UTC
(In reply to comment #12)
> The trick is to bind the type into the method, using something like:
> 
> class Widget(GObject): # in C
>   def do_size_request(self, req):
>     klass = g_class_ref(GTK_WIDGET_TYPE)
>     klass->do_size_request(self, req)
> 
> class Entry(Widget): # in C
>   def do_size_request(self, req):
>     klass = g_class_ref(GTK_ENTRY_TYPE)
>     klass->do_size_request(self, req)
> 
> class PyEntry(Entry):
>   def do_size_request(self, req):
>     super(PyEntry, self)->do_size_request(req)
> 
> Either generate C code for each type,

  I tried that already and it generated way too much code. gtk.c is already very big (over 96 klines).

> or use meta classes to do something like
> the following -- this is handwaving and may be a crazy idea
> 
> class Widget(GObject): # in C
>   def do_size_request(self, req, **kw):
>     klass = kw.get(' klass')
>     if klass == None:
>       klass = g_class_ref(GTK_WIDGET_TYPE)
>     klass->do_size_request(self, req)
> 
> class GObjectMeta(type):
>     "Metaclass for automatically registering GObject classes"
>     def __init__(cls, name, bases, dict_):
>         _add_do_virtuals(cls, bases) # or maybe it's a method on the meta-class
>         type.__init__(cls, name, bases, dict_)
>         cls._type_register(cls.__dict__)
> 
> def _add_do_virtuals(cls, bases):
>     for b in bases:
>         for name in _get_virtual_names(b):
>             def func(self, *args, **kw):
>                 base_func = getattr('do_' + name)
>                 kw_copy = dict(kw)
>                 kw_copy[' klass'] = cls
>                 base_func(self, *args, **kw_copy)
>             method = new.instancemethod(func, None, cls)
>             setattr(cls, 'do_' + name, method)

  Needless waste of memory, IMHO.
Comment 14 John Ehresman 2006-05-01 14:14:55 UTC
This is important api that needs to be implemented -- and I'll probably go ahead and do so.  The size of gtk.c isn't really an issue if most of it is generated.  The runtime performance and memory footprint may be an issue, but that's probably better solved by delaying creation of classes or other things.
Comment 15 Gustavo Carneiro 2006-05-01 14:58:19 UTC
I don't agree this is really 'important'.  Calling base class instead of super is not that big a difference, you know... and some people even think super harmful, see http://fuhm.net/super-harmful/
Comment 16 Johan (not receiving bugmail) Dahlin 2006-05-01 15:04:49 UTC
This is really important. Otherwise I wouldn't have opened the bug in the first place. super was added to the language for a reason, it has important usecases, and we should allow it to be used for virtual methods.
Comment 17 Johan (not receiving bugmail) Dahlin 2006-08-16 00:52:06 UTC
*** Bug 351566 has been marked as a duplicate of this bug. ***
Comment 18 Joe Wreschnig 2006-08-16 03:21:08 UTC
FWIW, this is not quite the same bug as #351566. That can be fixed "simply" by making GObjects call super (or rather, the C equivalent) in their __init__. It's an MRO issue, not a method signature one, and should be fixable without the kind of significant API hackage this bug seems like it requires.

You can see this by making a diamond inheritance hierarchy with GObject "on top"; super() will correctly walk through the MRO, reaching all the subclasses, and ending at GObject. However, if your MRO has GObject followed by some non-GObjects, it stops after GObject, as shown in the sample code in #351566.

That being said, I certainly don't mind both being fixed at the same time. :)
Comment 19 Sebastian Pölsterl 2012-04-21 14:14:11 UTC
Thanks for taking the time to report this bug. However, this bug report applies to a PyGObject version that is too old and not supported anymore.
PyGObject developers are no longer working on static bindings, so unfortunately there will not be any bug fixes for the version this report applies to.

By upgrading to a newer version of PyGObject you could receive bug fixes and new functionality. Please feel free to reopen this bug if the problem still occurs with a newer version of PyGObject.