Skip to content

Commit 5b1f54a

Browse files
committed
Refine arguments struct
1 parent 34ad77f commit 5b1f54a

File tree

4 files changed

+118
-100
lines changed

4 files changed

+118
-100
lines changed

pyo3-introspection/src/introspection.rs

Lines changed: 41 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::model::{Argument, Class, Function, Module, ParameterKind};
1+
use crate::model::{Argument, Arguments, Class, Function, Module};
22
use anyhow::{bail, Context, Result};
33
use goblin::elf::Elf;
44
use goblin::mach::symbols::N_SECT;
@@ -42,14 +42,14 @@ fn parse_chunks(chunks: &[Chunk], main_module_name: &str) -> Result<Module> {
4242
} = chunk
4343
{
4444
if name == main_module_name {
45-
return parse_module(name, members, &chunks_by_id);
45+
return convert_module(name, members, &chunks_by_id);
4646
}
4747
}
4848
}
4949
bail!("No module named {main_module_name} found")
5050
}
5151

52-
fn parse_module(
52+
fn convert_module(
5353
name: &str,
5454
members: &[String],
5555
chunks_by_id: &HashMap<&String, &Chunk>,
@@ -65,7 +65,7 @@ fn parse_module(
6565
members,
6666
id: _,
6767
} => {
68-
modules.push(parse_module(name, members, chunks_by_id)?);
68+
modules.push(convert_module(name, members, chunks_by_id)?);
6969
}
7070
Chunk::Class { name, id: _ } => classes.push(Class { name: name.into() }),
7171
Chunk::Function {
@@ -74,22 +74,21 @@ fn parse_module(
7474
arguments,
7575
} => functions.push(Function {
7676
name: name.into(),
77-
arguments: arguments
78-
.iter()
79-
.map(|arg| Argument {
80-
name: arg.name.clone(),
81-
kind: match arg.kind {
82-
ChunkArgumentKind::PositionalOnly => ParameterKind::PositionalOnly,
83-
ChunkArgumentKind::PositionalOrKeyword => {
84-
ParameterKind::PositionalOrKeyword
85-
}
86-
ChunkArgumentKind::VarPositional => ParameterKind::VarPositional,
87-
ChunkArgumentKind::KeywordOnly => ParameterKind::KeywordOnly,
88-
ChunkArgumentKind::VarKeyword => ParameterKind::VarKeyword,
89-
},
90-
default_value: arg.default_value.clone(),
91-
})
92-
.collect(),
77+
arguments: Arguments {
78+
positional_only_arguments: arguments
79+
.posonlyargs
80+
.iter()
81+
.map(convert_argument)
82+
.collect(),
83+
arguments: arguments.args.iter().map(convert_argument).collect(),
84+
vararg: arguments.vararg.as_ref().map(convert_argument),
85+
keyword_only_arguments: arguments
86+
.kwonlyargs
87+
.iter()
88+
.map(convert_argument)
89+
.collect(),
90+
kwarg: arguments.kwarg.as_ref().map(convert_argument),
91+
},
9392
}),
9493
}
9594
}
@@ -102,6 +101,13 @@ fn parse_module(
102101
})
103102
}
104103

