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 96658 - All methods should check if self is initialized
All methods should check if self is initialized
Status: RESOLVED OBSOLETE
Product: pygobject
Classification: Bindings
Component: codegen
Git master
Other Linux
: Normal enhancement
: ---
Assigned To: Python bindings maintainers
Python bindings maintainers
: 338760 554064 (view as bug list)
Depends on:
Blocks:
 
 
Reported: 2002-10-24 00:01 UTC by Drew Perttula
Modified: 2012-02-10 08:15 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
test code (1.65 KB, text/plain)
2002-10-24 00:03 UTC, Drew Perttula
  Details
Preliminary patch to use __new__ instead of __init__ (97.97 KB, patch)
2004-07-19 17:04 UTC, John Ehresman
rejected Details | Review
pygobject API to check if object is initialized (2.66 KB, patch)
2006-04-17 13:28 UTC, Gustavo Carneiro
none Details | Review
make pygtk codegen call pygobject_check_init for methods and getters (902 bytes, patch)
2006-04-17 13:30 UTC, Gustavo Carneiro
none Details | Review

Description Drew Perttula 2002-10-24 00:01:41 UTC
Everything's fine when I add this subclass to a canvas:

class QuickEntry(gtk.Entry):
  pass

Adding just this much leads to "invalid cast from (NULL) pointer to
`GtkEntry'" (and some other warnings):

class QuickEntry(gtk.Entry):
    def __init__(self,*a,**kw):
        gtk.Entry(*a,**kw)

The widget appears to work otherwise. Connecting a signal to the
new widget leads to a segfault.
Comment 1 Drew Perttula 2002-10-24 00:03:38 UTC
Created attachment 11796 [details]
test code
Comment 2 Drew Perttula 2002-10-24 00:06:22 UTC
With all three comment lines enabled in the test case, here's a dump
of the seg fault I get (on the first connect() line to execute):

  • #0 ??
  • #1 PyCFunction_Call
    at Objects/methodobject.c line 80
  • #2 eval_frame
    at Python/ceval.c line 1993
  • #3 PyEval_EvalCodeEx
    at Python/ceval.c line 2574
  • #4 fast_function
    at Python/ceval.c line 3150
  • #5 eval_frame
    at Python/ceval.c line 2013
  • #6 PyEval_EvalCodeEx
    at Python/ceval.c line 2574
  • #7 function_call
    at Objects/funcobject.c line 374
  • #8 PyObject_Call
    at Objects/abstract.c line 1665
  • #9 instancemethod_call
    at Objects/classobject.c line 2276
  • #10 PyObject_Call
    at Objects/abstract.c line 1665
  • #11 slot_tp_init
    at Objects/typeobject.c line 3344
  • #12 type_call
    at Objects/typeobject.c line 158
  • #13 PyObject_Call
    at Objects/abstract.c line 1665
  • #14 do_call
    at Python/ceval.c line 3251
  • #15 eval_frame
    at Python/ceval.c line 2016
  • #16 PyEval_EvalCodeEx
    at Python/ceval.c line 2574
  • #17 PyEval_EvalCode
    at Python/ceval.c line 483
  • #18 run_node
    at Python/pythonrun.c line 1083
  • #19 run_err_node
    at Python/pythonrun.c line 1070
  • #20 PyRun_FileExFlags
    at Python/pythonrun.c line 1061
  • #21 PyRun_SimpleFileExFlags
    at Python/pythonrun.c line 689
  • #22 PyRun_AnyFileExFlags
    at Python/pythonrun.c line 499
  • #23 Py_Main
    at Modules/main.c line 369
  • #24 main
    at Modules/python.c line 10
  • #25 __libc_start_main
    from /lib/libc.so.6

Comment 3 Drew Perttula 2002-10-24 00:34:30 UTC
The actual cause of the bug was a large PEBKAC, however pygtk failed 
to throw the exception that I would have expected to see. 

My gtk.Entry(*a,**kw) should have been gtk.Entry.__init__(self,*a,**kw).
Running connect() on an uninitialized gtk.Entry shouldn't cause a
segfault, though.
Comment 4 James Henstridge 2002-11-16 14:09:07 UTC
Considering marking this bug WONTFIX.  This would essentially involve
adding calls to every single wrapper in the binding, and doesn't
really have that much of a benefit.
Comment 5 Johan (not receiving bugmail) Dahlin 2002-11-19 21:53:28 UTC
Isn't it just another check in all functions, something like:

_wrap_foo_bar(self, args)
{
   if (!PYGTK_OBJECT_IS_INITALIZED(self))
       return NULL;
   
   if (!PyArg_ParseTuple .....

   .....

====
It can easily be done for all autogenerated functions and i can
volunteer to add it to all manually generated.

If it prevents a segfault it's good.
  
Comment 6 James Henstridge 2002-11-20 13:41:32 UTC
mass reassign of open pygtk and gnome-python bugs.
Comment 7 Johan (not receiving bugmail) Dahlin 2003-01-24 01:45:24 UTC
I guess i changed my mind over this one, it's not really worth it to
add checks everywhere. 
I'm suprised that it worked at all.

Marking as WONTFIX, since it's really to much work too fix it, with as
james said, to little benefit. Just use it in the correct way instead.

class Foo(gtk.Foo):
   def __init__(self):
      Foo.__init__(self)
Comment 8 Gustavo Carneiro 2003-01-30 18:25:21 UTC
  This really should be fixed. In my opinion, no python should ever
crash. For programming errors, exceptions were invented.
  I also don't see why this would be too much work. As Johan said,
checks can be done automatically by the code generator. For overridden
functions, a simple macro could be defined and placed in each function
at the beginning. Example:
(in pygobject.h)
#define pygtk_object_init_check() \
  if (!PYGTK_OBJECT_IS_INITALIZED(self)) { \
    PyErr_SetString(PyExc_RuntimeError, "The underlying GObject isn't
initialized"); \
    return NULL; \
}

  Then:

_wrap_foo_bar(self, args)
{
    pygtk_object_init_check();
     /* ... */

  Is this too much work?
  You know, it really gives a bad image to pygtk when it segfaults, no
matter whose fault it is...
Comment 9 Kevin Turner 2003-01-30 18:35:34 UTC
Just for the record: I disagree on "too little benefit."  drewp's
comment, "pygtk failed to throw the exception that I would have
expected to see" says much.  Judging from timestamps, it took him half
an hour to track down this very simple mistake, and that's *after* he
boiled it down to a minimal test case.  Errors that cannot be reported
sanely (i.e. segfaults) must be avoided at all costs.  I've come to
expect nothing less from Python, and that's one of the primary factors
making it my development tool of choice.
Comment 10 John Ehresman 2004-04-14 21:02:20 UTC
This should really be fixed on the grounds that it's a bug any time Python 
segfaults.  Is this a case of not checking if a GObject pointer is NULL?
Comment 11 James Henstridge 2004-04-15 00:25:25 UTC
john: I consider the goal of making pygtk crash proof impossible (the simple
fact that it can call abort() should prove that).  There are other places where
I've had to reduce the safety in order to just make things work

For example, try holding onto a pointer to the requisition object in
size_request.  If you try to modify the requisition after the signal handler has
completed you may corrupt the stack.  Previously, pygtk handled this safely, but
that made it impossible to implement the size_request signal handler.

If it was possible to make pygtk crash proof, then it would be obvious what to
do.  Given that that is not the case, its also worth considering maintainability
and bloat.
Comment 12 John Ehresman 2004-04-15 01:34:16 UTC
Yes, pygtk can never be crash proof.  The core python interpreter is not crash
proof and will probably never be.  That doesn't mean that we shouldn't try to
eliminate crashes where we can and even to go to some lengths to do so.  For
example the support for the size request handler is implemented so that if the
object pointing to the statically allocated structure outlives the function
call, the GValue will be copied so that access to the Python object will not
corrupt the stack.

I'm not asking anyone to go and fix this, but rather I think it should be left
as an open issue.  The question in my previous comment was intended to find out
whether the fix is simply adding a NULL pointer check or if there's more involved.
Comment 13 Bug flys 2004-04-15 03:24:10 UTC
Why cannot the base class every wrapper inheritated from check the
initialization first? Isn't all widget based on gobject? 
Comment 14 James Henstridge 2004-04-15 03:53:34 UTC
A fix for this would involve adding a check to every single method/function in
PyGTK (not just checking self->obj -- you'd also need to make sure the user
didn't pass one of these half initialised GObject wrappers to some other function).
Comment 15 John Ehresman 2004-04-15 04:35:50 UTC
This would be doable, if rather ugly, for generated code, but I agree it would
be a burden for handwritten code in the .override files.  On the other hand, the
Python core code does this level of checking in many cases.  

Could we use the tp_new slot to get around this (as was suggested on another
bug)?  The idea would be to have PyGObject's tp_new create the object using
g_object_new using the most derived subclass and passing in a list of
properties.  The tp_new slots for the descendant classes would then be
implemented as functions that took positional arguments and bound them to
property names.

For example (in python pseudo code):
  class Label(Widget):
    def __new__(cls, text = '', **kw):
      return Widget.__new__(text = text, **kw)

Descendants implemented in C could muck this up, but it's pretty hard (if not
impossible) to do from Python.  Could all the constructors be implemented this
way?  If there were a few exceptions, they could have tp_new slots which would
do something special if cls exactly matched a particular class.

This also allows arbitrary properties to be specified as arguments to the
constuctor, which I think is a feature worth adding.
Comment 16 Bug flys 2004-04-17 07:30:02 UTC
I don't get it. If there are some way to force every gobject instance to be
initialized at the creation time, then there should be no chance that an
uninitialized object derived from GObject being passed around.

Maybe the question is can python do that? force a function (a Mixin one?) to be
called at the creation time. 
Comment 17 Bug flys 2004-04-17 07:58:08 UTC
OK, if it is about unbounded method, then how python itself does unbounded
method checking?
Comment 18 John Ehresman 2004-07-16 14:56:09 UTC
Is this fixed by the constructing widgets through g_object_new() patch?  See
http://bugzilla.gnome.org/show_bug.cgi?id=96658  

I do think this should be reopened if it's not already fixed because anything
that cause a segfault in fairly typical usage and can be fixed should be listed
as a bug, even if there aren't plans to immediately fix it.
Comment 19 Christian Reis (not reading bugmail) 2004-07-16 16:58:36 UTC
Reopening for evaluation.
Comment 20 Gustavo Carneiro 2004-07-16 17:50:08 UTC
Nope, g_object_new() patch doesn't force the user to call the parent
constructor, therefore we still get this segfault.
I'm no longer sure we should fix this.  I tend to agree with James.  One thing
is to fix constructors, but this would require checks in _all_ pygtk methods
(this isn't just a problem with connect(), it is global).  Adding these checks
all over pygtk will make it marginally slower and its code larger.

Unless we agree to have conditional compiling in pygtk and a --enable-debug
configure option, so that we could only activate these extra checks for
developer packages.
Comment 21 John Ehresman 2004-07-19 17:04:03 UTC
Created attachment 29656 [details] [review]
Preliminary patch to use __new__ instead of __init__

Here a preliminary patch to create the GObject in __new__ rather than __init__
which means that the obj pointer is not NULL until __init__ or __gobject_init__
is invoked.  This change will affect user code because GObject subclasses will
now need to override __new__ instead of __init__ if the constructor requires a
different set of arguments than the base class.  I thought existing __init__
methods that didn't pass arguments to __gobject_init__ could be supported, but
positional arguments pass to __init__ are also passed to __new__ and the
__new__ of the superclass will interpret them as arguments for itself.

If a class used the following:
  class A(gtk.Window):
    def __init__(self, title):
      self.__gobject_init__()
      self.title = title

it would need to be changed to:
  class A(gtk.Window):
    def __new__(cls, title):
      self = gtk.Window.__new__(cls)
      self.title = title

So, the upside of this is it reduces the risk of segfaults and may allow for
better support for construct only properties, but the downside is code that
subclasses a GObject will need to change.

The patch is incomplete, but pygtk-demo seems to work.
Comment 22 Gustavo Carneiro 2004-07-19 22:08:18 UTC
In principle I agree with the patch, but the timing is less than perfect.
Comment 23 Johan (not receiving bugmail) Dahlin 2005-06-14 23:00:43 UTC
We cannot really break the API, existing code needs to continue to work.
Comment 24 Johan (not receiving bugmail) Dahlin 2005-10-03 15:56:16 UTC
I committed a patch which adds checks for all GObject method and will raise an
exception if the object is not initialized.
Comment 25 Gustavo Carneiro 2006-04-17 09:50:09 UTC
*** Bug 338760 has been marked as a duplicate of this bug. ***
Comment 26 Gustavo Carneiro 2006-04-17 13:28:38 UTC
Created attachment 63705 [details] [review]
pygobject API to check if object is initialized
Comment 27 Gustavo Carneiro 2006-04-17 13:30:43 UTC
Created attachment 63706 [details] [review]
make pygtk codegen call pygobject_check_init for methods and getters

It would be nice to evaluate the impact of this change in terms of code size; I think it's small, but...
Comment 28 Gustavo Carneiro 2006-04-17 13:38:10 UTC
OK, here's a test, compiled both times with gcc 4.0.3 with CFLAGS=-Os on amd64

_gtk.so before: 2631166 bytes
_gtk.so after:  2685523 bytes (+2%)
Comment 29 Gustavo Carneiro 2006-04-17 13:46:12 UTC
Same experiment, with stripped library:
_gtk.so before: 2159848 bytes
_gtk.so after: 2214184 bytes (+2.5%)
Comment 30 Johan (not receiving bugmail) Dahlin 2008-01-29 18:14:16 UTC
I think that we should get this ball rolling.

It should probably go into PyGObject where the code generator should live no (the pygtk copy is not quite removed).
Comment 31 Paul Pogonyshev 2008-09-27 19:38:13 UTC
*** Bug 554064 has been marked as a duplicate of this bug. ***
Comment 32 Martin Pitt 2012-02-10 08:15:46 UTC
codegen and the static bindings are obsolete since pygobject 3.0, which is GI only.

Thanks!