Improve the span and message for return types of pymethod/functions (#4220)

* Improve the span and message for return types of pymethod/functions

* Don't pass the span

* fixup trybuild output

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
This commit is contained in:
Bruno Kolenbrander 2024-06-20 11:23:40 +02:00 committed by GitHub
parent e6b2216b04
commit b25b3b3a7b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 162 additions and 101 deletions

View File

@ -32,7 +32,7 @@ impl<'ctx> Deprecations<'ctx> {
impl<'ctx> ToTokens for Deprecations<'ctx> { impl<'ctx> ToTokens for Deprecations<'ctx> {
fn to_tokens(&self, tokens: &mut TokenStream) { fn to_tokens(&self, tokens: &mut TokenStream) {
let Self(deprecations, Ctx { pyo3_path }) = self; let Self(deprecations, Ctx { pyo3_path, .. }) = self;
for (deprecation, span) in deprecations { for (deprecation, span) in deprecations {
let pyo3_path = pyo3_path.to_tokens_spanned(*span); let pyo3_path = pyo3_path.to_tokens_spanned(*span);

View File

@ -45,7 +45,7 @@ impl<'a> Enum<'a> {
/// Build derivation body for enums. /// Build derivation body for enums.
fn build(&self, ctx: &Ctx) -> (TokenStream, TokenStream) { fn build(&self, ctx: &Ctx) -> (TokenStream, TokenStream) {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let mut var_extracts = Vec::new(); let mut var_extracts = Vec::new();
let mut variant_names = Vec::new(); let mut variant_names = Vec::new();
let mut error_names = Vec::new(); let mut error_names = Vec::new();
@ -263,7 +263,7 @@ impl<'a> Container<'a> {
from_py_with: &Option<FromPyWithAttribute>, from_py_with: &Option<FromPyWithAttribute>,
ctx: &Ctx, ctx: &Ctx,
) -> (TokenStream, TokenStream) { ) -> (TokenStream, TokenStream) {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let self_ty = &self.path; let self_ty = &self.path;
let struct_name = self.name(); let struct_name = self.name();
if let Some(ident) = field_ident { if let Some(ident) = field_ident {
@ -329,7 +329,7 @@ impl<'a> Container<'a> {
struct_fields: &[TupleStructField], struct_fields: &[TupleStructField],
ctx: &Ctx, ctx: &Ctx,
) -> (TokenStream, TokenStream) { ) -> (TokenStream, TokenStream) {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let self_ty = &self.path; let self_ty = &self.path;
let struct_name = &self.name(); let struct_name = &self.name();
let field_idents: Vec<_> = (0..struct_fields.len()) let field_idents: Vec<_> = (0..struct_fields.len())
@ -382,7 +382,7 @@ impl<'a> Container<'a> {
struct_fields: &[NamedStructField<'_>], struct_fields: &[NamedStructField<'_>],
ctx: &Ctx, ctx: &Ctx,
) -> (TokenStream, TokenStream) { ) -> (TokenStream, TokenStream) {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let self_ty = &self.path; let self_ty = &self.path;
let struct_name = &self.name(); let struct_name = &self.name();
let mut fields: Punctuated<TokenStream, syn::Token![,]> = Punctuated::new(); let mut fields: Punctuated<TokenStream, syn::Token![,]> = Punctuated::new();
@ -670,8 +670,8 @@ pub fn build_derive_from_pyobject(tokens: &DeriveInput) -> Result<TokenStream> {
.push(parse_quote!(#gen_ident: FromPyObject<#lt_param>)) .push(parse_quote!(#gen_ident: FromPyObject<#lt_param>))
} }
let options = ContainerOptions::from_attrs(&tokens.attrs)?; let options = ContainerOptions::from_attrs(&tokens.attrs)?;
let ctx = &Ctx::new(&options.krate); let ctx = &Ctx::new(&options.krate, None);
let Ctx { pyo3_path } = &ctx; let Ctx { pyo3_path, .. } = &ctx;
let (derives, from_py_with_deprecations) = match &tokens.data { let (derives, from_py_with_deprecations) = match &tokens.data {
syn::Data::Enum(en) => { syn::Data::Enum(en) => {

View File

@ -30,7 +30,7 @@ impl ConstSpec<'_> {
/// Null-terminated Python name /// Null-terminated Python name
pub fn null_terminated_python_name(&self, ctx: &Ctx) -> TokenStream { pub fn null_terminated_python_name(&self, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let name = self.python_name().to_string(); let name = self.python_name().to_string();
quote!(#pyo3_path::ffi::c_str!(#name)) quote!(#pyo3_path::ffi::c_str!(#name))
} }

View File

@ -224,7 +224,7 @@ impl FnType {
holders: &mut Holders, holders: &mut Holders,
ctx: &Ctx, ctx: &Ctx,
) -> TokenStream { ) -> TokenStream {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
match self { match self {
FnType::Getter(st) | FnType::Setter(st) | FnType::Fn(st) => { FnType::Getter(st) | FnType::Setter(st) | FnType::Fn(st) => {
let mut receiver = st.receiver( let mut receiver = st.receiver(
@ -281,7 +281,7 @@ pub enum ExtractErrorMode {
impl ExtractErrorMode { impl ExtractErrorMode {
pub fn handle_error(self, extract: TokenStream, ctx: &Ctx) -> TokenStream { pub fn handle_error(self, extract: TokenStream, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
match self { match self {
ExtractErrorMode::Raise => quote! { #extract? }, ExtractErrorMode::Raise => quote! { #extract? },
ExtractErrorMode::NotImplemented => quote! { ExtractErrorMode::NotImplemented => quote! {
@ -306,7 +306,7 @@ impl SelfType {
// main macro callsite. // main macro callsite.
let py = syn::Ident::new("py", Span::call_site()); let py = syn::Ident::new("py", Span::call_site());
let slf = syn::Ident::new("_slf", Span::call_site()); let slf = syn::Ident::new("_slf", Span::call_site());
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
match self { match self {
SelfType::Receiver { span, mutable } => { SelfType::Receiver { span, mutable } => {
let method = if *mutable { let method = if *mutable {
@ -473,7 +473,7 @@ impl<'a> FnSpec<'a> {
} }
pub fn null_terminated_python_name(&self, ctx: &Ctx) -> TokenStream { pub fn null_terminated_python_name(&self, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let span = self.python_name.span(); let span = self.python_name.span();
let pyo3_path = pyo3_path.to_tokens_spanned(span); let pyo3_path = pyo3_path.to_tokens_spanned(span);
let name = self.python_name.to_string(); let name = self.python_name.to_string();
@ -600,7 +600,10 @@ impl<'a> FnSpec<'a> {
cls: Option<&syn::Type>, cls: Option<&syn::Type>,
ctx: &Ctx, ctx: &Ctx,
) -> Result<TokenStream> { ) -> Result<TokenStream> {
let Ctx { pyo3_path } = ctx; let Ctx {
pyo3_path,
output_span,
} = ctx;
let mut cancel_handle_iter = self let mut cancel_handle_iter = self
.signature .signature
.arguments .arguments
@ -703,7 +706,18 @@ impl<'a> FnSpec<'a> {
} }
} }
}; };
quotes::map_result_into_ptr(quotes::ok_wrap(call, ctx), ctx)
// We must assign the output_span to the return value of the call,
// but *not* of the call itself otherwise the spans get really weird
let ret_expr = quote! { let ret = #call; };
let ret_var = quote_spanned! {*output_span=> ret };
let return_conversion = quotes::map_result_into_ptr(quotes::ok_wrap(ret_var, ctx), ctx);
quote! {
{
#ret_expr
#return_conversion
}
}
}; };
let func_name = &self.name; let func_name = &self.name;
@ -731,7 +745,6 @@ impl<'a> FnSpec<'a> {
let call = rust_call(args, &mut holders); let call = rust_call(args, &mut holders);
let check_gil_refs = holders.check_gil_refs(); let check_gil_refs = holders.check_gil_refs();
let init_holders = holders.init_holders(ctx); let init_holders = holders.init_holders(ctx);
quote! { quote! {
unsafe fn #ident<'py>( unsafe fn #ident<'py>(
py: #pyo3_path::Python<'py>, py: #pyo3_path::Python<'py>,
@ -804,7 +817,7 @@ impl<'a> FnSpec<'a> {
let self_arg = self let self_arg = self
.tp .tp
.self_arg(cls, ExtractErrorMode::Raise, &mut holders, ctx); .self_arg(cls, ExtractErrorMode::Raise, &mut holders, ctx);
let call = quote! { #rust_name(#self_arg #(#args),*) }; let call = quote_spanned! {*output_span=> #rust_name(#self_arg #(#args),*) };
let init_holders = holders.init_holders(ctx); let init_holders = holders.init_holders(ctx);
let check_gil_refs = holders.check_gil_refs(); let check_gil_refs = holders.check_gil_refs();
quote! { quote! {
@ -833,7 +846,7 @@ impl<'a> FnSpec<'a> {
/// Return a `PyMethodDef` constructor for this function, matching the selected /// Return a `PyMethodDef` constructor for this function, matching the selected
/// calling convention. /// calling convention.
pub fn get_methoddef(&self, wrapper: impl ToTokens, doc: &PythonDoc, ctx: &Ctx) -> TokenStream { pub fn get_methoddef(&self, wrapper: impl ToTokens, doc: &PythonDoc, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let python_name = self.null_terminated_python_name(ctx); let python_name = self.null_terminated_python_name(ctx);
match self.convention { match self.convention {
CallingConvention::Noargs => quote! { CallingConvention::Noargs => quote! {

View File

@ -90,8 +90,8 @@ pub fn pymodule_module_impl(mut module: syn::ItemMod) -> Result<TokenStream> {
bail_spanned!(module.span() => "`#[pymodule]` can only be used on inline modules") bail_spanned!(module.span() => "`#[pymodule]` can only be used on inline modules")
}; };
let options = PyModuleOptions::from_attrs(attrs)?; let options = PyModuleOptions::from_attrs(attrs)?;
let ctx = &Ctx::new(&options.krate); let ctx = &Ctx::new(&options.krate, None);
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let doc = get_doc(attrs, None, ctx); let doc = get_doc(attrs, None, ctx);
let name = options.name.unwrap_or_else(|| ident.unraw()); let name = options.name.unwrap_or_else(|| ident.unraw());
let full_name = if let Some(module) = &options.module { let full_name = if let Some(module) = &options.module {
@ -326,9 +326,9 @@ pub fn pymodule_module_impl(mut module: syn::ItemMod) -> Result<TokenStream> {
pub fn pymodule_function_impl(mut function: syn::ItemFn) -> Result<TokenStream> { pub fn pymodule_function_impl(mut function: syn::ItemFn) -> Result<TokenStream> {
let options = PyModuleOptions::from_attrs(&mut function.attrs)?; let options = PyModuleOptions::from_attrs(&mut function.attrs)?;
process_functions_in_module(&options, &mut function)?; process_functions_in_module(&options, &mut function)?;
let ctx = &Ctx::new(&options.krate); let ctx = &Ctx::new(&options.krate, None);
let stmts = std::mem::take(&mut function.block.stmts); let stmts = std::mem::take(&mut function.block.stmts);
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let ident = &function.sig.ident; let ident = &function.sig.ident;
let name = options.name.unwrap_or_else(|| ident.unraw()); let name = options.name.unwrap_or_else(|| ident.unraw());
let vis = &function.vis; let vis = &function.vis;
@ -400,7 +400,7 @@ pub fn pymodule_function_impl(mut function: syn::ItemFn) -> Result<TokenStream>
} }
fn module_initialization(name: &syn::Ident, ctx: &Ctx) -> TokenStream { fn module_initialization(name: &syn::Ident, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let pyinit_symbol = format!("PyInit_{}", name); let pyinit_symbol = format!("PyInit_{}", name);
let name = name.to_string(); let name = name.to_string();
@ -424,8 +424,8 @@ fn module_initialization(name: &syn::Ident, ctx: &Ctx) -> TokenStream {
/// Finds and takes care of the #[pyfn(...)] in `#[pymodule]` /// Finds and takes care of the #[pyfn(...)] in `#[pymodule]`
fn process_functions_in_module(options: &PyModuleOptions, func: &mut syn::ItemFn) -> Result<()> { fn process_functions_in_module(options: &PyModuleOptions, func: &mut syn::ItemFn) -> Result<()> {
let ctx = &Ctx::new(&options.krate); let ctx = &Ctx::new(&options.krate, None);
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let mut stmts: Vec<syn::Stmt> = Vec::new(); let mut stmts: Vec<syn::Stmt> = Vec::new();
#[cfg(feature = "gil-refs")] #[cfg(feature = "gil-refs")]

View File

@ -48,7 +48,7 @@ impl Holders {
} }
pub fn init_holders(&self, ctx: &Ctx) -> TokenStream { pub fn init_holders(&self, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let holders = &self.holders; let holders = &self.holders;
let gil_refs_checkers = self.gil_refs_checkers.iter().map(|checker| match checker { let gil_refs_checkers = self.gil_refs_checkers.iter().map(|checker| match checker {
GilRefChecker::FunctionArg(ident) => ident, GilRefChecker::FunctionArg(ident) => ident,
@ -94,7 +94,7 @@ pub(crate) fn check_arg_for_gil_refs(
gil_refs_checker: syn::Ident, gil_refs_checker: syn::Ident,
ctx: &Ctx, ctx: &Ctx,
) -> TokenStream { ) -> TokenStream {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
quote! { quote! {
#pyo3_path::impl_::deprecations::inspect_type(#tokens, &#gil_refs_checker) #pyo3_path::impl_::deprecations::inspect_type(#tokens, &#gil_refs_checker)
} }
@ -108,7 +108,7 @@ pub fn impl_arg_params(
ctx: &Ctx, ctx: &Ctx,
) -> (TokenStream, Vec<TokenStream>) { ) -> (TokenStream, Vec<TokenStream>) {
let args_array = syn::Ident::new("output", Span::call_site()); let args_array = syn::Ident::new("output", Span::call_site());
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let from_py_with = spec let from_py_with = spec
.signature .signature
@ -242,7 +242,7 @@ fn impl_arg_param(
holders: &mut Holders, holders: &mut Holders,
ctx: &Ctx, ctx: &Ctx,
) -> TokenStream { ) -> TokenStream {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let args_array = syn::Ident::new("output", Span::call_site()); let args_array = syn::Ident::new("output", Span::call_site());
match arg { match arg {
@ -290,7 +290,7 @@ pub(crate) fn impl_regular_arg_param(
holders: &mut Holders, holders: &mut Holders,
ctx: &Ctx, ctx: &Ctx,
) -> TokenStream { ) -> TokenStream {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let pyo3_path = pyo3_path.to_tokens_spanned(arg.ty.span()); let pyo3_path = pyo3_path.to_tokens_spanned(arg.ty.span());
// Use this macro inside this function, to ensure that all code generated here is associated // Use this macro inside this function, to ensure that all code generated here is associated

View File

@ -227,7 +227,7 @@ pub fn build_py_class(
) -> syn::Result<TokenStream> { ) -> syn::Result<TokenStream> {
args.options.take_pyo3_options(&mut class.attrs)?; args.options.take_pyo3_options(&mut class.attrs)?;
let ctx = &Ctx::new(&args.options.krate); let ctx = &Ctx::new(&args.options.krate, None);
let doc = utils::get_doc(&class.attrs, None, ctx); let doc = utils::get_doc(&class.attrs, None, ctx);
if let Some(lt) = class.generics.lifetimes().next() { if let Some(lt) = class.generics.lifetimes().next() {
@ -383,7 +383,7 @@ fn impl_class(
methods_type: PyClassMethodsType, methods_type: PyClassMethodsType,
ctx: &Ctx, ctx: &Ctx,
) -> syn::Result<TokenStream> { ) -> syn::Result<TokenStream> {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let pytypeinfo_impl = impl_pytypeinfo(cls, args, None, ctx); let pytypeinfo_impl = impl_pytypeinfo(cls, args, None, ctx);
let (default_richcmp, default_richcmp_slot) = let (default_richcmp, default_richcmp_slot) =
@ -457,7 +457,7 @@ pub fn build_py_enum(
) -> syn::Result<TokenStream> { ) -> syn::Result<TokenStream> {
args.options.take_pyo3_options(&mut enum_.attrs)?; args.options.take_pyo3_options(&mut enum_.attrs)?;
let ctx = &Ctx::new(&args.options.krate); let ctx = &Ctx::new(&args.options.krate, None);
if let Some(extends) = &args.options.extends { if let Some(extends) = &args.options.extends {
bail_spanned!(extends.span() => "enums can't extend from other classes"); bail_spanned!(extends.span() => "enums can't extend from other classes");
} else if let Some(subclass) = &args.options.subclass { } else if let Some(subclass) = &args.options.subclass {
@ -872,7 +872,7 @@ fn impl_complex_enum(
methods_type: PyClassMethodsType, methods_type: PyClassMethodsType,
ctx: &Ctx, ctx: &Ctx,
) -> Result<TokenStream> { ) -> Result<TokenStream> {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let cls = complex_enum.ident; let cls = complex_enum.ident;
let ty: syn::Type = syn::parse_quote!(#cls); let ty: syn::Type = syn::parse_quote!(#cls);
@ -886,7 +886,7 @@ fn impl_complex_enum(
rigged_args rigged_args
}; };
let ctx = &Ctx::new(&args.options.krate); let ctx = &Ctx::new(&args.options.krate, None);
let cls = complex_enum.ident; let cls = complex_enum.ident;
let variants = complex_enum.variants; let variants = complex_enum.variants;
let pytypeinfo = impl_pytypeinfo(cls, &args, None, ctx); let pytypeinfo = impl_pytypeinfo(cls, &args, None, ctx);
@ -1071,7 +1071,7 @@ fn impl_complex_enum_struct_variant_cls(
variant: &PyClassEnumStructVariant<'_>, variant: &PyClassEnumStructVariant<'_>,
ctx: &Ctx, ctx: &Ctx,
) -> Result<(TokenStream, Vec<MethodAndMethodDef>, Vec<MethodAndSlotDef>)> { ) -> Result<(TokenStream, Vec<MethodAndMethodDef>, Vec<MethodAndSlotDef>)> {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let variant_ident = &variant.ident; let variant_ident = &variant.ident;
let variant_cls = gen_complex_enum_variant_class_ident(enum_name, variant.ident); let variant_cls = gen_complex_enum_variant_class_ident(enum_name, variant.ident);
let variant_cls_type = parse_quote!(#variant_cls); let variant_cls_type = parse_quote!(#variant_cls);
@ -1135,7 +1135,7 @@ fn impl_complex_enum_tuple_variant_field_getters(
field_names: &mut Vec<Ident>, field_names: &mut Vec<Ident>,
fields_types: &mut Vec<syn::Type>, fields_types: &mut Vec<syn::Type>,
) -> Result<(Vec<MethodAndMethodDef>, Vec<syn::ImplItemFn>)> { ) -> Result<(Vec<MethodAndMethodDef>, Vec<syn::ImplItemFn>)> {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let mut field_getters = vec![]; let mut field_getters = vec![];
let mut field_getter_impls = vec![]; let mut field_getter_impls = vec![];
@ -1182,7 +1182,7 @@ fn impl_complex_enum_tuple_variant_len(
variant_cls_type: &syn::Type, variant_cls_type: &syn::Type,
num_fields: usize, num_fields: usize,
) -> Result<(MethodAndSlotDef, syn::ImplItemFn)> { ) -> Result<(MethodAndSlotDef, syn::ImplItemFn)> {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let mut len_method_impl: syn::ImplItemFn = parse_quote! { let mut len_method_impl: syn::ImplItemFn = parse_quote! {
fn __len__(slf: #pyo3_path::PyRef<Self>) -> #pyo3_path::PyResult<usize> { fn __len__(slf: #pyo3_path::PyRef<Self>) -> #pyo3_path::PyResult<usize> {
@ -1202,7 +1202,7 @@ fn impl_complex_enum_tuple_variant_getitem(
variant_cls_type: &syn::Type, variant_cls_type: &syn::Type,
num_fields: usize, num_fields: usize,
) -> Result<(MethodAndSlotDef, syn::ImplItemFn)> { ) -> Result<(MethodAndSlotDef, syn::ImplItemFn)> {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let match_arms: Vec<_> = (0..num_fields) let match_arms: Vec<_> = (0..num_fields)
.map(|i| { .map(|i| {
@ -1243,7 +1243,7 @@ fn impl_complex_enum_tuple_variant_cls(
variant: &PyClassEnumTupleVariant<'_>, variant: &PyClassEnumTupleVariant<'_>,
ctx: &Ctx, ctx: &Ctx,
) -> Result<(TokenStream, Vec<MethodAndMethodDef>, Vec<MethodAndSlotDef>)> { ) -> Result<(TokenStream, Vec<MethodAndMethodDef>, Vec<MethodAndSlotDef>)> {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let variant_ident = &variant.ident; let variant_ident = &variant.ident;
let variant_cls = gen_complex_enum_variant_class_ident(enum_name, variant.ident); let variant_cls = gen_complex_enum_variant_class_ident(enum_name, variant.ident);
let variant_cls_type = parse_quote!(#variant_cls); let variant_cls_type = parse_quote!(#variant_cls);
@ -1400,7 +1400,7 @@ pub fn gen_complex_enum_variant_attr(
spec: &ConstSpec<'_>, spec: &ConstSpec<'_>,
ctx: &Ctx, ctx: &Ctx,
) -> MethodAndMethodDef { ) -> MethodAndMethodDef {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let member = &spec.rust_ident; let member = &spec.rust_ident;
let wrapper_ident = format_ident!("__pymethod_variant_cls_{}__", member); let wrapper_ident = format_ident!("__pymethod_variant_cls_{}__", member);
let deprecations = &spec.attributes.deprecations; let deprecations = &spec.attributes.deprecations;
@ -1449,7 +1449,7 @@ fn complex_enum_struct_variant_new<'a>(
variant: PyClassEnumStructVariant<'a>, variant: PyClassEnumStructVariant<'a>,
ctx: &Ctx, ctx: &Ctx,
) -> Result<MethodAndSlotDef> { ) -> Result<MethodAndSlotDef> {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let variant_cls = format_ident!("{}_{}", cls, variant.ident); let variant_cls = format_ident!("{}_{}", cls, variant.ident);
let variant_cls_type: syn::Type = parse_quote!(#variant_cls); let variant_cls_type: syn::Type = parse_quote!(#variant_cls);
@ -1506,7 +1506,7 @@ fn complex_enum_tuple_variant_new<'a>(
variant: PyClassEnumTupleVariant<'a>, variant: PyClassEnumTupleVariant<'a>,
ctx: &Ctx, ctx: &Ctx,
) -> Result<MethodAndSlotDef> { ) -> Result<MethodAndSlotDef> {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let variant_cls: Ident = format_ident!("{}_{}", cls, variant.ident); let variant_cls: Ident = format_ident!("{}_{}", cls, variant.ident);
let variant_cls_type: syn::Type = parse_quote!(#variant_cls); let variant_cls_type: syn::Type = parse_quote!(#variant_cls);
@ -1645,7 +1645,7 @@ fn impl_pytypeinfo(
deprecations: Option<&Deprecations<'_>>, deprecations: Option<&Deprecations<'_>>,
ctx: &Ctx, ctx: &Ctx,
) -> TokenStream { ) -> TokenStream {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let cls_name = get_class_python_name(cls, attr).to_string(); let cls_name = get_class_python_name(cls, attr).to_string();
let module = if let Some(ModuleAttribute { value, .. }) = &attr.options.module { let module = if let Some(ModuleAttribute { value, .. }) = &attr.options.module {
@ -1689,7 +1689,7 @@ fn pyclass_richcmp_arms(
options: &PyClassPyO3Options, options: &PyClassPyO3Options,
ctx: &Ctx, ctx: &Ctx,
) -> std::result::Result<TokenStream, syn::Error> { ) -> std::result::Result<TokenStream, syn::Error> {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let eq_arms = options let eq_arms = options
.eq .eq
@ -1743,7 +1743,7 @@ fn pyclass_richcmp_simple_enum(
repr_type: &syn::Ident, repr_type: &syn::Ident,
ctx: &Ctx, ctx: &Ctx,
) -> Result<(Option<syn::ImplItemFn>, Option<MethodAndSlotDef>)> { ) -> Result<(Option<syn::ImplItemFn>, Option<MethodAndSlotDef>)> {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
if let Some(eq_int) = options.eq_int { if let Some(eq_int) = options.eq_int {
ensure_spanned!(options.eq.is_some(), eq_int.span() => "The `eq_int` option requires the `eq` option."); ensure_spanned!(options.eq.is_some(), eq_int.span() => "The `eq_int` option requires the `eq` option.");
@ -1827,7 +1827,7 @@ fn pyclass_richcmp(
cls: &syn::Type, cls: &syn::Type,
ctx: &Ctx, ctx: &Ctx,
) -> Result<(Option<syn::ImplItemFn>, Option<MethodAndSlotDef>)> { ) -> Result<(Option<syn::ImplItemFn>, Option<MethodAndSlotDef>)> {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
if let Some(eq_int) = options.eq_int { if let Some(eq_int) = options.eq_int {
bail_spanned!(eq_int.span() => "`eq_int` can only be used on simple enums.") bail_spanned!(eq_int.span() => "`eq_int` can only be used on simple enums.")
} }
@ -1940,7 +1940,7 @@ impl<'a> PyClassImplsBuilder<'a> {
} }
fn impl_pyclass(&self, ctx: &Ctx) -> TokenStream { fn impl_pyclass(&self, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let cls = self.cls; let cls = self.cls;
let frozen = if self.attr.options.frozen.is_some() { let frozen = if self.attr.options.frozen.is_some() {
@ -1956,7 +1956,7 @@ impl<'a> PyClassImplsBuilder<'a> {
} }
} }
fn impl_extractext(&self, ctx: &Ctx) -> TokenStream { fn impl_extractext(&self, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let cls = self.cls; let cls = self.cls;
if self.attr.options.frozen.is_some() { if self.attr.options.frozen.is_some() {
quote! { quote! {
@ -1996,7 +1996,7 @@ impl<'a> PyClassImplsBuilder<'a> {
} }
fn impl_into_py(&self, ctx: &Ctx) -> TokenStream { fn impl_into_py(&self, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let cls = self.cls; let cls = self.cls;
let attr = self.attr; let attr = self.attr;
// If #cls is not extended type, we allow Self->PyObject conversion // If #cls is not extended type, we allow Self->PyObject conversion
@ -2013,7 +2013,7 @@ impl<'a> PyClassImplsBuilder<'a> {
} }
} }
fn impl_pyclassimpl(&self, ctx: &Ctx) -> Result<TokenStream> { fn impl_pyclassimpl(&self, ctx: &Ctx) -> Result<TokenStream> {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let cls = self.cls; let cls = self.cls;
let doc = self.doc.as_ref().map_or( let doc = self.doc.as_ref().map_or(
quote! {#pyo3_path::ffi::c_str!("")}, quote! {#pyo3_path::ffi::c_str!("")},
@ -2184,7 +2184,7 @@ impl<'a> PyClassImplsBuilder<'a> {
} }
fn impl_add_to_module(&self, ctx: &Ctx) -> TokenStream { fn impl_add_to_module(&self, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let cls = self.cls; let cls = self.cls;
quote! { quote! {
impl #cls { impl #cls {
@ -2196,7 +2196,7 @@ impl<'a> PyClassImplsBuilder<'a> {
fn impl_freelist(&self, ctx: &Ctx) -> TokenStream { fn impl_freelist(&self, ctx: &Ctx) -> TokenStream {
let cls = self.cls; let cls = self.cls;
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
self.attr.options.freelist.as_ref().map_or(quote!{}, |freelist| { self.attr.options.freelist.as_ref().map_or(quote!{}, |freelist| {
let freelist = &freelist.value; let freelist = &freelist.value;
@ -2219,7 +2219,7 @@ impl<'a> PyClassImplsBuilder<'a> {
} }
fn freelist_slots(&self, ctx: &Ctx) -> Vec<TokenStream> { fn freelist_slots(&self, ctx: &Ctx) -> Vec<TokenStream> {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let cls = self.cls; let cls = self.cls;
if self.attr.options.freelist.is_some() { if self.attr.options.freelist.is_some() {
@ -2244,7 +2244,7 @@ impl<'a> PyClassImplsBuilder<'a> {
} }
fn define_inventory_class(inventory_class_name: &syn::Ident, ctx: &Ctx) -> TokenStream { fn define_inventory_class(inventory_class_name: &syn::Ident, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
quote! { quote! {
#[doc(hidden)] #[doc(hidden)]
pub struct #inventory_class_name { pub struct #inventory_class_name {

View File

@ -205,8 +205,8 @@ pub fn impl_wrap_pyfunction(
krate, krate,
} = options; } = options;
let ctx = &Ctx::new(&krate); let ctx = &Ctx::new(&krate, Some(&func.sig));
let Ctx { pyo3_path } = &ctx; let Ctx { pyo3_path, .. } = &ctx;
let python_name = name let python_name = name
.as_ref() .as_ref()

View File

@ -90,7 +90,6 @@ pub fn impl_methods(
methods_type: PyClassMethodsType, methods_type: PyClassMethodsType,
options: PyImplOptions, options: PyImplOptions,
) -> syn::Result<TokenStream> { ) -> syn::Result<TokenStream> {
let ctx = &Ctx::new(&options.krate);
let mut trait_impls = Vec::new(); let mut trait_impls = Vec::new();
let mut proto_impls = Vec::new(); let mut proto_impls = Vec::new();
let mut methods = Vec::new(); let mut methods = Vec::new();
@ -101,6 +100,7 @@ pub fn impl_methods(
for iimpl in impls { for iimpl in impls {
match iimpl { match iimpl {
syn::ImplItem::Fn(meth) => { syn::ImplItem::Fn(meth) => {
let ctx = &Ctx::new(&options.krate, Some(&meth.sig));
let mut fun_options = PyFunctionOptions::from_attrs(&mut meth.attrs)?; let mut fun_options = PyFunctionOptions::from_attrs(&mut meth.attrs)?;
fun_options.krate = fun_options.krate.or_else(|| options.krate.clone()); fun_options.krate = fun_options.krate.or_else(|| options.krate.clone());
match pymethod::gen_py_method(ty, &mut meth.sig, &mut meth.attrs, fun_options, ctx)? match pymethod::gen_py_method(ty, &mut meth.sig, &mut meth.attrs, fun_options, ctx)?
@ -129,6 +129,7 @@ pub fn impl_methods(
} }
} }
syn::ImplItem::Const(konst) => { syn::ImplItem::Const(konst) => {
let ctx = &Ctx::new(&options.krate, None);
let attributes = ConstAttributes::from_attrs(&mut konst.attrs, ctx)?; let attributes = ConstAttributes::from_attrs(&mut konst.attrs, ctx)?;
if attributes.is_class_attr { if attributes.is_class_attr {
let spec = ConstSpec { let spec = ConstSpec {
@ -159,11 +160,10 @@ pub fn impl_methods(
_ => {} _ => {}
} }
} }
let ctx = &Ctx::new(&options.krate, None);
add_shared_proto_slots(ty, &mut proto_impls, implemented_proto_fragments, ctx); add_shared_proto_slots(ty, &mut proto_impls, implemented_proto_fragments, ctx);
let ctx = &Ctx::new(&options.krate);
let items = match methods_type { let items = match methods_type {
PyClassMethodsType::Specialization => impl_py_methods(ty, methods, proto_impls, ctx), PyClassMethodsType::Specialization => impl_py_methods(ty, methods, proto_impls, ctx),
PyClassMethodsType::Inventory => submit_methods_inventory(ty, methods, proto_impls, ctx), PyClassMethodsType::Inventory => submit_methods_inventory(ty, methods, proto_impls, ctx),
@ -187,7 +187,7 @@ pub fn gen_py_const(cls: &syn::Type, spec: &ConstSpec<'_>, ctx: &Ctx) -> MethodA
let wrapper_ident = format_ident!("__pymethod_{}__", member); let wrapper_ident = format_ident!("__pymethod_{}__", member);
let deprecations = &spec.attributes.deprecations; let deprecations = &spec.attributes.deprecations;
let python_name = spec.null_terminated_python_name(ctx); let python_name = spec.null_terminated_python_name(ctx);
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let associated_method = quote! { let associated_method = quote! {
fn #wrapper_ident(py: #pyo3_path::Python<'_>) -> #pyo3_path::PyResult<#pyo3_path::PyObject> { fn #wrapper_ident(py: #pyo3_path::Python<'_>) -> #pyo3_path::PyResult<#pyo3_path::PyObject> {
@ -217,7 +217,7 @@ fn impl_py_methods(
proto_impls: Vec<TokenStream>, proto_impls: Vec<TokenStream>,
ctx: &Ctx, ctx: &Ctx,
) -> TokenStream { ) -> TokenStream {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
quote! { quote! {
#[allow(unknown_lints, non_local_definitions)] #[allow(unknown_lints, non_local_definitions)]
impl #pyo3_path::impl_::pyclass::PyMethods<#ty> impl #pyo3_path::impl_::pyclass::PyMethods<#ty>
@ -240,7 +240,7 @@ fn add_shared_proto_slots(
mut implemented_proto_fragments: HashSet<String>, mut implemented_proto_fragments: HashSet<String>,
ctx: &Ctx, ctx: &Ctx,
) { ) {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
macro_rules! try_add_shared_slot { macro_rules! try_add_shared_slot {
($slot:ident, $($fragments:literal),*) => {{ ($slot:ident, $($fragments:literal),*) => {{
let mut implemented = false; let mut implemented = false;
@ -298,7 +298,7 @@ fn submit_methods_inventory(
proto_impls: Vec<TokenStream>, proto_impls: Vec<TokenStream>,
ctx: &Ctx, ctx: &Ctx,
) -> TokenStream { ) -> TokenStream {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
quote! { quote! {
#pyo3_path::inventory::submit! { #pyo3_path::inventory::submit! {
type Inventory = <#ty as #pyo3_path::impl_::pyclass::PyClassImpl>::Inventory; type Inventory = <#ty as #pyo3_path::impl_::pyclass::PyClassImpl>::Inventory;

View File

@ -196,7 +196,7 @@ pub fn gen_py_method(
ensure_function_options_valid(&options)?; ensure_function_options_valid(&options)?;
let method = PyMethod::parse(sig, meth_attrs, options, ctx)?; let method = PyMethod::parse(sig, meth_attrs, options, ctx)?;
let spec = &method.spec; let spec = &method.spec;
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
Ok(match (method.kind, &spec.tp) { Ok(match (method.kind, &spec.tp) {
// Class attributes go before protos so that class attributes can be used to set proto // Class attributes go before protos so that class attributes can be used to set proto
@ -318,7 +318,7 @@ pub fn impl_py_method_def(
flags: Option<TokenStream>, flags: Option<TokenStream>,
ctx: &Ctx, ctx: &Ctx,
) -> Result<MethodAndMethodDef> { ) -> Result<MethodAndMethodDef> {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let wrapper_ident = format_ident!("__pymethod_{}__", spec.python_name); let wrapper_ident = format_ident!("__pymethod_{}__", spec.python_name);
let associated_method = spec.get_wrapper_function(&wrapper_ident, Some(cls), ctx)?; let associated_method = spec.get_wrapper_function(&wrapper_ident, Some(cls), ctx)?;
let add_flags = flags.map(|flags| quote!(.flags(#flags))); let add_flags = flags.map(|flags| quote!(.flags(#flags)));
@ -343,7 +343,7 @@ pub fn impl_py_method_def_new(
spec: &FnSpec<'_>, spec: &FnSpec<'_>,
ctx: &Ctx, ctx: &Ctx,
) -> Result<MethodAndSlotDef> { ) -> Result<MethodAndSlotDef> {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let wrapper_ident = syn::Ident::new("__pymethod___new____", Span::call_site()); let wrapper_ident = syn::Ident::new("__pymethod___new____", Span::call_site());
let associated_method = spec.get_wrapper_function(&wrapper_ident, Some(cls), ctx)?; let associated_method = spec.get_wrapper_function(&wrapper_ident, Some(cls), ctx)?;
// Use just the text_signature_call_signature() because the class' Python name // Use just the text_signature_call_signature() because the class' Python name
@ -393,7 +393,7 @@ pub fn impl_py_method_def_new(
} }
fn impl_call_slot(cls: &syn::Type, mut spec: FnSpec<'_>, ctx: &Ctx) -> Result<MethodAndSlotDef> { fn impl_call_slot(cls: &syn::Type, mut spec: FnSpec<'_>, ctx: &Ctx) -> Result<MethodAndSlotDef> {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
// HACK: __call__ proto slot must always use varargs calling convention, so change the spec. // HACK: __call__ proto slot must always use varargs calling convention, so change the spec.
// Probably indicates there's a refactoring opportunity somewhere. // Probably indicates there's a refactoring opportunity somewhere.
@ -433,7 +433,7 @@ fn impl_traverse_slot(
spec: &FnSpec<'_>, spec: &FnSpec<'_>,
ctx: &Ctx, ctx: &Ctx,
) -> syn::Result<MethodAndSlotDef> { ) -> syn::Result<MethodAndSlotDef> {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
if let (Some(py_arg), _) = split_off_python_arg(&spec.signature.arguments) { if let (Some(py_arg), _) = split_off_python_arg(&spec.signature.arguments) {
return Err(syn::Error::new_spanned(py_arg.ty, "__traverse__ may not take `Python`. \ return Err(syn::Error::new_spanned(py_arg.ty, "__traverse__ may not take `Python`. \
Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` \ Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` \
@ -484,7 +484,7 @@ fn impl_py_class_attribute(
spec: &FnSpec<'_>, spec: &FnSpec<'_>,
ctx: &Ctx, ctx: &Ctx,
) -> syn::Result<MethodAndMethodDef> { ) -> syn::Result<MethodAndMethodDef> {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let (py_arg, args) = split_off_python_arg(&spec.signature.arguments); let (py_arg, args) = split_off_python_arg(&spec.signature.arguments);
ensure_spanned!( ensure_spanned!(
args.is_empty(), args.is_empty(),
@ -559,7 +559,7 @@ pub fn impl_py_setter_def(
property_type: PropertyType<'_>, property_type: PropertyType<'_>,
ctx: &Ctx, ctx: &Ctx,
) -> Result<MethodAndMethodDef> { ) -> Result<MethodAndMethodDef> {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let python_name = property_type.null_terminated_python_name(ctx)?; let python_name = property_type.null_terminated_python_name(ctx)?;
let doc = property_type.doc(ctx); let doc = property_type.doc(ctx);
let mut holders = Holders::new(); let mut holders = Holders::new();
@ -745,7 +745,7 @@ pub fn impl_py_getter_def(
property_type: PropertyType<'_>, property_type: PropertyType<'_>,
ctx: &Ctx, ctx: &Ctx,
) -> Result<MethodAndMethodDef> { ) -> Result<MethodAndMethodDef> {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let python_name = property_type.null_terminated_python_name(ctx)?; let python_name = property_type.null_terminated_python_name(ctx)?;
let doc = property_type.doc(ctx); let doc = property_type.doc(ctx);
@ -871,7 +871,7 @@ pub enum PropertyType<'a> {
impl PropertyType<'_> { impl PropertyType<'_> {
fn null_terminated_python_name(&self, ctx: &Ctx) -> Result<TokenStream> { fn null_terminated_python_name(&self, ctx: &Ctx) -> Result<TokenStream> {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
match self { match self {
PropertyType::Descriptor { PropertyType::Descriptor {
field, field,
@ -913,7 +913,7 @@ pub const __REPR__: SlotDef = SlotDef::new("Py_tp_repr", "reprfunc");
pub const __HASH__: SlotDef = SlotDef::new("Py_tp_hash", "hashfunc") pub const __HASH__: SlotDef = SlotDef::new("Py_tp_hash", "hashfunc")
.ret_ty(Ty::PyHashT) .ret_ty(Ty::PyHashT)
.return_conversion(TokenGenerator( .return_conversion(TokenGenerator(
|Ctx { pyo3_path }: &Ctx| quote! { #pyo3_path::callback::HashCallbackOutput }, |Ctx { pyo3_path, .. }: &Ctx| quote! { #pyo3_path::callback::HashCallbackOutput },
)); ));
pub const __RICHCMP__: SlotDef = SlotDef::new("Py_tp_richcompare", "richcmpfunc") pub const __RICHCMP__: SlotDef = SlotDef::new("Py_tp_richcompare", "richcmpfunc")
.extract_error_mode(ExtractErrorMode::NotImplemented) .extract_error_mode(ExtractErrorMode::NotImplemented)
@ -1036,7 +1036,11 @@ enum Ty {
impl Ty { impl Ty {
fn ffi_type(self, ctx: &Ctx) -> TokenStream { fn ffi_type(self, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx; let Ctx {
pyo3_path,
output_span,
} = ctx;
let pyo3_path = pyo3_path.to_tokens_spanned(*output_span);
match self { match self {
Ty::Object | Ty::MaybeNullObject => quote! { *mut #pyo3_path::ffi::PyObject }, Ty::Object | Ty::MaybeNullObject => quote! { *mut #pyo3_path::ffi::PyObject },
Ty::NonNullObject => quote! { ::std::ptr::NonNull<#pyo3_path::ffi::PyObject> }, Ty::NonNullObject => quote! { ::std::ptr::NonNull<#pyo3_path::ffi::PyObject> },
@ -1057,7 +1061,7 @@ impl Ty {
holders: &mut Holders, holders: &mut Holders,
ctx: &Ctx, ctx: &Ctx,
) -> TokenStream { ) -> TokenStream {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
match self { match self {
Ty::Object => extract_object( Ty::Object => extract_object(
extract_error_mode, extract_error_mode,
@ -1122,7 +1126,7 @@ fn extract_object(
source_ptr: TokenStream, source_ptr: TokenStream,
ctx: &Ctx, ctx: &Ctx,
) -> TokenStream { ) -> TokenStream {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let gil_refs_checker = holders.push_gil_refs_checker(arg.ty().span()); let gil_refs_checker = holders.push_gil_refs_checker(arg.ty().span());
let name = arg.name().unraw().to_string(); let name = arg.name().unraw().to_string();
@ -1162,7 +1166,7 @@ enum ReturnMode {
impl ReturnMode { impl ReturnMode {
fn return_call_output(&self, call: TokenStream, ctx: &Ctx, holders: &Holders) -> TokenStream { fn return_call_output(&self, call: TokenStream, ctx: &Ctx, holders: &Holders) -> TokenStream {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let check_gil_refs = holders.check_gil_refs(); let check_gil_refs = holders.check_gil_refs();
match self { match self {
ReturnMode::Conversion(conversion) => { ReturnMode::Conversion(conversion) => {
@ -1265,7 +1269,7 @@ impl SlotDef {
method_name: &str, method_name: &str,
ctx: &Ctx, ctx: &Ctx,
) -> Result<MethodAndSlotDef> { ) -> Result<MethodAndSlotDef> {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let SlotDef { let SlotDef {
slot, slot,
func_ty, func_ty,
@ -1345,7 +1349,7 @@ fn generate_method_body(
return_mode: Option<&ReturnMode>, return_mode: Option<&ReturnMode>,
ctx: &Ctx, ctx: &Ctx,
) -> Result<TokenStream> { ) -> Result<TokenStream> {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let self_arg = spec let self_arg = spec
.tp .tp
.self_arg(Some(cls), extract_error_mode, holders, ctx); .self_arg(Some(cls), extract_error_mode, holders, ctx);
@ -1397,7 +1401,7 @@ impl SlotFragmentDef {
spec: &FnSpec<'_>, spec: &FnSpec<'_>,
ctx: &Ctx, ctx: &Ctx,
) -> Result<TokenStream> { ) -> Result<TokenStream> {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
let SlotFragmentDef { let SlotFragmentDef {
fragment, fragment,
arguments, arguments,

View File

@ -1,23 +1,31 @@
use crate::utils::Ctx; use crate::utils::Ctx;
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::quote; use quote::{quote, quote_spanned};
pub(crate) fn some_wrap(obj: TokenStream, ctx: &Ctx) -> TokenStream { pub(crate) fn some_wrap(obj: TokenStream, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
quote! { quote! {
#pyo3_path::impl_::wrap::SomeWrap::wrap(#obj) #pyo3_path::impl_::wrap::SomeWrap::wrap(#obj)
} }
} }
pub(crate) fn ok_wrap(obj: TokenStream, ctx: &Ctx) -> TokenStream { pub(crate) fn ok_wrap(obj: TokenStream, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx; let Ctx {
quote! { pyo3_path,
output_span,
} = ctx;
let pyo3_path = pyo3_path.to_tokens_spanned(*output_span);
quote_spanned! {*output_span=>
#pyo3_path::impl_::wrap::OkWrap::wrap(#obj) #pyo3_path::impl_::wrap::OkWrap::wrap(#obj)
.map_err(::core::convert::Into::<#pyo3_path::PyErr>::into) .map_err(::core::convert::Into::<#pyo3_path::PyErr>::into)
} }
} }
pub(crate) fn map_result_into_ptr(result: TokenStream, ctx: &Ctx) -> TokenStream { pub(crate) fn map_result_into_ptr(result: TokenStream, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx; let Ctx {
quote! { #pyo3_path::impl_::wrap::map_result_into_ptr(py, #result) } pyo3_path,
output_span,
} = ctx;
let pyo3_path = pyo3_path.to_tokens_spanned(*output_span);
quote_spanned! {*output_span=> #pyo3_path::impl_::wrap::map_result_into_ptr(py, #result) }
} }

View File

@ -1,9 +1,9 @@
use crate::attributes::{CrateAttribute, RenamingRule};
use proc_macro2::{Span, TokenStream}; use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens}; use quote::{quote, ToTokens};
use syn::spanned::Spanned;
use syn::{punctuated::Punctuated, Token}; use syn::{punctuated::Punctuated, Token};
use crate::attributes::{CrateAttribute, RenamingRule};
/// Macro inspired by `anyhow::anyhow!` to create a compiler error with the given span. /// Macro inspired by `anyhow::anyhow!` to create a compiler error with the given span.
macro_rules! err_spanned { macro_rules! err_spanned {
($span:expr => $msg:expr) => { ($span:expr => $msg:expr) => {
@ -86,7 +86,7 @@ pub fn get_doc(
mut text_signature: Option<String>, mut text_signature: Option<String>,
ctx: &Ctx, ctx: &Ctx,
) -> PythonDoc { ) -> PythonDoc {
let Ctx { pyo3_path } = ctx; let Ctx { pyo3_path, .. } = ctx;
// insert special divider between `__text_signature__` and doc // insert special divider between `__text_signature__` and doc
// (assume text_signature is itself well-formed) // (assume text_signature is itself well-formed)
if let Some(text_signature) = &mut text_signature { if let Some(text_signature) = &mut text_signature {
@ -162,17 +162,35 @@ pub fn unwrap_ty_group(mut ty: &syn::Type) -> &syn::Type {
} }
pub struct Ctx { pub struct Ctx {
/// Where we can find the pyo3 crate
pub pyo3_path: PyO3CratePath, pub pyo3_path: PyO3CratePath,
/// If we are in a pymethod or pyfunction,
/// this will be the span of the return type
pub output_span: Span,
} }
impl Ctx { impl Ctx {
pub(crate) fn new(attr: &Option<CrateAttribute>) -> Self { pub(crate) fn new(attr: &Option<CrateAttribute>, signature: Option<&syn::Signature>) -> Self {
let pyo3_path = match attr { let pyo3_path = match attr {
Some(attr) => PyO3CratePath::Given(attr.value.0.clone()), Some(attr) => PyO3CratePath::Given(attr.value.0.clone()),
None => PyO3CratePath::Default, None => PyO3CratePath::Default,
}; };
Self { pyo3_path } let output_span = if let Some(syn::Signature {
output: syn::ReturnType::Type(_, output_type),
..
}) = &signature
{
output_type.span()
} else {
Span::call_site()
};
Self {
pyo3_path,
output_span,
}
} }
} }

View File

@ -152,7 +152,15 @@ pub trait ToPyObject {
/// # } /// # }
/// ``` /// ```
/// Python code will see this as any of the `int`, `string` or `None` objects. /// Python code will see this as any of the `int`, `string` or `None` objects.
#[doc(alias = "IntoPyCallbackOutput")] #[cfg_attr(
diagnostic_namespace,
diagnostic::on_unimplemented(
message = "`{Self}` cannot be converted to a Python object",
note = "`IntoPy` is automatically implemented by the `#[pyclass]` macro",
note = "if you do not wish to have a corresponding Python type, implement it manually",
note = "if you do not own `{Self}` you can perform a manual conversion to one of the types in `pyo3::types::*`"
)
)]
pub trait IntoPy<T>: Sized { pub trait IntoPy<T>: Sized {
/// Performs the conversion. /// Performs the conversion.
fn into_py(self, py: Python<'_>) -> T; fn into_py(self, py: Python<'_>) -> T;

View File

@ -20,6 +20,15 @@ impl<T> SomeWrap<T> for Option<T> {
} }
/// Used to wrap the result of `#[pyfunction]` and `#[pymethods]`. /// Used to wrap the result of `#[pyfunction]` and `#[pymethods]`.
#[cfg_attr(
diagnostic_namespace,
diagnostic::on_unimplemented(
message = "`{Self}` cannot be converted to a Python object",
note = "`IntoPy` is automatically implemented by the `#[pyclass]` macro",
note = "if you do not wish to have a corresponding Python type, implement `IntoPy` manually",
note = "if you do not own `{Self}` you can perform a manual conversion to one of the types in `pyo3::types::*`"
)
)]
pub trait OkWrap<T> { pub trait OkWrap<T> {
type Error; type Error;
fn wrap(self) -> Result<T, Self::Error>; fn wrap(self) -> Result<T, Self::Error>;

View File

@ -1,8 +1,8 @@
error[E0277]: the trait bound `PyErr: From<MyError>` is not satisfied error[E0277]: the trait bound `PyErr: From<MyError>` is not satisfied
--> tests/ui/invalid_result_conversion.rs:21:1 --> tests/ui/invalid_result_conversion.rs:22:25
| |
21 | #[pyfunction] 22 | fn should_not_work() -> Result<(), MyError> {
| ^^^^^^^^^^^^^ the trait `From<MyError>` is not implemented for `PyErr`, which is required by `MyError: Into<PyErr>` | ^^^^^^ the trait `From<MyError>` is not implemented for `PyErr`, which is required by `MyError: Into<PyErr>`
| |
= help: the following other types implement trait `From<T>`: = help: the following other types implement trait `From<T>`:
<PyErr as From<AddrParseError>> <PyErr as From<AddrParseError>>
@ -15,4 +15,3 @@ error[E0277]: the trait bound `PyErr: From<MyError>` is not satisfied
<PyErr as From<IntoInnerError<W>>> <PyErr as From<IntoInnerError<W>>>
and $N others and $N others
= note: required for `MyError` to implement `Into<PyErr>` = note: required for `MyError` to implement `Into<PyErr>`
= note: this error originates in the attribute macro `pyfunction` (in Nightly builds, run with -Z macro-backtrace for more info)

View File

@ -1,9 +1,11 @@
error[E0277]: the trait bound `Blah: OkWrap<Blah>` is not satisfied error[E0277]: `Blah` cannot be converted to a Python object
--> tests/ui/missing_intopy.rs:3:1 --> tests/ui/missing_intopy.rs:4:14
| |
3 | #[pyo3::pyfunction] 4 | fn blah() -> Blah{
| ^^^^^^^^^^^^^^^^^^^ the trait `IntoPy<Py<PyAny>>` is not implemented for `Blah`, which is required by `Blah: OkWrap<_>` | ^^^^ the trait `IntoPy<Py<PyAny>>` is not implemented for `Blah`, which is required by `Blah: OkWrap<_>`
| |
= note: `IntoPy` is automatically implemented by the `#[pyclass]` macro
= note: if you do not wish to have a corresponding Python type, implement `IntoPy` manually
= note: if you do not own `Blah` you can perform a manual conversion to one of the types in `pyo3::types::*`
= help: the trait `OkWrap<T>` is implemented for `Result<T, E>` = help: the trait `OkWrap<T>` is implemented for `Result<T, E>`
= note: required for `Blah` to implement `OkWrap<Blah>` = note: required for `Blah` to implement `OkWrap<Blah>`
= note: this error originates in the attribute macro `pyo3::pyfunction` (in Nightly builds, run with -Z macro-backtrace for more info)