1#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
5use proc_macro::TokenStream;
6use proc_macro2::TokenStream as TokenStream2;
7use pyo3_macros_backend::{
8 build_derive_from_pyobject, build_derive_into_pyobject, build_py_class, build_py_enum,
9 build_py_function, build_py_methods, pymodule_function_impl, pymodule_module_impl, PyClassArgs,
10 PyClassMethodsType, PyFunctionOptions, PyModuleOptions,
11};
12use quote::quote;
13use syn::{parse_macro_input, Item};
14
15#[doc = concat!("[1]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/module.html")]
39#[proc_macro_attribute]
40pub fn pymodule(args: TokenStream, input: TokenStream) -> TokenStream {
41 let options = parse_macro_input!(args as PyModuleOptions);
42
43 let mut ast = parse_macro_input!(input as Item);
44 let expanded = match &mut ast {
45 Item::Mod(module) => {
46 match pymodule_module_impl(module, options) {
47 Ok(expanded) => return expanded.into(),
49 Err(e) => Err(e),
50 }
51 }
52 Item::Fn(function) => pymodule_function_impl(function, options),
53 unsupported => Err(syn::Error::new_spanned(
54 unsupported,
55 "#[pymodule] only supports modules and functions.",
56 )),
57 }
58 .unwrap_or_compile_error();
59
60 quote!(
61 #ast
62 #expanded
63 )
64 .into()
65}
66
67#[proc_macro_attribute]
68pub fn pyclass(attr: TokenStream, input: TokenStream) -> TokenStream {
69 let item = parse_macro_input!(input as Item);
70 match item {
71 Item::Struct(struct_) => pyclass_impl(attr, struct_, methods_type()),
72 Item::Enum(enum_) => pyclass_enum_impl(attr, enum_, methods_type()),
73 unsupported => {
74 syn::Error::new_spanned(unsupported, "#[pyclass] only supports structs and enums.")
75 .into_compile_error()
76 .into()
77 }
78 }
79}
80
81#[doc = concat!("[1]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#instance-methods")]
103#[doc = concat!("[2]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/features.html#multiple-pymethods")]
104#[doc = concat!("[4]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#constructor")]
106#[doc = concat!("[5]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#object-properties-using-getter-and-setter")]
107#[doc = concat!("[6]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#static-methods")]
108#[doc = concat!("[7]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#class-methods")]
109#[doc = concat!("[8]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#callable-objects")]
110#[doc = concat!("[9]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#class-attributes")]
111#[doc = concat!("[10]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#method-arguments")]
112#[doc = concat!("[11]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/function.html#function-options")]
113#[doc = concat!("[12]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#object-properties-using-pyo3get-set")]
114#[proc_macro_attribute]
115pub fn pymethods(attr: TokenStream, input: TokenStream) -> TokenStream {
116 let methods_type = if cfg!(feature = "multiple-pymethods") {
117 PyClassMethodsType::Inventory
118 } else {
119 PyClassMethodsType::Specialization
120 };
121 pymethods_impl(attr, input, methods_type)
122}
123
124#[doc = concat!("[1]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/function.html")]
144#[proc_macro_attribute]
145pub fn pyfunction(attr: TokenStream, input: TokenStream) -> TokenStream {
146 let mut ast = parse_macro_input!(input as syn::ItemFn);
147 let options = parse_macro_input!(attr as PyFunctionOptions);
148
149 let expanded = build_py_function(&mut ast, options).unwrap_or_compile_error();
150
151 quote!(
152 #ast
153 #expanded
154 )
155 .into()
156}
157
158#[proc_macro_derive(IntoPyObject, attributes(pyo3))]
159pub fn derive_into_py_object(item: TokenStream) -> TokenStream {
160 let ast = parse_macro_input!(item as syn::DeriveInput);
161 let expanded = build_derive_into_pyobject::<false>(&ast).unwrap_or_compile_error();
162 quote!(
163 #expanded
164 )
165 .into()
166}
167
168#[proc_macro_derive(IntoPyObjectRef, attributes(pyo3))]
169pub fn derive_into_py_object_ref(item: TokenStream) -> TokenStream {
170 let ast = parse_macro_input!(item as syn::DeriveInput);
171 let expanded =
172 pyo3_macros_backend::build_derive_into_pyobject::<true>(&ast).unwrap_or_compile_error();
173 quote!(
174 #expanded
175 )
176 .into()
177}
178
179#[proc_macro_derive(FromPyObject, attributes(pyo3))]
180pub fn derive_from_py_object(item: TokenStream) -> TokenStream {
181 let ast = parse_macro_input!(item as syn::DeriveInput);
182 let expanded = build_derive_from_pyobject(&ast).unwrap_or_compile_error();
183 quote!(
184 #expanded
185 )
186 .into()
187}
188
189fn pyclass_impl(
190 attrs: TokenStream,
191 mut ast: syn::ItemStruct,
192 methods_type: PyClassMethodsType,
193) -> TokenStream {
194 let args = parse_macro_input!(attrs with PyClassArgs::parse_struct_args);
195 let expanded = build_py_class(&mut ast, args, methods_type).unwrap_or_compile_error();
196
197 quote!(
198 #ast
199 #expanded
200 )
201 .into()
202}
203
204fn pyclass_enum_impl(
205 attrs: TokenStream,
206 mut ast: syn::ItemEnum,
207 methods_type: PyClassMethodsType,
208) -> TokenStream {
209 let args = parse_macro_input!(attrs with PyClassArgs::parse_enum_args);
210 let expanded = build_py_enum(&mut ast, args, methods_type).unwrap_or_compile_error();
211
212 quote!(
213 #ast
214 #expanded
215 )
216 .into()
217}
218
219fn pymethods_impl(
220 attr: TokenStream,
221 input: TokenStream,
222 methods_type: PyClassMethodsType,
223) -> TokenStream {
224 let mut ast = parse_macro_input!(input as syn::ItemImpl);
225 let attr: TokenStream2 = attr.into();
229 ast.attrs.push(syn::parse_quote!( #[pyo3(#attr)] ));
230 let expanded = build_py_methods(&mut ast, methods_type).unwrap_or_compile_error();
231
232 quote!(
233 #ast
234 #expanded
235 )
236 .into()
237}
238
239fn methods_type() -> PyClassMethodsType {
240 if cfg!(feature = "multiple-pymethods") {
241 PyClassMethodsType::Inventory
242 } else {
243 PyClassMethodsType::Specialization
244 }
245}
246
247trait UnwrapOrCompileError {
248 fn unwrap_or_compile_error(self) -> TokenStream2;
249}
250
251impl UnwrapOrCompileError for syn::Result<TokenStream2> {
252 fn unwrap_or_compile_error(self) -> TokenStream2 {
253 self.unwrap_or_else(|e| e.into_compile_error())
254 }
255}