How can I find out why/when a Python object loses attributes? - python

Update 2013-02-08
I have an idea now why I haven't been able to reproduce this problem in a small piece of test code. In a small program, Python's garbage collector isn't very active. I believe the problem is that Python is collecting some objects that are only referenced in GObject. I think it's a regression involving this bug, or a new similar bug.
I figured this out because I encountered the same problem again, but with my own class (which has references only from GObject objects) -- this time the entire dict is getting wiped out on the object. Uf I use the code here to monitor one of the attributes that dissappears, it doesn't disappear! It seems the extra reference keeps the attributes around. That smells like a garbage collector problem. I confirmed this by having the object add itself to a global list during initialization... that also fixes the problem as it occurs now.
Original Problem
I am experiencing some bizarre behavior with a PyGTK GUI. I have an object which is consistently losing a large number of attributes. I am trying to determine if this is a bug in my code, the Python interpreter, or PyGTK.
I make no calls to delattr(). I have tried detecting if anything is calling the __delattr__() method of my object by overriding __delattr__() with code that always raises an exception. I am able to reproduce the event which causes the object to lose its attributes but the exception is never raised. I'm not sure of another way to find out what function calls (if any) are causing the object to lose attributes.
The object in question is working perfectly at all times up until it suddenly loses attributes I'm trying to access.
The attribute loss occurs consistently after performing some actions in the GUI that have nothing to do with the object that is losing attributes. I discovered it by accident; there may be other actions that cause the object to lose its attributes.
I have added print id(self) to the method which access the disappearing attribute. The id that is printed is the same before and after the attribute disappears.
Any suggestions on how to track down the source of this problem?
Reference code below: (Note: I will update this code if/when I come up with a simplified test case that reproduces the problem. Right now the total code required to reproduce the bug is too big to post here.)
Here is the class for my object which loses its attributes. This is obviously a minimized version of the real functional code, but I am using this for debugging and the problem still occurs.
It is a subclass of my custom MenuBar class.
Note that the on_file_import__activate() method is connected to the signal for the menu item by one of the parent classes.
class FluidClassManagerWindowMenu(MenuBar):
menu_items = [("File",("Import",))]
def __init__(self, parent):
# XXX: different name than self.parent to see if it stops disappearing
self._xxx_my_parent = parent
MenuBar.__init__(self, parent)
def __delattr__(self,attr):
# XXX: trying to find the cause for lost attributes
traceback.print_stack()
def on_file_import__activate(self, widget=None, data=None):
# XXX: this is the same before and after the attributes disappear
print id(self)
# XXX: print the list of attributes to see what disappears
print dir(self)
# XXX: this works until the _xxx_my_parent attribute disappears
print self._xxx_my_parent
If you're curious, here is the complete source for my MenuBar class. It is a pygtkhelpers SlaveView, which inherits from GObject. The pygtkhelpers delegate does the automagic signal connection to the on_file_import__activate method above.
class MenuBar(pygtkhelpers.delegates.SlaveView):
def __init__(self, parent):
SlaveView.__init__(self)
self.parent = parent
def create_ui(self):
menu_bar = gtk.MenuBar()
menu_bar.set_pack_direction(gtk.PACK_DIRECTION_LTR)
for menu_name, items in self.menu_items:
menu = gtk.Menu()
submenu = gtk.MenuItem(menu_name)
submenu.set_submenu(menu)
for item_name in items:
if not item_name:
menu.append(gtk.MenuItem())
continue
menuitem = gtk.MenuItem(item_name)
fixed_item_name = item_name.lower().replace(' ','_')
fixed_menu_name = menu_name.lower().replace(' ','_')
attr_name = '%s_%s' % (fixed_menu_name,fixed_item_name)
# set an attribute like self.edit_vial_layout
# so pygtkhelpers can find the widget to connect the signal from
setattr(self,attr_name,menuitem)
menu.append(menuitem)
menu_bar.append(submenu)
self.vbox = gtk.VBox()
self.vbox.pack_start(menu_bar)
self.vbox.show_all()
self.widget.add(self.vbox)
List of attributes which disappear from the object:
'_model', '_props', '_toplevel', '_xxx_my_parent', 'file_import', 'parent', 'slaves', 'testtesttest', 'vbox', 'widget'
The attribute parent is what originally was disappearing; I tried assigning its value to _xxx_my_parent in
ManagerWindowMenu.__init__() but it disappears as well. I also added a new attribute in MenuBar.__init__ that I never access, called testtesttest, and it disappears too.

