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