1use std::borrow::Cow;
2use std::ffi::CString;
3use std::fmt::Display;
4
5use proc_macro2::{Span, TokenStream};
6use quote::{format_ident, quote, quote_spanned, ToTokens};
7use syn::{ext::IdentExt, spanned::Spanned, Ident, Result};
8
9use crate::pyversions::is_abi3_before;
10use crate::utils::{Ctx, LitCStr};
11use crate::{
12 attributes::{FromPyWithAttribute, TextSignatureAttribute, TextSignatureAttributeValue},
13 params::{impl_arg_params, Holders},
14 pyfunction::{
15 FunctionSignature, PyFunctionArgPyO3Attributes, PyFunctionOptions, SignatureAttribute,
16 },
17 quotes,
18 utils::{self, PythonDoc},
19};
20
21#[derive(Clone, Debug)]
22pub struct RegularArg<'a> {
23 pub name: Cow<'a, syn::Ident>,
24 pub ty: &'a syn::Type,
25 pub from_py_with: Option<FromPyWithAttribute>,
26 pub default_value: Option<syn::Expr>,
27 pub option_wrapped_type: Option<&'a syn::Type>,
28}
29
30#[derive(Clone, Debug)]
32pub struct VarargsArg<'a> {
33 pub name: Cow<'a, syn::Ident>,
34 pub ty: &'a syn::Type,
35}
36
37#[derive(Clone, Debug)]
39pub struct KwargsArg<'a> {
40 pub name: Cow<'a, syn::Ident>,
41 pub ty: &'a syn::Type,
42}
43
44#[derive(Clone, Debug)]
45pub struct CancelHandleArg<'a> {
46 pub name: &'a syn::Ident,
47 pub ty: &'a syn::Type,
48}
49
50#[derive(Clone, Debug)]
51pub struct PyArg<'a> {
52 pub name: &'a syn::Ident,
53 pub ty: &'a syn::Type,
54}
55
56#[derive(Clone, Debug)]
57pub enum FnArg<'a> {
58 Regular(RegularArg<'a>),
59 VarArgs(VarargsArg<'a>),
60 KwArgs(KwargsArg<'a>),
61 Py(PyArg<'a>),
62 CancelHandle(CancelHandleArg<'a>),
63}
64
65impl<'a> FnArg<'a> {
66 pub fn name(&self) -> &syn::Ident {
67 match self {
68 FnArg::Regular(RegularArg { name, .. }) => name,
69 FnArg::VarArgs(VarargsArg { name, .. }) => name,
70 FnArg::KwArgs(KwargsArg { name, .. }) => name,
71 FnArg::Py(PyArg { name, .. }) => name,
72 FnArg::CancelHandle(CancelHandleArg { name, .. }) => name,
73 }
74 }
75
76 pub fn ty(&self) -> &'a syn::Type {
77 match self {
78 FnArg::Regular(RegularArg { ty, .. }) => ty,
79 FnArg::VarArgs(VarargsArg { ty, .. }) => ty,
80 FnArg::KwArgs(KwargsArg { ty, .. }) => ty,
81 FnArg::Py(PyArg { ty, .. }) => ty,
82 FnArg::CancelHandle(CancelHandleArg { ty, .. }) => ty,
83 }
84 }
85
86 #[allow(clippy::wrong_self_convention)]
87 pub fn from_py_with(&self) -> Option<&FromPyWithAttribute> {
88 if let FnArg::Regular(RegularArg { from_py_with, .. }) = self {
89 from_py_with.as_ref()
90 } else {
91 None
92 }
93 }
94
95 pub fn to_varargs_mut(&mut self) -> Result<&mut Self> {
96 if let Self::Regular(RegularArg {
97 name,
98 ty,
99 option_wrapped_type: None,
100 ..
101 }) = self
102 {
103 *self = Self::VarArgs(VarargsArg {
104 name: name.clone(),
105 ty,
106 });
107 Ok(self)
108 } else {
109 bail_spanned!(self.name().span() => "args cannot be optional")
110 }
111 }
112
113 pub fn to_kwargs_mut(&mut self) -> Result<&mut Self> {
114 if let Self::Regular(RegularArg {
115 name,
116 ty,
117 option_wrapped_type: Some(..),
118 ..
119 }) = self
120 {
121 *self = Self::KwArgs(KwargsArg {
122 name: name.clone(),
123 ty,
124 });
125 Ok(self)
126 } else {
127 bail_spanned!(self.name().span() => "kwargs must be Option<_>")
128 }
129 }
130
131 pub fn parse(arg: &'a mut syn::FnArg) -> Result<Self> {
133 match arg {
134 syn::FnArg::Receiver(recv) => {
135 bail_spanned!(recv.span() => "unexpected receiver")
136 } syn::FnArg::Typed(cap) => {
138 if let syn::Type::ImplTrait(_) = &*cap.ty {
139 bail_spanned!(cap.ty.span() => IMPL_TRAIT_ERR);
140 }
141
142 let PyFunctionArgPyO3Attributes {
143 from_py_with,
144 cancel_handle,
145 } = PyFunctionArgPyO3Attributes::from_attrs(&mut cap.attrs)?;
146 let ident = match &*cap.pat {
147 syn::Pat::Ident(syn::PatIdent { ident, .. }) => ident,
148 other => return Err(handle_argument_error(other)),
149 };
150
151 if utils::is_python(&cap.ty) {
152 return Ok(Self::Py(PyArg {
153 name: ident,
154 ty: &cap.ty,
155 }));
156 }
157
158 if cancel_handle.is_some() {
159 return Ok(Self::CancelHandle(CancelHandleArg {
164 name: ident,
165 ty: &cap.ty,
166 }));
167 }
168
169 Ok(Self::Regular(RegularArg {
170 name: Cow::Borrowed(ident),
171 ty: &cap.ty,
172 from_py_with,
173 default_value: None,
174 option_wrapped_type: utils::option_type_argument(&cap.ty),
175 }))
176 }
177 }
178 }
179}
180
181fn handle_argument_error(pat: &syn::Pat) -> syn::Error {
182 let span = pat.span();
183 let msg = match pat {
184 syn::Pat::Wild(_) => "wildcard argument names are not supported",
185 syn::Pat::Struct(_)
186 | syn::Pat::Tuple(_)
187 | syn::Pat::TupleStruct(_)
188 | syn::Pat::Slice(_) => "destructuring in arguments is not supported",
189 _ => "unsupported argument",
190 };
191 syn::Error::new(span, msg)
192}
193
194#[derive(Clone, Debug)]
196pub enum FnType {
197 Getter(SelfType),
199 Setter(SelfType),
201 Fn(SelfType),
203 FnNew,
205 FnNewClass(Span),
207 FnClass(Span),
209 FnStatic,
211 FnModule(Span),
213 ClassAttribute,
215}
216
217impl FnType {
218 pub fn skip_first_rust_argument_in_python_signature(&self) -> bool {
219 match self {
220 FnType::Getter(_)
221 | FnType::Setter(_)
222 | FnType::Fn(_)
223 | FnType::FnClass(_)
224 | FnType::FnNewClass(_)
225 | FnType::FnModule(_) => true,
226 FnType::FnNew | FnType::FnStatic | FnType::ClassAttribute => false,
227 }
228 }
229
230 pub fn signature_attribute_allowed(&self) -> bool {
231 match self {
232 FnType::Fn(_)
233 | FnType::FnNew
234 | FnType::FnStatic
235 | FnType::FnClass(_)
236 | FnType::FnNewClass(_)
237 | FnType::FnModule(_) => true,
238 FnType::Getter(_) | FnType::Setter(_) | FnType::ClassAttribute => false,
241 }
242 }
243
244 pub fn self_arg(
245 &self,
246 cls: Option<&syn::Type>,
247 error_mode: ExtractErrorMode,
248 holders: &mut Holders,
249 ctx: &Ctx,
250 ) -> Option<TokenStream> {
251 let Ctx { pyo3_path, .. } = ctx;
252 match self {
253 FnType::Getter(st) | FnType::Setter(st) | FnType::Fn(st) => {
254 let mut receiver = st.receiver(
255 cls.expect("no class given for Fn with a \"self\" receiver"),
256 error_mode,
257 holders,
258 ctx,
259 );
260 syn::Token).to_tokens(&mut receiver);
261 Some(receiver)
262 }
263 FnType::FnClass(span) | FnType::FnNewClass(span) => {
264 let py = syn::Ident::new("py", Span::call_site());
265 let slf: Ident = syn::Ident::new("_slf", Span::call_site());
266 let pyo3_path = pyo3_path.to_tokens_spanned(*span);
267 let ret = quote_spanned! { *span =>
268 #[allow(clippy::useless_conversion)]
269 ::std::convert::Into::into(
270 #pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(#py, &*(&#slf as *const _ as *const *mut _))
271 .downcast_unchecked::<#pyo3_path::types::PyType>()
272 )
273 };
274 Some(quote! { unsafe { #ret }, })
275 }
276 FnType::FnModule(span) => {
277 let py = syn::Ident::new("py", Span::call_site());
278 let slf: Ident = syn::Ident::new("_slf", Span::call_site());
279 let pyo3_path = pyo3_path.to_tokens_spanned(*span);
280 let ret = quote_spanned! { *span =>
281 #[allow(clippy::useless_conversion)]
282 ::std::convert::Into::into(
283 #pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(#py, &*(&#slf as *const _ as *const *mut _))
284 .downcast_unchecked::<#pyo3_path::types::PyModule>()
285 )
286 };
287 Some(quote! { unsafe { #ret }, })
288 }
289 FnType::FnNew | FnType::FnStatic | FnType::ClassAttribute => None,
290 }
291 }
292}
293
294#[derive(Clone, Debug)]
295pub enum SelfType {
296 Receiver { mutable: bool, span: Span },
297 TryFromBoundRef(Span),
298}
299
300#[derive(Clone, Copy)]
301pub enum ExtractErrorMode {
302 NotImplemented,
303 Raise,
304}
305
306impl ExtractErrorMode {
307 pub fn handle_error(self, extract: TokenStream, ctx: &Ctx) -> TokenStream {
308 let Ctx { pyo3_path, .. } = ctx;
309 match self {
310 ExtractErrorMode::Raise => quote! { #extract? },
311 ExtractErrorMode::NotImplemented => quote! {
312 match #extract {
313 ::std::result::Result::Ok(value) => value,
314 ::std::result::Result::Err(_) => { return #pyo3_path::impl_::callback::convert(py, py.NotImplemented()); },
315 }
316 },
317 }
318 }
319}
320
321impl SelfType {
322 pub fn receiver(
323 &self,
324 cls: &syn::Type,
325 error_mode: ExtractErrorMode,
326 holders: &mut Holders,
327 ctx: &Ctx,
328 ) -> TokenStream {
329 let py = syn::Ident::new("py", Span::call_site());
332 let slf = syn::Ident::new("_slf", Span::call_site());
333 let Ctx { pyo3_path, .. } = ctx;
334 let bound_ref =
335 quote! { unsafe { #pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(#py, &#slf) } };
336 match self {
337 SelfType::Receiver { span, mutable } => {
338 let method = if *mutable {
339 syn::Ident::new("extract_pyclass_ref_mut", *span)
340 } else {
341 syn::Ident::new("extract_pyclass_ref", *span)
342 };
343 let holder = holders.push_holder(*span);
344 let pyo3_path = pyo3_path.to_tokens_spanned(*span);
345 error_mode.handle_error(
346 quote_spanned! { *span =>
347 #pyo3_path::impl_::extract_argument::#method::<#cls>(
348 #bound_ref.0,
349 &mut #holder,
350 )
351 },
352 ctx,
353 )
354 }
355 SelfType::TryFromBoundRef(span) => {
356 let pyo3_path = pyo3_path.to_tokens_spanned(*span);
357 error_mode.handle_error(
358 quote_spanned! { *span =>
359 #bound_ref.downcast::<#cls>()
360 .map_err(::std::convert::Into::<#pyo3_path::PyErr>::into)
361 .and_then(
362 #[allow(unknown_lints, clippy::unnecessary_fallible_conversions)] |bound| ::std::convert::TryFrom::try_from(bound).map_err(::std::convert::Into::into)
364 )
365
366 },
367 ctx
368 )
369 }
370 }
371 }
372}
373
374#[derive(Clone, Debug)]
376pub enum CallingConvention {
377 Noargs, Varargs, Fastcall, TpNew, }
382
383impl CallingConvention {
384 pub fn from_signature(signature: &FunctionSignature<'_>) -> Self {
389 if signature.python_signature.has_no_args() {
390 Self::Noargs
391 } else if signature.python_signature.kwargs.is_none() && !is_abi3_before(3, 10) {
392 Self::Fastcall
397 } else {
398 Self::Varargs
399 }
400 }
401}
402
403pub struct FnSpec<'a> {
404 pub tp: FnType,
405 pub name: &'a syn::Ident,
407 pub python_name: syn::Ident,
410 pub signature: FunctionSignature<'a>,
411 pub convention: CallingConvention,
412 pub text_signature: Option<TextSignatureAttribute>,
413 pub asyncness: Option<syn::Token![async]>,
414 pub unsafety: Option<syn::Token![unsafe]>,
415}
416
417pub fn parse_method_receiver(arg: &syn::FnArg) -> Result<SelfType> {
418 match arg {
419 syn::FnArg::Receiver(
420 recv @ syn::Receiver {
421 reference: None, ..
422 },
423 ) => {
424 bail_spanned!(recv.span() => RECEIVER_BY_VALUE_ERR);
425 }
426 syn::FnArg::Receiver(recv @ syn::Receiver { mutability, .. }) => Ok(SelfType::Receiver {
427 mutable: mutability.is_some(),
428 span: recv.span(),
429 }),
430 syn::FnArg::Typed(syn::PatType { ty, .. }) => {
431 if let syn::Type::ImplTrait(_) = &**ty {
432 bail_spanned!(ty.span() => IMPL_TRAIT_ERR);
433 }
434 Ok(SelfType::TryFromBoundRef(ty.span()))
435 }
436 }
437}
438
439impl<'a> FnSpec<'a> {
440 pub fn parse(
442 sig: &'a mut syn::Signature,
444 meth_attrs: &mut Vec<syn::Attribute>,
445 options: PyFunctionOptions,
446 ) -> Result<FnSpec<'a>> {
447 let PyFunctionOptions {
448 text_signature,
449 name,
450 signature,
451 ..
452 } = options;
453
454 let mut python_name = name.map(|name| name.value.0);
455
456 let fn_type = Self::parse_fn_type(sig, meth_attrs, &mut python_name)?;
457 ensure_signatures_on_valid_method(&fn_type, signature.as_ref(), text_signature.as_ref())?;
458
459 let name = &sig.ident;
460 let python_name = python_name.as_ref().unwrap_or(name).unraw();
461
462 let arguments: Vec<_> = sig
463 .inputs
464 .iter_mut()
465 .skip(if fn_type.skip_first_rust_argument_in_python_signature() {
466 1
467 } else {
468 0
469 })
470 .map(FnArg::parse)
471 .collect::<Result<_>>()?;
472
473 let signature = if let Some(signature) = signature {
474 FunctionSignature::from_arguments_and_attribute(arguments, signature)?
475 } else {
476 FunctionSignature::from_arguments(arguments)
477 };
478
479 let convention = if matches!(fn_type, FnType::FnNew | FnType::FnNewClass(_)) {
480 CallingConvention::TpNew
481 } else {
482 CallingConvention::from_signature(&signature)
483 };
484
485 Ok(FnSpec {
486 tp: fn_type,
487 name,
488 convention,
489 python_name,
490 signature,
491 text_signature,
492 asyncness: sig.asyncness,
493 unsafety: sig.unsafety,
494 })
495 }
496
497 pub fn null_terminated_python_name(&self, ctx: &Ctx) -> LitCStr {
498 let name = self.python_name.to_string();
499 let name = CString::new(name).unwrap();
500 LitCStr::new(name, self.python_name.span(), ctx)
501 }
502
503 fn parse_fn_type(
504 sig: &syn::Signature,
505 meth_attrs: &mut Vec<syn::Attribute>,
506 python_name: &mut Option<syn::Ident>,
507 ) -> Result<FnType> {
508 let mut method_attributes = parse_method_attributes(meth_attrs)?;
509
510 let name = &sig.ident;
511 let parse_receiver = |msg: &'static str| {
512 let first_arg = sig
513 .inputs
514 .first()
515 .ok_or_else(|| err_spanned!(sig.span() => msg))?;
516 parse_method_receiver(first_arg)
517 };
518
519 let strip_fn_name = |prefix: &'static str| {
521 name.unraw()
522 .to_string()
523 .strip_prefix(prefix)
524 .map(|stripped| syn::Ident::new(stripped, name.span()))
525 };
526
527 let mut set_name_to_new = || {
528 if let Some(name) = &python_name {
529 bail_spanned!(name.span() => "`name` not allowed with `#[new]`");
530 }
531 *python_name = Some(syn::Ident::new("__new__", Span::call_site()));
532 Ok(())
533 };
534
535 let fn_type = match method_attributes.as_mut_slice() {
536 [] => FnType::Fn(parse_receiver(
537 "static method needs #[staticmethod] attribute",
538 )?),
539 [MethodTypeAttribute::StaticMethod(_)] => FnType::FnStatic,
540 [MethodTypeAttribute::ClassAttribute(_)] => FnType::ClassAttribute,
541 [MethodTypeAttribute::New(_)] => {
542 set_name_to_new()?;
543 FnType::FnNew
544 }
545 [MethodTypeAttribute::New(_), MethodTypeAttribute::ClassMethod(span)]
546 | [MethodTypeAttribute::ClassMethod(span), MethodTypeAttribute::New(_)] => {
547 set_name_to_new()?;
548 FnType::FnNewClass(*span)
549 }
550 [MethodTypeAttribute::ClassMethod(_)] => {
551 let span = match sig.inputs.first() {
553 Some(syn::FnArg::Typed(first_arg)) => first_arg.ty.span(),
556 Some(syn::FnArg::Receiver(_)) | None => bail_spanned!(
557 sig.paren_token.span.join() => "Expected `&Bound<PyType>` or `Py<PyType>` as the first argument to `#[classmethod]`"
558 ),
559 };
560 FnType::FnClass(span)
561 }
562 [MethodTypeAttribute::Getter(_, name)] => {
563 if let Some(name) = name.take() {
564 ensure_spanned!(
565 python_name.replace(name).is_none(),
566 python_name.span() => "`name` may only be specified once"
567 );
568 } else if python_name.is_none() {
569 *python_name = strip_fn_name("get_");
571 }
572
573 FnType::Getter(parse_receiver("expected receiver for `#[getter]`")?)
574 }
575 [MethodTypeAttribute::Setter(_, name)] => {
576 if let Some(name) = name.take() {
577 ensure_spanned!(
578 python_name.replace(name).is_none(),
579 python_name.span() => "`name` may only be specified once"
580 );
581 } else if python_name.is_none() {
582 *python_name = strip_fn_name("set_");
584 }
585
586 FnType::Setter(parse_receiver("expected receiver for `#[setter]`")?)
587 }
588 [first, rest @ .., last] => {
589 let span = rest
591 .iter()
592 .fold(first.span(), |s, next| s.join(next.span()).unwrap_or(s));
593 let span = span.join(last.span()).unwrap_or(span);
594 let mut msg = format!("`{}` may not be combined with", first);
596 let mut is_first = true;
597 for attr in &*rest {
598 msg.push_str(&format!(" `{}`", attr));
599 if is_first {
600 is_first = false;
601 } else {
602 msg.push(',');
603 }
604 }
605 if !rest.is_empty() {
606 msg.push_str(" and");
607 }
608 msg.push_str(&format!(" `{}`", last));
609 bail_spanned!(span => msg)
610 }
611 };
612 Ok(fn_type)
613 }
614
615 pub fn get_wrapper_function(
617 &self,
618 ident: &proc_macro2::Ident,
619 cls: Option<&syn::Type>,
620 ctx: &Ctx,
621 ) -> Result<TokenStream> {
622 let Ctx {
623 pyo3_path,
624 output_span,
625 } = ctx;
626 let mut cancel_handle_iter = self
627 .signature
628 .arguments
629 .iter()
630 .filter(|arg| matches!(arg, FnArg::CancelHandle(..)));
631 let cancel_handle = cancel_handle_iter.next();
632 if let Some(FnArg::CancelHandle(CancelHandleArg { name, .. })) = cancel_handle {
633 ensure_spanned!(self.asyncness.is_some(), name.span() => "`cancel_handle` attribute can only be used with `async fn`");
634 if let Some(FnArg::CancelHandle(CancelHandleArg { name, .. })) =
635 cancel_handle_iter.next()
636 {
637 bail_spanned!(name.span() => "`cancel_handle` may only be specified once");
638 }
639 }
640
641 if self.asyncness.is_some() {
642 ensure_spanned!(
643 cfg!(feature = "experimental-async"),
644 self.asyncness.span() => "async functions are only supported with the `experimental-async` feature"
645 );
646 }
647
648 let rust_call = |args: Vec<TokenStream>, holders: &mut Holders| {
649 let mut self_arg = || self.tp.self_arg(cls, ExtractErrorMode::Raise, holders, ctx);
650
651 let call = if self.asyncness.is_some() {
652 let throw_callback = if cancel_handle.is_some() {
653 quote! { Some(__throw_callback) }
654 } else {
655 quote! { None }
656 };
657 let python_name = &self.python_name;
658 let qualname_prefix = match cls {
659 Some(cls) => quote!(Some(<#cls as #pyo3_path::PyTypeInfo>::NAME)),
660 None => quote!(None),
661 };
662 let arg_names = (0..args.len())
663 .map(|i| format_ident!("arg_{}", i))
664 .collect::<Vec<_>>();
665 let future = match self.tp {
666 FnType::Fn(SelfType::Receiver { mutable: false, .. }) => {
667 quote! {{
668 #(let #arg_names = #args;)*
669 let __guard = unsafe { #pyo3_path::impl_::coroutine::RefGuard::<#cls>::new(&#pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(py, &_slf))? };
670 async move { function(&__guard, #(#arg_names),*).await }
671 }}
672 }
673 FnType::Fn(SelfType::Receiver { mutable: true, .. }) => {
674 quote! {{
675 #(let #arg_names = #args;)*
676 let mut __guard = unsafe { #pyo3_path::impl_::coroutine::RefMutGuard::<#cls>::new(&#pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(py, &_slf))? };
677 async move { function(&mut __guard, #(#arg_names),*).await }
678 }}
679 }
680 _ => {
681 if let Some(self_arg) = self_arg() {
682 quote! {
683 function(
684 #self_arg
686 #(#args),*
687 )
688 }
689 } else {
690 quote! { function(#(#args),*) }
691 }
692 }
693 };
694 let mut call = quote! {{
695 let future = #future;
696 #pyo3_path::impl_::coroutine::new_coroutine(
697 #pyo3_path::intern!(py, stringify!(#python_name)),
698 #qualname_prefix,
699 #throw_callback,
700 async move {
701 let fut = future.await;
702 #pyo3_path::impl_::wrap::converter(&fut).wrap(fut)
703 },
704 )
705 }};
706 if cancel_handle.is_some() {
707 call = quote! {{
708 let __cancel_handle = #pyo3_path::coroutine::CancelHandle::new();
709 let __throw_callback = __cancel_handle.throw_callback();
710 #call
711 }};
712 }
713 call
714 } else if let Some(self_arg) = self_arg() {
715 quote! {
716 function(
717 #self_arg
719 #(#args),*
720 )
721 }
722 } else {
723 quote! { function(#(#args),*) }
724 };
725
726 let ret_ident = Ident::new("ret", *output_span);
729 let ret_expr = quote! { let #ret_ident = #call; };
730 let return_conversion =
731 quotes::map_result_into_ptr(quotes::ok_wrap(ret_ident.to_token_stream(), ctx), ctx);
732 quote! {
733 {
734 #ret_expr
735 #return_conversion
736 }
737 }
738 };
739
740 let func_name = &self.name;
741 let rust_name = if let Some(cls) = cls {
742 quote!(#cls::#func_name)
743 } else {
744 quote!(#func_name)
745 };
746
747 Ok(match self.convention {
748 CallingConvention::Noargs => {
749 let mut holders = Holders::new();
750 let args = self
751 .signature
752 .arguments
753 .iter()
754 .map(|arg| match arg {
755 FnArg::Py(..) => quote!(py),
756 FnArg::CancelHandle(..) => quote!(__cancel_handle),
757 _ => unreachable!("`CallingConvention::Noargs` should not contain any arguments (reaching Python) except for `self`, which is handled below."),
758 })
759 .collect();
760 let call = rust_call(args, &mut holders);
761 let init_holders = holders.init_holders(ctx);
762 quote! {
763 unsafe fn #ident<'py>(
764 py: #pyo3_path::Python<'py>,
765 _slf: *mut #pyo3_path::ffi::PyObject,
766 ) -> #pyo3_path::PyResult<*mut #pyo3_path::ffi::PyObject> {
767 let function = #rust_name; #init_holders
769 let result = #call;
770 result
771 }
772 }
773 }
774 CallingConvention::Fastcall => {
775 let mut holders = Holders::new();
776 let (arg_convert, args) = impl_arg_params(self, cls, true, &mut holders, ctx);
777 let call = rust_call(args, &mut holders);
778 let init_holders = holders.init_holders(ctx);
779
780 quote! {
781 unsafe fn #ident<'py>(
782 py: #pyo3_path::Python<'py>,
783 _slf: *mut #pyo3_path::ffi::PyObject,
784 _args: *const *mut #pyo3_path::ffi::PyObject,
785 _nargs: #pyo3_path::ffi::Py_ssize_t,
786 _kwnames: *mut #pyo3_path::ffi::PyObject
787 ) -> #pyo3_path::PyResult<*mut #pyo3_path::ffi::PyObject> {
788 let function = #rust_name; #arg_convert
790 #init_holders
791 let result = #call;
792 result
793 }
794 }
795 }
796 CallingConvention::Varargs => {
797 let mut holders = Holders::new();
798 let (arg_convert, args) = impl_arg_params(self, cls, false, &mut holders, ctx);
799 let call = rust_call(args, &mut holders);
800 let init_holders = holders.init_holders(ctx);
801
802 quote! {
803 unsafe fn #ident<'py>(
804 py: #pyo3_path::Python<'py>,
805 _slf: *mut #pyo3_path::ffi::PyObject,
806 _args: *mut #pyo3_path::ffi::PyObject,
807 _kwargs: *mut #pyo3_path::ffi::PyObject
808 ) -> #pyo3_path::PyResult<*mut #pyo3_path::ffi::PyObject> {
809 let function = #rust_name; #arg_convert
811 #init_holders
812 let result = #call;
813 result
814 }
815 }
816 }
817 CallingConvention::TpNew => {
818 let mut holders = Holders::new();
819 let (arg_convert, args) = impl_arg_params(self, cls, false, &mut holders, ctx);
820 let self_arg = self
821 .tp
822 .self_arg(cls, ExtractErrorMode::Raise, &mut holders, ctx);
823 let call = quote_spanned! {*output_span=> #rust_name(#self_arg #(#args),*) };
824 let init_holders = holders.init_holders(ctx);
825 quote! {
826 unsafe fn #ident(
827 py: #pyo3_path::Python<'_>,
828 _slf: *mut #pyo3_path::ffi::PyTypeObject,
829 _args: *mut #pyo3_path::ffi::PyObject,
830 _kwargs: *mut #pyo3_path::ffi::PyObject
831 ) -> #pyo3_path::PyResult<*mut #pyo3_path::ffi::PyObject> {
832 use #pyo3_path::impl_::callback::IntoPyCallbackOutput;
833 let function = #rust_name; #arg_convert
835 #init_holders
836 let result = #call;
837 let initializer: #pyo3_path::PyClassInitializer::<#cls> = result.convert(py)?;
838 #pyo3_path::impl_::pymethods::tp_new_impl(py, initializer, _slf)
839 }
840 }
841 }
842 })
843 }
844
845 pub fn get_methoddef(&self, wrapper: impl ToTokens, doc: &PythonDoc, ctx: &Ctx) -> TokenStream {
848 let Ctx { pyo3_path, .. } = ctx;
849 let python_name = self.null_terminated_python_name(ctx);
850 match self.convention {
851 CallingConvention::Noargs => quote! {
852 #pyo3_path::impl_::pymethods::PyMethodDef::noargs(
853 #python_name,
854 {
855 unsafe extern "C" fn trampoline(
856 _slf: *mut #pyo3_path::ffi::PyObject,
857 _args: *mut #pyo3_path::ffi::PyObject,
858 ) -> *mut #pyo3_path::ffi::PyObject
859 {
860 unsafe {
861 #pyo3_path::impl_::trampoline::noargs(
862 _slf,
863 _args,
864 #wrapper
865 )
866 }
867 }
868 trampoline
869 },
870 #doc,
871 )
872 },
873 CallingConvention::Fastcall => quote! {
874 #pyo3_path::impl_::pymethods::PyMethodDef::fastcall_cfunction_with_keywords(
875 #python_name,
876 {
877 unsafe extern "C" fn trampoline(
878 _slf: *mut #pyo3_path::ffi::PyObject,
879 _args: *const *mut #pyo3_path::ffi::PyObject,
880 _nargs: #pyo3_path::ffi::Py_ssize_t,
881 _kwnames: *mut #pyo3_path::ffi::PyObject
882 ) -> *mut #pyo3_path::ffi::PyObject
883 {
884 #pyo3_path::impl_::trampoline::fastcall_with_keywords(
885 _slf,
886 _args,
887 _nargs,
888 _kwnames,
889 #wrapper
890 )
891 }
892 trampoline
893 },
894 #doc,
895 )
896 },
897 CallingConvention::Varargs => quote! {
898 #pyo3_path::impl_::pymethods::PyMethodDef::cfunction_with_keywords(
899 #python_name,
900 {
901 unsafe extern "C" fn trampoline(
902 _slf: *mut #pyo3_path::ffi::PyObject,
903 _args: *mut #pyo3_path::ffi::PyObject,
904 _kwargs: *mut #pyo3_path::ffi::PyObject,
905 ) -> *mut #pyo3_path::ffi::PyObject
906 {
907 #pyo3_path::impl_::trampoline::cfunction_with_keywords(
908 _slf,
909 _args,
910 _kwargs,
911 #wrapper
912 )
913 }
914 trampoline
915 },
916 #doc,
917 )
918 },
919 CallingConvention::TpNew => unreachable!("tp_new cannot get a methoddef"),
920 }
921 }
922
923 pub fn get_doc(&self, attrs: &[syn::Attribute], ctx: &Ctx) -> PythonDoc {
925 let text_signature = self
926 .text_signature_call_signature()
927 .map(|sig| format!("{}{}", self.python_name, sig));
928 utils::get_doc(attrs, text_signature, ctx)
929 }
930
931 pub fn text_signature_call_signature(&self) -> Option<String> {
934 let self_argument = match &self.tp {
935 FnType::Getter(_) | FnType::Setter(_) | FnType::ClassAttribute => return None,
937 FnType::Fn(_) => Some("self"),
938 FnType::FnModule(_) => Some("module"),
939 FnType::FnClass(_) | FnType::FnNewClass(_) => Some("cls"),
940 FnType::FnStatic | FnType::FnNew => None,
941 };
942
943 match self.text_signature.as_ref().map(|attr| &attr.value) {
944 Some(TextSignatureAttributeValue::Str(s)) => Some(s.value()),
945 None => Some(self.signature.text_signature(self_argument)),
946 Some(TextSignatureAttributeValue::Disabled(_)) => None,
947 }
948 }
949}
950
951enum MethodTypeAttribute {
952 New(Span),
953 ClassMethod(Span),
954 StaticMethod(Span),
955 Getter(Span, Option<Ident>),
956 Setter(Span, Option<Ident>),
957 ClassAttribute(Span),
958}
959
960impl MethodTypeAttribute {
961 fn span(&self) -> Span {
962 match self {
963 MethodTypeAttribute::New(span)
964 | MethodTypeAttribute::ClassMethod(span)
965 | MethodTypeAttribute::StaticMethod(span)
966 | MethodTypeAttribute::Getter(span, _)
967 | MethodTypeAttribute::Setter(span, _)
968 | MethodTypeAttribute::ClassAttribute(span) => *span,
969 }
970 }
971
972 fn parse_if_matching_attribute(attr: &syn::Attribute) -> Result<Option<Self>> {
978 fn ensure_no_arguments(meta: &syn::Meta, ident: &str) -> syn::Result<()> {
979 match meta {
980 syn::Meta::Path(_) => Ok(()),
981 syn::Meta::List(l) => bail_spanned!(
982 l.span() => format!(
983 "`#[{ident}]` does not take any arguments\n= help: did you mean `#[{ident}] #[pyo3({meta})]`?",
984 ident = ident,
985 meta = l.tokens,
986 )
987 ),
988 syn::Meta::NameValue(nv) => {
989 bail_spanned!(nv.eq_token.span() => format!(
990 "`#[{}]` does not take any arguments\n= note: this was previously accepted and ignored",
991 ident
992 ))
993 }
994 }
995 }
996
997 fn extract_name(meta: &syn::Meta, ident: &str) -> Result<Option<Ident>> {
998 match meta {
999 syn::Meta::Path(_) => Ok(None),
1000 syn::Meta::NameValue(nv) => bail_spanned!(
1001 nv.eq_token.span() => format!("expected `#[{}(name)]` to set the name", ident)
1002 ),
1003 syn::Meta::List(l) => {
1004 if let Ok(name) = l.parse_args::<syn::Ident>() {
1005 Ok(Some(name))
1006 } else if let Ok(name) = l.parse_args::<syn::LitStr>() {
1007 name.parse().map(Some)
1008 } else {
1009 bail_spanned!(l.tokens.span() => "expected ident or string literal for property name");
1010 }
1011 }
1012 }
1013 }
1014
1015 let meta = &attr.meta;
1016 let path = meta.path();
1017
1018 if path.is_ident("new") {
1019 ensure_no_arguments(meta, "new")?;
1020 Ok(Some(MethodTypeAttribute::New(path.span())))
1021 } else if path.is_ident("classmethod") {
1022 ensure_no_arguments(meta, "classmethod")?;
1023 Ok(Some(MethodTypeAttribute::ClassMethod(path.span())))
1024 } else if path.is_ident("staticmethod") {
1025 ensure_no_arguments(meta, "staticmethod")?;
1026 Ok(Some(MethodTypeAttribute::StaticMethod(path.span())))
1027 } else if path.is_ident("classattr") {
1028 ensure_no_arguments(meta, "classattr")?;
1029 Ok(Some(MethodTypeAttribute::ClassAttribute(path.span())))
1030 } else if path.is_ident("getter") {
1031 let name = extract_name(meta, "getter")?;
1032 Ok(Some(MethodTypeAttribute::Getter(path.span(), name)))
1033 } else if path.is_ident("setter") {
1034 let name = extract_name(meta, "setter")?;
1035 Ok(Some(MethodTypeAttribute::Setter(path.span(), name)))
1036 } else {
1037 Ok(None)
1038 }
1039 }
1040}
1041
1042impl Display for MethodTypeAttribute {
1043 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1044 match self {
1045 MethodTypeAttribute::New(_) => "#[new]".fmt(f),
1046 MethodTypeAttribute::ClassMethod(_) => "#[classmethod]".fmt(f),
1047 MethodTypeAttribute::StaticMethod(_) => "#[staticmethod]".fmt(f),
1048 MethodTypeAttribute::Getter(_, _) => "#[getter]".fmt(f),
1049 MethodTypeAttribute::Setter(_, _) => "#[setter]".fmt(f),
1050 MethodTypeAttribute::ClassAttribute(_) => "#[classattr]".fmt(f),
1051 }
1052 }
1053}
1054
1055fn parse_method_attributes(attrs: &mut Vec<syn::Attribute>) -> Result<Vec<MethodTypeAttribute>> {
1056 let mut new_attrs = Vec::new();
1057 let mut found_attrs = Vec::new();
1058
1059 for attr in attrs.drain(..) {
1060 match MethodTypeAttribute::parse_if_matching_attribute(&attr)? {
1061 Some(attr) => found_attrs.push(attr),
1062 None => new_attrs.push(attr),
1063 }
1064 }
1065
1066 *attrs = new_attrs;
1067
1068 Ok(found_attrs)
1069}
1070
1071const IMPL_TRAIT_ERR: &str = "Python functions cannot have `impl Trait` arguments";
1072const RECEIVER_BY_VALUE_ERR: &str =
1073 "Python objects are shared, so 'self' cannot be moved out of the Python interpreter.
1074Try `&self`, `&mut self, `slf: PyRef<'_, Self>` or `slf: PyRefMut<'_, Self>`.";
1075
1076fn ensure_signatures_on_valid_method(
1077 fn_type: &FnType,
1078 signature: Option<&SignatureAttribute>,
1079 text_signature: Option<&TextSignatureAttribute>,
1080) -> syn::Result<()> {
1081 if let Some(signature) = signature {
1082 match fn_type {
1083 FnType::Getter(_) => {
1084 debug_assert!(!fn_type.signature_attribute_allowed());
1085 bail_spanned!(signature.kw.span() => "`signature` not allowed with `getter`")
1086 }
1087 FnType::Setter(_) => {
1088 debug_assert!(!fn_type.signature_attribute_allowed());
1089 bail_spanned!(signature.kw.span() => "`signature` not allowed with `setter`")
1090 }
1091 FnType::ClassAttribute => {
1092 debug_assert!(!fn_type.signature_attribute_allowed());
1093 bail_spanned!(signature.kw.span() => "`signature` not allowed with `classattr`")
1094 }
1095 _ => debug_assert!(fn_type.signature_attribute_allowed()),
1096 }
1097 }
1098 if let Some(text_signature) = text_signature {
1099 match fn_type {
1100 FnType::Getter(_) => {
1101 bail_spanned!(text_signature.kw.span() => "`text_signature` not allowed with `getter`")
1102 }
1103 FnType::Setter(_) => {
1104 bail_spanned!(text_signature.kw.span() => "`text_signature` not allowed with `setter`")
1105 }
1106 FnType::ClassAttribute => {
1107 bail_spanned!(text_signature.kw.span() => "`text_signature` not allowed with `classattr`")
1108 }
1109 _ => {}
1110 }
1111 }
1112 Ok(())
1113}