Keep in mind that objects in PyGTK often inherit from GObject. There is likely activity occuring within the GObject framework that is causing you to lose the attributes.

I had a very similar problem. I had a class (SearchWorker) that built a widget for adding to the GUI at runtime. In that widget, there was a button whose "clicked" signal was connected to one of the SearchWorker functions. Whenever the "clicked" signal was fired, many of the attributes of the SearchWorker self object were gone.
I was creating the SearchWorker object in another handler of a different class like this:
worker = SearchWorker()
I presume that once that handler exited something odd happened to the object behind the worker reference. Changing the creation of SearchWorker to:
self.worker = SearchWorker()
solved my problem.

How strange. BTW, using delattr and __delattr__ call is not very common, so I suspect if you are not dealing with two different objects by themselves, getting one while expecting another. Also it may not be a problem with the interpreter, it would crash at much lower level if there was a problem.

Related

Why am I getting this "wrapped C/C++ object ... has been deleted" error with pytest-qt?

Please note: this is on W10. This may well be significant.
Python: 3.9.4
pytest: 6.2.5
pytest-qt: 4.0.2
I've been using pytest-qt for about a week now to start developing a PyQt5 app. There have been a few baffling problems but none as baffling as this one.
My app code:
class LogTableView(QtWidgets.QTableView):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
def resizeEvent(self, resize_event):
super().resizeEvent(resize_event)
# self.resizeRowsToContents()
The last line above needs to be added. Using a TDD approach I therefore start writing the test:
def test_resize_event_should_result_in_resize_rows(request, qtbot):
t_logger.info(f'\n>>>>>> test name: {request.node.originalname}')
table_view = logger_table.LogTableView(QtWidgets.QSplitter())
# with unittest.mock.patch.object(table_view, 'resizeRowsToContents') as mock_resize:
# with unittest.mock.patch('logger_table.LogTableView.resizeRowsToContents') as mock_resize:
table_view.resizeEvent(QtGui.QResizeEvent(QtCore.QSize(10, 10), QtCore.QSize(20, 20)))
NB the commented-out lines show the kind of things I have been trying. But you can see that even just creating an object of the type LogTableView, and then calling the method, with no mocks around at all, causes the error.
On running this:
>pytest -s -v -k test_logger_table.py
I get this:
...
self = <logger_table.LogTableView object at 0x000002B672697670>
resize_event = <PyQt5.QtGui.QResizeEvent object at 0x000002B672743940>
def resizeEvent(self, resize_event):
> super().resizeEvent(resize_event)
E RuntimeError: wrapped C/C++ object of type LogTableView has been deleted
...
Has anyone got any idea what this is about?
PS FWIW, out of despair, I even tried this:
super(LogTableView, self).resizeEvent(resize_event)
... same error.
Creating a parent in the child constructor is not a very good idea.
Remember that PyQt is a binding, every reference used in Python is a wrapper for the Qt object: if the object is deleted on the C++ side, the python reference still exists, but any attempt to use its functions results in the RuntimeError above.
In your case, there's no persistent reference for the parent on the python side, only the pointer on the Qt side, which is not enough to avoid garbage collection: only parent objects take ownership in Qt (that's why you can avoid persistent references for a child Qt object), it's not the other way around. The problem is that the child believes that it has a parent (as it had one when it was created), but in the meantime that parent has been deleted, as soon as the child constructor is returned.
Just create a local variable for the parent.
def test_resize_event_should_result_in_resize_rows(request, qtbot):
t_logger.info(f'\n>>>>>> test name: {request.node.originalname}')
parent = QtWidgets.QSplitter()
table_view = logger_table.LogTableView(parent)
# ...
Besides the problem of the subject, technically speaking there's no point in using a very specific widget such as QSplitter as a parent (especially considering that in order to be properly used, the widget should be added with addWidget(), as the parenthood alone is pointless for a splitter); if you need a parent, just use a basic QWidget.

