Repository: RustPython/RustPython (language: rust)

Issue:
method_descriptor need to have `__objclass__
## Feature

<!-- What Python feature is missing from RustPython? Give a short description of the feature and how you ran into its absence. -->

## Python Documentation or reference to CPython source code

https://docs.python.org/3/reference/datamodel.html#object.__objclass__


## How to

`PyMethodDescriptor` in `vm/src/builtins/descriptor.rs` is the struct.



Discussion / hints:

Fix patch:
diff --git a/.cspell.json b/.cspell.json
index ad90153f56..e2723d6ce5 100644
--- a/.cspell.json
+++ b/.cspell.json
@@ -52,6 +52,7 @@
     "metas",
     "modpow",
     "nanos",
+    "objclass",
     "peekable",
     "powc",
     "powf",
diff --git a/vm/src/builtins/descriptor.rs b/vm/src/builtins/descriptor.rs
index 6f0705e8ec..16340c3a62 100644
--- a/vm/src/builtins/descriptor.rs
+++ b/vm/src/builtins/descriptor.rs
@@ -1,6 +1,6 @@
 use super::{PyStr, PyStrInterned, PyType};
 use crate::{
-    builtins::{builtin_func::PyNativeMethod, type_},
+    builtins::{builtin_func::PyNativeMethod, type_, PyTypeRef},
     class::PyClassImpl,
     function::{FuncArgs, PyMethodDef, PyMethodFlags, PySetterValue},
     types::{Callable, GetDescriptor, Representable, Unconstructible},
@@ -27,6 +27,7 @@ pub struct PyMethodDescriptor {
     pub common: PyDescriptor,
     pub method: &'static PyMethodDef,
     // vectorcall: vectorcallfunc,
+    pub objclass: &'static Py<PyType>, // TODO: move to tp_members
 }
 
 impl PyMethodDescriptor {
@@ -38,6 +39,7 @@ impl PyMethodDescriptor {
                 qualname: PyRwLock::new(None),
             },
             method,
+            objclass: typ,
         }
     }
 }
@@ -126,6 +128,10 @@ impl PyMethodDescriptor {
                 .map(|signature| signature.to_string())
         })
     }
+    #[pygetset(magic)]
+    fn objclass(&self) -> PyTypeRef {
+        self.objclass.to_owned()
+    }
     #[pymethod(magic)]
     fn reduce(
         &self,
diff --git a/vm/src/builtins/getset.rs b/vm/src/builtins/getset.rs
index 2fa1e41483..603ec021e2 100644
--- a/vm/src/builtins/getset.rs
+++ b/vm/src/builtins/getset.rs
@@ -1,7 +1,7 @@
 /*! Python `attribute` descriptor class. (PyGetSet)
 
 */
-use super::PyType;
+use super::{PyType, PyTypeRef};
 use crate::{
     class::PyClassImpl,
     function::{IntoPyGetterFunc, IntoPySetterFunc, PyGetterFunc, PySetterFunc, PySetterValue},
@@ -139,6 +139,11 @@ impl PyGetSet {
     fn qualname(&self) -> String {
         format!("{}.{}", self.class.slot_name(), self.name.clone())
     }
+
+    #[pygetset(magic)]
+    fn objclass(&self) -> PyTypeRef {
+        self.class.to_owned()
+    }
 }
 impl Unconstructible for PyGetSet {}
 
diff --git a/vm/src/function/method.rs b/vm/src/function/method.rs
index 513cb9383c..c8652ffb50 100644
--- a/vm/src/function/method.rs
+++ b/vm/src/function/method.rs
@@ -170,11 +170,8 @@ impl PyMethodDef {
         class: &'static Py<PyType>,
     ) -> PyRef<PyMethodDescriptor> {
         debug_assert!(self.flags.contains(PyMethodFlags::METHOD));
-        PyRef::new_ref(
-            self.to_method(class, ctx),
-            ctx.types.method_descriptor_type.to_owned(),
-            None,
-        )
+        let method = self.to_method(class, ctx);
+        PyRef::new_ref(method, ctx.types.method_descriptor_type.to_owned(), None)
     }
     pub fn build_bound_method(
         &'static self,
diff --git a/vm/src/vm/context.rs b/vm/src/vm/context.rs
index 217d4eecf5..f67035d0f3 100644
--- a/vm/src/vm/context.rs
+++ b/vm/src/vm/context.rs
@@ -174,6 +174,7 @@ declare_const_name! {
     __neg__,
     __new__,
     __next__,
+    __objclass__,
     __or__,
     __orig_bases__,
     __orig_class__,


Test patch:
diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py
index 8389ce67c9..eae8b42fce 100644
--- a/Lib/test/test_descr.py
+++ b/Lib/test/test_descr.py
@@ -4635,8 +4635,6 @@ def test_builtin_function_or_method(self):
         # hash([].append) should not be based on hash([])
         hash(l.append)
 
-    # TODO: RUSTPYTHON
-    @unittest.expectedFailure
     def test_special_unbound_method_types(self):
         # Testing objects of <type 'wrapper_descriptor'>...
         self.assertTrue(list.__add__ == list.__add__)