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