Threadful
Python Threads - from Dreadful to Threadful
Installation
pip install threadful
Usage
Example 1: Basic usage of @thread
from threadful import thread
@thread
def some_function():
time.sleep(10)
return " done "
some_function().then(lambda result: result.strip()).then(lambda result: print(result))
promise = some_function()
promise.result()
time.sleep(15)
promise.result()
result = promise.join()
What's happening:
- The
@thread
decorator wraps some_function
to make it run in a separate thread when invoked. - Calling
some_function()
doesn't start the thread immediately. Instead, it returns a ThreadWithResult
object, allowing you to attach callbacks using .then()
or .catch()
before the thread starts. - The thread starts running when you begin checking for the result (
result
, join
) or explicitly start it. - The
.then()
chain demonstrates how to process the result when the thread completes. - The
promise.result()
method gives access to the result (Ok
or Err
) if available. - The
.join()
method blocks until the thread finishes and directly returns the result or raises any exception from the thread.
Example 2: Handling exceptions in threads
@thread()
def raises() -> str:
raise ValueError()
promise = raises().catch(lambda err: TypeError())
promise.join()
promise.result()
promise = raises().catch(lambda err: "Something went wrong")
promise.join()
What's happening:
- The
@thread
decorator is used on raises()
, which deliberately raises a ValueError
. - The first
catch()
replaces the ValueError
with a TypeError
, so calling promise.join()
raises the new TypeError
. - The second
catch()
provides a fallback string "Something went wrong"
. When the thread completes, calling join()
returns this string instead of raising an exception. - This mechanism allows you to gracefully handle errors in the thread without crashing your program.
Example: Animating a Function with Different Options
from threadful import thread, animate
import time
@thread
def wait(duration: int):
time.sleep(duration)
return f"Waited for {duration} seconds"
result = animate(wait(3), text="Waiting...")
thread_result = animate(wait(3), text="Running asynchronously...", threaded=True)
thread_result.join()
animate(wait(3), text=lambda: f"Current time: {time.strftime('%H:%M:%S')}")
What's Happening:
- Basic Animation: Runs
wait
with a loading message ("Waiting..."
) and returns the result after completion. - Threaded Animation: Runs both
wait
and the animation in the background. Use .join()
to get the result. - Dynamic Text: Updates the animation text dynamically using a callback (e.g., current time).
These examples show how to use animate
for different scenarios.
Important Note:
A thread doesn't start running immediately when you invoke the decorated function (e.g., some_function()
). This delay allows you to attach callbacks (then
, catch
) before the thread begins execution. The thread starts:
- When you check for its result (
result
). - When you block for its completion (
join
). - In the background if you explicitly call
.start()
.
License
threadful
is distributed under the terms of the MIT license.
Changelog
See CHANGELOG.md