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