pyo3_stub_gen_derive/
gen_stub.rs

1//! Code generation for embedding metadata for generating Python stub file.
2//!
3//! These metadata are embedded as `inventory::submit!` block like:
4//!
5//! ```rust
6//! # use pyo3::*;
7//! # use pyo3_stub_gen::type_info::*;
8//! # struct PyPlaceholder;
9//! inventory::submit!{
10//!     PyClassInfo {
11//!         pyclass_name: "Placeholder",
12//!         module: Some("my_module"),
13//!         struct_id: std::any::TypeId::of::<PyPlaceholder>,
14//!         getters: &[
15//!             MemberInfo {
16//!                 name: "name",
17//!                 r#type: <String as ::pyo3_stub_gen::PyStubType>::type_output,
18//!                 doc: "",
19//!                 default: None,
20//!                 deprecated: None,
21//!             },
22//!             MemberInfo {
23//!                 name: "ndim",
24//!                 r#type: <usize as ::pyo3_stub_gen::PyStubType>::type_output,
25//!                 doc: "",
26//!                 default: None,
27//!                 deprecated: None,
28//!             },
29//!             MemberInfo {
30//!                 name: "description",
31//!                 r#type: <Option<String> as ::pyo3_stub_gen::PyStubType>::type_output,
32//!                 doc: "",
33//!                 default: None,
34//!                 deprecated: None,
35//!             },
36//!         ],
37//!         setters: &[],
38//!         doc: "",
39//!         bases: &[],
40//!     }
41//! }
42//! ```
43//!
44//! and this submodule responsible for generating such codes from Rust code like
45//!
46//! ```rust
47//! # use pyo3::*;
48//! #[pyclass(mapping, module = "my_module", name = "Placeholder")]
49//! #[derive(Debug, Clone)]
50//! pub struct PyPlaceholder {
51//!     #[pyo3(get)]
52//!     pub name: String,
53//!     #[pyo3(get)]
54//!     pub ndim: usize,
55//!     #[pyo3(get)]
56//!     pub description: Option<String>,
57//!     pub custom_latex: Option<String>,
58//! }
59//! ```
60//!
61//! Mechanism
62//! ----------
63//! Code generation will take three steps:
64//!
65//! 1. Parse input [proc_macro2::TokenStream] into corresponding syntax tree component in [syn],
66//!    - e.g. [ItemStruct] for `#[pyclass]`, [ItemImpl] for `#[pymethods]`, and so on.
67//! 2. Convert syntax tree components into `*Info` struct using [TryInto].
68//!    - e.g. [PyClassInfo] is converted from [ItemStruct], [PyMethodsInfo] is converted from [ItemImpl], and so on.
69//! 3. Generate token streams using implementation of [quote::ToTokens] trait for `*Info` structs.
70//!    - [quote::quote!] macro uses this trait.
71//!
72
73mod arg;
74mod attr;
75mod member;
76mod method;
77mod pyclass;
78mod pyclass_complex_enum;
79mod pyclass_enum;
80mod pyfunction;
81mod pymethods;
82mod renaming;
83mod signature;
84mod stub_type;
85mod util;
86mod variant;
87
88use arg::*;
89use attr::*;
90use member::*;
91use method::*;
92use pyclass::*;
93use pyclass_complex_enum::*;
94use pyclass_enum::*;
95use pyfunction::*;
96use pymethods::*;
97use renaming::*;
98use signature::*;
99use stub_type::*;
100use util::*;
101
102use proc_macro2::TokenStream as TokenStream2;
103use quote::quote;
104use syn::{parse2, ItemEnum, ItemFn, ItemImpl, ItemStruct, Result};
105
106pub fn pyclass(item: TokenStream2) -> Result<TokenStream2> {
107    let mut item_struct = parse2::<ItemStruct>(item)?;
108    let inner = PyClassInfo::try_from(item_struct.clone())?;
109    let derive_stub_type = StubType::from(&inner);
110    pyclass::prune_attrs(&mut item_struct);
111    Ok(quote! {
112        #item_struct
113        #derive_stub_type
114        pyo3_stub_gen::inventory::submit! {
115            #inner
116        }
117    })
118}
119
120pub fn pyclass_enum(item: TokenStream2) -> Result<TokenStream2> {
121    let inner = PyEnumInfo::try_from(parse2::<ItemEnum>(item.clone())?)?;
122    let derive_stub_type = StubType::from(&inner);
123    Ok(quote! {
124        #item
125        #derive_stub_type
126        pyo3_stub_gen::inventory::submit! {
127            #inner
128        }
129    })
130}
131
132pub fn pyclass_complex_enum(item: TokenStream2) -> Result<TokenStream2> {
133    let inner = PyComplexEnumInfo::try_from(parse2::<ItemEnum>(item.clone())?)?;
134    let derive_stub_type = StubType::from(&inner);
135    Ok(quote! {
136        #item
137        #derive_stub_type
138        pyo3_stub_gen::inventory::submit! {
139            #inner
140        }
141    })
142}
143
144pub fn pymethods(item: TokenStream2) -> Result<TokenStream2> {
145    let mut item_impl = parse2::<ItemImpl>(item)?;
146    let inner = PyMethodsInfo::try_from(item_impl.clone())?;
147    pymethods::prune_attrs(&mut item_impl);
148    Ok(quote! {
149        #item_impl
150        #[automatically_derived]
151        pyo3_stub_gen::inventory::submit! {
152            #inner
153        }
154    })
155}
156
157pub fn pyfunction(attr: TokenStream2, item: TokenStream2) -> Result<TokenStream2> {
158    let mut item_fn = parse2::<ItemFn>(item)?;
159    let mut inner = PyFunctionInfo::try_from(item_fn.clone())?;
160    inner.parse_attr(attr)?;
161    pyfunction::prune_attrs(&mut item_fn);
162    Ok(quote! {
163        #item_fn
164        #[automatically_derived]
165        pyo3_stub_gen::inventory::submit! {
166            #inner
167        }
168    })
169}