Skip to content

Instantly share code, notes, and snippets.

@redshiftzero
Created December 7, 2020 21:01
Show Gist options
  • Save redshiftzero/648e4feeff3843ffd9924f13625f839c to your computer and use it in GitHub Desktop.
Save redshiftzero/648e4feeff3843ffd9924f13625f839c to your computer and use it in GitHub Desktop.
alternative constructor for child class in pyo3
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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment