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//! members: &[
15//! MemberInfo {
16//! name: "name",
17//! r#type: <String as ::pyo3_stub_gen::PyStubType>::type_output,
18//! doc: "",
19//! },
20//! MemberInfo {
21//! name: "ndim",
22//! r#type: <usize as ::pyo3_stub_gen::PyStubType>::type_output,
23//! doc: "",
24//! },
25//! MemberInfo {
26//! name: "description",
27//! r#type: <Option<String> as ::pyo3_stub_gen::PyStubType>::type_output,
28//! doc: "",
29//! },
30//! ],
31//! doc: "",
32//! bases: &[],
33//! }
34//! }
35//! ```
36//!
37//! and this submodule responsible for generating such codes from Rust code like
38//!
39//! ```rust
40//! # use pyo3::*;
41//! #[pyclass(mapping, module = "my_module", name = "Placeholder")]
42//! #[derive(Debug, Clone)]
43//! pub struct PyPlaceholder {
44//! #[pyo3(get)]
45//! pub name: String,
46//! #[pyo3(get)]
47//! pub ndim: usize,
48//! #[pyo3(get)]
49//! pub description: Option<String>,
50//! pub custom_latex: Option<String>,
51//! }
52//! ```
53//!
54//! Mechanism
55//! ----------
56//! Code generation will take three steps:
57//!
58//! 1. Parse input [proc_macro2::TokenStream] into corresponding syntax tree component in [syn],
59//! - e.g. [ItemStruct] for `#[pyclass]`, [ItemImpl] for `#[pymethods]`, and so on.
60//! 2. Convert syntax tree components into `*Info` struct using [TryInto].
61//! - e.g. [PyClassInfo] is converted from [ItemStruct], [PyMethodsInfo] is converted from [ItemImpl], and so on.
62//! 3. Generate token streams using implementation of [quote::ToTokens] trait for `*Info` structs.
63//! - [quote::quote!] macro uses this trait.
64//!
65
66mod arg;
67mod attr;
68mod member;
69mod method;
70mod pyclass;
71mod pyclass_enum;
72mod pyfunction;
73mod pymethods;
74mod renaming;
75mod signature;
76mod stub_type;
77mod util;
78
79use arg::*;
80use attr::*;
81use member::*;
82use method::*;
83use pyclass::*;
84use pyclass_enum::*;
85use pyfunction::*;
86use pymethods::*;
87use renaming::*;
88use signature::*;
89use stub_type::*;
90use util::*;
91
92use proc_macro2::TokenStream as TokenStream2;
93use quote::quote;
94use syn::{parse2, ItemEnum, ItemFn, ItemImpl, ItemStruct, Result};
95
96pub fn pyclass(item: TokenStream2) -> Result<TokenStream2> {
97 let inner = PyClassInfo::try_from(parse2::<ItemStruct>(item.clone())?)?;
98 let derive_stub_type = StubType::from(&inner);
99 Ok(quote! {
100 #item
101 #derive_stub_type
102 pyo3_stub_gen::inventory::submit! {
103 #inner
104 }
105 })
106}
107
108pub fn pyclass_enum(item: TokenStream2) -> Result<TokenStream2> {
109 let inner = PyEnumInfo::try_from(parse2::<ItemEnum>(item.clone())?)?;
110 let derive_stub_type = StubType::from(&inner);
111 Ok(quote! {
112 #item
113 #derive_stub_type
114 pyo3_stub_gen::inventory::submit! {
115 #inner
116 }
117 })
118}
119
120pub fn pymethods(item: TokenStream2) -> Result<TokenStream2> {
121 let inner = PyMethodsInfo::try_from(parse2::<ItemImpl>(item.clone())?)?;
122 Ok(quote! {
123 #item
124 #[automatically_derived]
125 pyo3_stub_gen::inventory::submit! {
126 #inner
127 }
128 })
129}
130
131pub fn pyfunction(attr: TokenStream2, item: TokenStream2) -> Result<TokenStream2> {
132 let mut inner = PyFunctionInfo::try_from(parse2::<ItemFn>(item.clone())?)?;
133 inner.parse_attr(attr)?;
134 Ok(quote! {
135 #item
136 #[automatically_derived]
137 pyo3_stub_gen::inventory::submit! {
138 #inner
139 }
140 })
141}