From 2cc2327dc6974d7c7e64bdc367395d0acaf84ff5 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Fri, 31 Oct 2025 12:55:18 +0000 Subject: [PATCH 1/2] delete some unneeded complexity from `PyMethodDefType` --- pyo3-macros-backend/src/method.rs | 11 +++++--- pyo3-macros-backend/src/pyclass.rs | 5 ++-- pyo3-macros-backend/src/pymethod.rs | 39 +++++++---------------------- src/impl_/pymethods.rs | 6 +---- src/pyclass/create_type_object.rs | 4 +-- 5 files changed, 21 insertions(+), 44 deletions(-) diff --git a/pyo3-macros-backend/src/method.rs b/pyo3-macros-backend/src/method.rs index 5484b8d5d0c..65f1b24ce8f 100644 --- a/pyo3-macros-backend/src/method.rs +++ b/pyo3-macros-backend/src/method.rs @@ -903,6 +903,11 @@ impl<'a> FnSpec<'a> { pub fn get_methoddef(&self, wrapper: impl ToTokens, doc: &PythonDoc, ctx: &Ctx) -> TokenStream { let Ctx { pyo3_path, .. } = ctx; let python_name = self.null_terminated_python_name(); + let flags = match self.tp { + FnType::FnClass(_) => quote! { .flags(#pyo3_path::ffi::METH_CLASS) }, + FnType::FnStatic => quote! { .flags(#pyo3_path::ffi::METH_STATIC) }, + _ => quote! {}, + }; match self.convention { CallingConvention::Noargs => quote! { #pyo3_path::impl_::pymethods::PyMethodDef::noargs( @@ -924,7 +929,7 @@ impl<'a> FnSpec<'a> { trampoline }, #doc, - ) + ) #flags }, CallingConvention::Fastcall => quote! { #pyo3_path::impl_::pymethods::PyMethodDef::fastcall_cfunction_with_keywords( @@ -948,7 +953,7 @@ impl<'a> FnSpec<'a> { trampoline }, #doc, - ) + ) #flags }, CallingConvention::Varargs => quote! { #pyo3_path::impl_::pymethods::PyMethodDef::cfunction_with_keywords( @@ -970,7 +975,7 @@ impl<'a> FnSpec<'a> { trampoline }, #doc, - ) + ) #flags }, CallingConvention::TpNew => unreachable!("tp_new cannot get a methoddef"), } diff --git a/pyo3-macros-backend/src/pyclass.rs b/pyo3-macros-backend/src/pyclass.rs index 5167ce3571a..4bb4b47b08b 100644 --- a/pyo3-macros-backend/src/pyclass.rs +++ b/pyo3-macros-backend/src/pyclass.rs @@ -477,7 +477,7 @@ fn impl_class( )?; let (default_class_geitem, default_class_geitem_method) = - pyclass_class_geitem(&args.options, &syn::parse_quote!(#cls), ctx)?; + pyclass_class_getitem(&args.options, &syn::parse_quote!(#cls), ctx)?; if let Some(default_class_geitem_method) = default_class_geitem_method { default_methods.push(default_class_geitem_method); @@ -2233,7 +2233,7 @@ fn pyclass_hash( } } -fn pyclass_class_geitem( +fn pyclass_class_getitem( options: &PyClassPyO3Options, cls: &syn::Type, ctx: &Ctx, @@ -2264,7 +2264,6 @@ fn pyclass_class_geitem( cls, &spec, &spec.get_doc(&class_geitem_impl.attrs, ctx)?, - Some(quote!(#pyo3_path::ffi::METH_CLASS)), ctx, )?; Ok((Some(class_geitem_impl), Some(class_geitem_method))) diff --git a/pyo3-macros-backend/src/pymethod.rs b/pyo3-macros-backend/src/pymethod.rs index 755bd367f27..cdf0d4c016b 100644 --- a/pyo3-macros-backend/src/pymethod.rs +++ b/pyo3-macros-backend/src/pymethod.rs @@ -222,7 +222,6 @@ pub fn gen_py_method( ctx: &Ctx, ) -> Result { let spec = &method.spec; - let Ctx { pyo3_path, .. } = ctx; if spec.asyncness.is_some() { ensure_spanned!( @@ -260,27 +259,14 @@ pub fn gen_py_method( } } // ordinary functions (with some specialties) - (_, FnType::Fn(_)) => GeneratedPyMethod::Method(impl_py_method_def( - cls, - spec, - &spec.get_doc(meth_attrs, ctx)?, - None, - ctx, - )?), - (_, FnType::FnClass(_)) => GeneratedPyMethod::Method(impl_py_method_def( - cls, - spec, - &spec.get_doc(meth_attrs, ctx)?, - Some(quote!(#pyo3_path::ffi::METH_CLASS)), - ctx, - )?), - (_, FnType::FnStatic) => GeneratedPyMethod::Method(impl_py_method_def( - cls, - spec, - &spec.get_doc(meth_attrs, ctx)?, - Some(quote!(#pyo3_path::ffi::METH_STATIC)), - ctx, - )?), + (_, FnType::Fn(_)) | (_, FnType::FnClass(_)) | (_, FnType::FnStatic) => { + GeneratedPyMethod::Method(impl_py_method_def( + cls, + spec, + &spec.get_doc(meth_attrs, ctx)?, + ctx, + )?) + } // special prototypes (_, FnType::FnNew) | (_, FnType::FnNewClass(_)) => { GeneratedPyMethod::Proto(impl_py_method_def_new(cls, spec, ctx)?) @@ -351,21 +337,14 @@ pub fn impl_py_method_def( cls: &syn::Type, spec: &FnSpec<'_>, doc: &PythonDoc, - flags: Option, ctx: &Ctx, ) -> Result { let Ctx { pyo3_path, .. } = ctx; let wrapper_ident = format_ident!("__pymethod_{}__", spec.python_name); let associated_method = spec.get_wrapper_function(&wrapper_ident, Some(cls), ctx)?; - let add_flags = flags.map(|flags| quote!(.flags(#flags))); - let methoddef_type = match spec.tp { - FnType::FnStatic => quote!(Static), - FnType::FnClass(_) => quote!(Class), - _ => quote!(Method), - }; let methoddef = spec.get_methoddef(quote! { #cls::#wrapper_ident }, doc, ctx); let method_def = quote! { - #pyo3_path::impl_::pymethods::PyMethodDefType::#methoddef_type(#methoddef #add_flags) + #pyo3_path::impl_::pymethods::PyMethodDefType::Method(#methoddef) }; Ok(MethodAndMethodDef { associated_method, diff --git a/src/impl_/pymethods.rs b/src/impl_/pymethods.rs index 521914ea392..5a23221cdbf 100644 --- a/src/impl_/pymethods.rs +++ b/src/impl_/pymethods.rs @@ -59,11 +59,7 @@ impl IPowModulo { /// It is used by the `#[pymethods]` attribute. #[derive(Copy, Clone)] pub enum PyMethodDefType { - /// Represents class method - Class(PyMethodDef), - /// Represents static method - Static(PyMethodDef), - /// Represents normal method + /// Represents a class method (might be `classmethod` or `staticmethod`, depends on `ml_flags`) Method(PyMethodDef), /// Represents class attribute, used by `#[attribute]` ClassAttribute(PyClassAttributeDef), diff --git a/src/pyclass/create_type_object.rs b/src/pyclass/create_type_object.rs index 7fd3ed2b09e..8a7ee35c598 100644 --- a/src/pyclass/create_type_object.rs +++ b/src/pyclass/create_type_object.rs @@ -192,9 +192,7 @@ impl PyTypeBuilder { .entry(setter.name) .or_default() .add_setter(setter), - PyMethodDefType::Method(def) - | PyMethodDefType::Class(def) - | PyMethodDefType::Static(def) => self.method_defs.push(def.into_raw()), + PyMethodDefType::Method(def) => self.method_defs.push(def.into_raw()), // These class attributes are added after the type gets created by LazyStaticType PyMethodDefType::ClassAttribute(_) => {} PyMethodDefType::StructMember(def) => self.member_defs.push(*def), From 48dcd0ab3d590ed5593b3c2d177a56bc2c567c09 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Fri, 31 Oct 2025 13:48:21 +0000 Subject: [PATCH 2/2] `geitem` -> `getitem` --- guide/src/python-typing-hints.md | 2 +- pyo3-macros-backend/src/pyclass.rs | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/guide/src/python-typing-hints.md b/guide/src/python-typing-hints.md index 7f6661a2743..cbcc4ee3d56 100644 --- a/guide/src/python-typing-hints.md +++ b/guide/src/python-typing-hints.md @@ -237,7 +237,7 @@ To overcome this limitation, implementers can pass the `generic` parameter to `p #### Advanced Users `#[pyclass(generic)]` implements a very simple runtime behavior that accepts any generic argument. -Advanced users can opt to manually implement [`__class_geitem__`](https://docs.python.org/3/reference/datamodel.html#emulating-generic-types) for the generic class to have more control. +Advanced users can opt to manually implement [`__class_getitem__`](https://docs.python.org/3/reference/datamodel.html#emulating-generic-types) for the generic class to have more control. ```rust ignore impl MyClass { diff --git a/pyo3-macros-backend/src/pyclass.rs b/pyo3-macros-backend/src/pyclass.rs index 4bb4b47b08b..fc5d43c1815 100644 --- a/pyo3-macros-backend/src/pyclass.rs +++ b/pyo3-macros-backend/src/pyclass.rs @@ -476,11 +476,11 @@ fn impl_class( ctx, )?; - let (default_class_geitem, default_class_geitem_method) = + let (default_class_getitem, default_class_getitem_method) = pyclass_class_getitem(&args.options, &syn::parse_quote!(#cls), ctx)?; - if let Some(default_class_geitem_method) = default_class_geitem_method { - default_methods.push(default_class_geitem_method); + if let Some(default_class_getitem_method) = default_class_getitem_method { + default_methods.push(default_class_getitem_method); } let (default_str, default_str_slot) = @@ -514,7 +514,7 @@ fn impl_class( #default_richcmp #default_hash #default_str - #default_class_geitem + #default_class_getitem } }) } @@ -2242,7 +2242,7 @@ fn pyclass_class_getitem( match options.generic { Some(_) => { let ident = format_ident!("__class_getitem__"); - let mut class_geitem_impl: syn::ImplItemFn = { + let mut class_getitem_impl: syn::ImplItemFn = { parse_quote! { #[classmethod] fn #ident<'py>( @@ -2255,18 +2255,18 @@ fn pyclass_class_getitem( }; let spec = FnSpec::parse( - &mut class_geitem_impl.sig, - &mut class_geitem_impl.attrs, + &mut class_getitem_impl.sig, + &mut class_getitem_impl.attrs, Default::default(), )?; - let class_geitem_method = crate::pymethod::impl_py_method_def( + let class_getitem_method = crate::pymethod::impl_py_method_def( cls, &spec, - &spec.get_doc(&class_geitem_impl.attrs, ctx)?, + &spec.get_doc(&class_getitem_impl.attrs, ctx)?, ctx, )?; - Ok((Some(class_geitem_impl), Some(class_geitem_method))) + Ok((Some(class_getitem_impl), Some(class_getitem_method))) } None => Ok((None, None)), }