Skip to content

Commit e589c8a

Browse files
committed
wip
1 parent be8ba51 commit e589c8a

File tree

2 files changed

+30
-3
lines changed

2 files changed

+30
-3
lines changed

hcloud/_utils.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from collections.abc import Iterable, Iterator
44
from itertools import islice
55
from typing import TypeVar
6+
from functools import wraps
7+
from concurrent.futures import ThreadPoolExecutor
68

79
T = TypeVar("T")
810

@@ -17,3 +19,18 @@ def batched(iterable: Iterable[T], size: int) -> Iterator[tuple[T, ...]]:
1719
if not batch:
1820
break
1921
yield batch
22+
23+
24+
def with_timeout(seconds: float | None):
25+
def decorator(f):
26+
@wraps(f)
27+
def wrapper(*args, **kwargs):
28+
if seconds:
29+
with ThreadPoolExecutor(max_workers=1) as executor:
30+
future = executor.submit(f, *args, **kwargs)
31+
return future.result(timeout=seconds)
32+
else:
33+
return f(*args, **kwargs)
34+
35+
return wrapper
36+
return decorator

hcloud/actions/client.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
import time
44
import warnings
5+
from concurrent.futures import ThreadPoolExecutor
6+
from threading import Event
57
from typing import TYPE_CHECKING, Any, Callable, NamedTuple
68

7-
from .._utils import batched
9+
from .._utils import batched, with_timeout
810
from ..core import BoundModelBase, ClientEntityBase, Meta
911
from .domain import Action, ActionFailedException, ActionTimeoutException
1012

@@ -168,6 +170,7 @@ def _get_list_by_ids(self, ids: list[int]) -> list[BoundAction]:
168170

169171
def wait_for_function(
170172
self,
173+
stop: Event,
171174
handle_update: Callable[[BoundAction], None],
172175
actions: list[Action | BoundAction],
173176
) -> list[BoundAction]:
@@ -190,7 +193,9 @@ def wait_for_function(
190193
retries = 0
191194
while len(running_ids):
192195
# pylint: disable=protected-access
193-
time.sleep(self._client._poll_interval_func(retries))
196+
if stop.wait(self._client._poll_interval_func(retries)):
197+
raise ActionTimeoutException()
198+
194199
retries += 1
195200

196201
updates = self._get_list_by_ids(running_ids)
@@ -207,6 +212,7 @@ def wait_for_function(
207212
def wait_for(
208213
self,
209214
actions: list[Action | BoundAction],
215+
timeout: float | None = None,
210216
) -> list[BoundAction]:
211217
"""
212218
Waits until all Actions succeed by polling the API at the interval defined by
@@ -217,14 +223,18 @@ def wait_for(
217223
218224
:param actions: List of Actions to wait for.
219225
:raises: ActionFailedException when an Action failed.
226+
:raises: TimeoutError when the Actions did not succeed before timeout.
220227
:return: List of succeeded Actions.
221228
"""
222229

223230
def handle_update(update: BoundAction) -> None:
224231
if update.status == Action.STATUS_ERROR:
225232
raise ActionFailedException(action=update)
226233

227-
return self.wait_for_function(handle_update, actions)
234+
def run() -> list[BoundAction]:
235+
return self.wait_for_function(handle_update, actions)
236+
237+
return with_timeout(timeout)(run)()
228238

229239
def get_list(
230240
self,

0 commit comments

Comments
 (0)