pyo3_stub_gen_derive/gen_stub/
parse_python.rs1mod pyfunction;
7mod pymethods;
8
9pub use pyfunction::parse_python_function_stub;
10pub use pymethods::parse_python_methods_stub;
11
12use indexmap::IndexSet;
13use rustpython_parser::ast;
14use syn::{Result, Type};
15
16use super::{arg::ArgInfo, attr::DeprecatedInfo, util::TypeOrOverride};
17
18fn dedent(text: &str) -> String {
20 let lines: Vec<&str> = text.lines().collect();
21
22 let min_indent = lines
24 .iter()
25 .filter(|line| !line.trim().is_empty())
26 .map(|line| line.len() - line.trim_start().len())
27 .min()
28 .unwrap_or(0);
29
30 lines
32 .iter()
33 .map(|line| {
34 if line.len() >= min_indent {
35 &line[min_indent..]
36 } else {
37 line
38 }
39 })
40 .collect::<Vec<_>>()
41 .join("\n")
42}
43
44fn extract_docstring(func_def: &ast::StmtFunctionDef) -> String {
46 if let Some(ast::Stmt::Expr(expr_stmt)) = func_def.body.first() {
47 if let ast::Expr::Constant(constant) = &*expr_stmt.value {
48 if let ast::Constant::Str(s) = &constant.value {
49 return s.to_string();
50 }
51 }
52 }
53 String::new()
54}
55
56fn extract_deprecated_from_decorators(decorators: &[ast::Expr]) -> Option<DeprecatedInfo> {
58 for decorator in decorators {
59 match decorator {
61 ast::Expr::Name(name) if name.id.as_str() == "deprecated" => {
62 return Some(DeprecatedInfo {
63 since: None,
64 note: None,
65 });
66 }
67 ast::Expr::Call(call) => {
68 if let ast::Expr::Name(name) = &*call.func {
69 if name.id.as_str() == "deprecated" {
70 let note = call.args.first().and_then(|arg| match arg {
72 ast::Expr::Constant(constant) => match &constant.value {
73 ast::Constant::Str(s) => Some(s.to_string()),
74 _ => None,
75 },
76 _ => None,
77 });
78 return Some(DeprecatedInfo { since: None, note });
79 }
80 }
81 }
82 _ => {}
83 }
84 }
85 None
86}
87
88fn extract_args(args: &ast::Arguments, imports: &[String]) -> Result<Vec<ArgInfo>> {
90 let mut arg_infos = Vec::new();
91
92 let dummy_type: Type = syn::parse_str("()").unwrap();
94
95 for arg in &args.args {
97 let arg_name = arg.def.arg.to_string();
98
99 if arg_name == "self" {
101 continue;
102 }
103
104 let type_override = if let Some(annotation) = &arg.def.annotation {
105 type_annotation_to_type_override(annotation, imports, dummy_type.clone())?
106 } else {
107 TypeOrOverride::OverrideType {
109 r#type: dummy_type.clone(),
110 type_repr: "typing.Any".to_string(),
111 imports: IndexSet::from(["typing".to_string()]),
112 }
113 };
114
115 arg_infos.push(ArgInfo {
116 name: arg_name,
117 r#type: type_override,
118 });
119 }
120
121 Ok(arg_infos)
122}
123
124fn extract_return_type(
126 returns: &Option<Box<ast::Expr>>,
127 imports: &[String],
128) -> Result<Option<TypeOrOverride>> {
129 let dummy_type: Type = syn::parse_str("()").unwrap();
131
132 if let Some(return_annotation) = returns {
133 Ok(Some(type_annotation_to_type_override(
134 return_annotation,
135 imports,
136 dummy_type,
137 )?))
138 } else {
139 Ok(None)
141 }
142}
143
144fn type_annotation_to_type_override(
146 expr: &ast::Expr,
147 imports: &[String],
148 dummy_type: Type,
149) -> Result<TypeOrOverride> {
150 let type_str = expr_to_type_string(expr);
151
152 let import_set: IndexSet<String> = imports.iter().map(|s| s.to_string()).collect();
154
155 Ok(TypeOrOverride::OverrideType {
156 r#type: dummy_type,
157 type_repr: type_str,
158 imports: import_set,
159 })
160}
161
162fn expr_to_type_string(expr: &ast::Expr) -> String {
164 expr_to_type_string_inner(expr, false)
165}
166
167fn expr_to_type_string_inner(expr: &ast::Expr, in_subscript: bool) -> String {
169 match expr {
170 ast::Expr::Name(name) => name.id.to_string(),
171 ast::Expr::Attribute(attr) => {
172 format!(
173 "{}.{}",
174 expr_to_type_string_inner(&attr.value, false),
175 attr.attr
176 )
177 }
178 ast::Expr::Subscript(subscript) => {
179 let base = expr_to_type_string_inner(&subscript.value, false);
180 let slice = expr_to_type_string_inner(&subscript.slice, true);
181 format!("{}[{}]", base, slice)
182 }
183 ast::Expr::List(list) => {
184 let elements: Vec<String> = list
185 .elts
186 .iter()
187 .map(|e| expr_to_type_string_inner(e, false))
188 .collect();
189 format!("[{}]", elements.join(", "))
190 }
191 ast::Expr::Tuple(tuple) => {
192 let elements: Vec<String> = tuple
193 .elts
194 .iter()
195 .map(|e| expr_to_type_string_inner(e, in_subscript))
196 .collect();
197 if in_subscript {
198 elements.join(", ")
200 } else {
201 format!("({})", elements.join(", "))
202 }
203 }
204 ast::Expr::Constant(constant) => match &constant.value {
205 ast::Constant::Int(i) => i.to_string(),
206 ast::Constant::Str(s) => format!("\"{}\"", s),
207 ast::Constant::Bool(b) => b.to_string(),
208 ast::Constant::None => "None".to_string(),
209 _ => "Any".to_string(),
210 },
211 _ => "Any".to_string(),
212 }
213}