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