threads and exception in wxPython

  • Thread starter Zunbeltz Izaola
  • Start date
Z

Zunbeltz Izaola

Hi,

I've an wxPython windows that creates a thread. An object of this
thread raised an exception and i want to cach it in the main thread,
that of the windows (I want this becouse the first implementation
doesn't use thread). I know that exception can not be pass between
thread.

I've read something about wraping the run() method of the threaded
object in a try/except block and save the exception for inspection ,
but i don't know how to implement it. Can you give me and advice?

TIA

Zunbeltz

--
Zunbeltz Izaola Azkona | wmbizazz at lg dot ehu
dotes
Materia Kondentsatuaren Fisika Saila |
Zientzia eta Teknologia Fakultatea | Phone: 34946015326
Euskal Herriko Unibertsitatea |
PK 644 | Fax: 34 944648500
48080 Bilbo (SPAIN) |
 
J

Josiah Carlson

Zunbeltz Izaola said:
I've an wxPython windows that creates a thread. An object of this
thread raised an exception and i want to cach it in the main thread,
that of the windows (I want this becouse the first implementation
doesn't use thread). I know that exception can not be pass between
thread.

I've read something about wraping the run() method of the threaded
object in a try/except block and save the exception for inspection ,
but i don't know how to implement it. Can you give me and advice?

from StringIO import StringIO
import traceback
import Queue

traceback_queue = Queue.Queue()

...
def run(self):
try:
...
except:
tb = StringIO()
traceback.print_exc(tb)
traceback_queue.put(tb.getvalue())
...


- Josiah
 
Z

Zunbeltz Izaola

Josiah Carlson said:
from StringIO import StringIO
import traceback
import Queue

traceback_queue = Queue.Queue()

...
def run(self):
try:
...
except:
tb = StringIO()
traceback.print_exc(tb)
traceback_queue.put(tb.getvalue())
...


- Josiah

Ok, thanks for the help. But I still have a problem. When the lines
in the try block raise an exception it is saved in the traceback_quee
and the thread is finished. Is that correct?. The problem is how do I
inspect the queue? The function that creates the thread is something
like that

def OnMeasurement(self):
self.CurrentMeasurement.start()

If I put some code after the .start() method the queue will be
empty. Do I need to insert a infinite loop to see when the exception
is raised?

TIA

Zunbeltz Izaola

--
Zunbeltz Izaola Azkona | wmbizazz at lg dot ehu
dotes
Materia Kondentsatuaren Fisika Saila |
Zientzia eta Teknologia Fakultatea | Phone: 34946015326
Euskal Herriko Unibertsitatea |
PK 644 | Fax: 34 944648500
48080 Bilbo (SPAIN) |
 
A

Antoon Pardon

Op 2004-11-02 said:
Hi,

I've an wxPython windows that creates a thread. An object of this
thread raised an exception and i want to cach it in the main thread,
that of the windows (I want this becouse the first implementation
doesn't use thread). I know that exception can not be pass between
thread.

Yes they can. Unfortunately in order to protect us from ourselves
the powers that be, decided to only allow this to people who know C
and have access to a C compilor.

from http://docs.python.org/api/threads.html:

int PyThreadState_SetAsyncExc( long id, PyObject *exc)
Asynchronously raise an exception in a thread. The id argument is
the thread id of the target thread; exc is the exception object to
be raised. This function does not steal any references to exc. To
prevent naive misuse, you must write your own C extension to call
this. Must be called with the GIL held. Returns the number of thread
states modified; if it returns a number greater than one, you're in
trouble, and you should call it again with exc set to NULL to revert
the effect. This raises no exceptions. New in version 2.3.


I find this most unfortunate, because in my view being naive is
not in opposition with easily knowing to write a C extention.
I also find it a bad idea to force people to know C for a feature
which has some perfect uses for people who don't know C and normally
have no need to know C.
 
P

Peter Hansen

Antoon said:
Op 2004-11-02, Zunbeltz Izaola schreef <[email protected]>:
Yes they can. Unfortunately in order to protect us from ourselves
the powers that be, decided to only allow this to people who know C
and have access to a C compilor.

int PyThreadState_SetAsyncExc( long id, PyObject *exc)

There was a comment by someone that perhaps ctypes would allow
calling this directly. I don't know whether that's possible,
but if it is it's probably a fairly simple operation. I've
used ctypes in a fairly limited fashion, so I suspect it
would take me a while to figure it out, but this much works
anyway:
<ctypes._CdeclFuncPtr object at 0x0098FB88>

Presumably the "id" above is a non-issue, but I think you
need to do something more complex to get a PyObject reference
to an exception in ctypes... not yet in my repertoire.

A quick test gave this when trying the brainless approach,
with t being a thread and exc a KeyboardInterrupt instance.
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: expected CData instance


Anyone? Anyone? Bueller?

-Peter
 
P

Peter Hansen

Peter said:
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: expected CData instance

First error in the above: the thread id is not id(thread)
but the value returned by thread.start_new_thread or the
value returned by threading._get_ident().

Also ctypes provides a py_object class which I have no
idea how to use but could guess at wildly.

Also, ctypes provides a "pythonapi" thingy which should
be used in preference to cdll.python23, so the code
becomes more like this:
<T(Thread-2, started)>

Hmm... clearly that's not enough, but perhaps closer.

(I find it hard to believe nobody has tried this yet, unless
it's the case that those who know how to do it also know that
it's actually not possible for some reason...)

-Peter
 
P

Peter Hansen

Peter said:
<T(Thread-2, started)>

Hmm... clearly that's not enough, but perhaps closer.

But shortly after, trying to create a new thread, I got:
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 2, in __init__
File "c:\a\python23\lib\threading.py", line 384, in __init__
self.__block = Condition(Lock())
File "c:\a\python23\lib\threading.py", line 148, in Condition
return _Condition(*args, **kwargs)
File "c:\a\python23\lib\threading.py", line 154, in __init__
if lock is None:
ValueError<T(Thread-2, started)>


So the exception managed to get to the new thread, even though
I called SetAsyncExc *before* the thread was even created.

Now _that's_ what I call "asynchronous". ;-)

-Peter
 
P

Peter Hansen

Peter said:
So the exception managed to get to the new thread, even though
I called SetAsyncExc *before* the thread was even created.

Now _that's_ what I call "asynchronous". ;-)

Actually, it's what you call pilot error. I was retrieving
threading._get_ident() in the thread initializer *before*
the thread was even started... stupid pilot.

Here's the final working version:
.... def __init__(self):
.... threading.Thread.__init__(self)
.... self.stopped = False
.... self.start()
.... def run(self):
.... self.id = threading._get_ident()
.... try:
.... while not self.stopped:
.... time.sleep(0.1)
.... except:
.... print 'thread terminated!'
....<T(Thread-7, stopped)>


-Peter
 
T

Thomas Heller

Peter Hansen said:
Actually, it's what you call pilot error. I was retrieving
threading._get_ident() in the thread initializer *before*
the thread was even started... stupid pilot.

Here's the final working version:

... def __init__(self):
... threading.Thread.__init__(self)
... self.stopped = False
... self.start()
... def run(self):
... self.id = threading._get_ident()
... try:
... while not self.stopped:
... time.sleep(0.1)
... except:
... print 'thread terminated!'
...

I'm not sure this is the way to do it (but py_object is completely
undocumented, so far, and I don't rememeber the details myself). But,
it is fine to pass the id() of a python object, which is an integer
specifying it's address, when the function expects a PyObject*:

asyncexc(t.id, id(ValueError(42)))
1
<T(Thread-7, stopped)>


-Peter

Thomas
 
N

Nick Craig-Wood

Peter Hansen said:
<T(Thread-7, stopped)>

On a related subject, I'm trying to forcibly terminate a thread thats
already running. I don't see any means of doing this in the docs.
Sending it an exception would be ideal. Is the above the only way of
doing this?
 
P

Peter Hansen

Nick said:
On a related subject, I'm trying to forcibly terminate a thread thats
already running. I don't see any means of doing this in the docs.
Sending it an exception would be ideal. Is the above the only way of
doing this?

This isn't just a related subject, this is exactly what
I was trying to do above.

I have a test case here which is failing, however, because
the exception is *not* being delivered asynchronously for
some reason, but appears in the thread only when the thread
is already terminating itself. I'm still experimenting, but
I will probably post the failing test case shortly for
others to play with.

Nick, if you want to learn more, read the threads from this
google search: http://groups.google.ca/groups?q=PyThreadState_SetAsyncExc

Then you'll know as much as anyone about this...

-Peter
 
M

Michael Hobbs

Zunbeltz Izaola said:
Ok, thanks for the help. But I still have a problem. When the lines
in the try block raise an exception it is saved in the traceback_quee
and the thread is finished. Is that correct?. The problem is how do I
inspect the queue? The function that creates the thread is something
like that

def OnMeasurement(self):
self.CurrentMeasurement.start()

If I put some code after the .start() method the queue will be
empty. Do I need to insert a infinite loop to see when the exception
is raised?

In addition to adding the exception to the queue, you should post
a custom event to the main window or wx.App. The callback bound to
that event would then read the exception out of the queue and print
it, raise it, send an email, whatever.

-- Mike
 
T

Thomas Heller

Peter Hansen said:
This isn't just a related subject, this is exactly what
I was trying to do above.

I have a test case here which is failing, however, because
the exception is *not* being delivered asynchronously for
some reason, but appears in the thread only when the thread
is already terminating itself. I'm still experimenting, but
I will probably post the failing test case shortly for
others to play with.

Can it be that the exception is delivered asynchronously, but still has
to be checked by some code somewhere (my guess would be ceval.c)?
It seems sys.setcheckinterval() plays a role here.

I'l append my script at the end, I have the impression that the thread
has to execute quite some byte codes before it notices the pending
exception. That's the reason for the 'for i in range(100):' loop in the
thread.

Thomas

<code>
import thread, time, sys
from ctypes import pythonapi, py_object

##sys.setcheckinterval(1)

def run():
while 1:
time.sleep(0.3)
for i in range(100):
pass
sys.stdout.write(".")

exc = ValueError(42)

t = thread.start_new_thread(run, ())
# allow the thread to start
time.sleep(1)

# raise exception in thread
print pythonapi.PyThreadState_SetAsyncExc(t, id(exc))


##print pythonapi.PyThreadState_SetAsyncExc(t, py_object(exc))

# allow thread to print the exception
time.sleep(1)
print "done"

</code>
 
P

Peter Hansen

Thomas said:
Can it be that the exception is delivered asynchronously, but still has
to be checked by some code somewhere (my guess would be ceval.c)?
It seems sys.setcheckinterval() plays a role here.

Exactly! That is definitely it. I was sitting in a small
loop doing a time.sleep() every so often, and during the
entire test that thread probably executed only about forty
or fifty bytecodes.

Your technique with the "busy loop" works, as does setting
sys.setcheckinterval() to a much smaller value than the
default 100.

Thanks Thomas!

-Peter
 
T

Thomas Heller

Peter Hansen said:
I should note for the record that neither Thomas'
example above nor even my more complex one
(posted earlier, but unfortunately messed up on
my news server so I can't reply to it), implements
the required API fully.

The docs for that function say that you should check
the return value, and if it returns a value greater
than 1, you must call it again with a NULL as the
second argument (anyone know if None would be
okay when using ctypes?), or you probably risk screwing
up even more than this messy way of killing a
thread will screw things up normally.

Correct.

And yes, None is the ctypes way to spell NULL (although it would accept
the interger 0 as well).

Thomas
 
P

Peter Hansen

Thomas said:
# raise exception in thread
print pythonapi.PyThreadState_SetAsyncExc(t, id(exc))

I should note for the record that neither Thomas'
example above nor even my more complex one
(posted earlier, but unfortunately messed up on
my news server so I can't reply to it), implements
the required API fully.

The docs for that function say that you should check
the return value, and if it returns a value greater
than 1, you must call it again with a NULL as the
second argument (anyone know if None would be
okay when using ctypes?), or you probably risk screwing
up even more than this messy way of killing a
thread will screw things up normally.

-Peter
 
J

Josiah Carlson

Zunbeltz Izaola said:
Ok, thanks for the help. But I still have a problem. When the lines
in the try block raise an exception it is saved in the traceback_quee
and the thread is finished. Is that correct?. The problem is how do I
inspect the queue? The function that creates the thread is something
like that

def OnMeasurement(self):
self.CurrentMeasurement.start()

If I put some code after the .start() method the queue will be
empty. Do I need to insert a infinite loop to see when the exception
is raised?

In your main thread (the one handling the GUI), you can set up a
wx.Timer() whose bound event checks the queue every second or so.

As another poster mentioned, you can also post events to the GUI, which
can either say "there is an exception traceback in the queue" or "here
is the exception traceback".

It is really all a matter of taste.


- Josiah
 
J

Jeff Shannon

Josiah said:
In your main thread (the one handling the GUI), you can set up a
wx.Timer() whose bound event checks the queue every second or so.

As another poster mentioned, you can also post events to the GUI, which
can either say "there is an exception traceback in the queue" or "here
is the exception traceback".

Another possibility is to check the queue in an idle-event handler.
When you place something in the queue, you can call wx.WakeUpIdle() to
ensure that the main thread will do idle processing at its soonest
opportunity.

Jeff Shannon
Technician/Programmer
Credit International
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

save an opengl canvas (wxPython) 2
handle a log file. 3
Using ConfigParse 5

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,906
Latest member
SkinfixSkintag

Latest Threads

Top