Skip to content

Commit 8c21e4b

Browse files
committed
Add timedout and canceled errors
Also changing out error codes to two digits to avoid confusion with http error codes.
1 parent 26f0cf7 commit 8c21e4b

File tree

3 files changed

+33
-11
lines changed

3 files changed

+33
-11
lines changed

resonate/errors/errors.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import datetime
34
import json
45
from typing import Any
56

@@ -20,17 +21,31 @@ def __str__(self) -> str:
2021

2122
class ResonateValidationError(ResonateError):
2223
def __init__(self, mesg: str) -> None:
23-
super().__init__(mesg, "100")
24+
super().__init__(mesg, "10")
2425

2526

2627
class ResonateShutdownError(ResonateError):
2728
def __init__(self, mesg: str) -> None:
28-
super().__init__(mesg, "200")
29+
super().__init__(mesg, "20")
2930

3031

3132
class ResonateStoreError(ResonateError):
3233
def __init__(self, *args: Any, **kwargs: Any) -> None:
3334
mesg = kwargs.pop("message", "Unknown store error")
3435
code = kwargs.pop("code", "0")
3536
details = kwargs.pop("details", [])
36-
super().__init__(mesg, f"300.{code}", details)
37+
super().__init__(mesg, f"30.{code}", details)
38+
39+
40+
class ResonateCanceledError(ResonateError):
41+
def __init__(self, promise_id: str) -> None:
42+
super().__init__(f"Promise {promise_id} canceled", "40")
43+
self.promise_id = promise_id
44+
45+
46+
class ResonateTimedoutError(ResonateError):
47+
def __init__(self, promise_id: str, timeout: float) -> None:
48+
formatted = datetime.datetime.fromtimestamp(timeout, tz=datetime.UTC).isoformat()
49+
super().__init__(f"Promise {promise_id} timedout at {formatted}", "41")
50+
self.promise_id = promise_id
51+
self.timeout = timeout

resonate/models/durable_promise.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from dataclasses import dataclass, field
44
from typing import TYPE_CHECKING, Any, Literal
55

6+
from resonate.errors.errors import ResonateCanceledError, ResonateTimedoutError
67
from resonate.models.result import Ko, Ok, Result
78

89
if TYPE_CHECKING:
@@ -32,7 +33,15 @@ def params(self) -> Any:
3233
@property
3334
def result(self) -> Result:
3435
assert self.completed, "Promise must be completed"
35-
return Ok(self.value.data) if self.resolved else Ko(self.value.data)
36+
37+
if self.rejected:
38+
return Ko(self.value.data)
39+
if self.canceled:
40+
return Ko(ResonateCanceledError(self.id))
41+
if self.timedout:
42+
return Ko(ResonateTimedoutError(self.id, self.abs_timeout))
43+
44+
return Ok(self.value.data)
3645

3746
@property
3847
def pending(self) -> bool:

resonate/stores/remote.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,18 +72,16 @@ def call(self, req: PreparedRequest) -> Any:
7272
error = {"message": e.response.text, "code": 0}
7373

7474
# Only a 500 response code should be retried
75-
if e.response.status_code == 500 and not delay:
76-
continue
77-
78-
raise ResonateStoreError(**error) from e
75+
if delay is None or e.response.status_code != 500:
76+
raise ResonateStoreError(**error) from e
7977
except requests.exceptions.Timeout as e:
80-
if not delay:
78+
if delay is None:
8179
raise ResonateStoreError(message="Request timed out", code=0) from e
8280
except requests.exceptions.ConnectionError as e:
83-
if not delay:
81+
if delay is None:
8482
raise ResonateStoreError(message="Failed to connect", code=0) from e
8583
except Exception as e:
86-
if not delay:
84+
if delay is None:
8785
raise ResonateStoreError(message="Unknown exception", code=0) from e
8886
else:
8987
return data

0 commit comments

Comments
 (0)