Skip to content

Commit 2faef0c

Browse files
amaziahubAmazia Gurrockem
authored
[#17] Allow to define minimum time for busy waiting (#62)
* Introduce at_least functionality * DISCLAMER: Timeout message for min time isn't supported yet --------- Co-authored-by: Amazia Gur <[email protected]> Co-authored-by: Eli Segal <[email protected]>
1 parent 3816e06 commit 2faef0c

File tree

3 files changed

+40
-18
lines changed

3 files changed

+40
-18
lines changed

Diff for: busypie/awaiter.py

+26-13
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import asyncio
2-
3-
import busypie
42
import time
3+
from functools import partial
4+
from typing import Callable
55

6+
import busypie
67
from busypie.condition import Condition
78
from busypie.func import describe
89
from busypie.types import Checker, ConditionEvaluator
@@ -16,36 +17,48 @@ def __init__(self, condition: 'Condition', evaluator_checker: Checker):
1617
self._last_error = None
1718

1819
def _validate_condition(self):
19-
if self._condition.poll_delay > self._condition.wait_time_in_secs:
20+
if self._condition.poll_delay > self._condition.max_wait_time:
2021
raise ValueError('Poll delay should be shorter than maximum wait constraint')
22+
if self._condition.min_wait_time >= self._condition.max_wait_time:
23+
raise ValueError('at least should be shorter than maximum wait constraint')
2124

2225
async def wait_for(self, evaluator: ConditionEvaluator) -> any:
2326
start_time = time.time()
27+
time_bounds_checker = partial(self._validate_wait_bounds, evaluator, start_time)
2428
await asyncio.sleep(self._condition.poll_delay)
2529
while True:
2630
try:
2731
result = await self._evaluator_check(evaluator)
2832
if result:
33+
time_bounds_checker(self.is_under_min_wait_time)
2934
return result
3035
except Exception as e:
3136
self._raise_exception_if_not_ignored(e)
3237
self._last_error = e
33-
self._validate_wait_constraint(evaluator, start_time)
38+
time_bounds_checker(self.is_max_wait_time_passed)
3439
await asyncio.sleep(self._condition.poll_interval)
3540

41+
def _validate_wait_bounds(self, condition_evaluator: ConditionEvaluator, start_time: float,
42+
constraint: Callable[[float], bool]):
43+
execution_time = time.time() - start_time
44+
if constraint(execution_time):
45+
raise busypie.ConditionTimeoutError(
46+
self._describe(condition_evaluator), self._condition.max_wait_time) from self._last_error
47+
48+
def _describe(self, condition_evaluator: ConditionEvaluator) -> str:
49+
return self._condition.description or describe(condition_evaluator)
50+
51+
def is_under_min_wait_time(self, execute_time: float):
52+
return execute_time <= self._condition.min_wait_time
53+
3654
def _raise_exception_if_not_ignored(self, e: Exception):
3755
ignored_exceptions = self._condition.ignored_exceptions
3856
if ignored_exceptions is None or \
3957
(ignored_exceptions and e.__class__ not in ignored_exceptions):
4058
raise e
4159

42-
def _validate_wait_constraint(self, condition_evaluator: ConditionEvaluator, start_time: float):
43-
if (time.time() - start_time) > self._condition.wait_time_in_secs:
44-
raise busypie.ConditionTimeoutError(
45-
self._describe(condition_evaluator), self._condition.wait_time_in_secs) from self._last_error
46-
47-
def _describe(self, condition_evaluator: ConditionEvaluator) -> str:
48-
return self._condition.description or describe(condition_evaluator)
60+
def is_max_wait_time_passed(self, execute_time: float):
61+
return execute_time > self._condition.max_wait_time
4962

5063

5164
class ReturnOnTimeoutAwaiter:
@@ -64,7 +77,7 @@ async def wait_for(self, evaluator: ConditionEvaluator) -> any:
6477

6578

6679
class ConditionTimeoutError(Exception):
67-
def __init__(self, description: str, wait_time_in_secs: float):
80+
def __init__(self, description: str, max_wait_time: float):
6881
super(ConditionTimeoutError, self).__init__("Failed to meet condition of [{}] within {} seconds"
69-
.format(description, wait_time_in_secs))
82+
.format(description, max_wait_time))
7083
self.description = description

Diff for: busypie/condition.py

+8-5
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@ def __init__(self, condition: 'Condition' = None):
2121
self._create_time_based_evaluatortions()
2222

2323
def _create_time_based_evaluatortions(self):
24-
self.at_most = self._time_property_evaluator_for('wait_time_in_secs')
24+
self.at_most = self._time_property_evaluator_for('max_wait_time')
2525
self.wait_at_most = self.at_most
2626
self.poll_delay = self._time_property_evaluator_for('poll_delay')
2727
self.poll_interval = self._time_property_evaluator_for('poll_interval')
28+
self.at_least = self._time_property_evaluator_for('min_wait_time')
2829

2930
def _time_property_evaluator_for(self, name: str):
3031
return partial(time_value_operator, visitor=partial(self._time_property, name=name))
@@ -85,12 +86,13 @@ def return_on_timeout(self) -> 'ConditionBuilder':
8586

8687

8788
class Condition:
88-
wait_time_in_secs = DEFAULT_MAX_WAIT_TIME
89+
max_wait_time = DEFAULT_MAX_WAIT_TIME
8990
ignored_exceptions = None
9091
poll_interval = DEFAULT_POLL_INTERVAL
9192
poll_delay = DEFAULT_POLL_DELAY
9293
description = None
9394
return_on_timeout = False
95+
min_wait_time = 0
9496

9597
def append_exception(self, exception: Type[Exception]):
9698
if self.ignored_exceptions is None:
@@ -100,17 +102,18 @@ def append_exception(self, exception: Type[Exception]):
100102
def __eq__(self, other):
101103
if not isinstance(other, Condition):
102104
return False
105+
103106
return \
104-
self.wait_time_in_secs == other.wait_time_in_secs and \
107+
self.max_wait_time == other.max_wait_time and \
105108
self.ignored_exceptions == other.ignored_exceptions and \
106109
self.poll_interval == other.poll_interval and \
107110
self.poll_delay == other.poll_delay
108111

109112

110113
set_default_timeout = partial(
111114
time_value_operator,
112-
visitor=partial(setattr, Condition, 'wait_time_in_secs'))
115+
visitor=partial(setattr, Condition, 'max_wait_time'))
113116

114117

115118
def reset_defaults() -> None:
116-
Condition.wait_time_in_secs = DEFAULT_MAX_WAIT_TIME
119+
Condition.max_wait_time = DEFAULT_MAX_WAIT_TIME

Diff for: tests/test_wait.py

+6
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,9 @@ def condition():
4545

4646
def test_return_on_timeout():
4747
assert wait().at_most(100 * MILLISECOND).return_on_timeout().until(lambda: 2 == 3) is False
48+
49+
50+
def test_raise_given_condition_invoked_before_at_least():
51+
with pytest.raises(ConditionTimeoutError):
52+
with assert_done_after(0.4) as c:
53+
wait().at_least(0.6).until(lambda: c.done)

0 commit comments

Comments
 (0)