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