rust/hg-cpython/src/utils.rs
author Georges Racinet <georges.racinet@cloudcrane.io>
Sat, 30 Nov 2024 20:57:02 +0100
changeset 52411 c2480ac4c5e2
parent 52213 96b113d22b34
permissions -rw-r--r--
rust-pyo3: retrieving the InnerRevlog of hg-cpython This allows PyO3-based code to use the InnerRevlog, access its shared data (core InnerRevlog), which will then allow, e.g., to retrieve references on the core Index. On the `hg-cpython` (`rusthg` crate, `rustext` Python extension module), we had to also build as a Rust library, and open up some accesses (see notably the public accessor for `inner`, the core `InnerRevlog`). Retrieving the Rust struct underlying a Python object defined by another extension module written in Rust is tricky because the Python type objects are duplicated in the extension modules, leading to failure of the normal type checking. See the doc-comment of `convert_cpython::extract_inner_revlog` for a complete explanation. To solve this, we import the Python type object of `rustext` (defined by `hg-cpython`) and perform a manual check. Checking the Python type is necessary, as PyO3 documentation clearly state that downcasting an object that has not the proper type is Undefined Behaviour. At this point, we do not have conversion facilities for exceptions (`PyErr` on both sides), hence the remaining unwraps).
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
52213
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
     1
use cpython::exc::{KeyboardInterrupt, ValueError};
52046
de317a87ea6a rust-pathauditor: match more of Python's behavior and display messages
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52042
diff changeset
     2
use cpython::{
52213
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
     3
    ObjectProtocol, PyBytes, PyClone, PyDict, PyErr, PyObject, PyResult,
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
     4
    PyTuple, Python, ToPyObject,
52046
de317a87ea6a rust-pathauditor: match more of Python's behavior and display messages
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52042
diff changeset
     5
};
52042
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
     6
use hg::config::Config;
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
     7
use hg::errors::HgError;
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
     8
use hg::repo::{Repo, RepoError};
44505
d738b7a18438 rust-nodemap: add utils to create `Node`s from Python objects
Georges Racinet <georges.racinet@octobus.net>
parents: 43251
diff changeset
     9
use hg::revlog::Node;
52042
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    10
use hg::utils::files::get_path_from_bytes;
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    11
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    12
use crate::exceptions::FallbackError;
43251
970978975574 rust-utils: introduce a debug util to print the python stack trace
Rapha?l Gom?s <rgomes@octobus.net>
parents:
diff changeset
    13
970978975574 rust-utils: introduce a debug util to print the python stack trace
Rapha?l Gom?s <rgomes@octobus.net>
parents:
diff changeset
    14
#[allow(unused)]
970978975574 rust-utils: introduce a debug util to print the python stack trace
Rapha?l Gom?s <rgomes@octobus.net>
parents:
diff changeset
    15
pub fn print_python_trace(py: Python) -> PyResult<PyObject> {
970978975574 rust-utils: introduce a debug util to print the python stack trace
Rapha?l Gom?s <rgomes@octobus.net>
parents:
diff changeset
    16
    eprintln!("===============================");
970978975574 rust-utils: introduce a debug util to print the python stack trace
Rapha?l Gom?s <rgomes@octobus.net>
parents:
diff changeset
    17
    eprintln!("Printing Python stack from Rust");
970978975574 rust-utils: introduce a debug util to print the python stack trace
Rapha?l Gom?s <rgomes@octobus.net>
parents:
diff changeset
    18
    eprintln!("===============================");
970978975574 rust-utils: introduce a debug util to print the python stack trace
Rapha?l Gom?s <rgomes@octobus.net>
parents:
diff changeset
    19
    let traceback = py.import("traceback")?;
970978975574 rust-utils: introduce a debug util to print the python stack trace
Rapha?l Gom?s <rgomes@octobus.net>
parents:
diff changeset
    20
    let sys = py.import("sys")?;
970978975574 rust-utils: introduce a debug util to print the python stack trace
Rapha?l Gom?s <rgomes@octobus.net>
parents:
diff changeset
    21
    let kwargs = PyDict::new(py);
970978975574 rust-utils: introduce a debug util to print the python stack trace
Rapha?l Gom?s <rgomes@octobus.net>
parents:
diff changeset
    22
    kwargs.set_item(py, "file", sys.get(py, "stderr")?)?;
970978975574 rust-utils: introduce a debug util to print the python stack trace
Rapha?l Gom?s <rgomes@octobus.net>
parents:
diff changeset
    23
    traceback.call(py, "print_stack", PyTuple::new(py, &[]), Some(&kwargs))
970978975574 rust-utils: introduce a debug util to print the python stack trace
Rapha?l Gom?s <rgomes@octobus.net>
parents:
diff changeset
    24
}
44505
d738b7a18438 rust-nodemap: add utils to create `Node`s from Python objects
Georges Racinet <georges.racinet@octobus.net>
parents: 43251
diff changeset
    25
