Tracking Elapsed Time with TimeoutWatch
Track how much time remains in a custom polling loop, orchestration workflow, or multi-step operation using TimeoutWatch — a lightweight countdown timer that starts when you create it.
Prerequisites
timeout-samplerinstalled in your project (see Getting Started with timeout-sampler)
Quick Example
from timeout_sampler import TimeoutWatch
watch = TimeoutWatch(timeout=30)
while watch.remaining_time() > 0:
result = do_something()
if result:
break
TimeoutWatch records the current time when instantiated and returns how many seconds are left each time you call remaining_time().
Step-by-Step Usage
1. Create a TimeoutWatch
Pass the total number of seconds you want to track:
from timeout_sampler import TimeoutWatch
watch = TimeoutWatch(timeout=60)
The countdown starts immediately — there is no separate start() call.
2. Check Remaining Time
Call remaining_time() to get the seconds left:
seconds_left = watch.remaining_time()
print(f"{seconds_left:.1f} seconds remaining")
- Returns a
floatwhen time remains. - Returns
0once the timeout has elapsed (it never returns a negative value).
3. Use in a Loop
Build a polling loop that runs until the timeout expires:
from timeout_sampler import TimeoutWatch
watch = TimeoutWatch(timeout=10)
while watch.remaining_time() > 0:
status = check_service_health()
if status == "ready":
print("Service is up!")
break
time.sleep(1)
else:
print("Timed out waiting for service.")
Tip: The
while/elsepattern in Python lets you run theelseblock only when the loop condition becomes false — a clean way to handle timeouts without extra flags.
4. Calculate Elapsed Time
Since TimeoutWatch tracks remaining time, you can derive how much time has passed:
watch = TimeoutWatch(timeout=30)
# ... some work ...
elapsed = watch.timeout - watch.remaining_time()
print(f"Elapsed: {elapsed:.2f}s")
This is the same technique that TimeoutSampler uses internally to report elapsed time in its logs.
API Reference
TimeoutWatch(timeout)
| Parameter | Type | Description |
|---|---|---|
timeout |
float |
Total countdown duration in seconds |
Creates a new watch and records the start time immediately.
remaining_time()
def remaining_time(self) -> int | float
Returns the number of seconds left until the timeout expires. The return value is clamped to 0 — it will never be negative.
| Condition | Return value |
|---|---|
| Called before timeout expires | Positive float |
| Called after timeout expires | 0 |
Advanced Usage
Coordinating Multiple Steps Under One Budget
When you need several sequential operations to fit within a shared time budget, create one TimeoutWatch and pass its remaining time to each step:
from timeout_sampler import TimeoutSampler, TimeoutWatch
overall = TimeoutWatch(timeout=120)
# Step 1: Wait for database
for sample in TimeoutSampler(
wait_timeout=overall.remaining_time(),
sleep=2,
func=check_database,
):
if sample:
break
# Step 2: Wait for cache (uses whatever time is left)
for sample in TimeoutSampler(
wait_timeout=overall.remaining_time(),
sleep=2,
func=check_cache,
):
if sample:
break
Each TimeoutSampler receives only the remaining portion of the overall budget, so the total wall-clock time never exceeds 120 seconds regardless of how long step 1 takes.
Note: If
remaining_time()returns0before a step begins, theTimeoutSamplerwill raise aTimeoutExpiredErrorimmediately. See TimeoutExpiredError Reference for details on that exception.
Passing Fractional Timeouts
TimeoutWatch accepts float values, so sub-second precision works out of the box:
watch = TimeoutWatch(timeout=0.5)
# Half-second budget
Using TimeoutWatch Without TimeoutSampler
TimeoutWatch has no dependency on TimeoutSampler — use it anywhere you need a simple countdown:
from timeout_sampler import TimeoutWatch
watch = TimeoutWatch(timeout=5)
items = get_work_items()
for item in items:
if watch.remaining_time() == 0:
print("Time budget exhausted, stopping early.")
break
process(item)
Troubleshooting
| Problem | Cause | Fix |
|---|---|---|
remaining_time() returns 0 immediately |
timeout was set to 0 or a negative value |
Use a positive timeout value |
| Elapsed time calculation seems wrong | You created the TimeoutWatch too early (e.g., at module import time) |
Create the instance right before the work begins |
| Loop never exits | Your loop body doesn't call remaining_time() on each iteration |
Ensure the while condition re-evaluates remaining_time() every pass |
Related Pages
- Polling a Function with TimeoutSampler — the primary polling interface that uses
TimeoutWatchunder the hood - TimeoutWatch API — full constructor and method reference
- TimeoutExpiredError Reference — the exception raised when time runs out