341 e => PyRuntimeError::new_err(e.to_string()), |
341 e => PyRuntimeError::new_err(e.to_string()), |
342 }) |
342 }) |
343 } |
343 } |
344 } |
344 } |
345 |
345 |
|
346 /// Wrap a call to `func` so that Python's `SIGINT` handler is first stored, |
|
347 /// then restored after the call to `func` and finally raised if |
|
348 /// `func` returns a [`HgError::InterruptReceived`]. |
|
349 /// |
|
350 /// We cannot use [`Python::check_signals`] because it only works from the main |
|
351 /// thread of the main interpreter. To that end, long-running Rust functions |
|
352 /// need to cooperate by listening to their own `SIGINT` signal and return |
|
353 /// the appropriate error on catching that signal: this is especially helpful |
|
354 /// in multithreaded operations. |
|
355 pub fn with_sigint_wrapper<R>( |
|
356 py: Python, |
|
357 func: impl Fn() -> Result<R, HgError>, |
|
358 ) -> PyResult<Result<R, HgError>> { |
|
359 let signal_py_mod = py.import(intern!(py, "signal"))?; |
|
360 let sigint_py_const = signal_py_mod.getattr(intern!(py, "SIGINT"))?; |
|
361 let old_handler = signal_py_mod |
|
362 .call_method1(intern!(py, "getsignal"), (sigint_py_const.clone(),))?; |
|
363 let res = func(); |
|
364 // Reset the old signal handler in Python because we may have changed it |
|
365 signal_py_mod.call_method1( |
|
366 intern!(py, "signal"), |
|
367 (sigint_py_const.clone(), old_handler), |
|
368 )?; |
|
369 if let Err(HgError::InterruptReceived) = res { |
|
370 // Trigger the signal in Python |
|
371 signal_py_mod |
|
372 .call_method1(intern!(py, "raise_signal"), (sigint_py_const,))?; |
|
373 } |
|
374 Ok(res) |
|
375 } |