52042
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    26
pub fn hgerror_to_pyerr<T>(
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    27
    py: Python,
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    28
    error: Result<T, HgError>,
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    29
) -> PyResult<T> {
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    30
    error.map_err(|e| match e {
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    31
        HgError::IoError { .. } => {
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    32
            PyErr::new::<cpython::exc::IOError, _>(py, e.to_string())
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    33
        }
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    34
        HgError::UnsupportedFeature(e) => {
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    35
            let as_string = e.to_string();
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    36
            log::trace!("Update from null fallback: {}", as_string);
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    37
            PyErr::new::<FallbackError, _>(py, &as_string)
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    38
        }
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    39
        HgError::RaceDetected(_) => {
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    40
            unreachable!("must not surface to the user")
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    41
        }
52046
de317a87ea6a rust-pathauditor: match more of Python's behavior and display messages
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52042
diff changeset
    42
        HgError::Path(path_error) => {
de317a87ea6a rust-pathauditor: match more of Python's behavior and display messages
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52042
diff changeset
    43
            let msg = PyBytes::new(py, path_error.to_string().as_bytes());
de317a87ea6a rust-pathauditor: match more of Python's behavior and display messages
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52042
diff changeset
    44
            let cls = py
de317a87ea6a rust-pathauditor: match more of Python's behavior and display messages
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52042
diff changeset
    45
                .import("mercurial.error")
de317a87ea6a rust-pathauditor: match more of Python's behavior and display messages
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52042
diff changeset
    46
                .and_then(|m| m.get(py, "InputError"))
de317a87ea6a rust-pathauditor: match more of Python's behavior and display messages
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52042
diff changeset
    47
                .unwrap();
de317a87ea6a rust-pathauditor: match more of Python's behavior and display messages
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52042
diff changeset
    48
            PyErr::from_instance(
de317a87ea6a rust-pathauditor: match more of Python's behavior and display messages
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52042
diff changeset
    49
                py,
de317a87ea6a rust-pathauditor: match more of Python's behavior and display messages
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52042
diff changeset
    50
                cls.call(py, (msg,), None).ok().into_py_object(py),
de317a87ea6a rust-pathauditor: match more of Python's behavior and display messages
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52042
diff changeset
    51
            )
de317a87ea6a rust-pathauditor: match more of Python's behavior and display messages
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52042
diff changeset
    52
        }
52213
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
    53
        HgError::InterruptReceived => {
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
    54
            PyErr::new::<KeyboardInterrupt, _>(py, "")
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
    55
        }
52042
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    56
        e => PyErr::new::<cpython::exc::RuntimeError, _>(py, e.to_string()),
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    57
    })
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    58
}
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    59
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    60
pub fn repo_error_to_pyerr<T>(
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    61
    py: Python,
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    62
    error: Result<T, RepoError>,
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    63
) -> PyResult<T> {
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    64
    hgerror_to_pyerr(py, error.map_err(HgError::from))
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    65
}
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    66
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    67
/// Get a repository from a given [`PyObject`] path, and bubble up any error
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    68
/// that comes up.
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    69
pub fn repo_from_path(py: Python, repo_path: PyObject) -> Result<Repo, PyErr> {
52185
e698e3e75420 rust-cpython: add a TODO about repo reuse
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52046
diff changeset
    70
    // TODO make the Config a Python class and downcast it here, otherwise we
e698e3e75420 rust-cpython: add a TODO about repo reuse
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52046
diff changeset
    71
    // lose CLI args and runtime overrides done in Python.
52042
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    72
    let config =
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    73
        hgerror_to_pyerr(py, Config::load_non_repo().map_err(HgError::from))?;
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    74
    let py_bytes = &repo_path.extract::<PyBytes>(py)?;
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    75
    let repo_path = py_bytes.data(py);
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    76
    let repo = repo_error_to_pyerr(
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    77
        py,
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    78
        Repo::find(&config, Some(get_path_from_bytes(repo_path).to_owned())),
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    79
    )?;
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    80
    Ok(repo)
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    81
}
28a0eb21ff04 rust-cpython: add a util to get a `Repo` from a python path
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49631
diff changeset
    82
