1use std::borrow::Cow;
2use std::ffi::CString;
3
4use crate::attributes::{FromPyWithAttribute, NameAttribute, RenamingRule};
5use crate::method::{CallingConvention, ExtractErrorMode, PyArg};
6use crate::params::{impl_regular_arg_param, Holders};
7use crate::utils::{deprecated_from_py_with, PythonDoc, TypeExt as _};
8use crate::utils::{Ctx, LitCStr};
9use crate::{
10 method::{FnArg, FnSpec, FnType, SelfType},
11 pyfunction::PyFunctionOptions,
12};
13use crate::{quotes, utils};
14use proc_macro2::{Span, TokenStream};
15use quote::{format_ident, quote, quote_spanned, ToTokens};
16use syn::{ext::IdentExt, spanned::Spanned, Result};
17
18/// Generated code for a single pymethod item.
19pub struct MethodAndMethodDef {
20 /// The implementation of the Python wrapper for the pymethod
21 pub associated_method: TokenStream,
22 /// The method def which will be used to register this pymethod
23 pub method_def: TokenStream,
24}
25
26/// Generated code for a single pymethod item which is registered by a slot.
27pub struct MethodAndSlotDef {
28 /// The implementation of the Python wrapper for the pymethod
29 pub associated_method: TokenStream,
30 /// The slot def which will be used to register this pymethod
31 pub slot_def: TokenStream,
32}
33
34pub enum GeneratedPyMethod {
35 Method(MethodAndMethodDef),
36 Proto(MethodAndSlotDef),
37 SlotTraitImpl(String, TokenStream),
38}
39
40pub struct PyMethod<'a> {
41 kind: PyMethodKind,
42 method_name: String,
43 spec: FnSpec<'a>,
44}
45
46enum PyMethodKind {
47 Fn,
48 Proto(PyMethodProtoKind),
49}
50
51impl PyMethodKind {
52 fn from_name(name: &str) -> Self {
53 match name {
54 // Protocol implemented through slots
55 "__str__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__STR__)),
56 "__repr__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__REPR__)),
57 "__hash__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__HASH__)),
58 "__richcmp__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__RICHCMP__)),
59 "__get__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__GET__)),
60 "__iter__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__ITER__)),
61 "__next__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__NEXT__)),
62 "__await__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__AWAIT__)),
63 "__aiter__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__AITER__)),
64 "__anext__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__ANEXT__)),
65 "__len__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__LEN__)),
66 "__contains__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__CONTAINS__)),
67 "__concat__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__CONCAT__)),
68 "__repeat__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__REPEAT__)),
69 "__inplace_concat__" => {
70 PyMethodKind::Proto(PyMethodProtoKind::Slot(&__INPLACE_CONCAT__))
71 }
72 "__inplace_repeat__" => {
73 PyMethodKind::Proto(PyMethodProtoKind::Slot(&__INPLACE_REPEAT__))
74 }
75 "__getitem__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__GETITEM__)),
76 "__pos__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__POS__)),
77 "__neg__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__NEG__)),
78 "__abs__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__ABS__)),
79 "__invert__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__INVERT__)),
80 "__index__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__INDEX__)),
81 "__int__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__INT__)),
82 "__float__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__FLOAT__)),
83 "__bool__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__BOOL__)),
84 "__iadd__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__IADD__)),
85 "__isub__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__ISUB__)),
86 "__imul__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__IMUL__)),
87 "__imatmul__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__IMATMUL__)),
88 "__itruediv__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__ITRUEDIV__)),
89 "__ifloordiv__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__IFLOORDIV__)),
90 "__imod__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__IMOD__)),
91 "__ipow__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__IPOW__)),
92 "__ilshift__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__ILSHIFT__)),
93 "__irshift__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__IRSHIFT__)),
94 "__iand__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__IAND__)),
95 "__ixor__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__IXOR__)),
96 "__ior__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__IOR__)),
97 "__getbuffer__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__GETBUFFER__)),
98 "__releasebuffer__" => PyMethodKind::Proto(PyMethodProtoKind::Slot(&__RELEASEBUFFER__)),
99 // Protocols implemented through traits
100 "__getattribute__" => {
101 PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__GETATTRIBUTE__))
102 }
103 "__getattr__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__GETATTR__)),
104 "__setattr__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__SETATTR__)),
105 "__delattr__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__DELATTR__)),
106 "__set__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__SET__)),
107 "__delete__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__DELETE__)),
108 "__setitem__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__SETITEM__)),
109 "__delitem__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__DELITEM__)),
110 "__add__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__ADD__)),
111 "__radd__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__RADD__)),
112 "__sub__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__SUB__)),
113 "__rsub__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__RSUB__)),
114 "__mul__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__MUL__)),
115 "__rmul__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__RMUL__)),
116 "__matmul__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__MATMUL__)),
117 "__rmatmul__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__RMATMUL__)),
118 "__floordiv__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__FLOORDIV__)),
119 "__rfloordiv__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__RFLOORDIV__)),
120 "__truediv__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__TRUEDIV__)),
121 "__rtruediv__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__RTRUEDIV__)),
122 "__divmod__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__DIVMOD__)),
123 "__rdivmod__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__RDIVMOD__)),
124 "__mod__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__MOD__)),
125 "__rmod__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__RMOD__)),
126 "__lshift__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__LSHIFT__)),
127 "__rlshift__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__RLSHIFT__)),
128 "__rshift__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__RSHIFT__)),
129 "__rrshift__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__RRSHIFT__)),
130 "__and__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__AND__)),
131 "__rand__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__RAND__)),
132 "__xor__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__XOR__)),
133 "__rxor__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__RXOR__)),
134 "__or__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__OR__)),
135 "__ror__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__ROR__)),
136 "__pow__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__POW__)),
137 "__rpow__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__RPOW__)),
138 "__lt__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__LT__)),
139 "__le__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__LE__)),
140 "__eq__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__EQ__)),
141 "__ne__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__NE__)),
142 "__gt__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__GT__)),
143 "__ge__" => PyMethodKind::Proto(PyMethodProtoKind::SlotFragment(&__GE__)),
144 // Some tricky protocols which don't fit the pattern of the rest
145 "__call__" => PyMethodKind::Proto(PyMethodProtoKind::Call),
146 "__traverse__" => PyMethodKind::Proto(PyMethodProtoKind::Traverse),
147 "__clear__" => PyMethodKind::Proto(PyMethodProtoKind::Clear),
148 // Not a proto
149 _ => PyMethodKind::Fn,
150 }
151 }
152}
153
154enum PyMethodProtoKind {
155 Slot(&'static SlotDef),
156 Call,
157 Traverse,
158 Clear,
159 SlotFragment(&'static SlotFragmentDef),
160}
161
162impl<'a> PyMethod<'a> {
163 fn parse(
164 sig: &'a mut syn::Signature,
165 meth_attrs: &mut Vec<syn::Attribute>,
166 options: PyFunctionOptions,
167 ) -> Result<Self> {
168 let spec: FnSpec<'_> = FnSpec::parse(sig, meth_attrs, options)?;
169
170 let method_name: String = spec.python_name.to_string();
171 let kind: PyMethodKind = PyMethodKind::from_name(&method_name);
172
173 Ok(Self {
174 kind,
175 method_name,
176 spec,
177 })
178 }
179}
180
181pub fn is_proto_method(name: &str) -> bool {
182 match PyMethodKind::from_name(name) {
183 PyMethodKind::Fn => false,
184 PyMethodKind::Proto(_) => true,
185 }
186}
187
188pub fn gen_py_method(
189 cls: &syn::Type,
190 sig: &mut syn::Signature,
191 meth_attrs: &mut Vec<syn::Attribute>,
192 options: PyFunctionOptions,
193 ctx: &Ctx,
194) -> Result<GeneratedPyMethod> {
195 check_generic(sig)?;
196 ensure_function_options_valid(&options)?;
197 let method = PyMethod::parse(sig, meth_attrs, options)?;
198 let spec = &method.spec;
199 let Ctx { pyo3_path, .. } = ctx;
200
201 Ok(match (method.kind, &spec.tp) {
202 // Class attributes go before protos so that class attributes can be used to set proto
203 // method to None.
204 (_, FnType::ClassAttribute) => {
205 GeneratedPyMethod::Method(impl_py_class_attribute(cls, spec, ctx)?)
206 }
207 (PyMethodKind::Proto(proto_kind), _) => {
208 ensure_no_forbidden_protocol_attributes(&proto_kind, spec, &method.method_name)?;
209 match proto_kind {
210 PyMethodProtoKind::Slot(slot_def) => {
211 let slot = slot_def.generate_type_slot(cls, spec, &method.method_name, ctx)?;
212 GeneratedPyMethod::Proto(slot)
213 }
214 PyMethodProtoKind::Call => {
215 GeneratedPyMethod::Proto(impl_call_slot(cls, method.spec, ctx)?)
216 }
217 PyMethodProtoKind::Traverse => {
218 GeneratedPyMethod::Proto(impl_traverse_slot(cls, spec, ctx)?)
219 }
220 PyMethodProtoKind::Clear => {
221 GeneratedPyMethod::Proto(impl_clear_slot(cls, spec, ctx)?)
222 }
223 PyMethodProtoKind::SlotFragment(slot_fragment_def) => {
224 let proto = slot_fragment_def.generate_pyproto_fragment(cls, spec, ctx)?;
225 GeneratedPyMethod::SlotTraitImpl(method.method_name, proto)
226 }
227 }
228 }
229 // ordinary functions (with some specialties)
230 (_, FnType::Fn(_)) => GeneratedPyMethod::Method(impl_py_method_def(
231 cls,
232 spec,
233 &spec.get_doc(meth_attrs, ctx),
234 None,
235 ctx,
236 )?),
237 (_, FnType::FnClass(_)) => GeneratedPyMethod::Method(impl_py_method_def(
238 cls,
239 spec,
240 &spec.get_doc(meth_attrs, ctx),
241 Some(quote!(#pyo3_path::ffi::METH_CLASS)),
242 ctx,
243 )?),
244 (_, FnType::FnStatic) => GeneratedPyMethod::Method(impl_py_method_def(
245 cls,
246 spec,
247 &spec.get_doc(meth_attrs, ctx),
248 Some(quote!(#pyo3_path::ffi::METH_STATIC)),
249 ctx,
250 )?),
251 // special prototypes
252 (_, FnType::FnNew) | (_, FnType::FnNewClass(_)) => {
253 GeneratedPyMethod::Proto(impl_py_method_def_new(cls, spec, ctx)?)
254 }
255
256 (_, FnType::Getter(self_type)) => GeneratedPyMethod::Method(impl_py_getter_def(
257 cls,
258 PropertyType::Function {
259 self_type,
260 spec,
261 doc: spec.get_doc(meth_attrs, ctx),
262 },
263 ctx,
264 )?),
265 (_, FnType::Setter(self_type)) => GeneratedPyMethod::Method(impl_py_setter_def(
266 cls,
267 PropertyType::Function {
268 self_type,
269 spec,
270 doc: spec.get_doc(meth_attrs, ctx),
271 },
272 ctx,
273 )?),
274 (_, FnType::FnModule(_)) => {
275 unreachable!("methods cannot be FnModule")
276 }
277 })
278}
279
280pub fn check_generic(sig: &syn::Signature) -> syn::Result<()> {
281 let err_msg: impl Fn(&'static str) -> String = |typ: &'static str| format!("Python functions cannot have generic {} parameters", typ);
282 for param: &GenericParam in &sig.generics.params {
283 match param {
284 syn::GenericParam::Lifetime(_) => {}
285 syn::GenericParam::Type(_) => bail_spanned!(param.span() => err_msg("type")),
286 syn::GenericParam::Const(_) => bail_spanned!(param.span() => err_msg("const")),
287 }
288 }
289 Ok(())
290}
291
292fn ensure_function_options_valid(options: &PyFunctionOptions) -> syn::Result<()> {
293 if let Some(pass_module: &pass_module) = &options.pass_module {
294 bail_spanned!(pass_module.span() => "`pass_module` cannot be used on Python methods");
295 }
296 Ok(())
297}
298
299fn ensure_no_forbidden_protocol_attributes(
300 proto_kind: &PyMethodProtoKind,
301 spec: &FnSpec<'_>,
302 method_name: &str,
303) -> syn::Result<()> {
304 if let Some(signature: &KeywordAttribute) = &spec.signature.attribute {
305 // __call__ is allowed to have a signature, but nothing else is.
306 if !matches!(proto_kind, PyMethodProtoKind::Call) {
307 bail_spanned!(signature.kw.span() => format!("`signature` cannot be used with magic method `{}`", method_name));
308 }
309 }
310 if let Some(text_signature: &KeywordAttribute) = &spec.text_signature {
311 bail_spanned!(text_signature.kw.span() => format!("`text_signature` cannot be used with magic method `{}`", method_name));
312 }
313 Ok(())
314}
315
316/// Also used by pyfunction.
317pub fn impl_py_method_def(
318 cls: &syn::Type,
319 spec: &FnSpec<'_>,
320 doc: &PythonDoc,
321 flags: Option<TokenStream>,
322 ctx: &Ctx,
323) -> Result<MethodAndMethodDef> {
324 let Ctx { pyo3_path: &PyO3CratePath, .. } = ctx;
325 let wrapper_ident: Ident = format_ident!("__pymethod_{}__", spec.python_name);
326 let associated_method: TokenStream = spec.get_wrapper_function(&wrapper_ident, cls:Some(cls), ctx)?;
327 let add_flags: Option = flags.map(|flags: TokenStream| quote!(.flags(#flags)));
328 let methoddef_type: TokenStream = match spec.tp {
329 FnType::FnStatic => quote!(Static),
330 FnType::FnClass(_) => quote!(Class),
331 _ => quote!(Method),
332 };
333 let methoddef: TokenStream = spec.get_methoddef(wrapper:quote! { #cls::#wrapper_ident }, doc, ctx);
334 let method_def: TokenStream = quote! {
335 #pyo3_path::impl_::pyclass::MaybeRuntimePyMethodDef::Static(
336 #pyo3_path::impl_::pymethods::PyMethodDefType::#methoddef_type(#methoddef #add_flags)
337 )
338 };
339 Ok(MethodAndMethodDef {
340 associated_method,
341 method_def,
342 })
343}
344
345/// Also used by pyclass.
346pub fn impl_py_method_def_new(
347 cls: &syn::Type,
348 spec: &FnSpec<'_>,
349 ctx: &Ctx,
350) -> Result<MethodAndSlotDef> {
351 let Ctx { pyo3_path, .. } = ctx;
352 let wrapper_ident = syn::Ident::new("__pymethod___new____", Span::call_site());
353 let associated_method = spec.get_wrapper_function(&wrapper_ident, Some(cls), ctx)?;
354 // Use just the text_signature_call_signature() because the class' Python name
355 // isn't known to `#[pymethods]` - that has to be attached at runtime from the PyClassImpl
356 // trait implementation created by `#[pyclass]`.
357 let text_signature_body = spec.text_signature_call_signature().map_or_else(
358 || quote!(::std::option::Option::None),
359 |text_signature| quote!(::std::option::Option::Some(#text_signature)),
360 );
361 let slot_def = quote! {
362 #pyo3_path::ffi::PyType_Slot {
363 slot: #pyo3_path::ffi::Py_tp_new,
364 pfunc: {
365 unsafe extern "C" fn trampoline(
366 subtype: *mut #pyo3_path::ffi::PyTypeObject,
367 args: *mut #pyo3_path::ffi::PyObject,
368 kwargs: *mut #pyo3_path::ffi::PyObject,
369 ) -> *mut #pyo3_path::ffi::PyObject {
370 #[allow(unknown_lints, non_local_definitions)]
371 impl #pyo3_path::impl_::pyclass::PyClassNewTextSignature<#cls> for #pyo3_path::impl_::pyclass::PyClassImplCollector<#cls> {
372 #[inline]
373 fn new_text_signature(self) -> ::std::option::Option<&'static str> {
374 #text_signature_body
375 }
376 }
377
378 #pyo3_path::impl_::trampoline::newfunc(
379 subtype,
380 args,
381 kwargs,
382 #cls::#wrapper_ident
383 )
384 }
385 trampoline
386 } as #pyo3_path::ffi::newfunc as _
387 }
388 };
389 Ok(MethodAndSlotDef {
390 associated_method,
391 slot_def,
392 })
393}
394
395fn impl_call_slot(cls: &syn::Type, mut spec: FnSpec<'_>, ctx: &Ctx) -> Result<MethodAndSlotDef> {
396 let Ctx { pyo3_path, .. } = ctx;
397
398 // HACK: __call__ proto slot must always use varargs calling convention, so change the spec.
399 // Probably indicates there's a refactoring opportunity somewhere.
400 spec.convention = CallingConvention::Varargs;
401
402 let wrapper_ident = syn::Ident::new("__pymethod___call____", Span::call_site());
403 let associated_method = spec.get_wrapper_function(&wrapper_ident, Some(cls), ctx)?;
404 let slot_def = quote! {
405 #pyo3_path::ffi::PyType_Slot {
406 slot: #pyo3_path::ffi::Py_tp_call,
407 pfunc: {
408 unsafe extern "C" fn trampoline(
409 slf: *mut #pyo3_path::ffi::PyObject,
410 args: *mut #pyo3_path::ffi::PyObject,
411 kwargs: *mut #pyo3_path::ffi::PyObject,
412 ) -> *mut #pyo3_path::ffi::PyObject
413 {
414 #pyo3_path::impl_::trampoline::ternaryfunc(
415 slf,
416 args,
417 kwargs,
418 #cls::#wrapper_ident
419 )
420 }
421 trampoline
422 } as #pyo3_path::ffi::ternaryfunc as _
423 }
424 };
425 Ok(MethodAndSlotDef {
426 associated_method,
427 slot_def,
428 })
429}
430
431fn impl_traverse_slot(
432 cls: &syn::Type,
433 spec: &FnSpec<'_>,
434 ctx: &Ctx,
435) -> syn::Result<MethodAndSlotDef> {
436 let Ctx { pyo3_path, .. } = ctx;
437 if let (Some(py_arg), _) = split_off_python_arg(&spec.signature.arguments) {
438 return Err(syn::Error::new_spanned(py_arg.ty, "__traverse__ may not take `Python`. \
439 Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` \
440 should do nothing but calls to `visit.call`. Most importantly, safe access to the GIL is prohibited \
441 inside implementations of `__traverse__`, i.e. `Python::with_gil` will panic."));
442 }
443
444 // check that the receiver does not try to smuggle an (implicit) `Python` token into here
445 if let FnType::Fn(SelfType::TryFromBoundRef(span))
446 | FnType::Fn(SelfType::Receiver {
447 mutable: true,
448 span,
449 }) = spec.tp
450 {
451 bail_spanned! { span =>
452 "__traverse__ may not take a receiver other than `&self`. Usually, an implementation of \
453 `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` \
454 should do nothing but calls to `visit.call`. Most importantly, safe access to the GIL is prohibited \
455 inside implementations of `__traverse__`, i.e. `Python::with_gil` will panic."
456 }
457 }
458
459 let rust_fn_ident = spec.name;
460
461 let associated_method = quote! {
462 pub unsafe extern "C" fn __pymethod_traverse__(
463 slf: *mut #pyo3_path::ffi::PyObject,
464 visit: #pyo3_path::ffi::visitproc,
465 arg: *mut ::std::os::raw::c_void,
466 ) -> ::std::os::raw::c_int {
467 #pyo3_path::impl_::pymethods::_call_traverse::<#cls>(slf, #cls::#rust_fn_ident, visit, arg, #cls::__pymethod_traverse__)
468 }
469 };
470 let slot_def = quote! {
471 #pyo3_path::ffi::PyType_Slot {
472 slot: #pyo3_path::ffi::Py_tp_traverse,
473 pfunc: #cls::__pymethod_traverse__ as #pyo3_path::ffi::traverseproc as _
474 }
475 };
476 Ok(MethodAndSlotDef {
477 associated_method,
478 slot_def,
479 })
480}
481
482fn impl_clear_slot(cls: &syn::Type, spec: &FnSpec<'_>, ctx: &Ctx) -> syn::Result<MethodAndSlotDef> {
483 let Ctx { pyo3_path, .. } = ctx;
484 let (py_arg, args) = split_off_python_arg(&spec.signature.arguments);
485 let self_type = match &spec.tp {
486 FnType::Fn(self_type) => self_type,
487 _ => bail_spanned!(spec.name.span() => "expected instance method for `__clear__` function"),
488 };
489 let mut holders = Holders::new();
490 let slf = self_type.receiver(cls, ExtractErrorMode::Raise, &mut holders, ctx);
491
492 if let [arg, ..] = args {
493 bail_spanned!(arg.ty().span() => "`__clear__` function expected to have no arguments");
494 }
495
496 let name = &spec.name;
497 let holders = holders.init_holders(ctx);
498 let fncall = if py_arg.is_some() {
499 quote!(#cls::#name(#slf, py))
500 } else {
501 quote!(#cls::#name(#slf))
502 };
503
504 let associated_method = quote! {
505 pub unsafe extern "C" fn __pymethod___clear____(
506 _slf: *mut #pyo3_path::ffi::PyObject,
507 ) -> ::std::os::raw::c_int {
508 #pyo3_path::impl_::pymethods::_call_clear(_slf, |py, _slf| {
509 #holders
510 let result = #fncall;
511 let result = #pyo3_path::impl_::wrap::converter(&result).wrap(result)?;
512 ::std::result::Result::Ok(result)
513 }, #cls::__pymethod___clear____)
514 }
515 };
516 let slot_def = quote! {
517 #pyo3_path::ffi::PyType_Slot {
518 slot: #pyo3_path::ffi::Py_tp_clear,
519 pfunc: #cls::__pymethod___clear____ as #pyo3_path::ffi::inquiry as _
520 }
521 };
522 Ok(MethodAndSlotDef {
523 associated_method,
524 slot_def,
525 })
526}
527
528pub(crate) fn impl_py_class_attribute(
529 cls: &syn::Type,
530 spec: &FnSpec<'_>,
531 ctx: &Ctx,
532) -> syn::Result<MethodAndMethodDef> {
533 let Ctx { pyo3_path, .. } = ctx;
534 let (py_arg, args) = split_off_python_arg(&spec.signature.arguments);
535 ensure_spanned!(
536 args.is_empty(),
537 args[0].ty().span() => "#[classattr] can only have one argument (of type pyo3::Python)"
538 );
539
540 let name = &spec.name;
541 let fncall = if py_arg.is_some() {
542 quote!(function(py))
543 } else {
544 quote!(function())
545 };
546
547 let wrapper_ident = format_ident!("__pymethod_{}__", name);
548 let python_name = spec.null_terminated_python_name(ctx);
549 let body = quotes::ok_wrap(fncall, ctx);
550
551 let associated_method = quote! {
552 fn #wrapper_ident(py: #pyo3_path::Python<'_>) -> #pyo3_path::PyResult<#pyo3_path::PyObject> {
553 let function = #cls::#name; // Shadow the method name to avoid #3017
554 let result = #body;
555 #pyo3_path::impl_::wrap::converter(&result).map_into_pyobject(py, result)
556 }
557 };
558
559 let method_def = quote! {
560 #pyo3_path::impl_::pyclass::MaybeRuntimePyMethodDef::Static(
561 #pyo3_path::impl_::pymethods::PyMethodDefType::ClassAttribute({
562 #pyo3_path::impl_::pymethods::PyClassAttributeDef::new(
563 #python_name,
564 #cls::#wrapper_ident
565 )
566 })
567 )
568 };
569
570 Ok(MethodAndMethodDef {
571 associated_method,
572 method_def,
573 })
574}
575
576fn impl_call_setter(
577 cls: &syn::Type,
578 spec: &FnSpec<'_>,
579 self_type: &SelfType,
580 holders: &mut Holders,
581 ctx: &Ctx,
582) -> syn::Result<TokenStream> {
583 let (py_arg: Option<&PyArg<'_>>, args: &[FnArg<'_>]) = split_off_python_arg(&spec.signature.arguments);
584 let slf: TokenStream = self_type.receiver(cls, error_mode:ExtractErrorMode::Raise, holders, ctx);
585
586 if args.is_empty() {
587 bail_spanned!(spec.name.span() => "setter function expected to have one argument");
588 } else if args.len() > 1 {
589 bail_spanned!(
590 args[1].ty().span() =>
591 "setter function can have at most two arguments ([pyo3::Python,] and value)"
592 );
593 }
594
595 let name: &&Ident = &spec.name;
596 let fncall: TokenStream = if py_arg.is_some() {
597 quote!(#cls::#name(#slf, py, _val))
598 } else {
599 quote!(#cls::#name(#slf, _val))
600 };
601
602 Ok(fncall)
603}
604
605// Used here for PropertyType::Function, used in pyclass for descriptors.
606pub fn impl_py_setter_def(
607 cls: &syn::Type,
608 property_type: PropertyType<'_>,
609 ctx: &Ctx,
610) -> Result<MethodAndMethodDef> {
611 let Ctx { pyo3_path, .. } = ctx;
612 let python_name = property_type.null_terminated_python_name(ctx)?;
613 let doc = property_type.doc(ctx);
614 let mut holders = Holders::new();
615 let setter_impl = match property_type {
616 PropertyType::Descriptor {
617 field_index, field, ..
618 } => {
619 let slf = SelfType::Receiver {
620 mutable: true,
621 span: Span::call_site(),
622 }
623 .receiver(cls, ExtractErrorMode::Raise, &mut holders, ctx);
624 if let Some(ident) = &field.ident {
625 // named struct field
626 quote!({ #slf.#ident = _val; })
627 } else {
628 // tuple struct field
629 let index = syn::Index::from(field_index);
630 quote!({ #slf.#index = _val; })
631 }
632 }
633 PropertyType::Function {
634 spec, self_type, ..
635 } => impl_call_setter(cls, spec, self_type, &mut holders, ctx)?,
636 };
637
638 let wrapper_ident = match property_type {
639 PropertyType::Descriptor {
640 field: syn::Field {
641 ident: Some(ident), ..
642 },
643 ..
644 } => {
645 format_ident!("__pymethod_set_{}__", ident)
646 }
647 PropertyType::Descriptor { field_index, .. } => {
648 format_ident!("__pymethod_set_field_{}__", field_index)
649 }
650 PropertyType::Function { spec, .. } => {
651 format_ident!("__pymethod_set_{}__", spec.name)
652 }
653 };
654
655 let extract = match &property_type {
656 PropertyType::Function { spec, .. } => {
657 let (_, args) = split_off_python_arg(&spec.signature.arguments);
658 let value_arg = &args[0];
659 let (from_py_with, ident) =
660 if let Some(from_py_with) = &value_arg.from_py_with().as_ref().map(|f| &f.value) {
661 let ident = syn::Ident::new("from_py_with", from_py_with.span());
662 let d = deprecated_from_py_with(from_py_with).unwrap_or_default();
663 (
664 quote_spanned! { from_py_with.span() =>
665 #d
666 let #ident = #from_py_with;
667 },
668 ident,
669 )
670 } else {
671 (quote!(), syn::Ident::new("dummy", Span::call_site()))
672 };
673
674 let arg = if let FnArg::Regular(arg) = &value_arg {
675 arg
676 } else {
677 bail_spanned!(value_arg.name().span() => "The #[setter] value argument can't be *args, **kwargs or `cancel_handle`.");
678 };
679
680 let extract = impl_regular_arg_param(
681 arg,
682 ident,
683 quote!(::std::option::Option::Some(_value.into())),
684 &mut holders,
685 ctx,
686 );
687
688 quote! {
689 #from_py_with
690 let _val = #extract;
691 }
692 }
693 PropertyType::Descriptor { field, .. } => {
694 let span = field.ty.span();
695 let name = field
696 .ident
697 .as_ref()
698 .map(|i| i.to_string())
699 .unwrap_or_default();
700
701 let holder = holders.push_holder(span);
702 let ty = field.ty.clone().elide_lifetimes();
703 quote! {
704 #[allow(unused_imports)]
705 use #pyo3_path::impl_::pyclass::Probe as _;
706 let _val = #pyo3_path::impl_::extract_argument::extract_argument::<
707 _,
708 { #pyo3_path::impl_::pyclass::IsOption::<#ty>::VALUE }
709 >(_value.into(), &mut #holder, #name)?;
710 }
711 }
712 };
713
714 let mut cfg_attrs = TokenStream::new();
715 if let PropertyType::Descriptor { field, .. } = &property_type {
716 for attr in field
717 .attrs
718 .iter()
719 .filter(|attr| attr.path().is_ident("cfg"))
720 {
721 attr.to_tokens(&mut cfg_attrs);
722 }
723 }
724
725 let init_holders = holders.init_holders(ctx);
726 let associated_method = quote! {
727 #cfg_attrs
728 unsafe fn #wrapper_ident(
729 py: #pyo3_path::Python<'_>,
730 _slf: *mut #pyo3_path::ffi::PyObject,
731 _value: *mut #pyo3_path::ffi::PyObject,
732 ) -> #pyo3_path::PyResult<::std::os::raw::c_int> {
733 use ::std::convert::Into;
734 let _value = #pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr_or_opt(py, &_value)
735 .ok_or_else(|| {
736 #pyo3_path::exceptions::PyAttributeError::new_err("can't delete attribute")
737 })?;
738 #init_holders
739 #extract
740 let result = #setter_impl;
741 #pyo3_path::impl_::callback::convert(py, result)
742 }
743 };
744
745 let method_def = quote! {
746 #cfg_attrs
747 #pyo3_path::impl_::pyclass::MaybeRuntimePyMethodDef::Static(
748 #pyo3_path::impl_::pymethods::PyMethodDefType::Setter(
749 #pyo3_path::impl_::pymethods::PySetterDef::new(
750 #python_name,
751 #cls::#wrapper_ident,
752 #doc
753 )
754 )
755 )
756 };
757
758 Ok(MethodAndMethodDef {
759 associated_method,
760 method_def,
761 })
762}
763
764fn impl_call_getter(
765 cls: &syn::Type,
766 spec: &FnSpec<'_>,
767 self_type: &SelfType,
768 holders: &mut Holders,
769 ctx: &Ctx,
770) -> syn::Result<TokenStream> {
771 let (py_arg: Option<&PyArg<'_>>, args: &[FnArg<'_>]) = split_off_python_arg(&spec.signature.arguments);
772 let slf: TokenStream = self_type.receiver(cls, error_mode:ExtractErrorMode::Raise, holders, ctx);
773 ensure_spanned!(
774 args.is_empty(),
775 args[0].ty().span() => "getter function can only have one argument (of type pyo3::Python)"
776 );
777
778 let name: &&Ident = &spec.name;
779 let fncall: TokenStream = if py_arg.is_some() {
780 quote!(#cls::#name(#slf, py))
781 } else {
782 quote!(#cls::#name(#slf))
783 };
784
785 Ok(fncall)
786}
787
788// Used here for PropertyType::Function, used in pyclass for descriptors.
789pub fn impl_py_getter_def(
790 cls: &syn::Type,
791 property_type: PropertyType<'_>,
792 ctx: &Ctx,
793) -> Result<MethodAndMethodDef> {
794 let Ctx { pyo3_path, .. } = ctx;
795 let python_name = property_type.null_terminated_python_name(ctx)?;
796 let doc = property_type.doc(ctx);
797
798 let mut cfg_attrs = TokenStream::new();
799 if let PropertyType::Descriptor { field, .. } = &property_type {
800 for attr in field
801 .attrs
802 .iter()
803 .filter(|attr| attr.path().is_ident("cfg"))
804 {
805 attr.to_tokens(&mut cfg_attrs);
806 }
807 }
808
809 let mut holders = Holders::new();
810 match property_type {
811 PropertyType::Descriptor {
812 field_index, field, ..
813 } => {
814 let ty = &field.ty;
815 let field = if let Some(ident) = &field.ident {
816 ident.to_token_stream()
817 } else {
818 syn::Index::from(field_index).to_token_stream()
819 };
820
821 // TODO: on MSRV 1.77+, we can use `::std::mem::offset_of!` here, and it should
822 // make it possible for the `MaybeRuntimePyMethodDef` to be a `Static` variant.
823 let generator = quote_spanned! { ty.span() =>
824 #pyo3_path::impl_::pyclass::MaybeRuntimePyMethodDef::Runtime(
825 || GENERATOR.generate(#python_name, #doc)
826 )
827 };
828 // This is separate so that the unsafe below does not inherit the span and thus does not
829 // trigger the `unsafe_code` lint
830 let method_def = quote! {
831 #cfg_attrs
832 {
833 #[allow(unused_imports)] // might not be used if all probes are positve
834 use #pyo3_path::impl_::pyclass::Probe;
835
836 struct Offset;
837 unsafe impl #pyo3_path::impl_::pyclass::OffsetCalculator<#cls, #ty> for Offset {
838 fn offset() -> usize {
839 #pyo3_path::impl_::pyclass::class_offset::<#cls>() +
840 #pyo3_path::impl_::pyclass::offset_of!(#cls, #field)
841 }
842 }
843
844 const GENERATOR: #pyo3_path::impl_::pyclass::PyClassGetterGenerator::<
845 #cls,
846 #ty,
847 Offset,
848 { #pyo3_path::impl_::pyclass::IsPyT::<#ty>::VALUE },
849 { #pyo3_path::impl_::pyclass::IsToPyObject::<#ty>::VALUE },
850 { #pyo3_path::impl_::pyclass::IsIntoPy::<#ty>::VALUE },
851 { #pyo3_path::impl_::pyclass::IsIntoPyObjectRef::<#ty>::VALUE },
852 { #pyo3_path::impl_::pyclass::IsIntoPyObject::<#ty>::VALUE },
853 > = unsafe { #pyo3_path::impl_::pyclass::PyClassGetterGenerator::new() };
854 #generator
855 }
856 };
857
858 Ok(MethodAndMethodDef {
859 associated_method: quote! {},
860 method_def,
861 })
862 }
863 // Forward to `IntoPyCallbackOutput`, to handle `#[getter]`s returning results.
864 PropertyType::Function {
865 spec, self_type, ..
866 } => {
867 let wrapper_ident = format_ident!("__pymethod_get_{}__", spec.name);
868 let call = impl_call_getter(cls, spec, self_type, &mut holders, ctx)?;
869 let body = quote! {
870 #pyo3_path::impl_::callback::convert(py, #call)
871 };
872
873 let init_holders = holders.init_holders(ctx);
874 let associated_method = quote! {
875 #cfg_attrs
876 unsafe fn #wrapper_ident(
877 py: #pyo3_path::Python<'_>,
878 _slf: *mut #pyo3_path::ffi::PyObject
879 ) -> #pyo3_path::PyResult<*mut #pyo3_path::ffi::PyObject> {
880 #init_holders
881 let result = #body;
882 result
883 }
884 };
885
886 let method_def = quote! {
887 #cfg_attrs
888 #pyo3_path::impl_::pyclass::MaybeRuntimePyMethodDef::Static(
889 #pyo3_path::impl_::pymethods::PyMethodDefType::Getter(
890 #pyo3_path::impl_::pymethods::PyGetterDef::new(
891 #python_name,
892 #cls::#wrapper_ident,
893 #doc
894 )
895 )
896 )
897 };
898
899 Ok(MethodAndMethodDef {
900 associated_method,
901 method_def,
902 })
903 }
904 }
905}
906
907/// Split an argument of pyo3::Python from the front of the arg list, if present
908fn split_off_python_arg<'a, 'b>(args: &'a [FnArg<'b>]) -> (Option<&'a PyArg<'b>>, &'a [FnArg<'b>]) {
909 match args {
910 [FnArg::Py(py: &PyArg<'_>), args: &[FnArg<'b>] @ ..] => (Some(py), args),
911 args: &'a [FnArg<'b>] => (None, args),
912 }
913}
914
915pub enum PropertyType<'a> {
916 Descriptor {
917 field_index: usize,
918 field: &'a syn::Field,
919 python_name: Option<&'a NameAttribute>,
920 renaming_rule: Option<RenamingRule>,
921 },
922 Function {
923 self_type: &'a SelfType,
924 spec: &'a FnSpec<'a>,
925 doc: PythonDoc,
926 },
927}
928
929impl PropertyType<'_> {
930 fn null_terminated_python_name(&self, ctx: &Ctx) -> Result<LitCStr> {
931 match self {
932 PropertyType::Descriptor {
933 field,
934 python_name,
935 renaming_rule,
936 ..
937 } => {
938 let name = match (python_name, &field.ident) {
939 (Some(name), _) => name.value.0.to_string(),
940 (None, Some(field_name)) => {
941 let mut name = field_name.unraw().to_string();
942 if let Some(rule) = renaming_rule {
943 name = utils::apply_renaming_rule(*rule, &name);
944 }
945 name
946 }
947 (None, None) => {
948 bail_spanned!(field.span() => "`get` and `set` with tuple struct fields require `name`");
949 }
950 };
951 let name = CString::new(name).unwrap();
952 Ok(LitCStr::new(name, field.span(), ctx))
953 }
954 PropertyType::Function { spec, .. } => Ok(spec.null_terminated_python_name(ctx)),
955 }
956 }
957
958 fn doc(&self, ctx: &Ctx) -> Cow<'_, PythonDoc> {
959 match self {
960 PropertyType::Descriptor { field, .. } => {
961 Cow::Owned(utils::get_doc(&field.attrs, None, ctx))
962 }
963 PropertyType::Function { doc, .. } => Cow::Borrowed(doc),
964 }
965 }
966}
967
968pub const __STR__: SlotDef = SlotDef::new(slot:"Py_tp_str", func_ty:"reprfunc");
969pub const __REPR__: SlotDef = SlotDef::new(slot:"Py_tp_repr", func_ty:"reprfunc");
970pub const __HASH__: SlotDef = SlotDefSlotDef::new(slot:"Py_tp_hash", func_ty:"hashfunc")
971 .ret_ty(Ty::PyHashT)
972 .return_conversion(TokenGenerator(
973 |Ctx { pyo3_path: &PyO3CratePath, .. }: &Ctx| quote! { #pyo3_path::impl_::callback::HashCallbackOutput },
974 ));
975pub const __RICHCMP__: SlotDef = SlotDefSlotDef::new(slot:"Py_tp_richcompare", func_ty:"richcmpfunc")
976 .extract_error_mode(ExtractErrorMode::NotImplemented)
977 .arguments(&[Ty::Object, Ty::CompareOp]);
978const __GET__: SlotDef = SlotDefSlotDef::new(slot:"Py_tp_descr_get", func_ty:"descrgetfunc")
979 .arguments(&[Ty::MaybeNullObject, Ty::MaybeNullObject]);
980const __ITER__: SlotDef = SlotDef::new(slot:"Py_tp_iter", func_ty:"getiterfunc");
981const __NEXT__: SlotDef = SlotDef::new("Py_tp_iternext", "iternextfunc")
982 .return_specialized_conversion(
983 traits:TokenGenerator(|_| quote! { IterBaseKind, IterOptionKind, IterResultOptionKind }),
984 tag:TokenGenerator(|_| quote! { iter_tag }),
985 );
986const __AWAIT__: SlotDef = SlotDef::new(slot:"Py_am_await", func_ty:"unaryfunc");
987const __AITER__: SlotDef = SlotDef::new(slot:"Py_am_aiter", func_ty:"unaryfunc");
988const __ANEXT__: SlotDef = SlotDef::new("Py_am_anext", "unaryfunc").return_specialized_conversion(
989 traits:TokenGenerator(
990 |_| quote! { AsyncIterBaseKind, AsyncIterOptionKind, AsyncIterResultOptionKind },
991 ),
992 tag:TokenGenerator(|_| quote! { async_iter_tag }),
993);
994pub const __LEN__: SlotDef = SlotDef::new(slot:"Py_mp_length", func_ty:"lenfunc").ret_ty(Ty::PySsizeT);
995const __CONTAINS__: SlotDef = SlotDefSlotDef::new(slot:"Py_sq_contains", func_ty:"objobjproc")
996 .arguments(&[Ty::Object])
997 .ret_ty(Ty::Int);
998const __CONCAT__: SlotDef = SlotDef::new(slot:"Py_sq_concat", func_ty:"binaryfunc").arguments(&[Ty::Object]);
999const __REPEAT__: SlotDef = SlotDef::new(slot:"Py_sq_repeat", func_ty:"ssizeargfunc").arguments(&[Ty::PySsizeT]);
1000const __INPLACE_CONCAT__: SlotDef =
1001 SlotDef::new(slot:"Py_sq_concat", func_ty:"binaryfunc").arguments(&[Ty::Object]);
1002const __INPLACE_REPEAT__: SlotDef =
1003 SlotDef::new(slot:"Py_sq_repeat", func_ty:"ssizeargfunc").arguments(&[Ty::PySsizeT]);
1004pub const __GETITEM__: SlotDef =
1005 SlotDef::new(slot:"Py_mp_subscript", func_ty:"binaryfunc").arguments(&[Ty::Object]);
1006
1007const __POS__: SlotDef = SlotDef::new(slot:"Py_nb_positive", func_ty:"unaryfunc");
1008const __NEG__: SlotDef = SlotDef::new(slot:"Py_nb_negative", func_ty:"unaryfunc");
1009const __ABS__: SlotDef = SlotDef::new(slot:"Py_nb_absolute", func_ty:"unaryfunc");
1010const __INVERT__: SlotDef = SlotDef::new(slot:"Py_nb_invert", func_ty:"unaryfunc");
1011const __INDEX__: SlotDef = SlotDef::new(slot:"Py_nb_index", func_ty:"unaryfunc");
1012pub const __INT__: SlotDef = SlotDef::new(slot:"Py_nb_int", func_ty:"unaryfunc");
1013const __FLOAT__: SlotDef = SlotDef::new(slot:"Py_nb_float", func_ty:"unaryfunc");
1014const __BOOL__: SlotDef = SlotDef::new(slot:"Py_nb_bool", func_ty:"inquiry").ret_ty(Ty::Int);
1015
1016const __IADD__: SlotDef = SlotDefSlotDef::new(slot:"Py_nb_inplace_add", func_ty:"binaryfunc")
1017 .arguments(&[Ty::Object])
1018 .extract_error_mode(ExtractErrorMode::NotImplemented)
1019 .return_self();
1020const __ISUB__: SlotDef = SlotDefSlotDef::new(slot:"Py_nb_inplace_subtract", func_ty:"binaryfunc")
1021 .arguments(&[Ty::Object])
1022 .extract_error_mode(ExtractErrorMode::NotImplemented)
1023 .return_self();
1024const __IMUL__: SlotDef = SlotDefSlotDef::new(slot:"Py_nb_inplace_multiply", func_ty:"binaryfunc")
1025 .arguments(&[Ty::Object])
1026 .extract_error_mode(ExtractErrorMode::NotImplemented)
1027 .return_self();
1028const __IMATMUL__: SlotDef = SlotDefSlotDef::new(slot:"Py_nb_inplace_matrix_multiply", func_ty:"binaryfunc")
1029 .arguments(&[Ty::Object])
1030 .extract_error_mode(ExtractErrorMode::NotImplemented)
1031 .return_self();
1032const __ITRUEDIV__: SlotDef = SlotDefSlotDef::new(slot:"Py_nb_inplace_true_divide", func_ty:"binaryfunc")
1033 .arguments(&[Ty::Object])
1034 .extract_error_mode(ExtractErrorMode::NotImplemented)
1035 .return_self();
1036const __IFLOORDIV__: SlotDef = SlotDefSlotDef::new(slot:"Py_nb_inplace_floor_divide", func_ty:"binaryfunc")
1037 .arguments(&[Ty::Object])
1038 .extract_error_mode(ExtractErrorMode::NotImplemented)
1039 .return_self();
1040const __IMOD__: SlotDef = SlotDefSlotDef::new(slot:"Py_nb_inplace_remainder", func_ty:"binaryfunc")
1041 .arguments(&[Ty::Object])
1042 .extract_error_mode(ExtractErrorMode::NotImplemented)
1043 .return_self();
1044const __IPOW__: SlotDef = SlotDefSlotDef::new(slot:"Py_nb_inplace_power", func_ty:"ipowfunc")
1045 .arguments(&[Ty::Object, Ty::IPowModulo])
1046 .extract_error_mode(ExtractErrorMode::NotImplemented)
1047 .return_self();
1048const __ILSHIFT__: SlotDef = SlotDefSlotDef::new(slot:"Py_nb_inplace_lshift", func_ty:"binaryfunc")
1049 .arguments(&[Ty::Object])
1050 .extract_error_mode(ExtractErrorMode::NotImplemented)
1051 .return_self();
1052const __IRSHIFT__: SlotDef = SlotDefSlotDef::new(slot:"Py_nb_inplace_rshift", func_ty:"binaryfunc")
1053 .arguments(&[Ty::Object])
1054 .extract_error_mode(ExtractErrorMode::NotImplemented)
1055 .return_self();
1056const __IAND__: SlotDef = SlotDefSlotDef::new(slot:"Py_nb_inplace_and", func_ty:"binaryfunc")
1057 .arguments(&[Ty::Object])
1058 .extract_error_mode(ExtractErrorMode::NotImplemented)
1059 .return_self();
1060const __IXOR__: SlotDef = SlotDefSlotDef::new(slot:"Py_nb_inplace_xor", func_ty:"binaryfunc")
1061 .arguments(&[Ty::Object])
1062 .extract_error_mode(ExtractErrorMode::NotImplemented)
1063 .return_self();
1064const __IOR__: SlotDef = SlotDefSlotDef::new(slot:"Py_nb_inplace_or", func_ty:"binaryfunc")
1065 .arguments(&[Ty::Object])
1066 .extract_error_mode(ExtractErrorMode::NotImplemented)
1067 .return_self();
1068const __GETBUFFER__: SlotDef = SlotDefSlotDef::new(slot:"Py_bf_getbuffer", func_ty:"getbufferproc")
1069 .arguments(&[Ty::PyBuffer, Ty::Int])
1070 .ret_ty(Ty::Int)
1071 .require_unsafe();
1072const __RELEASEBUFFER__: SlotDef = SlotDefSlotDef::new(slot:"Py_bf_releasebuffer", func_ty:"releasebufferproc")
1073 .arguments(&[Ty::PyBuffer])
1074 .ret_ty(Ty::Void)
1075 .require_unsafe();
1076const __CLEAR__: SlotDef = SlotDefSlotDef::new(slot:"Py_tp_clear", func_ty:"inquiry")
1077 .arguments(&[])
1078 .ret_ty(Ty::Int);
1079
1080#[derive(Clone, Copy)]
1081enum Ty {
1082 Object,
1083 MaybeNullObject,
1084 NonNullObject,
1085 IPowModulo,
1086 CompareOp,
1087 Int,
1088 PyHashT,
1089 PySsizeT,
1090 Void,
1091 PyBuffer,
1092}
1093
1094impl Ty {
1095 fn ffi_type(self, ctx: &Ctx) -> TokenStream {
1096 let Ctx {
1097 pyo3_path,
1098 output_span,
1099 } = ctx;
1100 let pyo3_path = pyo3_path.to_tokens_spanned(*output_span);
1101 match self {
1102 Ty::Object | Ty::MaybeNullObject => quote! { *mut #pyo3_path::ffi::PyObject },
1103 Ty::NonNullObject => quote! { ::std::ptr::NonNull<#pyo3_path::ffi::PyObject> },
1104 Ty::IPowModulo => quote! { #pyo3_path::impl_::pymethods::IPowModulo },
1105 Ty::Int | Ty::CompareOp => quote! { ::std::os::raw::c_int },
1106 Ty::PyHashT => quote! { #pyo3_path::ffi::Py_hash_t },
1107 Ty::PySsizeT => quote! { #pyo3_path::ffi::Py_ssize_t },
1108 Ty::Void => quote! { () },
1109 Ty::PyBuffer => quote! { *mut #pyo3_path::ffi::Py_buffer },
1110 }
1111 }
1112
1113 fn extract(
1114 self,
1115 ident: &syn::Ident,
1116 arg: &FnArg<'_>,
1117 extract_error_mode: ExtractErrorMode,
1118 holders: &mut Holders,
1119 ctx: &Ctx,
1120 ) -> TokenStream {
1121 let Ctx { pyo3_path, .. } = ctx;
1122 match self {
1123 Ty::Object => extract_object(
1124 extract_error_mode,
1125 holders,
1126 arg,
1127 quote! { #ident },
1128 ctx
1129 ),
1130 Ty::MaybeNullObject => extract_object(
1131 extract_error_mode,
1132 holders,
1133 arg,
1134 quote! {
1135 if #ident.is_null() {
1136 #pyo3_path::ffi::Py_None()
1137 } else {
1138 #ident
1139 }
1140 },
1141 ctx
1142 ),
1143 Ty::NonNullObject => extract_object(
1144 extract_error_mode,
1145 holders,
1146 arg,
1147 quote! { #ident.as_ptr() },
1148 ctx
1149 ),
1150 Ty::IPowModulo => extract_object(
1151 extract_error_mode,
1152 holders,
1153 arg,
1154 quote! { #ident.as_ptr() },
1155 ctx
1156 ),
1157 Ty::CompareOp => extract_error_mode.handle_error(
1158 quote! {
1159 #pyo3_path::class::basic::CompareOp::from_raw(#ident)
1160 .ok_or_else(|| #pyo3_path::exceptions::PyValueError::new_err("invalid comparison operator"))
1161 },
1162 ctx
1163 ),
1164 Ty::PySsizeT => {
1165 let ty = arg.ty();
1166 extract_error_mode.handle_error(
1167 quote! {
1168 ::std::convert::TryInto::<#ty>::try_into(#ident).map_err(|e| #pyo3_path::exceptions::PyValueError::new_err(e.to_string()))
1169 },
1170 ctx
1171 )
1172 }
1173 // Just pass other types through unmodified
1174 Ty::PyBuffer | Ty::Int | Ty::PyHashT | Ty::Void => quote! { #ident },
1175 }
1176 }
1177}
1178
1179fn extract_object(
1180 extract_error_mode: ExtractErrorMode,
1181 holders: &mut Holders,
1182 arg: &FnArg<'_>,
1183 source_ptr: TokenStream,
1184 ctx: &Ctx,
1185) -> TokenStream {
1186 let Ctx { pyo3_path, .. } = ctx;
1187 let name = arg.name().unraw().to_string();
1188
1189 let extract = if let Some(FromPyWithAttribute {
1190 kw,
1191 value: extractor,
1192 }) = arg.from_py_with()
1193 {
1194 let extractor = quote_spanned! { kw.span =>
1195 { let from_py_with: fn(_) -> _ = #extractor; from_py_with }
1196 };
1197
1198 quote! {
1199 #pyo3_path::impl_::extract_argument::from_py_with(
1200 unsafe { #pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(py, &#source_ptr).0 },
1201 #name,
1202 #extractor,
1203 )
1204 }
1205 } else {
1206 let holder = holders.push_holder(Span::call_site());
1207 let ty = arg.ty().clone().elide_lifetimes();
1208 quote! {{
1209 #[allow(unused_imports)]
1210 use #pyo3_path::impl_::pyclass::Probe as _;
1211 #pyo3_path::impl_::extract_argument::extract_argument::<
1212 _,
1213 { #pyo3_path::impl_::pyclass::IsOption::<#ty>::VALUE }
1214 >(
1215 unsafe { #pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(py, &#source_ptr).0 },
1216 &mut #holder,
1217 #name
1218 )
1219 }}
1220 };
1221
1222 let extracted = extract_error_mode.handle_error(extract, ctx);
1223 quote!(#extracted)
1224}
1225
1226enum ReturnMode {
1227 ReturnSelf,
1228 Conversion(TokenGenerator),
1229 SpecializedConversion(TokenGenerator, TokenGenerator),
1230}
1231
1232impl ReturnMode {
1233 fn return_call_output(&self, call: TokenStream, ctx: &Ctx) -> TokenStream {
1234 let Ctx { pyo3_path, .. } = ctx;
1235 match self {
1236 ReturnMode::Conversion(conversion) => {
1237 let conversion = TokenGeneratorCtx(*conversion, ctx);
1238 quote! {
1239 let _result: #pyo3_path::PyResult<#conversion> = #pyo3_path::impl_::callback::convert(py, #call);
1240 #pyo3_path::impl_::callback::convert(py, _result)
1241 }
1242 }
1243 ReturnMode::SpecializedConversion(traits, tag) => {
1244 let traits = TokenGeneratorCtx(*traits, ctx);
1245 let tag = TokenGeneratorCtx(*tag, ctx);
1246 quote! {
1247 let _result = #call;
1248 use #pyo3_path::impl_::pymethods::{#traits};
1249 (&_result).#tag().convert(py, _result)
1250 }
1251 }
1252 ReturnMode::ReturnSelf => quote! {
1253 let _result: #pyo3_path::PyResult<()> = #pyo3_path::impl_::callback::convert(py, #call);
1254 _result?;
1255 #pyo3_path::ffi::Py_XINCREF(_raw_slf);
1256 ::std::result::Result::Ok(_raw_slf)
1257 },
1258 }
1259 }
1260}
1261
1262pub struct SlotDef {
1263 slot: StaticIdent,
1264 func_ty: StaticIdent,
1265 arguments: &'static [Ty],
1266 ret_ty: Ty,
1267 extract_error_mode: ExtractErrorMode,
1268 return_mode: Option<ReturnMode>,
1269 require_unsafe: bool,
1270}
1271
1272const NO_ARGUMENTS: &[Ty] = &[];
1273
1274impl SlotDef {
1275 const fn new(slot: &'static str, func_ty: &'static str) -> Self {
1276 SlotDef {
1277 slot: StaticIdent(slot),
1278 func_ty: StaticIdent(func_ty),
1279 arguments: NO_ARGUMENTS,
1280 ret_ty: Ty::Object,
1281 extract_error_mode: ExtractErrorMode::Raise,
1282 return_mode: None,
1283 require_unsafe: false,
1284 }
1285 }
1286
1287 const fn arguments(mut self, arguments: &'static [Ty]) -> Self {
1288 self.arguments = arguments;
1289 self
1290 }
1291
1292 const fn ret_ty(mut self, ret_ty: Ty) -> Self {
1293 self.ret_ty = ret_ty;
1294 self
1295 }
1296
1297 const fn return_conversion(mut self, return_conversion: TokenGenerator) -> Self {
1298 self.return_mode = Some(ReturnMode::Conversion(return_conversion));
1299 self
1300 }
1301
1302 const fn return_specialized_conversion(
1303 mut self,
1304 traits: TokenGenerator,
1305 tag: TokenGenerator,
1306 ) -> Self {
1307 self.return_mode = Some(ReturnMode::SpecializedConversion(traits, tag));
1308 self
1309 }
1310
1311 const fn extract_error_mode(mut self, extract_error_mode: ExtractErrorMode) -> Self {
1312 self.extract_error_mode = extract_error_mode;
1313 self
1314 }
1315
1316 const fn return_self(mut self) -> Self {
1317 self.return_mode = Some(ReturnMode::ReturnSelf);
1318 self
1319 }
1320
1321 const fn require_unsafe(mut self) -> Self {
1322 self.require_unsafe = true;
1323 self
1324 }
1325
1326 pub fn generate_type_slot(
1327 &self,
1328 cls: &syn::Type,
1329 spec: &FnSpec<'_>,
1330 method_name: &str,
1331 ctx: &Ctx,
1332 ) -> Result<MethodAndSlotDef> {
1333 let Ctx { pyo3_path, .. } = ctx;
1334 let SlotDef {
1335 slot,
1336 func_ty,
1337 arguments,
1338 extract_error_mode,
1339 ret_ty,
1340 return_mode,
1341 require_unsafe,
1342 } = self;
1343 if *require_unsafe {
1344 ensure_spanned!(
1345 spec.unsafety.is_some(),
1346 spec.name.span() => format!("`{}` must be `unsafe fn`", method_name)
1347 );
1348 }
1349 let arg_types: &Vec<_> = &arguments.iter().map(|arg| arg.ffi_type(ctx)).collect();
1350 let arg_idents: &Vec<_> = &(0..arguments.len())
1351 .map(|i| format_ident!("arg{}", i))
1352 .collect();
1353 let wrapper_ident = format_ident!("__pymethod_{}__", method_name);
1354 let ret_ty = ret_ty.ffi_type(ctx);
1355 let mut holders = Holders::new();
1356 let body = generate_method_body(
1357 cls,
1358 spec,
1359 arguments,
1360 *extract_error_mode,
1361 &mut holders,
1362 return_mode.as_ref(),
1363 ctx,
1364 )?;
1365 let name = spec.name;
1366 let holders = holders.init_holders(ctx);
1367 let associated_method = quote! {
1368 #[allow(non_snake_case)]
1369 unsafe fn #wrapper_ident(
1370 py: #pyo3_path::Python<'_>,
1371 _raw_slf: *mut #pyo3_path::ffi::PyObject,
1372 #(#arg_idents: #arg_types),*
1373 ) -> #pyo3_path::PyResult<#ret_ty> {
1374 let function = #cls::#name; // Shadow the method name to avoid #3017
1375 let _slf = _raw_slf;
1376 #holders
1377 #body
1378 }
1379 };
1380 let slot_def = quote! {{
1381 unsafe extern "C" fn trampoline(
1382 _slf: *mut #pyo3_path::ffi::PyObject,
1383 #(#arg_idents: #arg_types),*
1384 ) -> #ret_ty
1385 {
1386 #pyo3_path::impl_::trampoline:: #func_ty (
1387 _slf,
1388 #(#arg_idents,)*
1389 #cls::#wrapper_ident
1390 )
1391 }
1392
1393 #pyo3_path::ffi::PyType_Slot {
1394 slot: #pyo3_path::ffi::#slot,
1395 pfunc: trampoline as #pyo3_path::ffi::#func_ty as _
1396 }
1397 }};
1398 Ok(MethodAndSlotDef {
1399 associated_method,
1400 slot_def,
1401 })
1402 }
1403}
1404
1405fn generate_method_body(
1406 cls: &syn::Type,
1407 spec: &FnSpec<'_>,
1408 arguments: &[Ty],
1409 extract_error_mode: ExtractErrorMode,
1410 holders: &mut Holders,
1411 return_mode: Option<&ReturnMode>,
1412 ctx: &Ctx,
1413) -> Result<TokenStream> {
1414 let Ctx { pyo3_path: &PyO3CratePath, .. } = ctx;
1415 let self_arg: Option = spec
1416 .tp
1417 .self_arg(cls:Some(cls), extract_error_mode, holders, ctx);
1418 let rust_name: &Ident = spec.name;
1419 let args: Vec = extract_proto_arguments(spec, proto_args:arguments, extract_error_mode, holders, ctx)?;
1420 let call: TokenStream = quote! { #cls::#rust_name(#self_arg #(#args),*) };
1421 Ok(if let Some(return_mode: &ReturnMode) = return_mode {
1422 return_mode.return_call_output(call, ctx)
1423 } else {
1424 quote! {
1425 let result = #call;
1426 #pyo3_path::impl_::callback::convert(py, result)
1427 }
1428 })
1429}
1430
1431struct SlotFragmentDef {
1432 fragment: &'static str,
1433 arguments: &'static [Ty],
1434 extract_error_mode: ExtractErrorMode,
1435 ret_ty: Ty,
1436}
1437
1438impl SlotFragmentDef {
1439 const fn new(fragment: &'static str, arguments: &'static [Ty]) -> Self {
1440 SlotFragmentDef {
1441 fragment,
1442 arguments,
1443 extract_error_mode: ExtractErrorMode::Raise,
1444 ret_ty: Ty::Void,
1445 }
1446 }
1447
1448 const fn extract_error_mode(mut self, extract_error_mode: ExtractErrorMode) -> Self {
1449 self.extract_error_mode = extract_error_mode;
1450 self
1451 }
1452
1453 const fn ret_ty(mut self, ret_ty: Ty) -> Self {
1454 self.ret_ty = ret_ty;
1455 self
1456 }
1457
1458 fn generate_pyproto_fragment(
1459 &self,
1460 cls: &syn::Type,
1461 spec: &FnSpec<'_>,
1462 ctx: &Ctx,
1463 ) -> Result<TokenStream> {
1464 let Ctx { pyo3_path, .. } = ctx;
1465 let SlotFragmentDef {
1466 fragment,
1467 arguments,
1468 extract_error_mode,
1469 ret_ty,
1470 } = self;
1471 let fragment_trait = format_ident!("PyClass{}SlotFragment", fragment);
1472 let method = syn::Ident::new(fragment, Span::call_site());
1473 let wrapper_ident = format_ident!("__pymethod_{}__", fragment);
1474 let arg_types: &Vec<_> = &arguments.iter().map(|arg| arg.ffi_type(ctx)).collect();
1475 let arg_idents: &Vec<_> = &(0..arguments.len())
1476 .map(|i| format_ident!("arg{}", i))
1477 .collect();
1478 let mut holders = Holders::new();
1479 let body = generate_method_body(
1480 cls,
1481 spec,
1482 arguments,
1483 *extract_error_mode,
1484 &mut holders,
1485 None,
1486 ctx,
1487 )?;
1488 let ret_ty = ret_ty.ffi_type(ctx);
1489 let holders = holders.init_holders(ctx);
1490 Ok(quote! {
1491 impl #cls {
1492 #[allow(non_snake_case)]
1493 unsafe fn #wrapper_ident(
1494 py: #pyo3_path::Python,
1495 _raw_slf: *mut #pyo3_path::ffi::PyObject,
1496 #(#arg_idents: #arg_types),*
1497 ) -> #pyo3_path::PyResult<#ret_ty> {
1498 let _slf = _raw_slf;
1499 #holders
1500 #body
1501 }
1502 }
1503
1504 impl #pyo3_path::impl_::pyclass::#fragment_trait<#cls> for #pyo3_path::impl_::pyclass::PyClassImplCollector<#cls> {
1505
1506 #[inline]
1507 unsafe fn #method(
1508 self,
1509 py: #pyo3_path::Python,
1510 _raw_slf: *mut #pyo3_path::ffi::PyObject,
1511 #(#arg_idents: #arg_types),*
1512 ) -> #pyo3_path::PyResult<#ret_ty> {
1513 #cls::#wrapper_ident(py, _raw_slf, #(#arg_idents),*)
1514 }
1515 }
1516 })
1517 }
1518}
1519
1520const __GETATTRIBUTE__: SlotFragmentDef =
1521 SlotFragmentDef::new(fragment:"__getattribute__", &[Ty::Object]).ret_ty(Ty::Object);
1522const __GETATTR__: SlotFragmentDef =
1523 SlotFragmentDef::new(fragment:"__getattr__", &[Ty::Object]).ret_ty(Ty::Object);
1524const __SETATTR__: SlotFragmentDef =
1525 SlotFragmentDef::new(fragment:"__setattr__", &[Ty::Object, Ty::NonNullObject]);
1526const __DELATTR__: SlotFragmentDef = SlotFragmentDef::new(fragment:"__delattr__", &[Ty::Object]);
1527const __SET__: SlotFragmentDef = SlotFragmentDef::new(fragment:"__set__", &[Ty::Object, Ty::NonNullObject]);
1528const __DELETE__: SlotFragmentDef = SlotFragmentDef::new(fragment:"__delete__", &[Ty::Object]);
1529const __SETITEM__: SlotFragmentDef =
1530 SlotFragmentDef::new(fragment:"__setitem__", &[Ty::Object, Ty::NonNullObject]);
1531const __DELITEM__: SlotFragmentDef = SlotFragmentDef::new(fragment:"__delitem__", &[Ty::Object]);
1532
1533macro_rules! binary_num_slot_fragment_def {
1534 ($ident:ident, $name:literal) => {
1535 const $ident: SlotFragmentDef = SlotFragmentDef::new($name, &[Ty::Object])
1536 .extract_error_mode(ExtractErrorMode::NotImplemented)
1537 .ret_ty(Ty::Object);
1538 };
1539}
1540
1541binary_num_slot_fragment_def!(__ADD__, "__add__");
1542binary_num_slot_fragment_def!(__RADD__, "__radd__");
1543binary_num_slot_fragment_def!(__SUB__, "__sub__");
1544binary_num_slot_fragment_def!(__RSUB__, "__rsub__");
1545binary_num_slot_fragment_def!(__MUL__, "__mul__");
1546binary_num_slot_fragment_def!(__RMUL__, "__rmul__");
1547binary_num_slot_fragment_def!(__MATMUL__, "__matmul__");
1548binary_num_slot_fragment_def!(__RMATMUL__, "__rmatmul__");
1549binary_num_slot_fragment_def!(__FLOORDIV__, "__floordiv__");
1550binary_num_slot_fragment_def!(__RFLOORDIV__, "__rfloordiv__");
1551binary_num_slot_fragment_def!(__TRUEDIV__, "__truediv__");
1552binary_num_slot_fragment_def!(__RTRUEDIV__, "__rtruediv__");
1553binary_num_slot_fragment_def!(__DIVMOD__, "__divmod__");
1554binary_num_slot_fragment_def!(__RDIVMOD__, "__rdivmod__");
1555binary_num_slot_fragment_def!(__MOD__, "__mod__");
1556binary_num_slot_fragment_def!(__RMOD__, "__rmod__");
1557binary_num_slot_fragment_def!(__LSHIFT__, "__lshift__");
1558binary_num_slot_fragment_def!(__RLSHIFT__, "__rlshift__");
1559binary_num_slot_fragment_def!(__RSHIFT__, "__rshift__");
1560binary_num_slot_fragment_def!(__RRSHIFT__, "__rrshift__");
1561binary_num_slot_fragment_def!(__AND__, "__and__");
1562binary_num_slot_fragment_def!(__RAND__, "__rand__");
1563binary_num_slot_fragment_def!(__XOR__, "__xor__");
1564binary_num_slot_fragment_def!(__RXOR__, "__rxor__");
1565binary_num_slot_fragment_def!(__OR__, "__or__");
1566binary_num_slot_fragment_def!(__ROR__, "__ror__");
1567
1568const __POW__: SlotFragmentDef = SlotFragmentDefSlotFragmentDef::new(fragment:"__pow__", &[Ty::Object, Ty::Object])
1569 .extract_error_mode(ExtractErrorMode::NotImplemented)
1570 .ret_ty(Ty::Object);
1571const __RPOW__: SlotFragmentDef = SlotFragmentDefSlotFragmentDef::new(fragment:"__rpow__", &[Ty::Object, Ty::Object])
1572 .extract_error_mode(ExtractErrorMode::NotImplemented)
1573 .ret_ty(Ty::Object);
1574
1575const __LT__: SlotFragmentDef = SlotFragmentDefSlotFragmentDef::new(fragment:"__lt__", &[Ty::Object])
1576 .extract_error_mode(ExtractErrorMode::NotImplemented)
1577 .ret_ty(Ty::Object);
1578const __LE__: SlotFragmentDef = SlotFragmentDefSlotFragmentDef::new(fragment:"__le__", &[Ty::Object])
1579 .extract_error_mode(ExtractErrorMode::NotImplemented)
1580 .ret_ty(Ty::Object);
1581const __EQ__: SlotFragmentDef = SlotFragmentDefSlotFragmentDef::new(fragment:"__eq__", &[Ty::Object])
1582 .extract_error_mode(ExtractErrorMode::NotImplemented)
1583 .ret_ty(Ty::Object);
1584const __NE__: SlotFragmentDef = SlotFragmentDefSlotFragmentDef::new(fragment:"__ne__", &[Ty::Object])
1585 .extract_error_mode(ExtractErrorMode::NotImplemented)
1586 .ret_ty(Ty::Object);
1587const __GT__: SlotFragmentDef = SlotFragmentDefSlotFragmentDef::new(fragment:"__gt__", &[Ty::Object])
1588 .extract_error_mode(ExtractErrorMode::NotImplemented)
1589 .ret_ty(Ty::Object);
1590const __GE__: SlotFragmentDef = SlotFragmentDefSlotFragmentDef::new(fragment:"__ge__", &[Ty::Object])
1591 .extract_error_mode(ExtractErrorMode::NotImplemented)
1592 .ret_ty(Ty::Object);
1593
1594fn extract_proto_arguments(
1595 spec: &FnSpec<'_>,
1596 proto_args: &[Ty],
1597 extract_error_mode: ExtractErrorMode,
1598 holders: &mut Holders,
1599 ctx: &Ctx,
1600) -> Result<Vec<TokenStream>> {
1601 let mut args: Vec = Vec::with_capacity(spec.signature.arguments.len());
1602 let mut non_python_args: usize = 0;
1603
1604 for arg: &FnArg<'_> in &spec.signature.arguments {
1605 if let FnArg::Py(..) = arg {
1606 args.push(quote! { py });
1607 } else {
1608 let ident: Ident = syn::Ident::new(&format!("arg{}", non_python_args), Span::call_site());
1609 let conversions: TokenStream = proto_args&Ty.get(non_python_args)
1610 .ok_or_else(|| err_spanned!(arg.ty().span() => format!("Expected at most {} non-python arguments", proto_args.len())))?
1611 .extract(&ident, arg, extract_error_mode, holders, ctx);
1612 non_python_args += 1;
1613 args.push(conversions);
1614 }
1615 }
1616
1617 if non_python_args != proto_args.len() {
1618 bail_spanned!(spec.name.span() => format!("Expected {} arguments, got {}", proto_args.len(), non_python_args));
1619 }
1620 Ok(args)
1621}
1622
1623struct StaticIdent(&'static str);
1624
1625impl ToTokens for StaticIdent {
1626 fn to_tokens(&self, tokens: &mut TokenStream) {
1627 syn::Ident::new(self.0, Span::call_site()).to_tokens(tokens)
1628 }
1629}
1630
1631#[derive(Clone, Copy)]
1632struct TokenGenerator(fn(&Ctx) -> TokenStream);
1633
1634struct TokenGeneratorCtx<'ctx>(TokenGenerator, &'ctx Ctx);
1635
1636impl ToTokens for TokenGeneratorCtx<'_> {
1637 fn to_tokens(&self, tokens: &mut TokenStream) {
1638 let Self(TokenGenerator(gen: &fn(&Ctx) -> TokenStream), ctx: &&Ctx) = self;
1639 (gen)(ctx).to_tokens(tokens)
1640 }
1641}
1642