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