As has been noted several times, calling Thread.join() with no argument will wait until the thread finishes before throwing a KeyboardInterrupt. What I find curious is that if you provide any argument at all, then join will exit immediately when interrupted (rather than waiting for the next timeout). Why is this?
# test.py
import threading
x = threading.Event()
def waitForever():
x.wait()
t = threading.Thread(target = waitForever)
t.start()
try:
t.join(float('inf'))
except KeyboardInterrupt:
x.set()
print "Killed"
$ python test.py
^CKilled
$