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