#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use pyo3_macros_backend::{
build_derive_from_pyobject, build_derive_into_pyobject, build_py_class, build_py_enum,
build_py_function, build_py_methods, pymodule_function_impl, pymodule_module_impl, PyClassArgs,
PyClassMethodsType, PyFunctionOptions, PyModuleOptions,
};
use quote::quote;
use syn::{parse_macro_input, Item};
#[doc = concat!("[1]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/module.html")]
#[proc_macro_attribute]
pub fn pymodule(args: TokenStream, input: TokenStream) -> TokenStream {
let options = parse_macro_input!(args as PyModuleOptions);
let mut ast = parse_macro_input!(input as Item);
let expanded = match &mut ast {
Item::Mod(module) => {
match pymodule_module_impl(module, options) {
Ok(expanded) => return expanded.into(),
Err(e) => Err(e),
}
}
Item::Fn(function) => pymodule_function_impl(function, options),
unsupported => Err(syn::Error::new_spanned(
unsupported,
"#[pymodule] only supports modules and functions.",
)),
}
.unwrap_or_compile_error();
quote!(
#ast
#expanded
)
.into()
}
#[proc_macro_attribute]
pub fn pyclass(attr: TokenStream, input: TokenStream) -> TokenStream {
let item = parse_macro_input!(input as Item);
match item {
Item::Struct(struct_) => pyclass_impl(attr, struct_, methods_type()),
Item::Enum(enum_) => pyclass_enum_impl(attr, enum_, methods_type()),
unsupported => {
syn::Error::new_spanned(unsupported, "#[pyclass] only supports structs and enums.")
.into_compile_error()
.into()
}
}
}
#[doc = concat!("[1]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#instance-methods")]
#[doc = concat!("[2]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/features.html#multiple-pymethods")]
#[doc = concat!("[4]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#constructor")]
#[doc = concat!("[5]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#object-properties-using-getter-and-setter")]
#[doc = concat!("[6]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#static-methods")]
#[doc = concat!("[7]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#class-methods")]
#[doc = concat!("[8]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#callable-objects")]
#[doc = concat!("[9]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#class-attributes")]
#[doc = concat!("[10]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#method-arguments")]
#[doc = concat!("[11]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#object-properties-using-pyo3get-set")]
#[proc_macro_attribute]
pub fn pymethods(attr: TokenStream, input: TokenStream) -> TokenStream {
let methods_type = if cfg!(feature = "multiple-pymethods") {
PyClassMethodsType::Inventory
} else {
PyClassMethodsType::Specialization
};
pymethods_impl(attr, input, methods_type)
}
#[doc = concat!("[1]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/function.html")]
#[proc_macro_attribute]
pub fn pyfunction(attr: TokenStream, input: TokenStream) -> TokenStream {
let mut ast = parse_macro_input!(input as syn::ItemFn);
let options = parse_macro_input!(attr as PyFunctionOptions);
let expanded = build_py_function(&mut ast, options).unwrap_or_compile_error();
quote!(
#ast
#expanded
)
.into()
}
#[proc_macro_derive(IntoPyObject, attributes(pyo3))]
pub fn derive_into_py_object(item: TokenStream) -> TokenStream {
let ast = parse_macro_input!(item as syn::DeriveInput);
let expanded = build_derive_into_pyobject::<false>(&ast).unwrap_or_compile_error();
quote!(
#expanded
)
.into()
}
#[proc_macro_derive(IntoPyObjectRef, attributes(pyo3))]
pub fn derive_into_py_object_ref(item: TokenStream) -> TokenStream {
let ast = parse_macro_input!(item as syn::DeriveInput);
let expanded =
pyo3_macros_backend::build_derive_into_pyobject::<true>(&ast).unwrap_or_compile_error();
quote!(
#expanded
)
.into()
}
#[proc_macro_derive(FromPyObject, attributes(pyo3))]
pub fn derive_from_py_object(item: TokenStream) -> TokenStream {
let ast = parse_macro_input!(item as syn::DeriveInput);
let expanded = build_derive_from_pyobject(&ast).unwrap_or_compile_error();
quote!(
#expanded
)
.into()
}
fn pyclass_impl(
attrs: TokenStream,
mut ast: syn::ItemStruct,
methods_type: PyClassMethodsType,
) -> TokenStream {
let args = parse_macro_input!(attrs with PyClassArgs::parse_struct_args);
let expanded = build_py_class(&mut ast, args, methods_type).unwrap_or_compile_error();
quote!(
#ast
#expanded
)
.into()
}
fn pyclass_enum_impl(
attrs: TokenStream,
mut ast: syn::ItemEnum,
methods_type: PyClassMethodsType,
) -> TokenStream {
let args = parse_macro_input!(attrs with PyClassArgs::parse_enum_args);
let expanded = build_py_enum(&mut ast, args, methods_type).unwrap_or_compile_error();
quote!(
#ast
#expanded
)
.into()
}
fn pymethods_impl(
attr: TokenStream,
input: TokenStream,
methods_type: PyClassMethodsType,
) -> TokenStream {
let mut ast = parse_macro_input!(input as syn::ItemImpl);
let attr: TokenStream2 = attr.into();
ast.attrs.push(syn::parse_quote!( #[pyo3(#attr)] ));
let expanded = build_py_methods(&mut ast, methods_type).unwrap_or_compile_error();
quote!(
#ast
#expanded
)
.into()
}
fn methods_type() -> PyClassMethodsType {
if cfg!(feature = "multiple-pymethods") {
PyClassMethodsType::Inventory
} else {
PyClassMethodsType::Specialization
}
}
trait UnwrapOrCompileError {
fn unwrap_or_compile_error(self) -> TokenStream2;
}
impl UnwrapOrCompileError for syn::Result<TokenStream2> {
fn unwrap_or_compile_error(self) -> TokenStream2 {
self.unwrap_or_else(|e| e.into_compile_error())
}
}