I am trying to understand what eventlet.tpool is useful for. The docs say that tpool.execute() lets you take a blocking function and run it in a new thread. However, the tpool.execute() method itself blocks until the thread is complete! So how is this possibly useful? If I have some blocking/long running function myfunc() and call it directly, it will block. If I call it inside tpool.execute(myfunc) then the tpool.execute(myfunc) call will block. What exactly is the difference? 
The only thing I can guess is that when myfunc() is called directly, it not only blocks this coroutine but also prevents other coroutines from running, while calling tpool.execute() will block the current coroutine but somehow yields so that other coroutines can run. Is this the case? Otherwise I don't see how tpool can be useful.
You wrote the answer yourself, I can only rephrase it.
With regard to Eventlet, Gevent, Twisted, Asyncio and other cooperative multitasking libraries we use term "blocking" to denote that it blocks everything. Unpatched time.sleep(1) will block all coroutines/greenthreads as opposed to OS threads semantics where it would only block caller OS thread and allow other OS threads to continue.
To differentiate things that block OS thread from things that block coroutine/greenthread we use term "yielding". A yielding function is one that allows execution to rest of coroutines, while blocking (due to Python execution semantics) only caller coroutine.
Armed with that powerful terminology, tpool.execute() turns blocking call into yielding one.
Combined with eventlet.spawn(tpool.execute, fun, ...) it would not block even the caller coroutine. Maybe you find this a helpful combination.
And patches are always welcome. Eventlet is a great library because it contains combined effort of many great people.
How is eventlet tpool useful?