TypeError when using GridWithLabelRenderersMixin

Hello Robin and all,

A couple of times, very unreproducibly, I have gotten the following error in two unrelated grids:

21-01-03 17:18:51 ERROR [wx.lib.mixins.gridlabelrenderer]: An unexpected error occurred in wx.lib.mixins.gridlabelrenderer. 
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/dist-packages/wx/lib/mixins/gridlabelrenderer.py", line 121, in _onPaintColLabels
    dc = wx.PaintDC(window)
TypeError: PaintDC(): argument 1 has unexpected type 'SizerItem'

Both times this has happenend, the error appears every time the grid tries to repaint its column labels and it won’t go away until I restart the application.

I have observed the error in two different grids that use the GridWithLabelRenderersMixin as in

class MyGrid(wx.grid.Grid,
             wx.lib.mixins.gridlabelrenderer.GridWithLabelRenderersMixin):
    ...

Then inits of both grids follow the recommended initialization:

    def __init__(self, *args, **kwargs):
        wx.grid.Grid.__init__(self, *args, **kwargs)
        wx.lib.mixins.gridlabelrenderer.GridWithLabelRenderersMixin.__init__(self)
        ...

My current hypothesis is that when initializing the mixing class, concretely line 30 of gridlabelrenderer.py (https://github.com/wxWidgets/Phoenix/blob/12db41dad117598b8c850278597dd277e3812834/wx/lib/mixins/gridlabelrenderer.py#L30)

        self.GetGridColLabelWindow().Bind(wx.EVT_PAINT, self._onPaintColLabels)

somehow self.GetGridColLabelWindow() returns (from wxWidgets) the sizer item that contains the grid rather than the grid subwindow, but this seems farfetched and I don’t know what else to look for.

I don’t think this has to do with my specific code because the two times I have observed this were in two different panels containing two different grids. I run this code frequently and it usually runs just fine. Unfortunately, neither of the two times where I have observed this happened in a computer where I could attach a debugger to the process.

Both computers were debian linux running a self-compiled, relatively recent version (less than 30days old) version of wxPython. I wonder if this might be a wxWidgets issue but I thought I would ask here since I experience the issue only through the pure-python mixin class.

Has anyone experienced anything similar?

Thank you. Best regards,

Jorge

No, that should not be possible. And even if it was, SizerItem has no Bind method so the error would have been at that point of the code, not later in the paint event handler. Just to be sure you can insert code like this before the Binds to double-check:

    assert isinstance(self.GetGridColLabelWindow(), wx.Window)

There is one possibility that comes to mind, but it should be extremely rare (I’ve never seen it happen.) When a C++ object is not created from Python (such as the label widgets, which are created in the grid C++ code) then initially there is not an entry for it in SIP’s data that tracks the C++ and Python proxy objects. When one of those untracked objects is returned from some method call then SIP makes a new proxy object for it of the correct type. So if there is a scenario where a C++ wxSizerItem has an address A, and then later the grid is created and one of its label widgets has the same address A, then SIP could find the old wx.SizerItem proxy object and returns that instead of making a new proxy for it with a wx.Window type. Maybe this gives you enough information to confirm or deny this idea in your app. It’s been a long time since I looked closely at that code but I’m pretty sure that there are some safeguards in place that are intended to prevent that from happening. But I suppose there could still be some corner cases that are not covered.

One possible workaround that comes to mind would be to hold on to the label window proxy objects, so they don’t get removed from SIPs cache. Something like this in GridWithLabelRenderersMixin.__init__:

        self._rowLabelWindow = self.GetGridRowLabelWindow()
        self._colLabelWindow = self.GetGridColLabelWindow()
        self._cornerLabelWindow = self.GetGridCornerLabelWindow()

If you decide to try that let me know if it prevents the problem in your code.

Thank you Robin. Very interesting. What you are suggesting could be the problem: My application bootstraps itself to override the python import mechanism to retrieve modules from an http server instead of a local filesystem, which creates unusual timings at start time which might exacerbate these kind of issues.

As I mentioned it is very rare, I have only seen it twice in hundreds of runs over several months, so it is going to be hard to know if any particular change solves it, but I will definitely keep your suggestions in mind when it happens next in my development computer. When/if I learn more about it, I will let you know. Thank you again.