{"id":9565,"library":"busypie","title":"Easy and expressive busy-waiting for Python","description":"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.","status":"active","version":"0.6.1","language":"en","source_language":"en","source_url":"https://github.com/rockem/busypie","tags":["testing","waiting","busy-waiting","timeout","assertion","async","test-automation"],"install":[{"cmd":"pip install busypie","lang":"bash","label":"Install latest version"}],"dependencies":[],"imports":[{"symbol":"wait","correct":"from busypie import wait"},{"symbol":"ConditionTimeoutError","correct":"from busypie import ConditionTimeoutError"}],"quickstart":{"code":"from busypie import wait, ConditionTimeoutError\nimport time\n\n# Simulate an external service state\n_external_service_status = 'starting'\n\ndef get_service_status():\n    global _external_service_status\n    if _external_service_status == 'starting':\n        # Simulate service becoming ready after some time\n        time.sleep(0.05) \n        _external_service_status = 'ready'\n    return _external_service_status\n\ndef is_service_ready():\n    return get_service_status() == 'ready'\n\ndef process_completes_slowly():\n    time.sleep(0.4)\n    return True\n\ntry:\n    print('Waiting for service to be ready...')\n    wait().at_most(1).poll_interval(0.1).until(is_service_ready)\n    print('Service is ready!')\n\n    # Example with at_least (v0.6.0+)\n    print('Waiting for a process that must take at least 0.3s...')\n    wait().at_most(1).at_least(0.3).until(process_completes_slowly)\n    print('Process completed and met minimum duration.')\n\nexcept ConditionTimeoutError as e:\n    print(f'A condition timed out: {e}')\n\n_external_service_status = 'starting'\ntry:\n    # This will timeout because the condition is met too quickly, \n    # but 'at_least' expects a longer duration.\n    print('Waiting for a process that must take at least 1s (will fail)...')\n    wait().at_most(0.5).at_least(1).until(is_service_ready)\n    print('Process completed and met minimum duration (unexpected).')\nexcept ConditionTimeoutError as e:\n    print(f'Caught expected timeout for at_least condition: {e}')","lang":"python","description":"This quickstart demonstrates basic busy-waiting using `wait().until()` and handling `ConditionTimeoutError`. It also shows the usage of `at_most()` for maximum wait time, `poll_interval()` for checking frequency, and `at_least()` for specifying a minimum duration a condition must be met for (introduced in v0.6.0)."},"warnings":[{"fix":"Upgrade your Python environment to 3.8 or higher, or pin busypie to a version prior to 0.6.0 (e.g., `busypie==0.5.1`).","message":"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.","severity":"breaking","affected_versions":">=0.6.0"},{"fix":"Remove access to the `description` attribute from `ConditionTimeoutError` instances. Custom descriptions for conditions can be passed directly to `until()` methods if needed.","message":"The `description` field of the `ConditionTimeoutError` exception was removed in v0.6.0. Any code accessing `error.description` will raise an `AttributeError`.","severity":"breaking","affected_versions":">=0.6.0"},{"fix":"Understand that `at_least()` imposes a *minimum required time* for the condition to be true. If you only need to wait until a condition is true, omit `at_least()`. Use `at_least()` only when verifying that a process or state change takes a certain amount of time.","message":"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.","severity":"gotcha","affected_versions":">=0.6.0"},{"fix":"Ensure you are using `busypie` version 0.4.5 or newer if you intend to perform nested `wait().until()` calls.","message":"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.","severity":"gotcha","affected_versions":"<0.4.5"}],"env_vars":null,"last_verified":"2026-04-17T00:00:00.000Z","next_check":"2026-07-16T00:00:00.000Z","problems":[{"fix":"Run `pip install busypie` to install the library.","cause":"The busypie library is not installed in your current Python environment.","error":"ModuleNotFoundError: No module named 'busypie'"},{"fix":"Increase 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).","cause":"The condition function passed to `until()` did not return a truthy value within the specified (or default) timeout period.","error":"ConditionTimeoutError: Condition was not met within X seconds. Condition: <function my_condition at 0x...>"},{"fix":"Update 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.","cause":"Your code is trying to access the `description` attribute on a `ConditionTimeoutError` instance, but this attribute was removed in busypie v0.6.0.","error":"AttributeError: 'ConditionTimeoutError' object has no attribute 'description'"},{"fix":"Adjust 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.","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.","error":"ConditionTimeoutError: Condition was met, but not for at least X seconds. Condition: <function my_condition at 0x...>"}]}