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