[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.python

Interesting Thread Gotcha

H J van Rooyen

1/15/2008 3:08:00 PM


I thought I would share this nasty little gotcha with the group.

Consider the following code fragment:

<start>
print 'starting kbd thread'
keyboard_thread = thread.start_new_thread(kbd_driver (port_q,kbd_q))
print 'starting main loop'
error = Mainloop(s,port_q,active_q_list)
<end>

It produces, as output, the following:

starting kbd thread
we get here - a

It does not print 'starting main loop', the Mainloop routine
is never executed, and no exceptions are raised.

Here is the offending routine that seems to capture the control:

<start>
def kbd_driver(out_q,in_q):
"""
thread to look for keyboard input and to put it on the queue out_q
also looks for replies on in_q and prints them
"""

kbdname = '/dev/stdin'

kbd = open(kbdname,'r+',1) # Reading, line buffered

unblock(kbd) # Call the magic to unblock keyboard
print 'we get here - a'
while True:

try:
d = kbd.readline() # see if any kbd input
except:
IOError
try:
msg=in_q.get(block=False)
except Queue.Empty:
time.sleep(0.1)
continue
print msg
time.sleep(0.1)
continue
d = d.rstrip() # get rid of line feed
out_q.put([d + '\r',in_q]) # add a carriage return and return q and send
to port
<end>


The unblock is a routine that unblocks a port using fcntl - it
is not the problem. In case you don't believe me, here it is:

def unblock(f):
"""Given file 'f', sets its unblock flag to true."""

fcntl.fcntl(f.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)

I will post the solution tomorrow when I read my mail,
if no one has spotted it by then.

- Hendrik


16 Answers

Dan

1/15/2008 3:51:00 PM

0

On Jan 15, 10:07 am, "Hendrik van Rooyen" <m...@microcorp.co.za>
wrote:
> I thought I would share this nasty little gotcha with the group.
>
> Consider the following code fragment:
>
> <start>
> print 'starting kbd thread'
> keyboard_thread = thread.start_new_thread(kbd_driver (port_q,kbd_q))
> print 'starting main loop'
> error = Mainloop(s,port_q,active_q_list)
> <end>
>
> It produces, as output, the following:
>
> starting kbd thread
> we get here - a
>
> It does not print 'starting main loop', the Mainloop routine
> is never executed, and no exceptions are raised.
>
> Here is the offending routine that seems to capture the control:
>
> <start>
> def kbd_driver(out_q,in_q):
> """
> thread to look for keyboard input and to put it on the queue out_q
> also looks for replies on in_q and prints them
> """
>
> kbdname = '/dev/stdin'
>
> kbd = open(kbdname,'r+',1) # Reading, line buffered
>
> unblock(kbd) # Call the magic to unblock keyboard
> print 'we get here - a'
> while True:
>
> try:
> d = kbd.readline() # see if any kbd input
> except:
> IOError
> try:
> msg=in_q.get(block=False)
> except Queue.Empty:
> time.sleep(0.1)
> continue
> print msg
> time.sleep(0.1)
> continue
> d = d.rstrip() # get rid of line feed
> out_q.put([d + '\r',in_q]) # add a carriage return and return q and send
> to port
> <end>
>
> The unblock is a routine that unblocks a port using fcntl - it
> is not the problem. In case you don't believe me, here it is:
>
> def unblock(f):
> """Given file 'f', sets its unblock flag to true."""
>
> fcntl.fcntl(f.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
>
> I will post the solution tomorrow when I read my mail,
> if no one has spotted it by then.
>
> - Hendrik

>>> keyboard_thread = thread.start_new_thread(kbd_driver (port_q,kbd_q))

Needs to be
>>> keyboard_thread = thread.start_new_thread(kbd_driver, (port_q,kbd_q))

Commas are important!

-Dan

H J van Rooyen

1/16/2008 7:11:00 AM

0

"Dan" <the,,,ail.com> wrote:


> >>> keyboard_thread = thread.start_new_thread(kbd_driver (port_q,kbd_q))
>
> Needs to be
> >>> keyboard_thread = thread.start_new_thread(kbd_driver, (port_q,kbd_q))
>
> Commas are important!
>
> -Dan

Absolutely! - well spotted!

As the first correct respondent, you win the freedom to spend a week in
Naboomspruit at your own expense.

It would have been nice, however, to have gotten something like:

TypeError - This routine needs a tuple.

instead of the silent in line calling of the routine in question,
while failing actually to start a new thread.

It seems to act no different from plain old:

kbd_driver (port_q,kbd_q)

Is it worth the trouble of learning how to submit a bug report?

- Hendrik



Duncan Booth

1/16/2008 1:51:00 PM

0

"Hendrik van Rooyen" <mail@microcorp.co.za> wrote:

> It would have been nice, however, to have gotten something like:
>
> TypeError - This routine needs a tuple.
>
> instead of the silent in line calling of the routine in question,
> while failing actually to start a new thread.

Given that the start_new_thread function never actually got called, what
code exactly do you expect to complain about the absence of a tuple?

>
> It seems to act no different from plain old:
>
> kbd_driver (port_q,kbd_q)
>
> Is it worth the trouble of learning how to submit a bug report?

On your own code? There doesn't appear to be a bug in anyone else's code
here.

Bjoern Schliessmann

1/16/2008 2:11:00 PM

0

Hendrik van Rooyen wrote:
> Absolutely! - well spotted!

This is no threading problem at all; not even a syntax problem. If
you don't know exactly what start_new_thread and kbd_driver
functions do it's impossible to tell if your code does what is
intended.

> It would have been nice, however, to have gotten something like:
>
> TypeError - This routine needs a tuple.
>
> instead of the silent in line calling of the routine in question,
> while failing actually to start a new thread.

Exactly which part of the code should give you this warning?

> Is it worth the trouble of learning how to submit a bug report?

For your problem not, IMHO, as a bug report for it will be closed
quickly.

Regards,


Björn

--
BOFH excuse #330:

quantum decoherence

Diez B. Roggisch

1/16/2008 4:07:00 PM

0

Hendrik van Rooyen wrote:

> "Dan" <the,,,ail.com> wrote:
>
>
>> >>> keyboard_thread = thread.start_new_thread(kbd_driver (port_q,kbd_q))
>>
>> Needs to be
>> >>> keyboard_thread = thread.start_new_thread(kbd_driver, (port_q,kbd_q))
>>
>> Commas are important!
>>
>> -Dan
>
> Absolutely! - well spotted!
>
> As the first correct respondent, you win the freedom to spend a week in
> Naboomspruit at your own expense.
>
> It would have been nice, however, to have gotten something like:
>
> TypeError - This routine needs a tuple.
>
> instead of the silent in line calling of the routine in question,
> while failing actually to start a new thread.

You can't prevent the silent inline-calling - otherwise, how would you do
this:

def compute_thread_target():
def target():
pass
return target

thread.start_new_thread(compute_thread_target())


Of course start_new_thread could throw an error if it got nothing callable
as first argument. No idea why it doesn't.

Diez

Dan

1/16/2008 6:08:00 PM

0

On Jan 16, 11:06 am, "Diez B. Roggisch" <de...@nospam.web.de> wrote:
> Hendrik van Rooyen wrote:
> > "Dan" <the,,,ail.com> wrote:
>
> >> >>> keyboard_thread = thread.start_new_thread(kbd_driver (port_q,kbd_q))
>
> >> Needs to be
> >> >>> keyboard_thread = thread.start_new_thread(kbd_driver, (port_q,kbd_q))
>
> >> Commas are important!
>
> >> -Dan
>
> > Absolutely! - well spotted!
>
> > As the first correct respondent, you win the freedom to spend a week in
> > Naboomspruit at your own expense.
>
> > It would have been nice, however, to have gotten something like:
>
> > TypeError - This routine needs a tuple.
>
> > instead of the silent in line calling of the routine in question,
> > while failing actually to start a new thread.
>
> You can't prevent the silent inline-calling - otherwise, how would you do
> this:
>
> def compute_thread_target():
> def target():
> pass
> return target
>
> thread.start_new_thread(compute_thread_target())
>
> Of course start_new_thread could throw an error if it got nothing callable
> as first argument. No idea why it doesn't.
>
> Diez

Of course, in his case, having start_new_thread throw an error
wouldn't have helped, since he went into an infinite loop while
evaluating the parameters for start_new_thread.

Would it be possible to have pychecker (or some such) warn that there
is an insufficient parameter count to start_new_thread? I guess that
would require knowing the type of thread. . .

-Dan

Diez B. Roggisch

1/16/2008 6:33:00 PM

0

Dan schrieb:
> On Jan 16, 11:06 am, "Diez B. Roggisch" <de...@nospam.web.de> wrote:
>> Hendrik van Rooyen wrote:
>>> "Dan" <the,,,ail.com> wrote:
>>>>>>> keyboard_thread = thread.start_new_thread(kbd_driver (port_q,kbd_q))
>>>> Needs to be
>>>>>>> keyboard_thread = thread.start_new_thread(kbd_driver, (port_q,kbd_q))
>>>> Commas are important!
>>>> -Dan
>>> Absolutely! - well spotted!
>>> As the first correct respondent, you win the freedom to spend a week in
>>> Naboomspruit at your own expense.
>>> It would have been nice, however, to have gotten something like:
>>> TypeError - This routine needs a tuple.
>>> instead of the silent in line calling of the routine in question,
>>> while failing actually to start a new thread.
>> You can't prevent the silent inline-calling - otherwise, how would you do
>> this:
>>
>> def compute_thread_target():
>> def target():
>> pass
>> return target
>>
>> thread.start_new_thread(compute_thread_target())
>>
>> Of course start_new_thread could throw an error if it got nothing callable
>> as first argument. No idea why it doesn't.
>>
>> Diez
>
> Of course, in his case, having start_new_thread throw an error
> wouldn't have helped, since he went into an infinite loop while
> evaluating the parameters for start_new_thread.
>
> Would it be possible to have pychecker (or some such) warn that there
> is an insufficient parameter count to start_new_thread? I guess that
> would require knowing the type of thread. . .

What has this to do with the second argument? It's perfectly legal to
have a function as thread-target that takes no arguments at all, so
enforcing a second argument wouldn't be helpful - all it would do is to
force all developers that don't need an argument tuple to pass the empty
tuple. So there was no insufficient argument count.

And none of these would solve the underlying problem that in python
expressions are evaluated eagerly. Changing that would mean that you end
up with a totally new language.

the only thing that could help to a certain extend would be static
types. Which we don't want here :)

Diez

Dan

1/16/2008 6:46:00 PM

0

On Jan 16, 1:33 pm, "Diez B. Roggisch" <de...@nospam.web.de> wrote:
> Dan schrieb:
>
>
>
> > On Jan 16, 11:06 am, "Diez B. Roggisch" <de...@nospam.web.de> wrote:
> >> Hendrik van Rooyen wrote:
> >>> "Dan" <the,,,ail.com> wrote:
> >>>>>>> keyboard_thread = thread.start_new_thread(kbd_driver (port_q,kbd_q))
> >>>> Needs to be
> >>>>>>> keyboard_thread = thread.start_new_thread(kbd_driver, (port_q,kbd_q))
> >>>> Commas are important!
> >>>> -Dan
> >>> Absolutely! - well spotted!
> >>> As the first correct respondent, you win the freedom to spend a week in
> >>> Naboomspruit at your own expense.
> >>> It would have been nice, however, to have gotten something like:
> >>> TypeError - This routine needs a tuple.
> >>> instead of the silent in line calling of the routine in question,
> >>> while failing actually to start a new thread.
> >> You can't prevent the silent inline-calling - otherwise, how would you do
> >> this:
>
> >> def compute_thread_target():
> >> def target():
> >> pass
> >> return target
>
> >> thread.start_new_thread(compute_thread_target())
>
> >> Of course start_new_thread could throw an error if it got nothing callable
> >> as first argument. No idea why it doesn't.
>
> >> Diez
>
> > Of course, in his case, having start_new_thread throw an error
> > wouldn't have helped, since he went into an infinite loop while
> > evaluating the parameters for start_new_thread.
>
> > Would it be possible to have pychecker (or some such) warn that there
> > is an insufficient parameter count to start_new_thread? I guess that
> > would require knowing the type of thread. . .
>
> What has this to do with the second argument? It's perfectly legal to
> have a function as thread-target that takes no arguments at all, so
> enforcing a second argument wouldn't be helpful - all it would do is to
> force all developers that don't need an argument tuple to pass the empty
> tuple. So there was no insufficient argument count.
>
> And none of these would solve the underlying problem that in python
> expressions are evaluated eagerly. Changing that would mean that you end
> up with a totally new language.
>
> the only thing that could help to a certain extend would be static
> types. Which we don't want here :)
>
> Diez

It doesn't seem to be legal in my version of python (or the doc):

>>> import thread
>>> def bat():
print "hello"


>>> thread.start_new_thread(bat)

Traceback (most recent call last):
File "<pyshell#12>", line 1, in <module>
thread.start_new_thread(bat)
TypeError: start_new_thread expected at least 2 arguments, got 1
>>> thread.start_new_thread(bat, ())
2256hello


>>>

-Dan

Diez B. Roggisch

1/16/2008 7:29:00 PM

0

Dan schrieb:
> On Jan 16, 1:33 pm, "Diez B. Roggisch" <de...@nospam.web.de> wrote:
>> Dan schrieb:
>>
>>
>>
>>> On Jan 16, 11:06 am, "Diez B. Roggisch" <de...@nospam.web.de> wrote:
>>>> Hendrik van Rooyen wrote:
>>>>> "Dan" <the,,,ail.com> wrote:
>>>>>>>>> keyboard_thread = thread.start_new_thread(kbd_driver (port_q,kbd_q))
>>>>>> Needs to be
>>>>>>>>> keyboard_thread = thread.start_new_thread(kbd_driver, (port_q,kbd_q))
>>>>>> Commas are important!
>>>>>> -Dan
>>>>> Absolutely! - well spotted!
>>>>> As the first correct respondent, you win the freedom to spend a week in
>>>>> Naboomspruit at your own expense.
>>>>> It would have been nice, however, to have gotten something like:
>>>>> TypeError - This routine needs a tuple.
>>>>> instead of the silent in line calling of the routine in question,
>>>>> while failing actually to start a new thread.
>>>> You can't prevent the silent inline-calling - otherwise, how would you do
>>>> this:
>>>> def compute_thread_target():
>>>> def target():
>>>> pass
>>>> return target
>>>> thread.start_new_thread(compute_thread_target())
>>>> Of course start_new_thread could throw an error if it got nothing callable
>>>> as first argument. No idea why it doesn't.
>>>> Diez
>>> Of course, in his case, having start_new_thread throw an error
>>> wouldn't have helped, since he went into an infinite loop while
>>> evaluating the parameters for start_new_thread.
>>> Would it be possible to have pychecker (or some such) warn that there
>>> is an insufficient parameter count to start_new_thread? I guess that
>>> would require knowing the type of thread. . .
>> What has this to do with the second argument? It's perfectly legal to
>> have a function as thread-target that takes no arguments at all, so
>> enforcing a second argument wouldn't be helpful - all it would do is to
>> force all developers that don't need an argument tuple to pass the empty
>> tuple. So there was no insufficient argument count.
>>
>> And none of these would solve the underlying problem that in python
>> expressions are evaluated eagerly. Changing that would mean that you end
>> up with a totally new language.
>>
>> the only thing that could help to a certain extend would be static
>> types. Which we don't want here :)
>>
>> Diez
>
> It doesn't seem to be legal in my version of python (or the doc):
>
>>>> import thread
>>>> def bat():
> print "hello"
>
>
>>>> thread.start_new_thread(bat)
>
> Traceback (most recent call last):
> File "<pyshell#12>", line 1, in <module>
> thread.start_new_thread(bat)
> TypeError: start_new_thread expected at least 2 arguments, got 1
>>>> thread.start_new_thread(bat, ())
> 2256hello

Ah, I thought it was optional, as in the threading.Thread(target=...,
args=....)-version. Sorry for not looking that up.

Then you'd might stand a chance that pychecker can find such a situation
- but of course not on a general level, as in the above - that would
only work with type-annotations.



Diez

H J van Rooyen

1/17/2008 7:36:00 AM

0

"Duncan Booth" <dunc...d.invalid> wrote:

> Given that the start_new_thread function never actually got called, what
> code exactly do you expect to complain about the absence of a tuple?

I don't understand this assertion.

I thought that start_new_thread was called with a missing comma in
its argument list, which had the effect that I am complaining about.

Putting the comma in place solved the problem, without any other
changes, so why do you say that start_new_thread was not called?

- Hendrik