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: &&'static LazyLock }, Self::Assign { default: r_default: &&'static LazyLock }) => {
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/// Info of usual method appears in `#[pymethod]`
70#[derive(Debug)]
71pub struct MethodInfo {
72 pub name: &'static str,
73 pub args: &'static [ArgInfo],
74 pub r#return: fn() -> TypeInfo,
75 pub doc: &'static str,
76 pub is_static: bool,
77 pub is_class: bool,
78}
79
80/// Info of getter method decorated with `#[getter]` or `#[pyo3(get, set)]` appears in `#[pyclass]`
81#[derive(Debug)]
82pub struct MemberInfo {
83 pub name: &'static str,
84 pub r#type: fn() -> TypeInfo,
85}
86
87/// Info of `#[new]`-attributed methods appears in `#[pymethods]`
88#[derive(Debug)]
89pub struct NewInfo {
90 pub args: &'static [ArgInfo],
91}
92
93/// Info of `#[pymethod]`
94#[derive(Debug)]
95pub struct PyMethodsInfo {
96 // The Rust struct type-id of `impl` block where `#[pymethod]` acts on
97 pub struct_id: fn() -> TypeId,
98 /// Method specified `#[new]` attribute
99 pub new: Option<NewInfo>,
100 /// Methods decorated with `#[getter]`
101 pub getters: &'static [MemberInfo],
102 /// Other usual methods
103 pub methods: &'static [MethodInfo],
104}
105
106inventory::collect!(PyMethodsInfo);
107
108/// Info of `#[pyclass]` with Rust struct
109#[derive(Debug)]
110pub struct PyClassInfo {
111 // Rust struct type-id
112 pub struct_id: fn() -> TypeId,
113 // The name exposed to Python
114 pub pyclass_name: &'static str,
115 /// Module name specified by `#[pyclass(module = "foo.bar")]`
116 pub module: Option<&'static str>,
117 /// Docstring
118 pub doc: &'static str,
119 /// static members by `#[pyo3(get, set)]`
120 pub members: &'static [MemberInfo],
121}
122
123inventory::collect!(PyClassInfo);
124
125/// Info of `#[pyclass]` with Rust enum
126#[derive(Debug)]
127pub struct PyEnumInfo {
128 // Rust struct type-id
129 pub enum_id: fn() -> TypeId,
130 // The name exposed to Python
131 pub pyclass_name: &'static str,
132 /// Module name specified by `#[pyclass(module = "foo.bar")]`
133 pub module: Option<&'static str>,
134 /// Docstring
135 pub doc: &'static str,
136 /// Variants of enum
137 pub variants: &'static [&'static str],
138}
139
140inventory::collect!(PyEnumInfo);
141
142/// Info of `#[pyfunction]`
143#[derive(Debug)]
144pub struct PyFunctionInfo {
145 pub name: &'static str,
146 pub args: &'static [ArgInfo],
147 pub r#return: fn() -> TypeInfo,
148 pub doc: &'static str,
149 pub module: Option<&'static str>,
150}
151
152inventory::collect!(PyFunctionInfo);
153
154#[derive(Debug)]
155pub struct PyErrorInfo {
156 pub name: &'static str,
157 pub module: &'static str,
158 pub base: fn() -> &'static str,
159}
160
161inventory::collect!(PyErrorInfo);
162
163#[derive(Debug)]
164pub struct PyVariableInfo {
165 pub name: &'static str,
166 pub module: &'static str,
167 pub r#type: fn() -> TypeInfo,
168}
169
170inventory::collect!(PyVariableInfo);
171