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}
95
96/// Info of `#[pymethod]`
97#[derive(Debug)]
98pub struct PyMethodsInfo {
99    // The Rust struct type-id of `impl` block where `#[pymethod]` acts on
100    pub struct_id: fn() -> TypeId,
101    /// Methods decorated with `#[getter]`
102    pub getters: &'static [MemberInfo],
103    /// Other usual methods
104    pub methods: &'static [MethodInfo],
105}
106
107inventory::collect!(PyMethodsInfo);
108
109/// Info of `#[pyclass]` with Rust struct
110#[derive(Debug)]
111pub struct PyClassInfo {
112    // Rust struct type-id
113    pub struct_id: fn() -> TypeId,
114    // The name exposed to Python
115    pub pyclass_name: &'static str,
116    /// Module name specified by `#[pyclass(module = "foo.bar")]`
117    pub module: Option<&'static str>,
118    /// Docstring
119    pub doc: &'static str,
120    /// static members by `#[pyo3(get, set)]`
121    pub members: &'static [MemberInfo],
122    /// Base classes specified by `#[pyclass(extends = Type)]`
123    pub bases: &'static [fn() -> TypeInfo],
124}
125
126inventory::collect!(PyClassInfo);
127
128/// Info of `#[pyclass]` with Rust enum
129#[derive(Debug)]
130pub struct PyEnumInfo {
131    // Rust struct type-id
132    pub enum_id: fn() -> TypeId,
133    // The name exposed to Python
134    pub pyclass_name: &'static str,
135    /// Module name specified by `#[pyclass(module = "foo.bar")]`
136    pub module: Option<&'static str>,
137    /// Docstring
138    pub doc: &'static str,
139    /// Variants of enum (name, doc)
140    pub variants: &'static [(&'static str, &'static str)],
141}
142
143inventory::collect!(PyEnumInfo);
144
145/// Info of `#[pyfunction]`
146#[derive(Debug)]
147pub struct PyFunctionInfo {
148    pub name: &'static str,
149    pub args: &'static [ArgInfo],
150    pub r#return: fn() -> TypeInfo,
151    pub doc: &'static str,
152    pub module: Option<&'static str>,
153}
154
155inventory::collect!(PyFunctionInfo);
156
157#[derive(Debug)]
158pub struct PyErrorInfo {
159    pub name: &'static str,
160    pub module: &'static str,
161    pub base: fn() -> &'static str,
162}
163
164inventory::collect!(PyErrorInfo);
165
166#[derive(Debug)]
167pub struct PyVariableInfo {
168    pub name: &'static str,
169    pub module: &'static str,
170    pub r#type: fn() -> TypeInfo,
171}
172
173inventory::collect!(PyVariableInfo);