Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions newsfragments/5957.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add `#[pyo3(overload(...))]` attribute for declaring `@typing.overload` variants in generated stubs.
82 changes: 56 additions & 26 deletions pyo3-introspection/src/introspection.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::model::{
Argument, Arguments, Attribute, Class, Constant, Expr, Function, Module, Operator,
VariableLengthArgument,
OverloadSignature, VariableLengthArgument,
};
use anyhow::{anyhow, bail, ensure, Context, Result};
use goblin::elf::section_header::SHN_XINDEX;
Expand Down Expand Up @@ -177,13 +177,15 @@ fn convert_members<'a>(
is_async,
returns,
doc,
overloads,
} => functions.push(convert_function(
name,
arguments,
decorators,
returns,
*is_async,
doc.as_deref(),
overloads,
type_hint_for_annotation_id,
)),
Chunk::Attribute {
Expand Down Expand Up @@ -268,13 +270,15 @@ fn convert_class(
})
}

#[expect(clippy::too_many_arguments)]
fn convert_function(
name: &str,
arguments: &ChunkArguments,
decorators: &[ChunkExpr],
returns: &Option<ChunkExpr>,
is_async: bool,
docstring: Option<&str>,
overloads: &[ChunkOverload],
type_hint_for_annotation_id: &HashMap<String, Expr>,
) -> Function {
Function {
Expand All @@ -283,36 +287,53 @@ fn convert_function(
.iter()
.map(|e| convert_expr(e, type_hint_for_annotation_id))
.collect(),
arguments: Arguments {
positional_only_arguments: arguments
.posonlyargs
.iter()
.map(|a| convert_argument(a, type_hint_for_annotation_id))
.collect(),
arguments: arguments
.args
.iter()
.map(|a| convert_argument(a, type_hint_for_annotation_id))
.collect(),
vararg: arguments
.vararg
.as_ref()
.map(|a| convert_variable_length_argument(a, type_hint_for_annotation_id)),
keyword_only_arguments: arguments
.kwonlyargs
.iter()
.map(|e| convert_argument(e, type_hint_for_annotation_id))
.collect(),
kwarg: arguments
.kwarg
.as_ref()
.map(|a| convert_variable_length_argument(a, type_hint_for_annotation_id)),
},
arguments: convert_arguments(arguments, type_hint_for_annotation_id),
returns: returns
.as_ref()
.map(|a| convert_expr(a, type_hint_for_annotation_id)),
is_async,
docstring: docstring.map(Into::into),
overloads: overloads
.iter()
.map(|o| OverloadSignature {
arguments: convert_arguments(&o.arguments, type_hint_for_annotation_id),
returns: o
.returns
.as_ref()
.map(|r| convert_expr(r, type_hint_for_annotation_id)),
})
.collect(),
}
}

fn convert_arguments(
arguments: &ChunkArguments,
type_hint_for_annotation_id: &HashMap<String, Expr>,
) -> Arguments {
Arguments {
positional_only_arguments: arguments
.posonlyargs
.iter()
.map(|a| convert_argument(a, type_hint_for_annotation_id))
.collect(),
arguments: arguments
.args
.iter()
.map(|a| convert_argument(a, type_hint_for_annotation_id))
.collect(),
vararg: arguments
.vararg
.as_ref()
.map(|a| convert_variable_length_argument(a, type_hint_for_annotation_id)),
keyword_only_arguments: arguments
.kwonlyargs
.iter()
.map(|e| convert_argument(e, type_hint_for_annotation_id))
.collect(),
kwarg: arguments
.kwarg
.as_ref()
.map(|a| convert_variable_length_argument(a, type_hint_for_annotation_id)),
}
}

Expand Down Expand Up @@ -700,6 +721,8 @@ enum Chunk {
is_async: bool,
#[serde(default)]
doc: Option<String>,
#[serde(default)]
overloads: Vec<ChunkOverload>,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

open design question: do we want to have overloads field in the introspection JSON with the olverloads or be closer to the final Python stubs and just have multiple Function chunks with the @overload decorator (and not chunk for the base function)?

I feel that it's underlying a deeper question on if the introspection data must be a more "semantic" AST or more "syntactic" one closer to the Python stubs.

},
Attribute {
#[serde(default)]
Expand Down Expand Up @@ -739,6 +762,13 @@ struct ChunkArgument {
annotation: Option<ChunkExpr>,
}

#[derive(Deserialize)]
struct ChunkOverload {
arguments: Box<ChunkArguments>,
#[serde(default)]
returns: Option<ChunkExpr>,
}

#[derive(Deserialize)]
#[serde(tag = "type", rename_all = "lowercase")]
enum ChunkExpr {
Expand Down
8 changes: 8 additions & 0 deletions pyo3-introspection/src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ pub struct Function {
pub returns: Option<Expr>,
pub is_async: bool,
pub docstring: Option<String>,
/// `@overload` variants for this function
pub overloads: Vec<OverloadSignature>,
}

#[derive(Debug, Eq, PartialEq, Clone, Hash)]
pub struct OverloadSignature {
pub arguments: Arguments,
pub returns: Option<Expr>,
}

#[derive(Debug, Eq, PartialEq, Clone, Hash)]
Expand Down
Loading
Loading