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 | |
24 | use crate::{PyStubType, TypeInfo}; |
25 | use std::any::TypeId; |
26 | |
27 | /// Work around for `CompareOp` for `__richcmp__` argument, |
28 | /// which does not implements `FromPyObject` |
29 | pub fn compare_op_type_input() -> TypeInfo { |
30 | <isize as PyStubType>::type_input() |
31 | } |
32 | |
33 | pub fn no_return_type_output() -> TypeInfo { |
34 | TypeInfo::none() |
35 | } |
36 | |
37 | /// Info of method argument appears in `#[pymethods]` |
38 | #[derive (Debug)] |
39 | pub struct ArgInfo { |
40 | pub name: &'static str, |
41 | pub r#type: fn() -> TypeInfo, |
42 | pub signature: Option<SignatureArg>, |
43 | } |
44 | #[derive (Debug, Clone)] |
45 | pub enum SignatureArg { |
46 | Ident, |
47 | Assign { |
48 | default: &'static std::sync::LazyLock<String>, |
49 | }, |
50 | Star, |
51 | Args, |
52 | Keywords, |
53 | } |
54 | |
55 | impl 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)] |
71 | pub 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)] |
82 | pub 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)] |
89 | pub struct NewInfo { |
90 | pub args: &'static [ArgInfo], |
91 | } |
92 | |
93 | /// Info of `#[pymethod]` |
94 | #[derive (Debug)] |
95 | pub 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 | |
106 | inventory::collect!(PyMethodsInfo); |
107 | |
108 | /// Info of `#[pyclass]` with Rust struct |
109 | #[derive (Debug)] |
110 | pub 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 | |
123 | inventory::collect!(PyClassInfo); |
124 | |
125 | /// Info of `#[pyclass]` with Rust enum |
126 | #[derive (Debug)] |
127 | pub 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 | |
140 | inventory::collect!(PyEnumInfo); |
141 | |
142 | /// Info of `#[pyfunction]` |
143 | #[derive (Debug)] |
144 | pub 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 | |
152 | inventory::collect!(PyFunctionInfo); |
153 | |
154 | #[derive (Debug)] |
155 | pub struct PyErrorInfo { |
156 | pub name: &'static str, |
157 | pub module: &'static str, |
158 | pub base: fn() -> &'static str, |
159 | } |
160 | |
161 | inventory::collect!(PyErrorInfo); |
162 | |
163 | #[derive (Debug)] |
164 | pub struct PyVariableInfo { |
165 | pub name: &'static str, |
166 | pub module: &'static str, |
167 | pub r#type: fn() -> TypeInfo, |
168 | } |
169 | |
170 | inventory::collect!(PyVariableInfo); |
171 | |