Troubleshooting sip assignHelper != NULL assertion

Phil Thompson phil at riverbankcomputing.com
Mon Jun 1 17:07:06 BST 2020


On 31/05/2020 17:48, Scott Talbert wrote:
> On Sun, 31 May 2020, Phil Thompson wrote:
> 
>> On 31/05/2020 16:29, Scott Talbert wrote:
>>> On Sun, 31 May 2020, Phil Thompson wrote:
>>> 
>>>> On 31/05/2020 01:23, Scott Talbert wrote:
>>>>> On Sat, 30 May 2020, Phil Thompson wrote:
>>>>> 
>>>>>> On 28/05/2020 16:52, Scott Talbert wrote:
>>>>>>> On Thu, 28 May 2020, Phil Thompson wrote:
>>>>>>> 
>>>>>>>> On 28/05/2020 16:30, Scott Talbert wrote:
>>>>>>>>> On Thu, 28 May 2020, Phil Thompson wrote:
>>>>>>>>> 
>>>>>>>>>>>>> Hi,
>>>>>>>>>>>>> 
>>>>>>>>>>>>> I'm running into the following assertion in wxPython that I 
>>>>>>>>>>>>> don't
>>>>>>>>>>>>> quite understand:
>>>>>>>>>>>>> 
>>>>>>>>>>>>> python3: ../../../../sip/siplib/siplib.c:3444: parseResult: 
>>>>>>>>>>>>> Assertion
>>>>>>>>>>>>> `assign_helper != NULL' failed.
>>>>>>>>>>>>> 
>>>>>>>>>>>>> This is the relevant C++ class:
>>>>>>>>>>>>> 
>>>>>>>>>>>>> class wxPGWindowList
>>>>>>>>>>>>> {
>>>>>>>>>>>>> public:
>>>>>>>>>>>>>     wxPGWindowList(wxWindow* primary, wxWindow* secondary = 
>>>>>>>>>>>>> NULL)
>>>>>>>>>>>>>         : m_primary(primary)
>>>>>>>>>>>>>         , m_secondary(secondary)
>>>>>>>>>>>>>     {
>>>>>>>>>>>>>     }
>>>>>>>>>>>>> 
>>>>>>>>>>>>>     void SetSecondary(wxWindow* secondary) { m_secondary = 
>>>>>>>>>>>>> secondary; }
>>>>>>>>>>>>> 
>>>>>>>>>>>>>     wxWindow* GetPrimary() const { return m_primary; }
>>>>>>>>>>>>>     wxWindow* GetSecondary() const { return m_secondary; }
>>>>>>>>>>>>> 
>>>>>>>>>>>>>     wxWindow*   m_primary;
>>>>>>>>>>>>>     wxWindow*   m_secondary;
>>>>>>>>>>>>> };
>>>>>>>>>>>> 
>>>>>>>>>>>> It doesn't matter what the C++ class looks like, it's the 
>>>>>>>>>>>> corresponding .sip that would be of interest.
>>>>>>>>>>> 
>>>>>>>>>>> My bad, here it is:
>>>>>>>>>>> class wxPGWindowList
>>>>>>>>>>> {
>>>>>>>>>>>     %Docstring
>>>>>>>>>>>         PGWindowList(primary, secondary=None)
>>>>>>>>>>> 
>>>>>>>>>>>         Contains a list of editor windows returned by 
>>>>>>>>>>> CreateControls.
>>>>>>>>>>>     %End
>>>>>>>>>>>     %TypeHeaderCode
>>>>>>>>>>>         #include <wx/propgrid/editors.h>
>>>>>>>>>>>     %End
>>>>>>>>>>> 
>>>>>>>>>>> public:
>>>>>>>>>>>     wxPGWindowList(
>>>>>>>>>>>         wxWindow * primary,
>>>>>>>>>>>         wxWindow * secondary = NULL
>>>>>>>>>>>     );
>>>>>>>>>>> 
>>>>>>>>>>>     void SetSecondary(
>>>>>>>>>>>         wxWindow * secondary
>>>>>>>>>>>     );
>>>>>>>>>>>     %Docstring
>>>>>>>>>>>         SetSecondary(secondary)
>>>>>>>>>>>     %End
>>>>>>>>>>> 
>>>>>>>>>>>     wxWindow * GetPrimary() const;
>>>>>>>>>>>     %Docstring
>>>>>>>>>>>         GetPrimary() -> wx.Window
>>>>>>>>>>> 
>>>>>>>>>>>         Gets window of primary editor.
>>>>>>>>>>>     %End
>>>>>>>>>>> 
>>>>>>>>>>>     wxWindow * GetSecondary() const;
>>>>>>>>>>>     %Docstring
>>>>>>>>>>>         GetSecondary() -> wx.Window
>>>>>>>>>>> 
>>>>>>>>>>>         Gets window of secondary editor.
>>>>>>>>>>>     %End
>>>>>>>>>>> 
>>>>>>>>>>>     public:
>>>>>>>>>>> 
>>>>>>>>>>> 
>>>>>>>>>>>     %Property(name=Primary, get=GetPrimary)
>>>>>>>>>>>     %Property(name=Secondary, get=GetSecondary, 
>>>>>>>>>>> set=SetSecondary)
>>>>>>>>>>> };  // end of class wxPGWindowList
>>>>>>>>>>> 
>>>>>>>>>>> 
>>>>>>>>>>>>> The assertion occurs when trying to return an instance of
>>>>>>>>>>>>> wxPGWindowList in a Python method, e.g.:
>>>>>>>>>>>>> 
>>>>>>>>>>>>> def foo():
>>>>>>>>>>>>>     return wxpg.PGWindowList(a, b)
>>>>>>>>>>>>> 
>>>>>>>>>>>>> From what I can tell, there is no assignment helper 
>>>>>>>>>>>>> assigned by sip
>>>>>>>>>>>>> because there is no default constructor?  I may be missing 
>>>>>>>>>>>>> something,
>>>>>>>>>>>>> but I can't see why a default constructor would be needed.
>>>>>>>>>>>> 
>>>>>>>>>>>> You don't say what version of the sip module you are using 
>>>>>>>>>>>> but I'm guessing that that assertion is when it's parsing 
>>>>>>>>>>>> the Python object returned by a re-implementation of a C++ 
>>>>>>>>>>>> virtual. That doesn't seem to be happening in the above 
>>>>>>>>>>>> which suggests things aren't happening where you think they 
>>>>>>>>>>>> are.
>>>>>>>>>>> 
>>>>>>>>>>> sip module version is 4.19.19.  You are correct, I 
>>>>>>>>>>> oversimplified what
>>>>>>>>>>> is actually happening.  This is really what is happening:
>>>>>>>>>>> 
>>>>>>>>>>> class LargeImageEditor(wxpg.PGEditor):
>>>>>>>>>>>     def CreateControls(self, propgrid, property, pos, sz):
>>>>>>>>>>>         ...
>>>>>>>>>>>         return wxpg.PGWindowList(self.tc, btn)
>>>>>>>>>>> 
>>>>>>>>>>> Where CreateControls is a C++ virtual, relevant .sip snippet:
>>>>>>>>>>> 
>>>>>>>>>>>     virtual
>>>>>>>>>>>     wxPGWindowList CreateControls(
>>>>>>>>>>>         wxPropertyGrid * propgrid,
>>>>>>>>>>>         wxPGProperty * property,
>>>>>>>>>>>         const wxPoint & pos,
>>>>>>>>>>>         const wxSize & size
>>>>>>>>>>>     ) const = 0;
>>>>>>>>>>>     %Docstring
>>>>>>>>>>>         CreateControls(propgrid, property, pos, size) -> 
>>>>>>>>>>> PGWindowList
>>>>>>>>>>> 
>>>>>>>>>>>         Instantiates editor controls.
>>>>>>>>>>>     %End
>>>>>>>>>> 
>>>>>>>>>> So SIP need to copy the wxPGWindowList from the stack to the 
>>>>>>>>>> heap. Shouldn't CreateControls return a pointer to the 
>>>>>>>>>> wxPGWindowList?
>>>>>>>>>> 
>>>>>>>>>> Of course SIP should detect this when generating the code 
>>>>>>>>>> rather than rely on a runtime assertion.
>>>>>>>>> 
>>>>>>>>> That is probably how I would have designed the API, but alas it 
>>>>>>>>> wasn't
>>>>>>>>> done that way.  :)
>>>>>>>>> 
>>>>>>>>> Shouldn't a (default) copy constructor be sufficient to copy 
>>>>>>>>> the
>>>>>>>>> wxPGWindowList in this case?
>>>>>>>> 
>>>>>>>> SIP may be being too conservative when determining if there is 
>>>>>>>> an implied copy ctor. Try adding an explicit copy ctor to the 
>>>>>>>> wxPGWindowList .sip file.
>>>>>>> 
>>>>>>> I don't think that will be enough, though.  SIP seems to only set 
>>>>>>> the
>>>>>>> assignment helper if there are *both* a public default 
>>>>>>> constructor and
>>>>>>> a public copy constructor, see:
>>>>>>> 
>>>>>>> https://www.riverbankcomputing.com/hg/sip/file/d85e9957e726/sipgen/transform.c#l596
>>>>>>> 
>>>>>>> Scott
>>>>>> 
>>>>>> Should be fixed in the current repo. SIP v4.19.23 will be released 
>>>>>> next week.
>>>>>> 
>>>>>> Be aware that the fix may expose missing private ctors in other 
>>>>>> .sip files - PyQt had two cases.
>>>>> 
>>>>> Thanks for the quick fix.
>>>>> 
>>>>> However, I think I found a situation with the new code where an 
>>>>> assign
>>>>> function is getting created where it probably shouldn't be.
>>>>> 
>>>>> struct wxSplitterRenderParams
>>>>> {
>>>>>     %Docstring
>>>>>         SplitterRenderParams(widthSash_, border_, isSens_)
>>>>> 
>>>>>         This is just a simple struct used as a return value of
>>>>>         wxRendererNative::GetSplitterParams().
>>>>>     %End
>>>>>     %TypeHeaderCode
>>>>>         #include <wx/renderer.h>
>>>>>     %End
>>>>> 
>>>>>     wxSplitterRenderParams(
>>>>>         wxCoord widthSash_,
>>>>>         wxCoord border_,
>>>>>         bool isSens_
>>>>>     );
>>>>> 
>>>>>     const wxCoord border;
>>>>> 
>>>>>     const bool isHotSensitive;
>>>>> 
>>>>>     const wxCoord widthSash;
>>>>> 
>>>>> };  // end of class wxSplitterRenderParams
>>>>> 
>>>>> This results in:
>>>>> 
>>>>> ../../../../sip/cpp/sip_corewxSplitterRenderParams.cpp: In function
>>>>> ‘void assign_wxSplitterRenderParams(void*, Py_ssize_t, void*)’:
>>>>> ../../../../sip/cpp/sip_corewxSplitterRenderParams.cpp:31:125: 
>>>>> error:
>>>>> use of deleted function ‘wxSplitterRenderParams&
>>>>> wxSplitterRenderParams::operator=(const wxSplitterRenderParams&)’
>>>>>    31 |     reinterpret_cast< ::wxSplitterRenderParams
>>>>> *>(sipDst)[sipDstIdx] = *reinterpret_cast< ::wxSplitterRenderParams
>>>>> *>(sipSrc);
>>>>>       | ^
>>>>> In file included from 
>>>>> ../../../../sip/cpp/sip_corewxSplitterRenderParams.cpp:12:
>>>>> ../../../../../Phoenix/ext/wxWidgets/include/wx/renderer.h:97:25:
>>>>> note: ‘wxSplitterRenderParams& 
>>>>> wxSplitterRenderParams::operator=(const
>>>>> wxSplitterRenderParams&)’ is implicitly deleted because the default
>>>>> definition would be ill-formed:
>>>>>    97 | struct WXDLLIMPEXP_CORE wxSplitterRenderParams
>>>>>       |                         ^~~~~~~~~~~~~~~~~~~~~~
>>>>> ../../../../../Phoenix/ext/wxWidgets/include/wx/renderer.h:97:25:
>>>>> error: non-static const member ‘const wxCoord
>>>>> wxSplitterRenderParams::widthSash’, cannot use default assignment
>>>>> operator
>>>>> ../../../../../Phoenix/ext/wxWidgets/include/wx/renderer.h:97:25:
>>>>> error: non-static const member ‘const wxCoord
>>>>> wxSplitterRenderParams::border’, cannot use default assignment
>>>>> operator
>>>>> ../../../../../Phoenix/ext/wxWidgets/include/wx/renderer.h:97:25:
>>>>> error: non-static const member ‘const bool
>>>>> wxSplitterRenderParams::isHotSensitive’, cannot use default 
>>>>> assignment
>>>>> operator
>>>> 
>>>> Yes, that's what I meant by missing private copy ctors. SIP cannot 
>>>> accurately determine whether a copy ctor is available unless it 
>>>> knows everything about the C++ class (including private members and 
>>>> their types), but SIP is not a full C++ parser (and never will be). 
>>>> Therefore you need to declare a private copy ctor in the .sip file 
>>>> to correct the assumption that there is a public copy ctor 
>>>> available. In the future I may add a class annotation to achieve the 
>>>> same thing.
>>> 
>>> Thanks, I understand that now.  :)  After declaring a private copy
>>> ctor in the above .sip file, I'm now getting this:
>>> 
>>> ../../../../sip/cpp/sip_corewxDelegateRendererNative.cpp: In function
>>> ‘PyObject* meth_wxDelegateRendererNative_GetSplitterParams(PyObject*,
>>> PyObject*, PyObject*)’:
>>> ../../../../sip/cpp/sip_corewxDelegateRendererNative.cpp:1360:38:
>>> error: taking address of rvalue [-fpermissive]
>>>  1360 |             sipRes = &(sipSelfWasArg ? sipCpp->
>>> ::wxDelegateRendererNative::GetSplitterParams(win) :
>>> sipCpp->GetSplitterParams(win));
>>>       |
>>> ~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>>> 
>>> So, since SIP can't copy the wxSplitterRenderParams, it is trying to
>>> return a reference to the original one, but it seems that doesn't
>>> work?
>> 
>> So what does the .sip file for wxDelegateRenderNative look like?
> 
> It's a bit long, so attached.

Can you try the current repo.

When you are happy I'll release v4.19.23.

Phil


More information about the PyQt mailing list