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