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