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