impl PreKeySignalMessage {
#[staticmethod]
pub fn try_from(data: &[u8]) -> PyResult<Py<PreKeySignalMessage>> {
let upstream_data = match libsignal_protocol_rust::PreKeySignalMessage::try_from(data) {
Ok(data) => data,
Err(err) => return Err(SignalProtocolError::new_err(err)),
};
let ciphertext =
libsignal_protocol_rust::CiphertextMessage::PreKeySignalMessage(upstream_data.clone());
// Workaround to allow two constructors with pyclass inheritence
let gil = Python::acquire_gil();
let py = gil.python();
Py::new(
py,
(
PreKeySignalMessage {
data: upstream_data,
},
CiphertextMessage { data: ciphertext },
),
)
}
Python is brought into scope by the Pyo3 prelude. By calling acquire_gil
, we are ensuring Python's Global Interpreter Lock is held. The return type of this method acquire_gil
is GILGuard
. By calling python()
on that object, we get access to the Python runtime. On this py
object, we could run arbitrary python code e.g. import a Python module.
What we're then doing is creating a Python object directly (in the Py::new
invokation). This is done via the Py
struct. The Py
struct (again brought into scope by the pyo3 prelude) represents any Python object of type T (where T: PyClass
). When we call new
, we pass in our Python GIL token, and then our tuple (T, U)
where U is the baseclass of T. The behavior of Py::new
is to allocate a new instance of Py<T>
on the python heap. It calls Into<PyClassInitializer<T>>
to do this.
See also the subclass/base class logic in PyClassInitializer: https://docs.rs/pyo3/0.12.4/src/pyo3/pyclass_init.rs.html#172-183