Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow __setitem__ on Hashmaps when #[pyo3(set)] is specified #3026

Closed
AntoineRR opened this issue Mar 8, 2023 · 3 comments
Closed

allow __setitem__ on Hashmaps when #[pyo3(set)] is specified #3026

AntoineRR opened this issue Mar 8, 2023 · 3 comments

Comments

@AntoineRR
Copy link
Contributor

Here is a small code snippet to explain what I am trying to achieve:

Rust:

use pyo3::{prelude::*, types::PyDict};
use std::collections::HashMap;

#[pyclass]
struct Dict(#[pyo3(get, set, name = "inner")] HashMap<String, String>);

#[pymethods]
impl Dict {
    #[new]
    fn new() -> Self {
        Self(HashMap::new())
    }
}

#[pyclass]
struct Dict2(#[pyo3(get, set, name = "inner")] Py<PyDict>);

#[pymethods]
impl Dict2 {
    #[new]
    fn new(py: Python) -> Self {
        Self(PyDict::new(py).into())
    }
}

#[pymodule]
fn pyo3_test(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
    m.add_class::<Dict>()?;
    m.add_class::<Dict2>()?;
    Ok(())
}

Python:

from pyo3_test import Dict, Dict2

print("dict")

test_dict = Dict()

print(test_dict.inner)
test_dict.inner["test"] = "hello"
print(test_dict.inner)
test_dict.inner = {"test2": "hello2"}
print(test_dict.inner)

print("dict 2")

test_dict_2 = Dict2()

print(test_dict_2.inner)
test_dict_2.inner["test"] = "hello"
print(test_dict_2.inner)
test_dict_2.inner = {"test2": "hello2"}
print(test_dict_2.inner)

Output:

dict
{}
{}
{'test2': 'hello2'}
dict 2
{}
{'test': 'hello'}
{'test2': 'hello2'}

As you can see, it seems the inner Hashmap of Dict cannot be modified through the __setitem__ dunder method (i.e. test_dict.inner["test"] = "hello") while it is doable for PyDict. Is it possible to enable the use of this method through #[pyo3(set)]?

@mejrs
Copy link
Member

mejrs commented Mar 9, 2023

It looks like you are running into https://pyo3.rs/v0.18.1/faq#pyo3get-clones-my-field

@AntoineRR
Copy link
Contributor Author

Thanks, that does make sense now. I checked and the getter is indeed called when running test_dict.inner["test"] = "hello", which means we only modify a copy of the dict. I guess there is no way to achieve what I want without a Py wrapper then (so I'll have to use the GIL to interact with the field from Rust) 😞 Anyway thanks for linking to the relevant doc!

@davidhewitt
Copy link
Member

Closing as duplicate of #1358

(It discusses classes rather than hash-map but the fundamental cause is the same.)

@davidhewitt davidhewitt closed this as not planned Won't fix, can't repro, duplicate, stale Sep 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants