0

I'm using Py03 to build a python module in Rust. I have a class in Rust which accepts a PyAny to refer to an object in Python. As part of the hash function for the rust class, I want to use the Python ID for this object in the hash function in Rust so I can deduplicate the rust class if the same Python object is referenced in multiple versions of the Rust class. I can see the python ID in the PyAny object in Rust, but can't figure out how to get it into a plain number that I can pass to the hasher.

For example, I have the following in Rust:

#[pyclass]
pub struct MyClass {
    obj: Option<Py<PyAny>>,
}
#[pymethods]
impl MyClass {
    #[new]
    fn new(obj: Option<Py<PyAny>>) -> Self {
        if obj.is_some() {
            println!("Obj: {:?}", obj.as_ref());
        }
        Self { obj }
    }
}

Then, I can run in Python:

obj = [1,2,3,4]
print(hex(id(obj)))
# '0x103da9100'
MyClass(obj)
# Obj: Some(Py(0x103da9100))

Both Python and Rust are showing the same number for the ID, which is great, but how can I get this number 0x103da9100 into a Rust variable? It looks like PyAny is just a tuple struct, so I tried the following but Rust complains that the fields of PyAny are private:

let obj_id = obj?.0;
David Chanin
  • 533
  • 6
  • 17
  • 1
    You can get at the contents of the `PyAny` tuple with [`as_ptr`](https://docs.rs/pyo3/0.17.3/pyo3/conversion/trait.AsPyPointer.html#tymethod.as_ptr), but I'm not sure how to get the id from there (unless the id is simply the value of the pointer). – Jmb Jan 16 '23 at 12:51
  • It looks like you can just cast `as_ptr()` to `isize` or `usize` and it works. Another relevant thread on the Py03 Github page: https://github.com/PyO3/pyo3/discussions/2878 – David Chanin Jan 17 '23 at 13:17

1 Answers1

0

obj in your code is of type Option<Py<PyAny>>. To get the underlaying FFI pointer(in your case it's list) you need to de-structure your option first. And then use destructured_object.as_ptr() to get T from Py<T>.

#[pymethods]
impl MyClass {
    #[new]
    fn new(obj: Option<Py<PyAny>>) -> Self {
        if let Some(ref obj1) = obj { // obj1 will be of type `Py<PyList> in case of List`
            let concreate_type = obj1.as_ptr(); // Get PyList from `Py<PyList>
            println!("{}", concreate_type as isize)

        }

        Self { obj }
    }
}

Now you have the raw pointer in concreate_type and you can cast the pointer type to isize to get the memory location of an object. That's exactly what Cpython returns as part of the implementation. see id.__doc__ for more details.

>>> print(id.__doc__)
Return the identity of an object.

This is guaranteed to be unique among simultaneously existing objects.
(CPython uses the object's memory address.)
>>> 
>>> a = [1, 2, 3, 4]
>>> id(a)
4357355968
>>> 
>>> from myid import MyClass
>>> MyClass(a)
4357355968
Abdul Niyas P M
  • 18,035
  • 2
  • 25
  • 46