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::{stub_type::ModuleRef, 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 with optional module qualification info
74    Expr {
75        /// Function returning the default value expression (without module prefix)
76        value: fn() -> String,
77        /// Source module of the type referenced in the default value
78        source_module: Option<fn() -> Option<ModuleRef>>,
79    },
80}
81
82impl PartialEq for ParameterDefault {
83    #[inline]
84    fn eq(&self, other: &Self) -> bool {
85        match (self, other) {
86            (Self::Expr { value: l, .. }, Self::Expr { value: r, .. }) => {
87                let l_val = l();
88                let r_val = r();
89                l_val.eq(&r_val)
90            }
91            (Self::None, Self::None) => true,
92            _ => false,
93        }
94    }
95}
96
97/// Information about a parameter in a Python function/method signature
98///
99/// This struct is used at compile time to store metadata about parameters
100/// that will be used to generate Python stub files.
101#[derive(Debug)]
102pub struct ParameterInfo {
103    /// Parameter name
104    pub name: &'static str,
105    /// Parameter kind (positional-only, keyword-only, etc.)
106    pub kind: ParameterKind,
107    /// Type information getter
108    pub type_info: fn() -> TypeInfo,
109    /// Default value
110    pub default: ParameterDefault,
111}
112
113/// Type of a method
114#[derive(Debug, Clone, Copy, PartialEq)]
115pub enum MethodType {
116    Instance,
117    Static,
118    Class,
119    New,
120}
121
122/// Info of usual method appears in `#[pymethod]`
123#[derive(Debug)]
124pub struct MethodInfo {
125    pub name: &'static str,
126    pub parameters: &'static [ParameterInfo],
127    pub r#return: fn() -> TypeInfo,
128    pub doc: &'static str,
129    pub r#type: MethodType,
130    pub is_async: bool,
131    pub deprecated: Option<DeprecatedInfo>,
132    pub type_ignored: Option<IgnoreTarget>,
133    /// Whether this method is marked as an overload variant
134    pub is_overload: bool,
135}
136
137/// Info of getter method decorated with `#[getter]` or `#[pyo3(get, set)]` appears in `#[pyclass]`
138#[derive(Debug)]
139pub struct MemberInfo {
140    pub name: &'static str,
141    pub r#type: fn() -> TypeInfo,
142    pub doc: &'static str,
143    pub default: Option<fn() -> String>,
144    pub deprecated: Option<DeprecatedInfo>,
145}
146
147/// Info of `#[pymethod]`
148#[derive(Debug)]
149pub struct PyMethodsInfo {
150    // The Rust struct type-id of `impl` block where `#[pymethod]` acts on
151    pub struct_id: fn() -> TypeId,
152    /// Method/Const with `#[classattr]`
153    pub attrs: &'static [MemberInfo],
154    /// Methods decorated with `#[getter]`
155    pub getters: &'static [MemberInfo],
156    /// Methods decorated with `#[getter]`
157    pub setters: &'static [MemberInfo],
158    /// Other usual methods
159    pub methods: &'static [MethodInfo],
160    /// Source file location for deterministic ordering
161    pub file: &'static str,
162    pub line: u32,
163    pub column: u32,
164}
165
166inventory::collect!(PyMethodsInfo);
167
168/// Info of `#[pyclass]` with Rust struct
169#[derive(Debug)]
170pub struct PyClassInfo {
171    // Rust struct type-id
172    pub struct_id: fn() -> TypeId,
173    // The name exposed to Python
174    pub pyclass_name: &'static str,
175    /// Module name specified by `#[pyclass(module = "foo.bar")]`
176    pub module: Option<&'static str>,
177    /// Docstring
178    pub doc: &'static str,
179    /// static members by `#[pyo3(get)]`
180    pub getters: &'static [MemberInfo],
181    /// static members by `#[pyo3(set)]`
182    pub setters: &'static [MemberInfo],
183    /// Base classes specified by `#[pyclass(extends = Type)]`
184    pub bases: &'static [fn() -> TypeInfo],
185    /// Whether the class has eq attribute
186    pub has_eq: bool,
187    /// Whether the class has ord attribute
188    pub has_ord: bool,
189    /// Whether the class has hash attribute
190    pub has_hash: bool,
191    /// Whether the class has str attribute
192    pub has_str: bool,
193    /// Whether the class has subclass attribute `#[pyclass(subclass)]`
194    pub subclass: bool,
195}
196
197inventory::collect!(PyClassInfo);
198
199#[derive(Debug, Clone, Copy, PartialEq)]
200pub enum VariantForm {
201    Unit,
202    Tuple,
203    Struct,
204}
205
206/// Info of a `#[pyclass]` with a single variant of a rich (structured) Rust enum
207#[derive(Debug)]
208pub struct VariantInfo {
209    pub pyclass_name: &'static str,
210    pub module: Option<&'static str>,
211    pub doc: &'static str,
212    pub fields: &'static [MemberInfo],
213    pub form: &'static VariantForm,
214    pub constr_args: &'static [ParameterInfo],
215}
216
217/// Info of a `#[pyclass]` with a rich (structured) Rust enum
218#[derive(Debug)]
219pub struct PyComplexEnumInfo {
220    // Rust struct type-id
221    pub enum_id: fn() -> TypeId,
222    // The name exposed to Python
223    pub pyclass_name: &'static str,
224    /// Module name specified by `#[pyclass(module = "foo.bar")]`
225    pub module: Option<&'static str>,
226    /// Docstring
227    pub doc: &'static str,
228    /// static members by `#[pyo3(get, set)]`
229    pub variants: &'static [VariantInfo],
230}
231
232inventory::collect!(PyComplexEnumInfo);
233
234/// Info of `#[pyclass]` with Rust enum
235#[derive(Debug)]
236pub struct PyEnumInfo {
237    // Rust struct type-id
238    pub enum_id: fn() -> TypeId,
239    // The name exposed to Python
240    pub pyclass_name: &'static str,
241    /// Module name specified by `#[pyclass(module = "foo.bar")]`
242    pub module: Option<&'static str>,
243    /// Docstring
244    pub doc: &'static str,
245    /// Variants of enum (name, doc)
246    pub variants: &'static [(&'static str, &'static str)],
247}
248
249inventory::collect!(PyEnumInfo);
250
251/// Info of `#[pyfunction]`
252#[derive(Debug)]
253pub struct PyFunctionInfo {
254    pub name: &'static str,
255    pub parameters: &'static [ParameterInfo],
256    pub r#return: fn() -> TypeInfo,
257    pub doc: &'static str,
258    pub module: Option<&'static str>,
259    pub is_async: bool,
260    pub deprecated: Option<DeprecatedInfo>,
261    pub type_ignored: Option<IgnoreTarget>,
262    /// Whether this function is marked as an overload variant
263    pub is_overload: bool,
264    /// Source file location for deterministic ordering
265    pub file: &'static str,
266    pub line: u32,
267    pub column: u32,
268    /// Index for ordering multiple functions from the same macro invocation
269    pub index: usize,
270}
271
272inventory::collect!(PyFunctionInfo);
273
274#[derive(Debug)]
275pub struct PyVariableInfo {
276    pub name: &'static str,
277    pub module: &'static str,
278    pub r#type: fn() -> TypeInfo,
279    pub default: Option<fn() -> String>,
280}
281
282inventory::collect!(PyVariableInfo);
283
284#[derive(Debug)]
285pub struct TypeAliasInfo {
286    pub name: &'static str,
287    pub module: &'static str,
288    pub r#type: fn() -> TypeInfo,
289    pub doc: &'static str,
290}
291
292inventory::collect!(TypeAliasInfo);
293
294#[derive(Debug)]
295pub struct ModuleDocInfo {
296    pub module: &'static str,
297    pub doc: fn() -> String,
298}
299
300inventory::collect!(ModuleDocInfo);
301
302/// Re-export items from another module into __all__
303#[derive(Debug)]
304pub struct ReexportModuleMembers {
305    pub target_module: &'static str,
306    pub source_module: &'static str,
307    pub items: Option<&'static [&'static str]>,
308}
309
310inventory::collect!(ReexportModuleMembers);
311
312/// Add verbatim entry to __all__
313#[derive(Debug)]
314pub struct ExportVerbatim {
315    pub target_module: &'static str,
316    pub name: &'static str,
317}
318
319inventory::collect!(ExportVerbatim);
320
321/// Exclude specific items from __all__
322#[derive(Debug)]
323pub struct ExcludeFromAll {
324    pub target_module: &'static str,
325    pub name: &'static str,
326}
327
328inventory::collect!(ExcludeFromAll);