Skip to content

Commit 42c0cc6

Browse files
committed
Replace Self with the current class type and ignore some magic methods
1 parent ba8dc02 commit 42c0cc6

File tree

4 files changed

+99
-9
lines changed

4 files changed

+99
-9
lines changed

pyo3-introspection/src/introspection.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ enum Chunk {
364364
#[serde(default)]
365365
id: Option<String>,
366366
name: String,
367-
arguments: ChunkArguments,
367+
arguments: Box<ChunkArguments>,
368368
#[serde(default)]
369369
parent: Option<String>,
370370
#[serde(default)]

pyo3-macros-backend/src/introspection.rs

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ pub fn function_introspection_code(
9191
("name", IntrospectionNode::String(name.into())),
9292
(
9393
"arguments",
94-
arguments_introspection_data(signature, first_argument),
94+
arguments_introspection_data(signature, first_argument, parent),
9595
),
9696
]);
9797
if let Some(ident) = ident {
@@ -119,6 +119,7 @@ pub fn function_introspection_code(
119119
fn arguments_introspection_data<'a>(
120120
signature: &'a FunctionSignature<'a>,
121121
first_argument: Option<&'a str>,
122+
class_type: Option<&Type>,
122123
) -> IntrospectionNode<'a> {
123124
let mut argument_desc = signature.arguments.iter().filter_map(|arg| {
124125
if let FnArg::Regular(arg) = arg {
@@ -151,7 +152,7 @@ fn arguments_introspection_data<'a>(
151152
} else {
152153
panic!("Less arguments than in python signature");
153154
};
154-
let arg = argument_introspection_data(param, arg_desc);
155+
let arg = argument_introspection_data(param, arg_desc, class_type);
155156
if i < signature.python_signature.positional_only_parameters {
156157
posonlyargs.push(arg);
157158
} else {
@@ -171,7 +172,7 @@ fn arguments_introspection_data<'a>(
171172
} else {
172173
panic!("Less arguments than in python signature");
173174
};
174-
kwonlyargs.push(argument_introspection_data(param, arg_desc));
175+
kwonlyargs.push(argument_introspection_data(param, arg_desc, class_type));
175176
}
176177

177178
if let Some(param) = &signature.python_signature.kwargs {
@@ -206,6 +207,7 @@ fn arguments_introspection_data<'a>(
206207
fn argument_introspection_data<'a>(
207208
name: &'a str,
208209
desc: &'a RegularArg<'_>,
210+
class_type: Option<&Type>,
209211
) -> IntrospectionNode<'a> {
210212
let mut params: HashMap<_, _> = [("name", IntrospectionNode::String(name.into()))].into();
211213
if desc.default_value.is_some() {
@@ -218,7 +220,11 @@ fn argument_introspection_data<'a>(
218220
// If from_py_with is set we don't know anything on the input type
219221
if let Some(ty) = desc.option_wrapped_type {
220222
// Special case to properly generate a `T | None` annotation
221-
let ty = ty.clone().elide_lifetimes();
223+
let mut ty = ty.clone();
224+
if let Some(class_type) = class_type {
225+
replace_self(&mut ty, class_type);
226+
}
227+
ty = ty.elide_lifetimes();
222228
params.insert(
223229
"annotation",
224230
IntrospectionNode::InputType {
@@ -227,7 +233,11 @@ fn argument_introspection_data<'a>(
227233
},
228234
);
229235
} else {
230-
let ty = desc.ty.clone().elide_lifetimes();
236+
let mut ty = desc.ty.clone();
237+
if let Some(class_type) = class_type {
238+
replace_self(&mut ty, class_type);
239+
}
240+
ty = ty.elide_lifetimes();
231241
params.insert(
232242
"annotation",
233243
IntrospectionNode::InputType {
@@ -417,3 +427,64 @@ fn ident_to_type(ident: &Ident) -> Cow<'static, Type> {
417427
.into(),
418428
)
419429
}
430+
431+
// Replace Self in types with the given type
432+
fn replace_self(ty: &mut Type, self_target: &Type) {
433+
match ty {
434+
syn::Type::Path(type_path) => {
435+
if type_path.qself.is_none()
436+
&& type_path.path.segments.len() == 1
437+
&& type_path.path.segments[0].ident == "Self"
438+
&& type_path.path.segments[0].arguments.is_empty()
439+
{
440+
// It is Self
441+
*ty = self_target.clone();
442+
return;
443+
}
444+
445+
// We look recursively
446+
if let Some(qself) = &mut type_path.qself {
447+
replace_self(&mut qself.ty, self_target)
448+
}
449+
for seg in &mut type_path.path.segments {
450+
if let syn::PathArguments::AngleBracketed(args) = &mut seg.arguments {
451+
for generic_arg in &mut args.args {
452+
match generic_arg {
453+
syn::GenericArgument::Type(ty) => replace_self(ty, self_target),
454+
syn::GenericArgument::AssocType(assoc) => {
455+
replace_self(&mut assoc.ty, self_target)
456+
}
457+
syn::GenericArgument::Lifetime(_)
458+
| syn::GenericArgument::Const(_)
459+
| syn::GenericArgument::AssocConst(_)
460+
| syn::GenericArgument::Constraint(_)
461+
| _ => {}
462+
}
463+
}
464+
}
465+
}
466+
}
467+
syn::Type::Reference(type_ref) => {
468+
replace_self(&mut type_ref.elem, self_target);
469+
}
470+
syn::Type::Tuple(type_tuple) => {
471+
for ty in &mut type_tuple.elems {
472+
replace_self(ty, self_target);
473+
}
474+
}
475+
syn::Type::Array(type_array) => replace_self(&mut type_array.elem, self_target),
476+
syn::Type::Slice(ty) => replace_self(&mut ty.elem, self_target),
477+
syn::Type::Group(ty) => replace_self(&mut ty.elem, self_target),
478+
syn::Type::Paren(ty) => replace_self(&mut ty.elem, self_target),
479+
syn::Type::Ptr(ty) => replace_self(&mut ty.elem, self_target),
480+
481+
syn::Type::BareFn(_)
482+
| syn::Type::ImplTrait(_)
483+
| syn::Type::Infer(_)
484+
| syn::Type::Macro(_)
485+
| syn::Type::Never(_)
486+
| syn::Type::TraitObject(_)
487+
| syn::Type::Verbatim(_)
488+
| _ => {}
489+
}
490+
}

pyo3-macros-backend/src/pyimpl.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,8 +347,25 @@ pub(crate) fn get_cfg_attributes(attrs: &[syn::Attribute]) -> Vec<&syn::Attribut
347347
fn method_introspection_code(spec: &FnSpec<'_>, parent: &syn::Type, ctx: &Ctx) -> TokenStream {
348348
let Ctx { pyo3_path, .. } = ctx;
349349

350-
// We introduce self/cls argument and setup decorators
351350
let name = spec.python_name.to_string();
351+
if matches!(
352+
name.as_str(),
353+
"__richcmp__"
354+
| "__concat__"
355+
| "__repeat__"
356+
| "__inplace_concat__"
357+
| "__inplace_repeat__"
358+
| "__getbuffer__"
359+
| "__releasebuffer__"
360+
| "__traverse__"
361+
| "__clear__"
362+
) {
363+
// This is not a magic Python method, ignore for now
364+
// TODO: properly implement
365+
return quote! {};
366+
}
367+
368+
// We introduce self/cls argument and setup decorators
352369
let mut first_argument = None;
353370
let mut decorators = Vec::new();
354371
match &spec.tp {

pytests/stubs/pyclasses.pyi

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1+
import typing
2+
13
class AssertingBaseClass:
2-
def __new__(cls, /, expected_type): ...
4+
def __new__(cls, /, expected_type: typing.Any): ...
35

46
class ClassWithDecorators:
57
def __new__(cls, /): ...
68
@property
79
def attr(self, /): ...
810
@attr.setter
9-
def attr(self, /, value): ...
11+
def attr(self, /, value: int): ...
1012
@classmethod
1113
@property
1214
def cls_attribute(cls, /): ...

0 commit comments

Comments
 (0)