I think I have proven myself wrong:
Tracking thread.get_ident() when the exception occurs always
shows one and the same thread, the main (GUI/wxpython)
thread.
And, no, I haven't been able to create a small sample - not
even been able to consistently duplicate the exception. It
seems to depend on complex interactions of
- scheduling data reload when certain signals are
received from a PostgreSQL database (sets a flag
checked at EVT_PAINT time)
- yes, the database listener runs in a different
thread but it does not touch the GUI AFAICT
- reloading data from the database on EVT_PAINT
-> this always runs in the main thread because
it is triggered by wxPython itself
-> should I just wx.CallAfter() reloading from there ?
- also reloading data on EVT_NOTEBOOK_PAGE_CHANGED
- both effects (EVT_NOTEBOOK_PAGE_CHANGED and EVT_PAINT)
appearing to get on top of each other DESPITE
proven to be running in the same thread !?!
- the user effecting scheduling of data reload
from elsewhere in the app by changing data while
the list in question is not in view and also
rapidly switching notebook pages while at it
- a potential timing issue may be confounded by
the fact that there are typically at least
50 items to be read from the database (which
does run over UNIX domain sockets on the local
machine, however), those items are, of course,
retrieved in one SQL go, not one by one
The corresponding snippet from above now reads:
def set_string_items(self, items=None):
"""All item members must be unicode()able or None."""
if self.debug is not None:
_log.debug('GetItemCount() before DeleteAllItems(): %s (%s, thread [%s])', self.GetItemCount(), self.debug, thread.get_ident())
if not self.DeleteAllItems():
_log.debug('DeleteAllItems() failed (%s)', self.debug)
item_count = self.GetItemCount()
if self.debug is not None:
_log.debug('GetItemCount() after DeleteAllItems(): %s (%s)', item_count, self.debug)
if item_count != 0:
_log.debug('GetItemCount() not 0 after DeleteAllItems() (%s)', self.debug)
...
(where self.debug let's me pinpoint individual listctrls as
needed)
This does keep logging things like
"GetItemCount() not 0 after DeleteAllItems() (provider inbox)"
from time to time which will then lead to a second set of
list items gladly being added onto the listctrl while the
corresponding set of list item data (which my listctrl is
tracking itself) is properly set to only the first set of
items. Which later on leads to a follow-on exception when a
lower-down list item is activated whereby its associated
data is attempted to be retrieved but does not exist.
If anyone has got more ideas for what to look at I'd be
indebted. Meanwhile I'm not reloading on the
EVT_NOTEBOOK_PAGE_CHANGED and logging problems in search for
more clues.
Karsten
···
On Thu, Jan 24, 2013 at 11:17:31AM -0800, Robin Dunn wrote:
>On Thu, Jan 24, 2013 at 10:52:30AM +0100, Karsten Hilbert wrote:
>
>>from time to time -- under not yet fully understood
>>circumstances -- the following code snippet actually
>>succeeds in throwing an exception in our application
>>(www.gnumed.de) running under wxPython 2.8.12.1 on
>>Debian (self is a wx.ListCtrl child):
>>
>> def set_string_items(self, items = None):
>> """All item members must be unicode()able or None."""
>>
>> self.DeleteAllItems()
>> if self.ItemCount != 0:
>> raise ValueError('.ItemCount not 0 after .DeleteAllItems()')
>
>Or maybe I should not trust self.ItemCount "too soon" (the
>web talks about it being updated only "later").
I don't see anything in the code for the generic listctrl (which is
the one used on wxGTK) that is delaying any part of the delete-all to
idle time or anything like that. It boils down to basically just
sending a notification and clearing the list of list items. The
GetItemCount simply returns the length of that list.
If you can consistently duplicate this problem in a small sample then
I would be happy to be proven wrong, but in the meantime I'm thinking
thats your speculation about a rogue thread interaction is probably
the problem.
--
GPG key ID E4071346 @ gpg-keyserver.de
E167 67FD A291 2BEA 73BD 4537 78B9 A9F9 E407 1346