44505
d738b7a18438 rust-nodemap: add utils to create `Node`s from Python objects
Georges Racinet <georges.racinet@octobus.net>
parents: 43251
diff changeset
    83
// Necessary evil for the time being, could maybe be moved to
d738b7a18438 rust-nodemap: add utils to create `Node`s from Python objects
Georges Racinet <georges.racinet@octobus.net>
parents: 43251
diff changeset
    84
// a TryFrom in Node itself
d738b7a18438 rust-nodemap: add utils to create `Node`s from Python objects
Georges Racinet <georges.racinet@octobus.net>
parents: 43251
diff changeset
    85
const NODE_BYTES_LENGTH: usize = 20;
d738b7a18438 rust-nodemap: add utils to create `Node`s from Python objects
Georges Racinet <georges.racinet@octobus.net>
parents: 43251
diff changeset
    86
type NodeData = [u8; NODE_BYTES_LENGTH];
d738b7a18438 rust-nodemap: add utils to create `Node`s from Python objects
Georges Racinet <georges.racinet@octobus.net>
parents: 43251
diff changeset
    87
d738b7a18438 rust-nodemap: add utils to create `Node`s from Python objects
Georges Racinet <georges.racinet@octobus.net>
parents: 43251
diff changeset
    88
/// Copy incoming Python bytes given as `PyObject` into `Node`,
d738b7a18438 rust-nodemap: add utils to create `Node`s from Python objects
Georges Racinet <georges.racinet@octobus.net>
parents: 43251
diff changeset
    89
/// doing the necessary checks
d738b7a18438 rust-nodemap: add utils to create `Node`s from Python objects
Georges Racinet <georges.racinet@octobus.net>
parents: 43251
diff changeset
    90
pub fn node_from_py_object<'a>(
d738b7a18438 rust-nodemap: add utils to create `Node`s from Python objects
Georges Racinet <georges.racinet@octobus.net>
parents: 43251
diff changeset
    91
    py: Python,
d738b7a18438 rust-nodemap: add utils to create `Node`s from Python objects
Georges Racinet <georges.racinet@octobus.net>
parents: 43251
diff changeset
    92
    bytes: &'a PyObject,
d738b7a18438 rust-nodemap: add utils to create `Node`s from Python objects
Georges Racinet <georges.racinet@octobus.net>
parents: 43251
diff changeset
    93
) -> PyResult<Node> {
d738b7a18438 rust-nodemap: add utils to create `Node`s from Python objects
Georges Racinet <georges.racinet@octobus.net>
parents: 43251
diff changeset
    94
    let as_py_bytes: &'a PyBytes = bytes.extract(py)?;
d738b7a18438 rust-nodemap: add utils to create `Node`s from Python objects
Georges Racinet <georges.racinet@octobus.net>
parents: 43251
diff changeset
    95
    node_from_py_bytes(py, as_py_bytes)
d738b7a18438 rust-nodemap: add utils to create `Node`s from Python objects
Georges Racinet <georges.racinet@octobus.net>
parents: 43251
diff changeset
    96
}
d738b7a18438 rust-nodemap: add utils to create `Node`s from Python objects
Georges Racinet <georges.racinet@octobus.net>
parents: 43251
diff changeset
    97
