pyo3_stub_gen/type_info.rs
1//! Store of metadata for generating Python stub file
2//!
3//! Stub file generation takes two steps:
4//!
5//! Store metadata (compile time)
6//! ------------------------------
7//! Embed compile-time information about Rust types and PyO3 macro arguments
8//! using [inventory::submit!](https://docs.rs/inventory/latest/inventory/macro.submit.html) macro into source codes,
9//! and these information will be gathered by [inventory::iter](https://docs.rs/inventory/latest/inventory/struct.iter.html).
10//! This submodule is responsible for this process.
11//!
12//! - [PyClassInfo] stores information obtained from `#[pyclass]` macro
13//! - [PyMethodsInfo] stores information obtained from `#[pymethods]` macro
14//!
15//! and others are their components.
16//!
17//! Gathering metadata and generating stub file (runtime)
18//! -------------------------------------------------------
19//! Since `#[pyclass]` and `#[pymethods]` definitions are not bundled in a single block,
20//! we have to reconstruct these information corresponding to a Python `class`.
21//! This process is done at runtime in [gen_stub](../../gen_stub) executable.
22//!
23
24use crate::{PyStubType, TypeInfo};
25use std::any::TypeId;
26
27/// Represents the target of type ignore comments
28#[derive(Debug, Clone, Copy, PartialEq)]
29pub enum IgnoreTarget {
30 /// Ignore all type checking errors `(# type: ignore)`
31 All,
32 /// Ignore specific type checking rules `(# type: ignore[rule1,rule2])`
33 Specified(&'static [&'static str]),
34}
35
36/// Information about deprecated items
37#[derive(Debug, Clone, PartialEq)]
38pub struct DeprecatedInfo {
39 pub since: Option<&'static str>,
40 pub note: Option<&'static str>,
41}
42
43/// Work around for `CompareOp` for `__richcmp__` argument,
44/// which does not implements `FromPyObject`
45pub fn compare_op_type_input() -> TypeInfo {
46 <isize as PyStubType>::type_input()
47}
48
49pub fn no_return_type_output() -> TypeInfo {
50 TypeInfo::none()
51}
52
53/// Kind of parameter in Python function signature
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub enum ParameterKind {
56 /// Positional-only parameter (before `/`)
57 PositionalOnly,
58 /// Positional or keyword parameter (default)
59 PositionalOrKeyword,
60 /// Keyword-only parameter (after `*`)
61 KeywordOnly,
62 /// Variable positional parameter (`*args`)
63 VarPositional,
64 /// Variable keyword parameter (`**kwargs`)
65 VarKeyword,
66}
67
68/// Default value of a parameter
69#[derive(Debug, Clone)]
70pub enum ParameterDefault {
71 /// No default value
72 None,
73 /// Default value expression as a string
74 Expr(fn() -> String),
75}
76
77impl PartialEq for ParameterDefault {
78 #[inline]
79 fn eq(&self, other: &Self) -> bool {
80 match (self, other) {
81 (Self::Expr(l), Self::Expr(r)) => {
82 let l_val = l();
83 let r_val = r();
84 l_val.eq(&r_val)
85 }
86 (Self::None, Self::None) => true,
87 _ => false,
88 }
89 }
90}
91
92/// Information about a parameter in a Python function/method signature
93///
94/// This struct is used at compile time to store metadata about parameters
95/// that will be used to generate Python stub files.
96#[derive(Debug)]
97pub struct ParameterInfo {
98 /// Parameter name
99 pub name: &'static str,
100 /// Parameter kind (positional-only, keyword-only, etc.)
101 pub kind: ParameterKind,
102 /// Type information getter
103 pub type_info: fn() -> TypeInfo,
104 /// Default value
105 pub default: ParameterDefault,
106}
107
108/// Type of a method
109#[derive(Debug, Clone, Copy, PartialEq)]
110pub enum MethodType {
111 Instance,
112 Static,
113 Class,
114 New,
115}
116
117/// Info of usual method appears in `#[pymethod]`
118#[derive(Debug)]
119pub struct MethodInfo {
120 pub name: &'static str,
121 pub parameters: &'static [ParameterInfo],
122 pub r#return: fn() -> TypeInfo,
123 pub doc: &'static str,
124 pub r#type: MethodType,
125 pub is_async: bool,
126 pub deprecated: Option<DeprecatedInfo>,
127 pub type_ignored: Option<IgnoreTarget>,
128 /// Whether this method is marked as an overload variant
129 pub is_overload: bool,
130}
131
132/// Info of getter method decorated with `#[getter]` or `#[pyo3(get, set)]` appears in `#[pyclass]`
133#[derive(Debug)]
134pub struct MemberInfo {
135 pub name: &'static str,
136 pub r#type: fn() -> TypeInfo,
137 pub doc: &'static str,
138 pub default: Option<fn() -> String>,
139 pub deprecated: Option<DeprecatedInfo>,
140}
141
142/// Info of `#[pymethod]`
143#[derive(Debug)]
144pub struct PyMethodsInfo {
145 // The Rust struct type-id of `impl` block where `#[pymethod]` acts on
146 pub struct_id: fn() -> TypeId,
147 /// Method/Const with `#[classattr]`
148 pub attrs: &'static [MemberInfo],
149 /// Methods decorated with `#[getter]`
150 pub getters: &'static [MemberInfo],
151 /// Methods decorated with `#[getter]`
152 pub setters: &'static [MemberInfo],
153 /// Other usual methods
154 pub methods: &'static [MethodInfo],
155 /// Source file location for deterministic ordering
156 pub file: &'static str,
157 pub line: u32,
158 pub column: u32,
159}
160
161inventory::collect!(PyMethodsInfo);
162
163/// Info of `#[pyclass]` with Rust struct
164#[derive(Debug)]
165pub struct PyClassInfo {
166 // Rust struct type-id
167 pub struct_id: fn() -> TypeId,
168 // The name exposed to Python
169 pub pyclass_name: &'static str,
170 /// Module name specified by `#[pyclass(module = "foo.bar")]`
171 pub module: Option<&'static str>,
172 /// Docstring
173 pub doc: &'static str,
174 /// static members by `#[pyo3(get)]`
175 pub getters: &'static [MemberInfo],
176 /// static members by `#[pyo3(set)]`
177 pub setters: &'static [MemberInfo],
178 /// Base classes specified by `#[pyclass(extends = Type)]`
179 pub bases: &'static [fn() -> TypeInfo],
180 /// Whether the class has eq attribute
181 pub has_eq: bool,
182 /// Whether the class has ord attribute
183 pub has_ord: bool,
184 /// Whether the class has hash attribute
185 pub has_hash: bool,
186 /// Whether the class has str attribute
187 pub has_str: bool,
188 /// Whether the class has subclass attribute `#[pyclass(subclass)]`
189 pub subclass: bool,
190}
191
192inventory::collect!(PyClassInfo);
193
194#[derive(Debug, Clone, Copy, PartialEq)]
195pub enum VariantForm {
196 Unit,
197 Tuple,
198 Struct,
199}
200
201/// Info of a `#[pyclass]` with a single variant of a rich (structured) Rust enum
202#[derive(Debug)]
203pub struct VariantInfo {
204 pub pyclass_name: &'static str,
205 pub module: Option<&'static str>,
206 pub doc: &'static str,
207 pub fields: &'static [MemberInfo],
208 pub form: &'static VariantForm,
209 pub constr_args: &'static [ParameterInfo],
210}
211
212/// Info of a `#[pyclass]` with a rich (structured) Rust enum
213#[derive(Debug)]
214pub struct PyComplexEnumInfo {
215 // Rust struct type-id
216 pub enum_id: fn() -> TypeId,
217 // The name exposed to Python
218 pub pyclass_name: &'static str,
219 /// Module name specified by `#[pyclass(module = "foo.bar")]`
220 pub module: Option<&'static str>,
221 /// Docstring
222 pub doc: &'static str,
223 /// static members by `#[pyo3(get, set)]`
224 pub variants: &'static [VariantInfo],
225}
226
227inventory::collect!(PyComplexEnumInfo);
228
229/// Info of `#[pyclass]` with Rust enum
230#[derive(Debug)]
231pub struct PyEnumInfo {
232 // Rust struct type-id
233 pub enum_id: fn() -> TypeId,
234 // The name exposed to Python
235 pub pyclass_name: &'static str,
236 /// Module name specified by `#[pyclass(module = "foo.bar")]`
237 pub module: Option<&'static str>,
238 /// Docstring
239 pub doc: &'static str,
240 /// Variants of enum (name, doc)
241 pub variants: &'static [(&'static str, &'static str)],
242}
243
244inventory::collect!(PyEnumInfo);
245
246/// Info of `#[pyfunction]`
247#[derive(Debug)]
248pub struct PyFunctionInfo {
249 pub name: &'static str,
250 pub parameters: &'static [ParameterInfo],
251 pub r#return: fn() -> TypeInfo,
252 pub doc: &'static str,
253 pub module: Option<&'static str>,
254 pub is_async: bool,
255 pub deprecated: Option<DeprecatedInfo>,
256 pub type_ignored: Option<IgnoreTarget>,
257 /// Whether this function is marked as an overload variant
258 pub is_overload: bool,
259 /// Source file location for deterministic ordering
260 pub file: &'static str,
261 pub line: u32,
262 pub column: u32,
263 /// Index for ordering multiple functions from the same macro invocation
264 pub index: usize,
265}
266
267inventory::collect!(PyFunctionInfo);
268
269#[derive(Debug)]
270pub struct PyVariableInfo {
271 pub name: &'static str,
272 pub module: &'static str,
273 pub r#type: fn() -> TypeInfo,
274 pub default: Option<fn() -> String>,
275}
276
277inventory::collect!(PyVariableInfo);
278
279#[derive(Debug)]
280pub struct ModuleDocInfo {
281 pub module: &'static str,
282 pub doc: fn() -> String,
283}
284
285inventory::collect!(ModuleDocInfo);