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/// Information about deprecated items
28#[derive(Debug, Clone, PartialEq)]
29pub struct DeprecatedInfo {
30    pub since: Option<&'static str>,
31    pub note: Option<&'static str>,
32}
33
34/// Work around for `CompareOp` for `__richcmp__` argument,
35/// which does not implements `FromPyObject`
36pub fn compare_op_type_input() -> TypeInfo {
37    <isize as PyStubType>::type_input()
38}
39
40pub fn no_return_type_output() -> TypeInfo {
41    TypeInfo::none()
42}
43
44/// Info of method argument appears in `#[pymethods]`
45#[derive(Debug)]
46pub struct ArgInfo {
47    pub name: &'static str,
48    pub r#type: fn() -> TypeInfo,
49    pub signature: Option<SignatureArg>,
50}
51#[derive(Debug, Clone)]
52pub enum SignatureArg {
53    Ident,
54    Assign {
55        default: &'static std::sync::LazyLock<String>,
56    },
57    Star,
58    Args,
59    Keywords,
60}
61
62impl PartialEq for SignatureArg {
63    #[inline]
64    fn eq(&self, other: &Self) -> bool {
65        match (self, other) {
66            (Self::Assign { default: l_default }, Self::Assign { default: r_default }) => {
67                let l_default: &String = l_default;
68                let r_default: &String = r_default;
69                l_default.eq(r_default)
70            }
71            _ => core::mem::discriminant(self) == core::mem::discriminant(other),
72        }
73    }
74}
75
76/// Type of a method
77#[derive(Debug, Clone, Copy, PartialEq)]
78pub enum MethodType {
79    Instance,
80    Static,
81    Class,
82    New,
83}
84
85/// Info of usual method appears in `#[pymethod]`
86#[derive(Debug)]
87pub struct MethodInfo {
88    pub name: &'static str,
89    pub args: &'static [ArgInfo],
90    pub r#return: fn() -> TypeInfo,
91    pub doc: &'static str,
92    pub r#type: MethodType,
93    pub is_async: bool,
94    pub deprecated: Option<DeprecatedInfo>,
95}
96
97/// Info of getter method decorated with `#[getter]` or `#[pyo3(get, set)]` appears in `#[pyclass]`
98#[derive(Debug)]
99pub struct MemberInfo {
100    pub name: &'static str,
101    pub r#type: fn() -> TypeInfo,
102    pub doc: &'static str,
103    pub default: Option<&'static std::sync::LazyLock<String>>,
104}
105
106/// Info of `#[pymethod]`
107#[derive(Debug)]
108pub struct PyMethodsInfo {
109    // The Rust struct type-id of `impl` block where `#[pymethod]` acts on
110    pub struct_id: fn() -> TypeId,
111    /// Method/Const with `#[classattr]`
112    pub attrs: &'static [MemberInfo],
113    /// Methods decorated with `#[getter]`
114    pub getters: &'static [MemberInfo],
115    /// Methods decorated with `#[getter]`
116    pub setters: &'static [MemberInfo],
117    /// Other usual methods
118    pub methods: &'static [MethodInfo],
119}
120
121inventory::collect!(PyMethodsInfo);
122
123/// Info of `#[pyclass]` with Rust struct
124#[derive(Debug)]
125pub struct PyClassInfo {
126    // Rust struct type-id
127    pub struct_id: fn() -> TypeId,
128    // The name exposed to Python
129    pub pyclass_name: &'static str,
130    /// Module name specified by `#[pyclass(module = "foo.bar")]`
131    pub module: Option<&'static str>,
132    /// Docstring
133    pub doc: &'static str,
134    /// static members by `#[pyo3(get)]`
135    pub getters: &'static [MemberInfo],
136    /// static members by `#[pyo3(set)]`
137    pub setters: &'static [MemberInfo],
138    /// Base classes specified by `#[pyclass(extends = Type)]`
139    pub bases: &'static [fn() -> TypeInfo],
140}
141
142inventory::collect!(PyClassInfo);
143
144#[derive(Debug, Clone, Copy, PartialEq)]
145pub enum VariantForm {
146    Unit,
147    Tuple,
148    Struct,
149}
150
151/// Info of a `#[pyclass]` with a single variant of a rich (structured) Rust enum
152#[derive(Debug)]
153pub struct VariantInfo {
154    pub pyclass_name: &'static str,
155    pub module: Option<&'static str>,
156    pub doc: &'static str,
157    pub fields: &'static [MemberInfo],
158    pub form: &'static VariantForm,
159    pub constr_args: &'static [ArgInfo],
160}
161
162/// Info of a `#[pyclass]` with a rich (structured) Rust enum
163#[derive(Debug)]
164pub struct PyComplexEnumInfo {
165    // Rust struct type-id
166    pub enum_id: fn() -> TypeId,
167    // The name exposed to Python
168    pub pyclass_name: &'static str,
169    /// Module name specified by `#[pyclass(module = "foo.bar")]`
170    pub module: Option<&'static str>,
171    /// Docstring
172    pub doc: &'static str,
173    /// static members by `#[pyo3(get, set)]`
174    pub variants: &'static [VariantInfo],
175}
176
177inventory::collect!(PyComplexEnumInfo);
178
179/// Info of `#[pyclass]` with Rust enum
180#[derive(Debug)]
181pub struct PyEnumInfo {
182    // Rust struct type-id
183    pub enum_id: fn() -> TypeId,
184    // The name exposed to Python
185    pub pyclass_name: &'static str,
186    /// Module name specified by `#[pyclass(module = "foo.bar")]`
187    pub module: Option<&'static str>,
188    /// Docstring
189    pub doc: &'static str,
190    /// Variants of enum (name, doc)
191    pub variants: &'static [(&'static str, &'static str)],
192}
193
194inventory::collect!(PyEnumInfo);
195
196/// Info of `#[pyfunction]`
197#[derive(Debug)]
198pub struct PyFunctionInfo {
199    pub name: &'static str,
200    pub args: &'static [ArgInfo],
201    pub r#return: fn() -> TypeInfo,
202    pub doc: &'static str,
203    pub module: Option<&'static str>,
204    pub is_async: bool,
205    pub deprecated: Option<DeprecatedInfo>,
206}
207
208inventory::collect!(PyFunctionInfo);
209
210#[derive(Debug)]
211pub struct PyErrorInfo {
212    pub name: &'static str,
213    pub module: &'static str,
214    pub base: fn() -> &'static str,
215}
216
217inventory::collect!(PyErrorInfo);
218
219#[derive(Debug)]
220pub struct PyVariableInfo {
221    pub name: &'static str,
222    pub module: &'static str,
223    pub r#type: fn() -> TypeInfo,
224}
225
226inventory::collect!(PyVariableInfo);