Infinite recursion subclassing GridCellNumberEditor

Hi!

I am facing an issue which I suspect may be a bug, but before filing a bug report, or become crazy trying to debug my problem, I would like to ask whether this could be instead something expected.

As per the topic title I’m trying to subclass GridCellNumberEditor (in order to make it accept also empty strings); as soon as I override EndEdit() and I call the superclass EndEdit() version in my method i.e. return wx.grid.GridCellNumberEditor.EndEdit( .... ) I got infinite recursion. (using super() leads to the same result).

Given that I’m not a python expert and I don’t know how the wxWidgets -> wxPython wrapping mechanism works, I’m wondering whether it is possible to subclass any class, or whether only certain classes have been explicitly designed to be subclassed, or something like that.

Is indeed possible to subclass any wxpy class, or is there some limitation on that?

Thanks
Andrea

Hi,

I’ve not tried subclassing any of the grid cell editors before. I noticed there is an example in the wxPython demo which subclasses GridCellEditor.

In that example, the subclass’s EndEdit() doesn’t call the superclass’s EndEdit() method. Is there a reason why you need to do this?

I have uploaded the example as it can be run standalone.

GridCustEditor.py (8.2 KB)

Hi Richard,

Thanks for your reply.

Indeed I’ve also successfully subclassed GridCellEditor, without calling the superclass EndEdit(), and it worked also for me. I did this to implement fully custom controls.

Now I would like to change the behavior of GridCellNumberEditor just slightly, so I just handled the “empty” case in the overridden EndEdit() method, while I’m falling back to the original EndEdit() implementation otherwise (which handles numbers).

Is calling the superclass EndEdit() method somehow wrong/forbidden?

I could reimplement from scratch the whole thing subclassing GridCellEditor, but I was actually trying to avoid that :slight_smile:

Andrea

Hi Andrea,

Thanks for your explanation. I think what you are trying to do makes sense and should be possible.

Please can you to post a simple runnable example which demonstrates the problem?

If you paste code into the editor, please put 3 backtick characters ` before the first line and three more after the last line. That will prevent the forum software from trying to reformat it.

Below a minimal example, which causes my terminal to print a huge number of lines “End edit enter”, then it shows the recursion error as follows (EDIT: you need to double click the cell, write a number inside, and confirm the edit to trigger the problem):

...
...
End edit enter
End edit enter
object address  : 0x7ff66a8bd240
object refcount : 3
object type     : 0x55fb65c4e7e0
object type name: RecursionError
object repr     : RecursionError('maximum recursion depth exceeded while calling a Python object')
lost sys.stderr
End edit exit
End edit exit
...
...

… then a huge number of lines “End edit exit”.

I’m using the following versions (installed with conda)
python 3.12.2
wxpython 4.2.1
wxwidgets 3.2.5

And I’ve hit this also yesterday on another setup which runs an older python version and wxpy installed with pip (can’t check the version now, but I may do that in next days).

Thanks
Andrea

import wx
import wx.grid as gridlib

class MyGridCellNumberEditor(gridlib.GridCellNumberEditor):
    def EndEdit(self, row, col, grid, oldval):
        print(f"End edit enter")
        super().EndEdit(row, col, grid, oldval)
        print(f"End edit exit")

class MyGrid(gridlib.Grid):
    def __init__(self, parent):
        super().__init__(parent, -1)
        self.CreateGrid(1, 1)
        self.SetCellEditor(0, 0, MyGridCellNumberEditor())

class MyFrame(wx.Frame):
    def __init__(self):
        super().__init__(None, title="Custom GridCellNumberEditor Example")
        panel = wx.Panel(self)
        grid = MyGrid(panel)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(grid, 1, wx.EXPAND)
        panel.SetSizer(sizer)
        self.SetSize((400, 300))

class MyApp(wx.App):
    def OnInit(self):
        frame = MyFrame()
        frame.Show()
        return True

if __name__ == "__main__":
    app = MyApp(False)
    app.MainLoop()

Thanks for the code sample. I get the same results as you. I am using wxPython 4.2.1 + Python 3.12.3 + Linux Mint 22.

I tried stepping through the code in PyCharm’s debugger. As soon as the line super().EndEdit(row, col, grid, oldval) is executed it jumps straight back into MyGridCellNumberEditor.EndEdit() causing the recursion.

At the moment I can’t think of an explanation, other than it being a bug.

Thanks for your help and for testing the code on your setup.

Do I need to file a bug somewhere, or does this thread already serve for that purpose?

Andrea

You need to raise an issue in the wxPython repository on GitHub - https://github.com/wxWidgets/Phoenix/issues for which you will need a GitHub account.

Richard

Going to do that. Thanks again

Andrea

I did wonder if there was something weird about the MyGridCellNumberEditor's method resolution order, but comparing it with that for wx.Frame (for example), it seems to be correct:

import wx
import wx.grid as gridlib
import inspect

class MyFrame(wx.Frame):
    pass

class MyGridCellNumberEditor(gridlib.GridCellNumberEditor):
    pass

# Print base classes in method resolution order
print(inspect.getmro(MyFrame))
print()
print(inspect.getmro(MyGridCellNumberEditor))

Outputs:

(<class '__main__.MyFrame'>, <class 'wx._core.Frame'>, <class 'wx._core.TopLevelWindow'>, <class 'wx._core.NonOwnedWindow'>, <class 'wx._core.Window'>, <class 'wx._core.WindowBase'>, <class 'wx._core.EvtHandler'>, <class 'wx._core.Object'>, <class 'wx._core.Trackable'>, <class 'sip.wrapper'>, <class 'sip.simplewrapper'>, <class 'object'>)

(<class '__main__.MyGridCellNumberEditor'>, <class 'wx._grid.GridCellNumberEditor'>, <class 'wx._grid.GridCellTextEditor'>, <class 'wx._grid.GridCellEditor'>, <class 'wx._core.SharedClientDataContainer'>, <class 'wx._core.RefCounter'>, <class 'sip.wrapper'>, <class 'sip.simplewrapper'>, <class 'object'>)

Ah, I didn’t thought to check class MRO…

From your code, it seems that subclassing from editors (i.e. not from the base GridCellEditor, but from its subclasses) is in general licit, because GridCellNumberEditor seems to inherit from GridCellTextEditor.

So I’ve tried to override the ApplyEdit() method instead, and this seems to work properly. Then I tried to subclass GriedCellTextEditor, and I got the very same behavior.

So it seems something more related to the specific method rather than the class in general.