2 from math
import isfinite
3 from typing
import Callable
, Optional
5 from test_driver
.logger
import AbstractLogger
8 class PollingConditionError(Exception):
12 class PollingCondition
:
13 condition
: Callable
[[], bool]
14 seconds_interval
: float
15 description
: Optional
[str]
16 logger
: AbstractLogger
23 condition
: Callable
[[], Optional
[bool]],
24 logger
: AbstractLogger
,
25 seconds_interval
: float = 2.0,
26 description
: Optional
[str] = None,
28 self
.condition
= condition
# type: ignore
29 self
.seconds_interval
= seconds_interval
32 if description
is None:
34 self
.description
= condition
.__doc
__
36 self
.description
= condition
.__name
__
38 self
.description
= str(description
)
40 self
.last_called
= float("-inf")
43 def check(self
, force
: bool = False) -> bool:
44 if (self
.entered
or not self
.overdue
) and not force
:
47 with self
, self
.logger
.nested(self
.nested_message
):
48 time_since_last
= time
.monotonic() - self
.last_called
50 f
"Time since last: {time_since_last:.2f}s"
51 if isfinite(time_since_last
)
52 else "(not called yet)"
55 self
.logger
.info(last_message
)
57 res
= self
.condition() # type: ignore
60 res
= res
is None or res
61 self
.logger
.info(self
.status_message(res
))
64 def maybe_raise(self
) -> None:
66 raise PollingConditionError(self
.status_message(False))
68 def status_message(self
, status
: bool) -> str:
69 return f
"Polling condition {'succeeded' if status else 'failed'}: {self.description}"
72 def nested_message(self
) -> str:
73 nested_message
= ["Checking polling condition"]
74 if self
.description
is not None:
75 nested_message
.append(repr(self
.description
))
77 return " ".join(nested_message
)
80 def overdue(self
) -> bool:
81 return self
.last_called
+ self
.seconds_interval
< time
.monotonic()
84 def entered(self
) -> bool:
85 # entry_count should never dip *below* zero
86 assert self
.entry_count
>= 0
87 return self
.entry_count
> 0
89 def __enter__(self
) -> None:
92 def __exit__(self
, exc_type
, exc_value
, traceback
) -> None: # type: ignore
95 self
.last_called
= time
.monotonic()