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

Cannot create slot (__len__) methods with staticmethod annotation #3039

Closed
willstott101 opened this issue Mar 11, 2023 · 3 comments · Fixed by #3055
Closed

Cannot create slot (__len__) methods with staticmethod annotation #3039

willstott101 opened this issue Mar 11, 2023 · 3 comments · Fixed by #3055

Comments

@willstott101
Copy link
Contributor

willstott101 commented Mar 11, 2023

I was just exploring the expanded macro source of the library I am working on and was curious if I could increase the performance of, say __len__ by avoiding the reference to &self. For context this is a Vector3(x, y, z) whose __len__ will always return 3.

So to start with I have this:

#[pymethods]
impl Vector3 {
    fn __len__(&self) -> usize {
        3
    }
}

which is fine, but there's a load of code generated around this call.
If I switch to an associated function (still within #[pymethods]):

    fn __len__() -> usize {
        3
    }

pyo3 asks me to mark it as a #[staticmethod]:

error: static method needs #[staticmethod] attribute                                                                                                  
   --> src/vec3.rs:140:5
    |
140 |     fn __len__() -> usize {
    |     ^^

However if I add that annotation:

error[E0061]: this function takes 0 arguments but 1 argument was supplied                                                                                    
   --> src/vec3.rs:40:6
    |
39  |   #[pymethods]
    |   ------------ argument of type `*mut pyo3::ffi::PyObject` unexpected
40  |   impl Vector3 {
    |  ______^
41  | |     #[new]
42  | |     fn new(x: Option<f64>, y: Option<f64>, z: Option<f64>) -> Self {
43  | |         Vector3(na::Vector3::new(
...   |
140 | |     #[staticmethod]
141 | |     fn __len__() -> usize {
    | |______________^
    |
note: associated function defined here
   --> src/vec3.rs:141:8
    |
141 |     fn __len__() -> usize {
    |        ^^^^^^^
help: remove the extra argument

As you can see the generated code is wrong:

               unsafe fn __pymethod___len____(_py: _pyo3::Python<'_>,
                    _raw_slf: *mut _pyo3::ffi::PyObject)
                    -> _pyo3::PyResult<_pyo3::ffi::Py_ssize_t> {
                    let _slf = _raw_slf;
                    _pyo3::callback::convert(_py, Vector3::__len__(_slf))
                }

So long story short, I think there's a bug here... specifically that slot methods that are also staticmethods try to pass an unsafe object pointer into the static method (at least for __len__).

@willstott101 willstott101 changed the title Cannot create slot (__len__) methods with static result. Cannot create slot (__len__) methods with staticmethod annotation Mar 11, 2023
@birkenfeld
Copy link
Member

I just tested and surprisingly, defining __len__ as a staticmethod works in pure Python...

@davidhewitt
Copy link
Member

Looks like this also works even for things like __add__ (although I can't really see why you'd want to do that).

I see no reason why we should prevent this pattern, so I'll have a play and see how easily we can support it. Certainly the current state of the implementation is a bug.

@willstott101
Copy link
Contributor Author

I think I found the problem: #3055

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants