Easy and expressive busy-waiting for Python
Busypie is a Python library for expressive busy-waiting, primarily used in integration and end-to-end tests to await conditions. It provides a fluent API for defining timeouts, retry intervals, and custom error messages. The current version is 0.6.1, with a release cadence that has seen several updates in the past year.
Common errors
-
ModuleNotFoundError: No module named 'busypie'
cause The busypie library is not installed in your current Python environment.fixRun `pip install busypie` to install the library. -
ConditionTimeoutError: Condition was not met within X seconds. Condition: <function my_condition at 0x...>
cause The condition function passed to `until()` did not return a truthy value within the specified (or default) timeout period.fixIncrease the timeout using `wait().at_most(Y_seconds)` or investigate why your condition is not becoming true (e.g., service not starting, asynchronous operation not completing). -
AttributeError: 'ConditionTimeoutError' object has no attribute 'description'
cause Your code is trying to access the `description` attribute on a `ConditionTimeoutError` instance, but this attribute was removed in busypie v0.6.0.fixUpdate your exception handling logic to no longer rely on the `description` attribute. If you need custom messages, include them in the exception message or use alternative methods for condition description. -
ConditionTimeoutError: Condition was met, but not for at least X seconds. Condition: <function my_condition at 0x...>
cause You are using `at_least(X_seconds)` and the condition became true, but it did so too quickly (i.e., in less than `X_seconds`). Busypie raises this error to enforce the minimum duration.fixAdjust the `at_least()` duration to match the actual minimum time your condition should be true, or remove `at_least()` if you only need the condition to eventually become true, regardless of how long it took.
Warnings
- breaking Busypie v0.6.0 and later officially dropped support for Python versions below 3.8. Installing or running on older Python versions may lead to `ModuleNotFoundError` or unexpected runtime issues.
- breaking The `description` field of the `ConditionTimeoutError` exception was removed in v0.6.0. Any code accessing `error.description` will raise an `AttributeError`.
- gotcha The `at_least()` method (introduced in v0.6.0) ensures a condition is met for a minimum duration. If the condition resolves *before* this minimum duration, a `ConditionTimeoutError` is raised, even if the condition was technically true. This can be counter-intuitive if not expected.
- gotcha In older versions (prior to v0.4.5), nested busy-waiting could lead to errors. This was fixed, but users migrating from very old versions might encounter issues if not updated.
Install
-
pip install busypie
Imports
- wait
from busypie import wait
- ConditionTimeoutError
from busypie import ConditionTimeoutError
Quickstart
from busypie import wait, ConditionTimeoutError
import time
# Simulate an external service state
_external_service_status = 'starting'
def get_service_status():
global _external_service_status
if _external_service_status == 'starting':
# Simulate service becoming ready after some time
time.sleep(0.05)
_external_service_status = 'ready'
return _external_service_status
def is_service_ready():
return get_service_status() == 'ready'
def process_completes_slowly():
time.sleep(0.4)
return True
try:
print('Waiting for service to be ready...')
wait().at_most(1).poll_interval(0.1).until(is_service_ready)
print('Service is ready!')
# Example with at_least (v0.6.0+)
print('Waiting for a process that must take at least 0.3s...')
wait().at_most(1).at_least(0.3).until(process_completes_slowly)
print('Process completed and met minimum duration.')
except ConditionTimeoutError as e:
print(f'A condition timed out: {e}')
_external_service_status = 'starting'
try:
# This will timeout because the condition is met too quickly,
# but 'at_least' expects a longer duration.
print('Waiting for a process that must take at least 1s (will fail)...')
wait().at_most(0.5).at_least(1).until(is_service_ready)
print('Process completed and met minimum duration (unexpected).')
except ConditionTimeoutError as e:
print(f'Caught expected timeout for at_least condition: {e}')