issues with doctest and threads

Tim Peters tim.peters at gmail.com
Tue Aug 9 01:17:10 CEST 2005


[Michele Simionato]
> I am getting a strange error with this script:
>
> $ cat doctest-threads.py
> """
> >>> import time, threading
> >>> def example():
> ...     thread.out = []
> ...     while thread.running:
> ...         time.sleep(.01)
> ...         thread.out.append(".")
> >>> thread = threading.Thread(None, example)
> >>> thread.running = True; thread.start()
> >>> time.sleep(.1)
> >>> thread.running = False
> >>> print thread.out
> ['.', '.', '.', '.', '.', '.', '.', '.', '.']
> """
> 
> if __name__ == "__main__":
>    import doctest; doctest.testmod()
> 
> $ python doctest-threads.py
> Exception in thread Thread-1:
> Traceback (most recent call last):
>  File "/usr/lib/python2.4/threading.py", line 442, in __bootstrap
>    self.run()
>  File "/usr/lib/python2.4/threading.py", line 422, in run
>    self.__target(*self.__args, **self.__kwargs)
>  File "<doctest __main__[1]>", line 5, in example
> NameError: global name 'thread' is not defined

It looks like pure thread-race accident to me.  The main program does
nothing to guarantee that the thread is finished before it prints
`thread.out`, neither anything to guarantee that Python doesn't exit
while the thread is still running.  Stuff, e.g., a time.sleep(5) after
"thread.running = False", and it's much more likely to work the way
you intended (but still not guaranteed).

A guarantee requires explicit synchronization; adding

>>> thread.join()

after "thread.running = False" should be sufficient.  That ensures two things:

1. The `example` thread is done before thread.out gets printed.
2. The *main* thread doesn't exit (and Python doesn't start tearing itself
   down) while the `example` thread is still running.

The exact output depends on OS scheduling accidents, but I expect
you'll see 10 dots most often.

BTW, trying to coordinate threads with sleep() calls is usually a Bad
Idea; you can't generally expect more from an OS than that it will
treat sleep's argument as a lower bound on the elapsed time the
sleeper actually yields the CPU.



More information about the Python-list mailing list