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