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::pyfunction::{PyFunctionWarning, WarningFactory};
10use crate::pyversions::is_abi3_before;
11use crate::utils::{expr_to_python, Ctx, LitCStr};
12use crate::{
13 attributes::{FromPyWithAttribute, TextSignatureAttribute, TextSignatureAttributeValue},
14 params::{impl_arg_params, Holders},
15 pyfunction::{
16 FunctionSignature, PyFunctionArgPyO3Attributes, PyFunctionOptions, SignatureAttribute,
17 },
18 quotes,
19 utils::{self, PythonDoc},
20};
21
22#[derive(Clone, Debug)]
23pub struct RegularArg<'a> {
24 pub name: Cow<'a, syn::Ident>,
25 pub ty: &'a syn::Type,
26 pub from_py_with: Option<FromPyWithAttribute>,
27 pub default_value: Option<syn::Expr>,
28 pub option_wrapped_type: Option<&'a syn::Type>,
29 #[cfg(feature = "experimental-inspect")]
30 pub annotation: Option<String>,
31}
32
33impl RegularArg<'_> {
34 pub fn default_value(&self) -> String {
35 if let Self {
36 default_value: Some(arg_default),
37 ..
38 } = self
39 {
40 expr_to_python(arg_default)
41 } else if let RegularArg {
42 option_wrapped_type: Some(..),
43 ..
44 } = self
45 {
46 "None".to_string()
49 } else {
50 "...".to_string()
51 }
52 }
53}
54
55#[derive(Clone, Debug)]
57pub struct VarargsArg<'a> {
58 pub name: Cow<'a, syn::Ident>,
59 pub ty: &'a syn::Type,
60 #[cfg(feature = "experimental-inspect")]
61 pub annotation: Option<String>,
62}
63
64#[derive(Clone, Debug)]
66pub struct KwargsArg<'a> {
67 pub name: Cow<'a, syn::Ident>,
68 pub ty: &'a syn::Type,
69 #[cfg(feature = "experimental-inspect")]
70 pub annotation: Option<String>,
71}
72
73#[derive(Clone, Debug)]
74pub struct CancelHandleArg<'a> {
75 pub name: &'a syn::Ident,
76 pub ty: &'a syn::Type,
77}
78
79#[derive(Clone, Debug)]
80pub struct PyArg<'a> {
81 pub name: &'a syn::Ident,
82 pub ty: &'a syn::Type,
83}
84
85#[allow(clippy::large_enum_variant)] #[derive(Clone, Debug)]
87pub enum FnArg<'a> {
88 Regular(RegularArg<'a>),
89 VarArgs(VarargsArg<'a>),
90 KwArgs(KwargsArg<'a>),
91 Py(PyArg<'a>),
92 CancelHandle(CancelHandleArg<'a>),
93}
94
95impl<'a> FnArg<'a> {
96 pub fn name(&self) -> &syn::Ident {
97 match self {
98 FnArg::Regular(RegularArg { name, .. }) => name,
99 FnArg::VarArgs(VarargsArg { name, .. }) => name,
100 FnArg::KwArgs(KwargsArg { name, .. }) => name,
101 FnArg::Py(PyArg { name, .. }) => name,
102 FnArg::CancelHandle(CancelHandleArg { name, .. }) => name,
103 }
104 }
105
106 pub fn ty(&self) -> &'a syn::Type {
107 match self {
108 FnArg::Regular(RegularArg { ty, .. }) => ty,
109 FnArg::VarArgs(VarargsArg { ty, .. }) => ty,
110 FnArg::KwArgs(KwargsArg { ty, .. }) => ty,
111 FnArg::Py(PyArg { ty, .. }) => ty,
112 FnArg::CancelHandle(CancelHandleArg { ty, .. }) => ty,
113 }
114 }
115
116 #[allow(clippy::wrong_self_convention)]
117 pub fn from_py_with(&self) -> Option<&FromPyWithAttribute> {
118 if let FnArg::Regular(RegularArg { from_py_with, .. }) = self {
119 from_py_with.as_ref()
120 } else {
121 None
122 }
123 }
124
125 pub fn to_varargs_mut(&mut self) -> Result<&mut Self> {
126 if let Self::Regular(RegularArg {
127 name,
128 ty,
129 option_wrapped_type: None,
130 #[cfg(feature = "experimental-inspect")]
131 annotation,
132 ..
133 }) = self
134 {
135 *self = Self::VarArgs(VarargsArg {
136 name: name.clone(),
137 ty,
138 #[cfg(feature = "experimental-inspect")]
139 annotation: annotation.clone(),
140 });
141 Ok(self)
142 } else {
143 bail_spanned!(self.name().span() => "args cannot be optional")
144 }
145 }
146
147 pub fn to_kwargs_mut(&mut self) -> Result<&mut Self> {
148 if let Self::Regular(RegularArg {
149 name,
150 ty,
151 option_wrapped_type: Some(..),
152 #[cfg(feature = "experimental-inspect")]
153 annotation,
154 ..
155 }) = self
156 {
157 *self = Self::KwArgs(KwargsArg {
158 name: name.clone(),
159 ty,
160 #[cfg(feature = "experimental-inspect")]
161 annotation: annotation.clone(),
162 });
163 Ok(self)
164 } else {
165 bail_spanned!(self.name().span() => "kwargs must be Option<_>")
166 }
167 }
168
169 pub fn parse(arg: &'a mut syn::FnArg) -> Result<Self> {
171 match arg {
172 syn::FnArg::Receiver(recv) => {
173 bail_spanned!(recv.span() => "unexpected receiver")
174 } syn::FnArg::Typed(cap) => {
176 if let syn::Type::ImplTrait(_) = &*cap.ty {
177 bail_spanned!(cap.ty.span() => IMPL_TRAIT_ERR);
178 }
179
180 let PyFunctionArgPyO3Attributes {
181 from_py_with,
182 cancel_handle,
183 } = PyFunctionArgPyO3Attributes::from_attrs(&mut cap.attrs)?;
184 let ident = match &*cap.pat {
185 syn::Pat::Ident(syn::PatIdent { ident, .. }) => ident,
186 other => return Err(handle_argument_error(other)),
187 };
188
189 if utils::is_python(&cap.ty) {
190 return Ok(Self::Py(PyArg {
191 name: ident,
192 ty: &cap.ty,
193 }));
194 }
195
196 if cancel_handle.is_some() {
197 return Ok(Self::CancelHandle(CancelHandleArg {
202 name: ident,
203 ty: &cap.ty,
204 }));
205 }
206
207 Ok(Self::Regular(RegularArg {
208 name: Cow::Borrowed(ident),
209 ty: &cap.ty,
210 from_py_with,
211 default_value: None,
212 option_wrapped_type: utils::option_type_argument(&cap.ty),
213 #[cfg(feature = "experimental-inspect")]
214 annotation: None,
215 }))
216 }
217 }
218 }
219
220 pub fn default_value(&self) -> String {
221 if let Self::Regular(args) = self {
222 args.default_value()
223 } else {
224 "...".to_string()
225 }
226 }
227}
228
229fn handle_argument_error(pat: &syn::Pat) -> syn::Error {
230 let span = pat.span();
231 let msg = match pat {
232 syn::Pat::Wild(_) => "wildcard argument names are not supported",
233 syn::Pat::Struct(_)
234 | syn::Pat::Tuple(_)
235 | syn::Pat::TupleStruct(_)
236 | syn::Pat::Slice(_) => "destructuring in arguments is not supported",
237 _ => "unsupported argument",
238 };
239 syn::Error::new(span, msg)
240}
241
242#[derive(Clone, Debug)]
244pub enum FnType {
245 Getter(SelfType),
247 Setter(SelfType),
249 Fn(SelfType),
251 FnNew,
253 FnNewClass(Span),
255 FnClass(Span),
257 FnStatic,
259 FnModule(Span),
261 ClassAttribute,
263}
264
265impl FnType {
266 pub fn skip_first_rust_argument_in_python_signature(&self) -> bool {
267 match self {
268 FnType::Getter(_)
269 | FnType::Setter(_)
270 | FnType::Fn(_)
271 | FnType::FnClass(_)
272 | FnType::FnNewClass(_)
273 | FnType::FnModule(_) => true,
274 FnType::FnNew | FnType::FnStatic | FnType::ClassAttribute => false,
275 }
276 }
277
278 pub fn signature_attribute_allowed(&self) -> bool {
279 match self {
280 FnType::Fn(_)
281 | FnType::FnNew
282 | FnType::FnStatic
283 | FnType::FnClass(_)
284 | FnType::FnNewClass(_)
285 | FnType::FnModule(_) => true,
286 FnType::Getter(_) | FnType::Setter(_) | FnType::ClassAttribute => false,
289 }
290 }
291
292 pub fn self_arg(
293 &self,
294 cls: Option<&syn::Type>,
295 error_mode: ExtractErrorMode,
296 holders: &mut Holders,
297 ctx: &Ctx,
298 ) -> Option<TokenStream> {
299 let Ctx { pyo3_path, .. } = ctx;
300 match self {
301 FnType::Getter(st) | FnType::Setter(st) | FnType::Fn(st) => {
302 let mut receiver = st.receiver(
303 cls.expect("no class given for Fn with a \"self\" receiver"),
304 error_mode,
305 holders,
306 ctx,
307 );
308 syn::Token).to_tokens(&mut receiver);
309 Some(receiver)
310 }
311 FnType::FnClass(span) | FnType::FnNewClass(span) => {
312 let py = syn::Ident::new("py", Span::call_site());
313 let slf: Ident = syn::Ident::new("_slf", Span::call_site());
314 let pyo3_path = pyo3_path.to_tokens_spanned(*span);
315 let ret = quote_spanned! { *span =>
316 #[allow(clippy::useless_conversion)]
317 ::std::convert::Into::into(
318 #pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(#py, &*(&#slf as *const _ as *const *mut _))
319 .downcast_unchecked::<#pyo3_path::types::PyType>()
320 )
321 };
322 Some(quote! { unsafe { #ret }, })
323 }
324 FnType::FnModule(span) => {
325 let py = syn::Ident::new("py", Span::call_site());
326 let slf: Ident = syn::Ident::new("_slf", Span::call_site());
327 let pyo3_path = pyo3_path.to_tokens_spanned(*span);
328 let ret = quote_spanned! { *span =>
329 #[allow(clippy::useless_conversion)]
330 ::std::convert::Into::into(
331 #pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(#py, &*(&#slf as *const _ as *const *mut _))
332 .downcast_unchecked::<#pyo3_path::types::PyModule>()
333 )
334 };
335 Some(quote! { unsafe { #ret }, })
336 }
337 FnType::FnNew | FnType::FnStatic | FnType::ClassAttribute => None,
338 }
339 }
340}
341
342#[derive(Clone, Debug)]
343pub enum SelfType {
344 Receiver { mutable: bool, span: Span },
345 TryFromBoundRef(Span),
346}
347
348#[derive(Clone, Copy)]
349pub enum ExtractErrorMode {
350 NotImplemented,
351 Raise,
352}
353
354impl ExtractErrorMode {
355 pub fn handle_error(self, extract: TokenStream, ctx: &Ctx) -> TokenStream {
356 let Ctx { pyo3_path, .. } = ctx;
357 match self {
358 ExtractErrorMode::Raise => quote! { #extract? },
359 ExtractErrorMode::NotImplemented => quote! {
360 match #extract {
361 ::std::result::Result::Ok(value) => value,
362 ::std::result::Result::Err(_) => { return #pyo3_path::impl_::callback::convert(py, py.NotImplemented()); },
363 }
364 },
365 }
366 }
367}
368
369impl SelfType {
370 pub fn receiver(
371 &self,
372 cls: &syn::Type,
373 error_mode: ExtractErrorMode,
374 holders: &mut Holders,
375 ctx: &Ctx,
376 ) -> TokenStream {
377 let py = syn::Ident::new("py", Span::call_site());
380 let slf = syn::Ident::new("_slf", Span::call_site());
381 let Ctx { pyo3_path, .. } = ctx;
382 let bound_ref =
383 quote! { unsafe { #pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(#py, &#slf) } };
384 match self {
385 SelfType::Receiver { span, mutable } => {
386 let method = if *mutable {
387 syn::Ident::new("extract_pyclass_ref_mut", *span)
388 } else {
389 syn::Ident::new("extract_pyclass_ref", *span)
390 };
391 let holder = holders.push_holder(*span);
392 let pyo3_path = pyo3_path.to_tokens_spanned(*span);
393 error_mode.handle_error(
394 quote_spanned! { *span =>
395 #pyo3_path::impl_::extract_argument::#method::<#cls>(
396 #bound_ref.0,
397 &mut #holder,
398 )
399 },
400 ctx,
401 )
402 }
403 SelfType::TryFromBoundRef(span) => {
404 let pyo3_path = pyo3_path.to_tokens_spanned(*span);
405 error_mode.handle_error(
406 quote_spanned! { *span =>
407 #bound_ref.downcast::<#cls>()
408 .map_err(::std::convert::Into::<#pyo3_path::PyErr>::into)
409 .and_then(
410 #[allow(unknown_lints, clippy::unnecessary_fallible_conversions)] |bound| ::std::convert::TryFrom::try_from(bound).map_err(::std::convert::Into::into)
412 )
413
414 },
415 ctx
416 )
417 }
418 }
419 }
420}
421
422#[derive(Clone, Debug)]
424pub enum CallingConvention {
425 Noargs, Varargs, Fastcall, TpNew, }
430
431impl CallingConvention {
432 pub fn from_signature(signature: &FunctionSignature<'_>) -> Self {
437 if signature.python_signature.has_no_args() {
438 Self::Noargs
439 } else if signature.python_signature.kwargs.is_none() && !is_abi3_before(3, 10) {
440 Self::Fastcall
445 } else {
446 Self::Varargs
447 }
448 }
449}
450
451#[derive(Clone)]
452pub struct FnSpec<'a> {
453 pub tp: FnType,
454 pub name: &'a syn::Ident,
456 pub python_name: syn::Ident,
459 pub signature: FunctionSignature<'a>,
460 pub convention: CallingConvention,
461 pub text_signature: Option<TextSignatureAttribute>,
462 pub asyncness: Option<syn::Token![async]>,
463 pub unsafety: Option<syn::Token![unsafe]>,
464 pub warnings: Vec<PyFunctionWarning>,
465 #[cfg(feature = "experimental-inspect")]
466 pub output: syn::ReturnType,
467}
468
469pub fn parse_method_receiver(arg: &syn::FnArg) -> Result<SelfType> {
470 match arg {
471 syn::FnArg::Receiver(
472 recv @ syn::Receiver {
473 reference: None, ..
474 },
475 ) => {
476 bail_spanned!(recv.span() => RECEIVER_BY_VALUE_ERR);
477 }
478 syn::FnArg::Receiver(recv @ syn::Receiver { mutability, .. }) => Ok(SelfType::Receiver {
479 mutable: mutability.is_some(),
480 span: recv.span(),
481 }),
482 syn::FnArg::Typed(syn::PatType { ty, .. }) => {
483 if let syn::Type::ImplTrait(_) = &**ty {
484 bail_spanned!(ty.span() => IMPL_TRAIT_ERR);
485 }
486 Ok(SelfType::TryFromBoundRef(ty.span()))
487 }
488 }
489}
490
491impl<'a> FnSpec<'a> {
492 pub fn parse(
494 sig: &'a mut syn::Signature,
496 meth_attrs: &mut Vec<syn::Attribute>,
497 options: PyFunctionOptions,
498 ) -> Result<FnSpec<'a>> {
499 let PyFunctionOptions {
500 text_signature,
501 name,
502 signature,
503 warnings,
504 ..
505 } = options;
506
507 let mut python_name = name.map(|name| name.value.0);
508
509 let fn_type = Self::parse_fn_type(sig, meth_attrs, &mut python_name)?;
510 ensure_signatures_on_valid_method(&fn_type, signature.as_ref(), text_signature.as_ref())?;
511
512 let name = &sig.ident;
513 let python_name = python_name.as_ref().unwrap_or(name).unraw();
514
515 let arguments: Vec<_> = sig
516 .inputs
517 .iter_mut()
518 .skip(if fn_type.skip_first_rust_argument_in_python_signature() {
519 1
520 } else {
521 0
522 })
523 .map(FnArg::parse)
524 .collect::<Result<_>>()?;
525
526 let signature = if let Some(signature) = signature {
527 FunctionSignature::from_arguments_and_attribute(arguments, signature)?
528 } else {
529 FunctionSignature::from_arguments(arguments)
530 };
531
532 let convention = if matches!(fn_type, FnType::FnNew | FnType::FnNewClass(_)) {
533 CallingConvention::TpNew
534 } else {
535 CallingConvention::from_signature(&signature)
536 };
537
538 Ok(FnSpec {
539 tp: fn_type,
540 name,
541 convention,
542 python_name,
543 signature,
544 text_signature,
545 asyncness: sig.asyncness,
546 unsafety: sig.unsafety,
547 warnings,
548 #[cfg(feature = "experimental-inspect")]
549 output: sig.output.clone(),
550 })
551 }
552
553 pub fn null_terminated_python_name(&self, ctx: &Ctx) -> LitCStr {
554 let name = self.python_name.to_string();
555 let name = CString::new(name).unwrap();
556 LitCStr::new(name, self.python_name.span(), ctx)
557 }
558
559 fn parse_fn_type(
560 sig: &syn::Signature,
561 meth_attrs: &mut Vec<syn::Attribute>,
562 python_name: &mut Option<syn::Ident>,
563 ) -> Result<FnType> {
564 let mut method_attributes = parse_method_attributes(meth_attrs)?;
565
566 let name = &sig.ident;
567 let parse_receiver = |msg: &'static str| {
568 let first_arg = sig
569 .inputs
570 .first()
571 .ok_or_else(|| err_spanned!(sig.span() => msg))?;
572 parse_method_receiver(first_arg)
573 };
574
575 let strip_fn_name = |prefix: &'static str| {
577 name.unraw()
578 .to_string()
579 .strip_prefix(prefix)
580 .map(|stripped| syn::Ident::new(stripped, name.span()))
581 };
582
583 let mut set_name_to_new = || {
584 if let Some(name) = &python_name {
585 bail_spanned!(name.span() => "`name` not allowed with `#[new]`");
586 }
587 *python_name = Some(syn::Ident::new("__new__", Span::call_site()));
588 Ok(())
589 };
590
591 let fn_type = match method_attributes.as_mut_slice() {
592 [] => FnType::Fn(parse_receiver(
593 "static method needs #[staticmethod] attribute",
594 )?),
595 [MethodTypeAttribute::StaticMethod(_)] => FnType::FnStatic,
596 [MethodTypeAttribute::ClassAttribute(_)] => FnType::ClassAttribute,
597 [MethodTypeAttribute::New(_)] => {
598 set_name_to_new()?;
599 FnType::FnNew
600 }
601 [MethodTypeAttribute::New(_), MethodTypeAttribute::ClassMethod(span)]
602 | [MethodTypeAttribute::ClassMethod(span), MethodTypeAttribute::New(_)] => {
603 set_name_to_new()?;
604 FnType::FnNewClass(*span)
605 }
606 [MethodTypeAttribute::ClassMethod(_)] => {
607 let span = match sig.inputs.first() {
609 Some(syn::FnArg::Typed(first_arg)) => first_arg.ty.span(),
612 Some(syn::FnArg::Receiver(_)) | None => bail_spanned!(
613 sig.paren_token.span.join() => "Expected `&Bound<PyType>` or `Py<PyType>` as the first argument to `#[classmethod]`"
614 ),
615 };
616 FnType::FnClass(span)
617 }
618 [MethodTypeAttribute::Getter(_, name)] => {
619 if let Some(name) = name.take() {
620 ensure_spanned!(
621 python_name.replace(name).is_none(),
622 python_name.span() => "`name` may only be specified once"
623 );
624 } else if python_name.is_none() {
625 *python_name = strip_fn_name("get_");
627 }
628
629 FnType::Getter(parse_receiver("expected receiver for `#[getter]`")?)
630 }
631 [MethodTypeAttribute::Setter(_, name)] => {
632 if let Some(name) = name.take() {
633 ensure_spanned!(
634 python_name.replace(name).is_none(),
635 python_name.span() => "`name` may only be specified once"
636 );
637 } else if python_name.is_none() {
638 *python_name = strip_fn_name("set_");
640 }
641
642 FnType::Setter(parse_receiver("expected receiver for `#[setter]`")?)
643 }
644 [first, rest @ .., last] => {
645 let span = rest
647 .iter()
648 .fold(first.span(), |s, next| s.join(next.span()).unwrap_or(s));
649 let span = span.join(last.span()).unwrap_or(span);
650 let mut msg = format!("`{first}` may not be combined with");
652 let mut is_first = true;
653 for attr in &*rest {
654 msg.push_str(&format!(" `{attr}`"));
655 if is_first {
656 is_first = false;
657 } else {
658 msg.push(',');
659 }
660 }
661 if !rest.is_empty() {
662 msg.push_str(" and");
663 }
664 msg.push_str(&format!(" `{last}`"));
665 bail_spanned!(span => msg)
666 }
667 };
668 Ok(fn_type)
669 }
670
671 pub fn get_wrapper_function(
673 &self,
674 ident: &proc_macro2::Ident,
675 cls: Option<&syn::Type>,
676 ctx: &Ctx,
677 ) -> Result<TokenStream> {
678 let Ctx {
679 pyo3_path,
680 output_span,
681 } = ctx;
682 let mut cancel_handle_iter = self
683 .signature
684 .arguments
685 .iter()
686 .filter(|arg| matches!(arg, FnArg::CancelHandle(..)));
687 let cancel_handle = cancel_handle_iter.next();
688 if let Some(FnArg::CancelHandle(CancelHandleArg { name, .. })) = cancel_handle {
689 ensure_spanned!(self.asyncness.is_some(), name.span() => "`cancel_handle` attribute can only be used with `async fn`");
690 if let Some(FnArg::CancelHandle(CancelHandleArg { name, .. })) =
691 cancel_handle_iter.next()
692 {
693 bail_spanned!(name.span() => "`cancel_handle` may only be specified once");
694 }
695 }
696
697 let rust_call = |args: Vec<TokenStream>, holders: &mut Holders| {
698 let mut self_arg = || self.tp.self_arg(cls, ExtractErrorMode::Raise, holders, ctx);
699
700 let call = if self.asyncness.is_some() {
701 let throw_callback = if cancel_handle.is_some() {
702 quote! { Some(__throw_callback) }
703 } else {
704 quote! { None }
705 };
706 let python_name = &self.python_name;
707 let qualname_prefix = match cls {
708 Some(cls) => quote!(Some(<#cls as #pyo3_path::PyTypeInfo>::NAME)),
709 None => quote!(None),
710 };
711 let arg_names = (0..args.len())
712 .map(|i| format_ident!("arg_{}", i))
713 .collect::<Vec<_>>();
714 let future = match self.tp {
715 FnType::Fn(SelfType::Receiver { mutable: false, .. }) => {
716 quote! {{
717 #(let #arg_names = #args;)*
718 let __guard = unsafe { #pyo3_path::impl_::coroutine::RefGuard::<#cls>::new(&#pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(py, &_slf))? };
719 async move { function(&__guard, #(#arg_names),*).await }
720 }}
721 }
722 FnType::Fn(SelfType::Receiver { mutable: true, .. }) => {
723 quote! {{
724 #(let #arg_names = #args;)*
725 let mut __guard = unsafe { #pyo3_path::impl_::coroutine::RefMutGuard::<#cls>::new(&#pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(py, &_slf))? };
726 async move { function(&mut __guard, #(#arg_names),*).await }
727 }}
728 }
729 _ => {
730 if let Some(self_arg) = self_arg() {
731 quote! {
732 function(
733 #self_arg
735 #(#args),*
736 )
737 }
738 } else {
739 quote! { function(#(#args),*) }
740 }
741 }
742 };
743 let mut call = quote! {{
744 let future = #future;
745 #pyo3_path::impl_::coroutine::new_coroutine(
746 #pyo3_path::intern!(py, stringify!(#python_name)),
747 #qualname_prefix,
748 #throw_callback,
749 async move {
750 let fut = future.await;
751 #pyo3_path::impl_::wrap::converter(&fut).wrap(fut)
752 },
753 )
754 }};
755 if cancel_handle.is_some() {
756 call = quote! {{
757 let __cancel_handle = #pyo3_path::coroutine::CancelHandle::new();
758 let __throw_callback = __cancel_handle.throw_callback();
759 #call
760 }};
761 }
762 call
763 } else if let Some(self_arg) = self_arg() {
764 quote! {
765 function(
766 #self_arg
768 #(#args),*
769 )
770 }
771 } else {
772 quote! { function(#(#args),*) }
773 };
774
775 let ret_ident = Ident::new("ret", *output_span);
778 let ret_expr = quote! { let #ret_ident = #call; };
779 let return_conversion =
780 quotes::map_result_into_ptr(quotes::ok_wrap(ret_ident.to_token_stream(), ctx), ctx);
781 quote! {
782 {
783 #ret_expr
784 #return_conversion
785 }
786 }
787 };
788
789 let func_name = &self.name;
790 let rust_name = if let Some(cls) = cls {
791 quote!(#cls::#func_name)
792 } else {
793 quote!(#func_name)
794 };
795
796 let warnings = self.warnings.build_py_warning(ctx);
797
798 Ok(match self.convention {
799 CallingConvention::Noargs => {
800 let mut holders = Holders::new();
801 let args = self
802 .signature
803 .arguments
804 .iter()
805 .map(|arg| match arg {
806 FnArg::Py(..) => quote!(py),
807 FnArg::CancelHandle(..) => quote!(__cancel_handle),
808 _ => unreachable!("`CallingConvention::Noargs` should not contain any arguments (reaching Python) except for `self`, which is handled below."),
809 })
810 .collect();
811 let call = rust_call(args, &mut holders);
812 let init_holders = holders.init_holders(ctx);
813 quote! {
814 unsafe fn #ident<'py>(
815 py: #pyo3_path::Python<'py>,
816 _slf: *mut #pyo3_path::ffi::PyObject,
817 ) -> #pyo3_path::PyResult<*mut #pyo3_path::ffi::PyObject> {
818 let function = #rust_name; #init_holders
820 #warnings
821 let result = #call;
822 result
823 }
824 }
825 }
826 CallingConvention::Fastcall => {
827 let mut holders = Holders::new();
828 let (arg_convert, args) = impl_arg_params(self, cls, true, &mut holders, ctx);
829 let call = rust_call(args, &mut holders);
830 let init_holders = holders.init_holders(ctx);
831
832 quote! {
833 unsafe fn #ident<'py>(
834 py: #pyo3_path::Python<'py>,
835 _slf: *mut #pyo3_path::ffi::PyObject,
836 _args: *const *mut #pyo3_path::ffi::PyObject,
837 _nargs: #pyo3_path::ffi::Py_ssize_t,
838 _kwnames: *mut #pyo3_path::ffi::PyObject
839 ) -> #pyo3_path::PyResult<*mut #pyo3_path::ffi::PyObject> {
840 let function = #rust_name; #arg_convert
842 #init_holders
843 #warnings
844 let result = #call;
845 result
846 }
847 }
848 }
849 CallingConvention::Varargs => {
850 let mut holders = Holders::new();
851 let (arg_convert, args) = impl_arg_params(self, cls, false, &mut holders, ctx);
852 let call = rust_call(args, &mut holders);
853 let init_holders = holders.init_holders(ctx);
854
855 quote! {
856 unsafe fn #ident<'py>(
857 py: #pyo3_path::Python<'py>,
858 _slf: *mut #pyo3_path::ffi::PyObject,
859 _args: *mut #pyo3_path::ffi::PyObject,
860 _kwargs: *mut #pyo3_path::ffi::PyObject
861 ) -> #pyo3_path::PyResult<*mut #pyo3_path::ffi::PyObject> {
862 let function = #rust_name; #arg_convert
864 #init_holders
865 #warnings
866 let result = #call;
867 result
868 }
869 }
870 }
871 CallingConvention::TpNew => {
872 let mut holders = Holders::new();
873 let (arg_convert, args) = impl_arg_params(self, cls, false, &mut holders, ctx);
874 let self_arg = self
875 .tp
876 .self_arg(cls, ExtractErrorMode::Raise, &mut holders, ctx);
877 let call = quote_spanned! {*output_span=> #rust_name(#self_arg #(#args),*) };
878 let init_holders = holders.init_holders(ctx);
879 quote! {
880 unsafe fn #ident(
881 py: #pyo3_path::Python<'_>,
882 _slf: *mut #pyo3_path::ffi::PyTypeObject,
883 _args: *mut #pyo3_path::ffi::PyObject,
884 _kwargs: *mut #pyo3_path::ffi::PyObject
885 ) -> #pyo3_path::PyResult<*mut #pyo3_path::ffi::PyObject> {
886 use #pyo3_path::impl_::callback::IntoPyCallbackOutput;
887 let function = #rust_name; #arg_convert
889 #init_holders
890 #warnings
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) -> syn::Result<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: PyClassGuard<'_, Self>` or `slf: PyClassGuardMut<'_, 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}