104+
fn convert_argument(arg: &ChunkArgument) -> Argument {
105+
Argument {
106+
name: arg.name.clone(),
107+
default_value: arg.default.clone(),
108+
}
109+
}
110+
105111
fn find_introspection_chunks_in_binary_object(path: &Path) -> Result<Vec<Chunk>> {
106112
let library_content =
107113
fs::read(path).with_context(|| format!("Failed to read {}", path.display()))?;
@@ -262,24 +268,27 @@ enum Chunk {
262268
Function {
263269
id: String,
264270
name: String,
265-
arguments: Vec<ChunkArgument>,
271+
arguments: ChunkArguments,
266272
},
267273
}
268274

269275
#[derive(Deserialize)]
270-
struct ChunkArgument {
271-
name: String,
272-
kind: ChunkArgumentKind,
276+
struct ChunkArguments {
277+
#[serde(default)]
278+
posonlyargs: Vec<ChunkArgument>,
273279
#[serde(default)]
274-
default_value: Option<String>,
280+
args: Vec<ChunkArgument>,
281+
#[serde(default)]
282+
vararg: Option<ChunkArgument>,
283+
#[serde(default)]
284+
kwonlyargs: Vec<ChunkArgument>,
285+
#[serde(default)]
286+
kwarg: Option<ChunkArgument>,
275287
}
276288

277289
#[derive(Deserialize)]
278-
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
279-
enum ChunkArgumentKind {
280-
PositionalOnly,
281-
PositionalOrKeyword,
282-
VarPositional,
283-
KeywordOnly,
284-
VarKeyword,
290+
struct ChunkArgument {
291+
name: String,
292+
#[serde(default)]
293+
default: Option<String>,
285294
}

pyo3-introspection/src/model.rs

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,27 +14,26 @@ pub struct Class {
1414
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
1515
pub struct Function {
1616
pub name: String,
17+
pub arguments: Arguments,
18+
}
19+
20+
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
21+
pub struct Arguments {
22+
/// Arguments before /
23+
pub positional_only_arguments: Vec<Argument>,
24+
/// Regular arguments (between / and *)
1725
pub arguments: Vec<Argument>,
26+
/// *vararg
27+
pub vararg: Option<Argument>,
28+
/// Arguments after *
29+
pub keyword_only_arguments: Vec<Argument>,
30+
/// **kwarg
31+
pub kwarg: Option<Argument>,
1832
}
1933

2034
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
2135
pub struct Argument {
2236
pub name: String,
23-
pub kind: ParameterKind,
2437
/// Default value as a Python expression
2538
pub default_value: Option<String>,
2639
}
27-
28-
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
29-
pub enum ParameterKind {
30-
/// Before /
31-
PositionalOnly,
32-
/// Between / and *
33-
PositionalOrKeyword,
34-
/// *args
35-
VarPositional,
36-
/// After *
37-
KeywordOnly,
38-
/// *kwargs
39-
VarKeyword,
40-
}

pyo3-introspection/src/stubs.rs

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::model::{Class, Function, Module, ParameterKind};
1+
use crate::model::{Argument, Class, Function, Module};
22
use std::collections::HashMap;
33
use std::path::{Path, PathBuf};
44

@@ -49,33 +49,35 @@ fn class_stubs(class: &Class) -> String {
4949

5050
fn function_stubs(function: &Function) -> String {
5151
// Signature
52-
let mut positional_only = true;
53-
let mut keyword_only = false;
5452
let mut parameters = Vec::new();
55-
for argument in &function.arguments {
56-
if positional_only && !matches!(argument.kind, ParameterKind::PositionalOnly) {
57-
if !parameters.is_empty() {
58-
parameters.push("/".into());
59-
}
60-
positional_only = false;
61-
}
62-
if !keyword_only && matches!(argument.kind, ParameterKind::KeywordOnly) {
63-
parameters.push("*".into());
64-
keyword_only = true;
65-
}
66-
let mut parameter_str = match argument.kind {
67-
ParameterKind::VarPositional => {
68-
keyword_only = true;
69-
format!("*{}", argument.name)
70-
}
71-
ParameterKind::VarKeyword => format!("**{}", argument.name),
72-
_ => argument.name.clone(),
73-
};
74-
if let Some(default_value) = &argument.default_value {
75-
parameter_str.push('=');
76-
parameter_str.push_str(default_value);
77-
}
78-
parameters.push(parameter_str);
53+
for argument in &function.arguments.positional_only_arguments {
54+
parameters.push(argument_stub(argument));
55+
}
56+
if !function.arguments.positional_only_arguments.is_empty() {
57+
parameters.push("/".into());
58+
}
59+
for argument in &function.arguments.arguments {
60+
parameters.push(argument_stub(argument));
61+
}
62+
if let Some(argument) = &function.arguments.vararg {
63+
parameters.push(format!("*{}", argument_stub(argument)));
64+
} else if !function.arguments.keyword_only_arguments.is_empty() {
65+
parameters.push("*".into());
66+
}
67+
for argument in &function.arguments.keyword_only_arguments {
68+
parameters.push(argument_stub(argument));
69+
}
70+
if let Some(argument) = &function.arguments.kwarg {
71+
parameters.push(format!("**{}", argument_stub(argument)));
7972
}
8073
format!("def {}({}): ...", function.name, parameters.join(", "))
8174
}
75+
76+
fn argument_stub(argument: &Argument) -> String {
77+
let mut output = argument.name.clone();
78+
if let Some(default_value) = &argument.default_value {
79+
output.push('=');
80+
output.push_str(default_value);
81+
}
82+
output
83+
}

pyo3-macros-backend/src/introspection.rs

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,11 @@ fn arguments_introspection_data<'a>(signature: &'a FunctionSignature<'a>) -> Int
9999
}
100100
});
101101

102-
let mut arguments = Vec::new();
102+
let mut posonlyargs = Vec::new();
103+
let mut args = Vec::new();
104+
let mut vararg = None;
105+
let mut kwonlyargs = Vec::new();
106+
let mut kwarg = None;
103107

104108
for (i, param) in signature
105109
.python_signature
@@ -112,24 +116,17 @@ fn arguments_introspection_data<'a>(signature: &'a FunctionSignature<'a>) -> Int
112116
} else {
113117
panic!("Less arguments than in python signature");
114118
};
115-
arguments.push(argument_introspection_data(
116-
param,
117-
if i < signature.python_signature.positional_only_parameters {
118-
"POSITIONAL_ONLY"
119-
} else {
120-
"POSITIONAL_OR_KEYWORD"
121-
},
122-
arg_desc,
123-
));
119+
let arg = argument_introspection_data(param, arg_desc);
120+
if i < signature.python_signature.positional_only_parameters {
121+
posonlyargs.push(arg);
122+
} else {
123+
args.push(arg)
124+
}
124125
}
125126

126127
if let Some(param) = &signature.python_signature.varargs {
127-
arguments.push(IntrospectionNode::Map(
128-
[
129-
("name", IntrospectionNode::String(param.into())),
130-
("kind", IntrospectionNode::String("VAR_POSITIONAL".into())),
131-
]
132-
.into(),
128+
vararg = Some(IntrospectionNode::Map(
129+
[("name", IntrospectionNode::String(param.into()))].into(),
133130
));
134131
}
135132

@@ -139,11 +136,11 @@ fn arguments_introspection_data<'a>(signature: &'a FunctionSignature<'a>) -> Int
139136
} else {
140137
panic!("Less arguments than in python signature");
141138
};
142-
arguments.push(argument_introspection_data(param, "KEYWORD_ONLY", arg_desc));
139+
kwonlyargs.push(argument_introspection_data(param, arg_desc));
143140
}
144141

145142
if let Some(param) = &signature.python_signature.kwargs {
146-
arguments.push(IntrospectionNode::Map(
143+
kwarg = Some(IntrospectionNode::Map(
147144
[
148145
("name", IntrospectionNode::String(param.into())),
149146
("kind", IntrospectionNode::String("VAR_KEYWORD".into())),
@@ -152,22 +149,33 @@ fn arguments_introspection_data<'a>(signature: &'a FunctionSignature<'a>) -> Int
152149
));
153150
}
154151

155-
IntrospectionNode::List(arguments)
152+
let mut map = HashMap::new();
153+
if !posonlyargs.is_empty() {
154+
map.insert("posonlyargs", IntrospectionNode::List(posonlyargs));
155+
}
156+
if !args.is_empty() {
157+
map.insert("args", IntrospectionNode::List(args));
158+
}
159+
if let Some(vararg) = vararg {
160+
map.insert("vararg", vararg);
161+
}
162+
if !kwonlyargs.is_empty() {
163+
map.insert("kwonlyargs", IntrospectionNode::List(kwonlyargs));
164+
}
165+
if let Some(kwarg) = kwarg {
166+
map.insert("kwarg", kwarg);
167+
}
168+
IntrospectionNode::Map(map)
156169
}
157170

158171
fn argument_introspection_data<'a>(
159172
name: &'a str,
160-
kind: &'a str,
161173
desc: &'a RegularArg<'_>,
162174
) -> IntrospectionNode<'a> {
163-
let mut params: HashMap<_, _> = [
164-
("name", IntrospectionNode::String(name.into())),
165-
("kind", IntrospectionNode::String(kind.into())),
166-
]
167-
.into();
175+
let mut params: HashMap<_, _> = [("name", IntrospectionNode::String(name.into()))].into();
168176
if desc.default_value.is_some() {
169177
params.insert(
170-
"default_value",
178+
"default",
171179
IntrospectionNode::String(desc.default_value().into()),
172180
);
173181
}

0 commit comments

Comments
 (0)