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}
129
130/// Info of getter method decorated with `#[getter]` or `#[pyo3(get, set)]` appears in `#[pyclass]`
131#[derive(Debug)]
132pub struct MemberInfo {
133    pub name: &'static str,
134    pub r#type: fn() -> TypeInfo,
135    pub doc: &'static str,
136    pub default: Option<fn() -> String>,
137    pub deprecated: Option<DeprecatedInfo>,
138}
139
140/// Info of `#[pymethod]`
141#[derive(Debug)]
142pub struct PyMethodsInfo {
143    // The Rust struct type-id of `impl` block where `#[pymethod]` acts on
144    pub struct_id: fn() -> TypeId,
145    /// Method/Const with `#[classattr]`
146    pub attrs: &'static [MemberInfo],
147    /// Methods decorated with `#[getter]`
148    pub getters: &'static [MemberInfo],
149    /// Methods decorated with `#[getter]`
150    pub setters: &'static [MemberInfo],
151    /// Other usual methods
152    pub methods: &'static [MethodInfo],
153}
154
155inventory::collect!(PyMethodsInfo);
156
157/// Info of `#[pyclass]` with Rust struct
158#[derive(Debug)]
159pub struct PyClassInfo {
160    // Rust struct type-id
161    pub struct_id: fn() -> TypeId,
162    // The name exposed to Python
163    pub pyclass_name: &'static str,
164    /// Module name specified by `#[pyclass(module = "foo.bar")]`
165    pub module: Option<&'static str>,
166    /// Docstring
167    pub doc: &'static str,
168    /// static members by `#[pyo3(get)]`
169    pub getters: &'static [MemberInfo],
170    /// static members by `#[pyo3(set)]`
171    pub setters: &'static [MemberInfo],
172    /// Base classes specified by `#[pyclass(extends = Type)]`
173    pub bases: &'static [fn() -> TypeInfo],
174    /// Whether the class has eq attribute
175    pub has_eq: bool,
176    /// Whether the class has ord attribute
177    pub has_ord: bool,
178    /// Whether the class has hash attribute
179    pub has_hash: bool,
180    /// Whether the class has str attribute
181    pub has_str: bool,
182    /// Whether the class has subclass attribute `#[pyclass(subclass)]`
183    pub subclass: bool,
184}
185
186inventory::collect!(PyClassInfo);
187
188#[derive(Debug, Clone, Copy, PartialEq)]
189pub enum VariantForm {
190    Unit,
191    Tuple,
192    Struct,
193}
194
195/// Info of a `#[pyclass]` with a single variant of a rich (structured) Rust enum
196#[derive(Debug)]
197pub struct VariantInfo {
198    pub pyclass_name: &'static str,
199    pub module: Option<&'static str>,
200    pub doc: &'static str,
201    pub fields: &'static [MemberInfo],
202    pub form: &'static VariantForm,
203    pub constr_args: &'static [ParameterInfo],
204}
205
206/// Info of a `#[pyclass]` with a rich (structured) Rust enum
207#[derive(Debug)]
208pub struct PyComplexEnumInfo {
209    // Rust struct type-id
210    pub enum_id: fn() -> TypeId,
211    // The name exposed to Python
212    pub pyclass_name: &'static str,
213    /// Module name specified by `#[pyclass(module = "foo.bar")]`
214    pub module: Option<&'static str>,
215    /// Docstring
216    pub doc: &'static str,
217    /// static members by `#[pyo3(get, set)]`
218    pub variants: &'static [VariantInfo],
219}
220
221inventory::collect!(PyComplexEnumInfo);
222
223/// Info of `#[pyclass]` with Rust enum
224#[derive(Debug)]
225pub struct PyEnumInfo {
226    // Rust struct type-id
227    pub enum_id: fn() -> TypeId,
228    // The name exposed to Python
229    pub pyclass_name: &'static str,
230    /// Module name specified by `#[pyclass(module = "foo.bar")]`
231    pub module: Option<&'static str>,
232    /// Docstring
233    pub doc: &'static str,
234    /// Variants of enum (name, doc)
235    pub variants: &'static [(&'static str, &'static str)],
236}
237
238inventory::collect!(PyEnumInfo);
239
240/// Info of `#[pyfunction]`
241#[derive(Debug)]
242pub struct PyFunctionInfo {
243    pub name: &'static str,
244    pub parameters: &'static [ParameterInfo],
245    pub r#return: fn() -> TypeInfo,
246    pub doc: &'static str,
247    pub module: Option<&'static str>,
248    pub is_async: bool,
249    pub deprecated: Option<DeprecatedInfo>,
250    pub type_ignored: Option<IgnoreTarget>,
251}
252
253inventory::collect!(PyFunctionInfo);
254
255#[derive(Debug)]
256pub struct PyVariableInfo {
257    pub name: &'static str,
258    pub module: &'static str,
259    pub r#type: fn() -> TypeInfo,
260    pub default: Option<fn() -> String>,
261}
262
263inventory::collect!(PyVariableInfo);
264
265#[derive(Debug)]
266pub struct ModuleDocInfo {
267    pub module: &'static str,
268    pub doc: fn() -> String,
269}
270
271inventory::collect!(ModuleDocInfo);