diff --git a/newsfragments/5634.changed.md b/newsfragments/5634.changed.md new file mode 100644 index 00000000000..fed806191af --- /dev/null +++ b/newsfragments/5634.changed.md @@ -0,0 +1 @@ +Introspection: properly generate annotations for `Option` in both input and output. \ No newline at end of file diff --git a/pyo3-macros-backend/src/introspection.rs b/pyo3-macros-backend/src/introspection.rs index deac4c807d2..af70480f92a 100644 --- a/pyo3-macros-backend/src/introspection.rs +++ b/pyo3-macros-backend/src/introspection.rs @@ -339,34 +339,12 @@ fn argument_introspection_data<'a>( params.insert("annotation", IntrospectionNode::String(annotation.into())); } else if desc.from_py_with.is_none() { // If from_py_with is set we don't know anything on the input type - if let Some(ty) = desc.option_wrapped_type { - // Special case to properly generate a `T | None` annotation - let mut ty = ty.clone(); - if let Some(class_type) = class_type { - replace_self(&mut ty, class_type); - } - elide_lifetimes(&mut ty); - params.insert( - "annotation", - IntrospectionNode::InputType { - rust_type: ty, - nullable: true, - }, - ); - } else { - let mut ty = desc.ty.clone(); - if let Some(class_type) = class_type { - replace_self(&mut ty, class_type); - } - elide_lifetimes(&mut ty); - params.insert( - "annotation", - IntrospectionNode::InputType { - rust_type: ty, - nullable: false, - }, - ); + let mut ty = desc.ty.clone(); + if let Some(class_type) = class_type { + replace_self(&mut ty, class_type); } + elide_lifetimes(&mut ty); + params.insert("annotation", IntrospectionNode::InputType(ty)); } IntrospectionNode::Map(params).into() } @@ -375,7 +353,7 @@ enum IntrospectionNode<'a> { String(Cow<'a, str>), Bool(bool), IntrospectionId(Option>), - InputType { rust_type: Type, nullable: bool }, + InputType(Type), OutputType { rust_type: Type, is_final: bool }, ConstantType(PythonIdentifier), Map(HashMap<&'static str, IntrospectionNode<'a>>), @@ -411,11 +389,8 @@ impl IntrospectionNode<'_> { }); content.push_str("\""); } - Self::InputType { - rust_type, - nullable, - } => { - let mut annotation = quote! { + Self::InputType(rust_type) => { + let annotation = quote! { <#rust_type as #pyo3_crate_path::impl_::extract_argument::PyFunctionArgument< { #[allow(unused_imports, reason = "`Probe` trait used on negative case only")] @@ -424,9 +399,6 @@ impl IntrospectionNode<'_> { } >>::INPUT_TYPE }; - if nullable { - annotation = quote! { #pyo3_crate_path::inspect::TypeHint::union(&[#annotation, #pyo3_crate_path::inspect::TypeHint::builtin("None")]) }; - } content.push_tokens(serialize_type_hint(annotation, pyo3_crate_path)); } Self::OutputType { diff --git a/src/conversions/std/option.rs b/src/conversions/std/option.rs index 8cec88b4e6b..729fcc56668 100644 --- a/src/conversions/std/option.rs +++ b/src/conversions/std/option.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "experimental-inspect")] +use crate::inspect::TypeHint; use crate::{ conversion::IntoPyObject, types::any::PyAnyMethods, BoundObject, FromPyObject, PyAny, Python, }; @@ -10,6 +12,8 @@ where type Target = PyAny; type Output = Bound<'py, Self::Target>; type Error = T::Error; + #[cfg(feature = "experimental-inspect")] + const OUTPUT_TYPE: TypeHint = TypeHint::union(&[T::OUTPUT_TYPE, TypeHint::builtin("None")]); fn into_pyobject(self, py: Python<'py>) -> Result { self.map_or_else( @@ -30,6 +34,8 @@ where type Target = PyAny; type Output = Bound<'py, Self::Target>; type Error = <&'a T as IntoPyObject<'py>>::Error; + #[cfg(feature = "experimental-inspect")] + const OUTPUT_TYPE: TypeHint = >::OUTPUT_TYPE; #[inline] fn into_pyobject(self, py: Python<'py>) -> Result { @@ -42,6 +48,8 @@ where T: FromPyObject<'a, 'py>, { type Error = T::Error; + #[cfg(feature = "experimental-inspect")] + const INPUT_TYPE: TypeHint = TypeHint::union(&[T::INPUT_TYPE, TypeHint::builtin("None")]); fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result { if obj.is_none() { diff --git a/src/impl_/extract_argument.rs b/src/impl_/extract_argument.rs index 8b5f94ad4dd..1188adf87e5 100644 --- a/src/impl_/extract_argument.rs +++ b/src/impl_/extract_argument.rs @@ -108,10 +108,7 @@ where type Error = T::Error; #[cfg(feature = "experimental-inspect")] - const INPUT_TYPE: TypeHint = TypeHint::union(&[ - TypeHint::module_attr("typing", "Any"), - TypeHint::builtin("None"), - ]); + const INPUT_TYPE: TypeHint = TypeHint::union(&[T::INPUT_TYPE, TypeHint::builtin("None")]); #[inline] fn extract(