Skip to content

Commit 3f02516

Browse files
Fix stacktrace propagation in python (#171)
1 parent 04c6479 commit 3f02516

File tree

3 files changed

+43
-15
lines changed

3 files changed

+43
-15
lines changed

python/restate/server_context.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ async def enter(self):
427427
cause = cause.__cause__
428428
else:
429429
# nothing interesting found, treat as unexpected exception
430-
stacktrace = "\n".join(traceback.format_exception(e))
430+
stacktrace = "".join(traceback.format_exception(e))
431431
self.vm.notify_error(repr(e), stacktrace)
432432
raise
433433
finally:
@@ -680,7 +680,7 @@ async def create_run_coroutine(
680680
except Exception as e:
681681
end = time.time()
682682
attempt_duration = int((end - start) * 1000)
683-
failure = Failure(code=500, message=str(e))
683+
failure = Failure(code=500, message=repr(e), stacktrace="".join(traceback.format_exception(e)))
684684
max_duration_ms = None if max_duration is None else int(max_duration.total_seconds() * 1000)
685685
initial_retry_interval_ms = (
686686
None if initial_retry_interval is None else int(initial_retry_interval.total_seconds() * 1000)

python/restate/vm.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class Failure:
7171

7272
code: int
7373
message: str
74+
stacktrace: typing.Optional[str] = None
7475

7576

7677
@dataclass
@@ -433,7 +434,7 @@ def propose_run_completion_transient(
433434
Exit a side effect with a transient Error.
434435
This requires a retry policy to be provided.
435436
"""
436-
py_failure = PyFailure(failure.code, failure.message)
437+
py_failure = PyFailure(failure.code, failure.message, failure.stacktrace)
437438
py_config = PyExponentialRetryConfig(
438439
config.initial_interval,
439440
config.max_attempts,

src/lib.rs

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -96,13 +96,20 @@ struct PyFailure {
9696
code: u16,
9797
#[pyo3(get, set)]
9898
message: String,
99+
#[pyo3(get, set)]
100+
stacktrace: Option<String>,
99101
}
100102

101103
#[pymethods]
102104
impl PyFailure {
103105
#[new]
104-
fn new(code: u16, message: String) -> PyFailure {
105-
Self { code, message }
106+
#[pyo3(signature = (code, message, stacktrace=None))]
107+
fn new(code: u16, message: String, stacktrace: Option<String>) -> PyFailure {
108+
Self {
109+
code,
110+
message,
111+
stacktrace,
112+
}
106113
}
107114
}
108115

@@ -173,6 +180,7 @@ impl From<TerminalFailure> for PyFailure {
173180
PyFailure {
174181
code: value.code,
175182
message: value.message,
183+
stacktrace: None,
176184
}
177185
}
178186
}
@@ -188,8 +196,18 @@ impl From<PyFailure> for TerminalFailure {
188196
}
189197

190198
impl From<PyFailure> for Error {
191-
fn from(value: PyFailure) -> Self {
192-
Self::new(value.code, value.message)
199+
fn from(
200+
PyFailure {
201+
code,
202+
message,
203+
stacktrace,
204+
}: PyFailure,
205+
) -> Self {
206+
let mut e = Self::new(code, message);
207+
if let Some(stacktrace) = stacktrace {
208+
e = e.with_stacktrace(stacktrace);
209+
}
210+
e
193211
}
194212
}
195213

@@ -501,7 +519,8 @@ impl PyVM {
501519
.map(Into::into)
502520
.collect(),
503521
},
504-
buffer.as_bytes().to_vec().into(),Default::default()
522+
buffer.as_bytes().to_vec().into(),
523+
Default::default(),
505524
)
506525
.map(Into::into)
507526
.map_err(Into::into)
@@ -539,8 +558,8 @@ impl PyVM {
539558
.duration_since(SystemTime::UNIX_EPOCH)
540559
.expect("Duration since unix epoch cannot fail")
541560
+ Duration::from_millis(millis)
542-
})
543-
, Default::default()
561+
}),
562+
Default::default(),
544563
)
545564
.map(|s| s.invocation_id_notification_handle.into())
546565
.map_err(Into::into)
@@ -566,7 +585,7 @@ impl PyVM {
566585
.sys_complete_awakeable(
567586
id,
568587
NonEmptyValue::Success(buffer.as_bytes().to_vec().into()),
569-
Default::default()
588+
Default::default(),
570589
)
571590
.map_err(Into::into)
572591
}
@@ -613,7 +632,8 @@ impl PyVM {
613632
.vm
614633
.sys_complete_promise(
615634
key,
616-
NonEmptyValue::Success(buffer.as_bytes().to_vec().into()),Default::default()
635+
NonEmptyValue::Success(buffer.as_bytes().to_vec().into()),
636+
Default::default(),
617637
)
618638
.map(Into::into)
619639
.map_err(Into::into)
@@ -626,7 +646,11 @@ impl PyVM {
626646
) -> Result<PyNotificationHandle, PyVMError> {
627647
self_
628648
.vm
629-
.sys_complete_promise(key, NonEmptyValue::Failure(value.into()),Default::default())
649+
.sys_complete_promise(
650+
key,
651+
NonEmptyValue::Failure(value.into()),
652+
Default::default(),
653+
)
630654
.map(Into::into)
631655
.map_err(Into::into)
632656
}
@@ -701,7 +725,10 @@ impl PyVM {
701725
) -> Result<(), PyVMError> {
702726
self_
703727
.vm
704-
.sys_write_output(NonEmptyValue::Success(buffer.as_bytes().to_vec().into()),Default::default())
728+
.sys_write_output(
729+
NonEmptyValue::Success(buffer.as_bytes().to_vec().into()),
730+
Default::default(),
731+
)
705732
.map_err(Into::into)
706733
}
707734

@@ -711,7 +738,7 @@ impl PyVM {
711738
) -> Result<(), PyVMError> {
712739
self_
713740
.vm
714-
.sys_write_output(NonEmptyValue::Failure(value.into()),Default::default())
741+
.sys_write_output(NonEmptyValue::Failure(value.into()), Default::default())
715742
.map_err(Into::into)
716743
}
717744

0 commit comments

Comments
 (0)