pyo3_stub_gen/generate/
method.rs1use crate::stub_type::ImportRef;
2use crate::{generate::*, rule_name::RuleName, type_info::*, TypeInfo};
3use itertools::Itertools;
4use std::{collections::HashSet, fmt};
5
6pub use crate::type_info::MethodType;
7
8#[derive(Debug, Clone, PartialEq)]
10pub struct MethodDef {
11 pub name: &'static str,
12 pub parameters: Parameters,
13 pub r#return: TypeInfo,
14 pub doc: &'static str,
15 pub r#type: MethodType,
16 pub is_async: bool,
17 pub deprecated: Option<DeprecatedInfo>,
18 pub type_ignored: Option<IgnoreTarget>,
19 pub is_overload: bool,
21}
22
23impl Import for MethodDef {
24 fn import(&self) -> HashSet<ImportRef> {
25 let mut import = self.r#return.import.clone();
26 import.extend(self.parameters.import());
27 if self.deprecated.is_some() {
29 import.insert("typing_extensions".into());
30 }
31 import
32 }
33}
34
35impl From<&MethodInfo> for MethodDef {
36 fn from(info: &MethodInfo) -> Self {
37 Self {
38 name: info.name,
39 parameters: Parameters::from_infos(info.parameters),
40 r#return: (info.r#return)(),
41 doc: info.doc,
42 r#type: info.r#type,
43 is_async: info.is_async,
44 deprecated: info.deprecated.clone(),
45 type_ignored: info.type_ignored,
46 is_overload: info.is_overload,
47 }
48 }
49}
50
51impl fmt::Display for MethodDef {
52 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
53 let indent = indent();
54 let async_ = if self.is_async { "async " } else { "" };
55
56 if let Some(deprecated) = &self.deprecated {
58 writeln!(f, "{indent}{deprecated}")?;
59 }
60
61 let params_str = if self.parameters.is_empty() {
62 String::new()
63 } else {
64 format!(", {}", self.parameters)
65 };
66
67 match self.r#type {
68 MethodType::Static => {
69 writeln!(f, "{indent}@staticmethod")?;
70 write!(f, "{indent}{async_}def {}({})", self.name, self.parameters)?;
71 }
72 MethodType::Class | MethodType::New => {
73 if self.r#type == MethodType::Class {
74 writeln!(f, "{indent}@classmethod")?;
76 }
77 write!(f, "{indent}{async_}def {}(cls{})", self.name, params_str)?;
78 }
79 MethodType::Instance => {
80 write!(f, "{indent}{async_}def {}(self{})", self.name, params_str)?;
81 }
82 }
83 write!(f, " -> {}:", self.r#return)?;
84
85 let type_ignore_comment = if let Some(target) = &self.type_ignored {
87 match target {
88 IgnoreTarget::All => Some(" # type: ignore".to_string()),
89 IgnoreTarget::Specified(rules) => {
90 let rules_str = rules
91 .iter()
92 .map(|r| {
93 let result = r.parse::<RuleName>().unwrap();
94 if let RuleName::Custom(custom) = &result {
95 log::warn!("Unknown custom rule name '{custom}' used in type ignore. Ensure this is intended.");
96 }
97 result
98 })
99 .join(",");
100 Some(format!(" # type: ignore[{rules_str}]"))
101 }
102 }
103 } else {
104 None
105 };
106
107 let doc = self.doc;
108 if !doc.is_empty() {
109 if let Some(comment) = &type_ignore_comment {
111 write!(f, "{comment}")?;
112 }
113 writeln!(f)?;
114 let double_indent = format!("{indent}{indent}");
115 docstring::write_docstring(f, self.doc, &double_indent)?;
116 } else {
117 write!(f, " ...")?;
118 if let Some(comment) = &type_ignore_comment {
120 write!(f, "{comment}")?;
121 }
122 writeln!(f)?;
123 }
124 Ok(())
125 }
126}
127
128impl MethodDef {
129 pub fn fmt_for_module(
134 &self,
135 target_module: &str,
136 f: &mut fmt::Formatter,
137 indent: &str,
138 ) -> fmt::Result {
139 let async_ = if self.is_async { "async " } else { "" };
140
141 if let Some(deprecated) = &self.deprecated {
143 writeln!(f, "{indent}{deprecated}")?;
144 }
145
146 let params_str = self.parameters.fmt_for_module(target_module);
147 let return_type = self.r#return.qualified_for_module(target_module);
148
149 let params_with_separator = if params_str.is_empty() {
150 String::new()
151 } else {
152 format!(", {}", params_str)
153 };
154
155 match self.r#type {
156 MethodType::Static => {
157 writeln!(f, "{indent}@staticmethod")?;
158 write!(f, "{indent}{async_}def {}({})", self.name, params_str)?;
159 }
160 MethodType::Class | MethodType::New => {
161 if self.r#type == MethodType::Class {
162 writeln!(f, "{indent}@classmethod")?;
164 }
165 write!(
166 f,
167 "{indent}{async_}def {}(cls{})",
168 self.name, params_with_separator
169 )?;
170 }
171 MethodType::Instance => {
172 write!(
173 f,
174 "{indent}{async_}def {}(self{})",
175 self.name, params_with_separator
176 )?;
177 }
178 }
179 write!(f, " -> {}:", return_type)?;
180
181 let type_ignore_comment = if let Some(target) = &self.type_ignored {
183 match target {
184 IgnoreTarget::All => Some(" # type: ignore".to_string()),
185 IgnoreTarget::Specified(rules) => {
186 let rules_str = rules
187 .iter()
188 .map(|r| {
189 let result = r.parse::<RuleName>().unwrap();
190 if let RuleName::Custom(custom) = &result {
191 log::warn!("Unknown custom rule name '{custom}' used in type ignore. Ensure this is intended.");
192 }
193 result
194 })
195 .join(",");
196 Some(format!(" # type: ignore[{rules_str}]"))
197 }
198 }
199 } else {
200 None
201 };
202
203 let doc = self.doc;
204 if !doc.is_empty() {
205 if let Some(comment) = &type_ignore_comment {
207 write!(f, "{comment}")?;
208 }
209 writeln!(f)?;
210 let double_indent = format!("{indent}{indent}");
211 docstring::write_docstring(f, self.doc, &double_indent)?;
212 } else {
213 write!(f, " ...")?;
214 if let Some(comment) = &type_ignore_comment {
216 write!(f, "{comment}")?;
217 }
218 writeln!(f)?;
219 }
220 Ok(())
221 }
222}