d738b7a18438 rust-nodemap: add utils to create `Node`s from Python objects
Georges Racinet <georges.racinet@octobus.net>
parents: 43251
diff changeset
    98
/// Clone incoming Python bytes given as `PyBytes` as a `Node`,
d738b7a18438 rust-nodemap: add utils to create `Node`s from Python objects
Georges Racinet <georges.racinet@octobus.net>
parents: 43251
diff changeset
    99
/// doing the necessary checks.
44973
26114bd6ec60 rust: do a clippy pass
Rapha?l Gom?s <rgomes@octobus.net>
parents: 44505
diff changeset
   100
pub fn node_from_py_bytes(py: Python, bytes: &PyBytes) -> PyResult<Node> {
44505
d738b7a18438 rust-nodemap: add utils to create `Node`s from Python objects
Georges Racinet <georges.racinet@octobus.net>
parents: 43251
diff changeset
   101
    <NodeData>::try_from(bytes.data(py))
d738b7a18438 rust-nodemap: add utils to create `Node`s from Python objects
Georges Racinet <georges.racinet@octobus.net>
parents: 43251
diff changeset
   102
        .map_err(|_| {
d738b7a18438 rust-nodemap: add utils to create `Node`s from Python objects
Georges Racinet <georges.racinet@octobus.net>
parents: 43251
diff changeset
   103
            PyErr::new::<ValueError, _>(
d738b7a18438 rust-nodemap: add utils to create `Node`s from Python objects
Georges Racinet <georges.racinet@octobus.net>
parents: 43251
diff changeset
   104
                py,
d738b7a18438 rust-nodemap: add utils to create `Node`s from Python objects
Georges Racinet <georges.racinet@octobus.net>
parents: 43251
diff changeset
   105
                format!("{}-byte hash required", NODE_BYTES_LENGTH),
d738b7a18438 rust-nodemap: add utils to create `Node`s from Python objects
Georges Racinet <georges.racinet@octobus.net>
parents: 43251
diff changeset
   106
            )
d738b7a18438 rust-nodemap: add utils to create `Node`s from Python objects
Georges Racinet <georges.racinet@octobus.net>
parents: 43251
diff changeset
   107
        })
44973
26114bd6ec60 rust: do a clippy pass
Rapha?l Gom?s <rgomes@octobus.net>
parents: 44505
diff changeset
   108
        .map(Into::into)
44505
d738b7a18438 rust-nodemap: add utils to create `Node`s from Python objects
Georges Racinet <georges.racinet@octobus.net>
parents: 43251
diff changeset
   109
}
52213
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   110
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   111
/// Wrap a call to `func` so that Python's `SIGINT` handler is first stored,
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   112
/// then restored after the call to `func` and finally raised if
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   113
/// `func` returns a [`HgError::InterruptReceived`]
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   114
pub fn with_sigint_wrapper<R>(
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   115
    py: Python,
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   116
    func: impl Fn() -> Result<R, HgError>,
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   117
) -> PyResult<Result<R, HgError>> {
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   118
    let signal_py_mod = py.import("signal")?;
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   119
    let sigint_py_const = signal_py_mod.get(py, "SIGINT")?;
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   120
    let old_handler = signal_py_mod.call(
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   121
        py,
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   122
        "getsignal",
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   123
        PyTuple::new(py, &[sigint_py_const.clone_ref(py)]),
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   124
        None,
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   125
    )?;
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   126
    let res = func();
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   127
    // Reset the old signal handler in Python because we've may have changed it
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   128
    signal_py_mod.call(
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   129
        py,
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   130
        "signal",
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   131
        PyTuple::new(py, &[sigint_py_const.clone_ref(py), old_handler]),
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   132
        None,
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   133
    )?;
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   134
    if let Err(HgError::InterruptReceived) = res {
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   135
        // Trigger the signal in Python
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   136
        signal_py_mod.call(
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   137
            py,
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   138
            "raise_signal",
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   139
            PyTuple::new(py, &[sigint_py_const]),
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   140
            None,
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   141
        )?;
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   142
    }
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   143
    Ok(res)
96b113d22b34 rust-update: handle SIGINT from long-running update threads
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52185
diff changeset
   144
}