How to check if method exist from within a Python object

Context and intentions: I want to use an object m_o of type My_object as a way of interfacing with another object called s_o of type Stubborn_object. For the sake of easy understanding, they should behave like if My_object inherited from Stubborn_object, in the way that calling an attribute that doesn't exist in My_object should call the attribute in Stubborn_object.
However, the tricky thing is that I wouldn't be asking this question if I could simply inherit My_object from Stubborn_object. It appears that I can't inherit from it, and, for many reasons, I also can't modify the code of the Stubborn_object class, so I have to use it as it is. Please note that trying to inherit isn't the issue of the question here. I know that other solutions exist for my practical problem, but I really want answers to stay on topic for many reasons. I suspect that other users can have different problems than mine and still be unable to inherit a class. Furthermore, not being able to inherit a class is not the only reason that could make someone read this question. In fact, it's quite a general Python object-oriented problem. I also believe the solution of my problem could be useful in other applications, like custom error handling within the object itself when an attribute is not found, or in thread management to lock the instance as soon as an attribute is called.
In addition to the problem of inheritance, let's suppose that I can't use conditions at higher levels to handle these cases, so everything has to be done inside My_object instance or its parents. That means that I can't use hasattr(m_o, attribute_name) to determine if I should call getattr(m_o, attribute_name) or getattr(s_o, attribute_name). This also means that any try/except blocks and other preconditions must be inside the My_object class or its parents. The point of this question is not about detecting exceptions when calling an attribute from outside the My_object instance. A try/catch block normally has to be outside the My_object class, and I previously stated that this can't be allowed.
For the sake of clarity and to provide a complete verifiable example, here is a sample code of the Stubborn_object class. I know that I said I can't inherit from Stubborn_object and the following code includes an inheritable class. Providing an example of an non-inheritable object would only bring confusion and it would'nt be really helpful to the question anyway, so here is a simple example of an inheritable object. The objective of this is to make an easy to understand question, so please just consider that you can't inherit from it:
class Stubborn_object:
def do_something(self):
print("do_something")
def action_to_override():
print("action_to_override")
def action_a(self):
print("action_a")
def action_b(self):
print("action_b")
Objective: Put it simply, I want my class My_object to detect all by itself that a lacking attribute has been called and run some instructions instead of throwing an AttributeError.
Current attempts: Right now, I manually redirect method calls to the Stubborn_object instance like so (it's successful, but not reliable nor scalable because of the use of hardcoding):
class My_object():
def __init__(self, s_o):
self.stubborn_object = s_o
def action_to_override(self):
# Do stuff. This method "overrides" the Stubborn_object.action_to_override method.
print("Here is stuff getting done instead of action_to_override")
def action_a(self):
return self.stubborn_object.action_a()
def action_b(self):
return self.stubborn_object.action_b()
s_o = Stubborn_object()
m_o = My_object(s_o)
m_o.action_to_override() # Executes Stubborn_object.do_something()
m_o.action_a() # Executes Stubborn_object.action_a()
m_o.action_b() # Executes Stubborn_object.action_b()
Executing this code along with the provided Stubborn_object code sample should print:
Here is stuff getting done instead of action_to_override
action_a
action_b
As you can see from methods action_a and action_b, I have to manually call the Stubborn_object methods from whithin the methods in My_object to mimic the attributes of Stubborn_object. This is ineficient, lacks of robustness and will throw an AttributeError exception if we attempt to make an action that wasn't included in the My_object code.
What if I wanted to automatically send method and attribute calls to the Stubborn_object instance without having to rewrite all of its method and attributes in My_object? I believe this can be achieved with detecting if a lacking attribute of My_object instance is called.
Expectations (or sort of): I am open to any solution that allows the My_object class or its parents to determine if the attribute is lacking or not, all within itself. So I believe I am ready to hear extremely original ideas, so go ahead.
On my part, I believe that something that uses parts of this code is the way to go, but it still lacks the "catch any called attribute" part:
class My_object():
def __init__(self, s_o):
# __init__ stays as it was.
self.stubborn_object = s_o
def action_to_override(self):
# This method also stays as it was.
# Do stuff. This method "overrides" the stubborn_object.action_to_override method.
print("Here is stuff getting done instead of action_to_override")
def run_me_when_method_is_not_found(self, method_name, **kwargs):
print("Method " + method_name + " not in 'My_object' class.")
return getattr(self.stubborn_object, method_name)(**kwargs)
So running those lines with the previous code sample
s_o = Stubborn_object()
m_o = My_object(s_o)
m_o.action_to_override() # Executes Stubborn_object.do_something()
m_o.action_a() # Executes Stubborn_object.action_a()
m_o.action_b() # Executes Stubborn_object.action_b()
will print
Here is stuff getting done instead of action_to_override
Method action_a not in 'My_object' class.
action_a
Method action_b not in 'My_object' class.
action_b
Some similar methods will have to be made for getters and setters, however, the idea stays the same. The thing is that this code lacks the ability to detect that an attribute is missing.
Question: How can I run the run_me_when_method_is_not_found when the method is not found in My_object? Especially, how can a My_object instance detect that the method doesn't exists in its class instead of throwing an AttributeError exception?
Thanks a lot.
Seems like overriding __getattribute__ will do exactly what you want: search for attribute in self.stubborn_object if it is missing in self. Put it into My_object class definition:
def __getattribute__(self, attr):
try:
return object.__getattribute__(self, attr)
except AttributeError:
return object.__getattribute__(self.stubborn_object, attr)

Setting up instance variables Python

I know that there are tons of questions asking this but no methods I have found anwer exactly what I want. When ever I run this code and subsiquently getIngredient() I get this error 'ListItem' object has no attribute 'ing'. Please help and thanks in advance.
class ListItem(FloatLayout):
def __init__(self, ing):
super(ListItem, self).__init__()
self.ing = ing
def getIngredient(self):
return self.ing.name
I am also calling this on an instance of the object put into a box layout in kivy.
Well, if I paste that code into a Python 2.7.10 iPython session, using a stub/empty FloatLayout class, then the error I get from instantiating with just a string, and calling the getIngredient() method, is:
AttributeError: 'str' object has no attribute 'name'
... which seems perfectly expectable. If I create some object and give it a .name attribute then the code works fine.
So it seems likely that your problem either involves some details of the real FloatLayout parent class; or the version of Python that you're using (or both).
In general I'd expect that you'd add your own custom attributes (.ing in your example) before your call to super() ... if you're adding stuff rather than over-riding it. (In general I'd also expect you to pass any initialization arguments up the chain to your super-classes (after filtering out any that you're intercepting as specific to your derived/child class). Done properly that should make you relatively robust even if the implementation of your parent class changes somewhat and even if descendants of your class attempt to utilize as yet un-imagined extensions of your parent classes.
(I know, that sort of "future proofing" is vague, and abstract, and even somewhat fragile ... but it's better than ignoring the issue entirely).

PySide/PyQt signal that can transmit any value (including None)

I have a problem using Qt signal/slot mechanism in PySide when I want to send None.
Suppose I have a class named Calculator (a subclass of QObject) that can perform certain calculations the result of which can be anything, i.e. any type of Python object or even None. I need the instances of Calculator class to be able to signal the result to some consumer objects (subclasses of QObject).
What I had:
class Calculator(QObject):
finished = PySide.QtCore.Signal(object) # object means it can transmit anything
def calc(self):
... # calculation of the result
self.finished.emit(result)
class Consumer(QObject):
def __init__(self, calculator):
...
calculator.finished.connect(self.consume)
def consume(self, result): # do something with the result
...
This works fine except the situation when result is None. Then the program crashes when emitting the signal. It seems as if None is not a Python object (which may be true, I am not that proficient in the language standards).
Now I have a workaround, using two signals:
finished = PySide.QtCore.Signal((),(object,))
and:
def calc(self):
... # calculation of the result
if result is None:
self.finished.emit()
else:
self.finished[object].emit(result)
This solution works but it is complicated because it requires the consumer objects to link the signal twice. Either to two slots or to one slot with one default argument equal to None.
class Consumer(QObject):
def __init__(self, calculator):
...
calculator.finished.connect(self.consume)
calculator.finished[object].connect(self.consume)
def consume(self, result=None): # do something with the result
...
This is complicated and prone to errors when creating many different consumer classes.
I think this problem is the same with PyQt, with the difference that alternative signals are defined using lists [], [object] rather than tuples (),(object,).
My question is: is there any simple solution to this problem? Ideally with just one signal?
As an aside, this bug still exists in PySide where it segfaults on the transmission of a None signal as of version 1.2.1 (has existed since 1.1.0). There is a bug report at:
https://bugreports.qt-project.org/browse/PYSIDE-17
Obviously V.K.'s solution of encapsulating the None within a separate class still works, as does choosing another type to transmit instead (I switched my None to float('nan')). I just wanted to update this as I ran into the same issue and found this, and later the bug report explaining the segfault.
Just after posting my problem I found an answer. So I appologize for answering my own question - I know it is not how it should be on StackOverflow. I created a class named ResultHolder which encapsulates the real result (result is a member variable of this class). Then I am transmitting instances of this ResultHolder class. The rest of the solution is then just straightforward.

Sub frame does not destroy in python

I have a program that I'm trying to develop but I guess I don't know the proper way to make subframes. I've looked at several examples and can't seem to get it to destroy the frame when it's closed. I get the following error readout (using pycharm):
Exception wx._core.PyDeadObjectError: PyDeadObjectError('The C++ part of the
Choice object has been deleted, attribute access no longer allowed.',) in
<bound method pvFileINdialog.__del__ of <fileIO.pvFileIOGUIFileIOdialog.pvFileINdialog;
proxy of <Swig Object of type 'wxDialog *' at 0x340cc70> >> ignored
Although the interpreter takes care of the problem, I would like to learn the proper way to take care of it.
If I move the destroy and close commands around no combination changes the result.
The code of interest is:
#in MainFrame.py:
def fileimport(self, event):
importprompt = pvFileIOGUIFileIOdialog.pvFileINdialog(self)
importprompt.ShowModal()
importprompt.Destroy()
#referring to pvFileIOGUIFileIOdialog.pvFileINdialog :
class pvFileINdialog(pvFileIOGUI.FileINdialog):
def __init__(self, child):
pvFileIOGUI.FileINdialog.__init__(self, child)
#which refers to pvFileIOGUI.FileINdialog :
class FileINdialog(wx.Dialog):
def __init__(self, child):
wx.Dialog.__init__(self, child, id=wx.ID_ANY, title=u"Select Import Method...", pos=wx.DefaultPosition,
size=wx.Size(800, 600), style=wx.DEFAULT_DIALOG_STYLE)
#and is closed by this routine in pvFileIOGUIFileIOdialog.py:
def cancelclk(self, event):
self.Close()
return 0
It's difficult to understand what goes on because your error message refers to a Choice, but your code doesn't show any choices at all, so something is probably missing, i.e. the problem is probably in the part which you don't show. But in any case, calling Destroy() is not necessary and you shouldn't do it unless you have some real reason (do you?). So I'd start by removing all calls to Destroy() from your code.

Categories

Resources