1//! This crate declares only the proc macro attributes, as a crate defining proc macro attributes
2//! must not contain any other public items.
34#![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};
1415/// A proc macro used to implement Python modules.
16///
17/// The name of the module will be taken from the function name, unless `#[pyo3(name = "my_name")]`
18/// is also annotated on the function to override the name. **Important**: the module name should
19/// match the `lib.name` setting in `Cargo.toml`, so that Python is able to import the module
20/// without needing a custom import loader.
21///
22/// Functions annotated with `#[pymodule]` can also be annotated with the following:
23///
24/// | Annotation | Description |
25/// | :- | :- |
26/// | `#[pyo3(name = "...")]` | Defines the name of the module in Python. |
27/// | `#[pyo3(submodule)]` | Skips adding a `PyInit_` FFI symbol to the compiled binary. |
28/// | `#[pyo3(module = "...")]` | Defines the Python `dotted.path` to the parent module for use in introspection. |
29/// | `#[pyo3(crate = "pyo3")]` | Defines the path to PyO3 to use code generated by the macro. |
30///
31/// For more on creating Python modules see the [module section of the guide][1].
32///
33/// Due to technical limitations on how `#[pymodule]` is implemented, a function marked
34/// `#[pymodule]` cannot have a module with the same name in the same scope. (The
35/// `#[pymodule]` implementation generates a hidden module with the same name containing
36/// metadata about the module, which is used by `wrap_pymodule!`).
37///
38#[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 {
41let options = parse_macro_input!(args as PyModuleOptions);
4243let mut ast = parse_macro_input!(input as Item);
44let expanded = match &mut ast {
45 Item::Mod(module) => {
46match pymodule_module_impl(module, options) {
47// #[pymodule] on a module will rebuild the original ast, so we don't emit it here
48Ok(expanded) => return expanded.into(),
49Err(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();
5960quote!(
61 #ast
62 #expanded
63 )
64 .into()
65}
6667#[proc_macro_attribute]
68pub fn pyclass(attr: TokenStream, input: TokenStream) -> TokenStream {
69let item = parse_macro_input!(input as Item);
70match 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}
8081/// A proc macro used to expose methods to Python.
82///
83/// Methods within a `#[pymethods]` block can be annotated with as well as the following:
84///
85/// | Annotation | Description |
86/// | :- | :- |
87/// | [`#[new]`][4] | Defines the class constructor, like Python's `__new__` method. |
88/// | [`#[getter]`][5] and [`#[setter]`][5] | These define getters and setters, similar to Python's `@property` decorator. This is useful for getters/setters that require computation or side effects; if that is not the case consider using [`#[pyo3(get, set)]`][11] on the struct's field(s).|
89/// | [`#[staticmethod]`][6]| Defines the method as a staticmethod, like Python's `@staticmethod` decorator.|
90/// | [`#[classmethod]`][7] | Defines the method as a classmethod, like Python's `@classmethod` decorator.|
91/// | [`#[classattr]`][9] | Defines a class variable. |
92/// | [`#[args]`][10] | Deprecated way to define a method's default arguments and allows the function to receive `*args` and `**kwargs`. Use `#[pyo3(signature = (...))]` instead. |
93/// | <nobr>[`#[pyo3(<option> = <value>)`][pyo3-method-options]</nobr> | Any of the `#[pyo3]` options supported on [`macro@pyfunction`]. |
94///
95/// For more on creating class methods,
96/// see the [class section of the guide][1].
97///
98/// If the [`multiple-pymethods`][2] feature is enabled, it is possible to implement
99/// multiple `#[pymethods]` blocks for a single `#[pyclass]`.
100/// This will add a transitive dependency on the [`inventory`][3] crate.
101///
102#[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/// [3]: https://docs.rs/inventory/
105#[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"), "/class.html#object-properties-using-pyo3get-set")]
113#[proc_macro_attribute]
114pub fn pymethods(attr: TokenStream, input: TokenStream) -> TokenStream {
115let methods_type = if cfg!(feature = "multiple-pymethods") {
116 PyClassMethodsType::Inventory
117 } else {
118 PyClassMethodsType::Specialization
119 };
120 pymethods_impl(attr, input, methods_type)
121}
122123/// A proc macro used to expose Rust functions to Python.
124///
125/// Functions annotated with `#[pyfunction]` can also be annotated with the following `#[pyo3]`
126/// options:
127///
128/// | Annotation | Description |
129/// | :- | :- |
130/// | `#[pyo3(name = "...")]` | Defines the name of the function in Python. |
131/// | `#[pyo3(text_signature = "...")]` | Defines the `__text_signature__` attribute of the function in Python. |
132/// | `#[pyo3(pass_module)]` | Passes the module containing the function as a `&PyModule` first argument to the function. |
133///
134/// For more on exposing functions see the [function section of the guide][1].
135///
136/// Due to technical limitations on how `#[pyfunction]` is implemented, a function marked
137/// `#[pyfunction]` cannot have a module with the same name in the same scope. (The
138/// `#[pyfunction]` implementation generates a hidden module with the same name containing
139/// metadata about the function, which is used by `wrap_pyfunction!`).
140///
141#[doc = concat!("[1]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/function.html")]
142#[proc_macro_attribute]
143pub fn pyfunction(attr: TokenStream, input: TokenStream) -> TokenStream {
144let mut ast = parse_macro_input!(input as syn::ItemFn);
145let options = parse_macro_input!(attr as PyFunctionOptions);
146147let expanded = build_py_function(&mut ast, options).unwrap_or_compile_error();
148149quote!(
150 #ast
151 #expanded
152 )
153 .into()
154}
155156#[proc_macro_derive(IntoPyObject, attributes(pyo3))]
157pub fn derive_into_py_object(item: TokenStream) -> TokenStream {
158let ast = parse_macro_input!(item as syn::DeriveInput);
159let expanded = build_derive_into_pyobject::<false>(&ast).unwrap_or_compile_error();
160quote!(
161 #expanded
162 )
163 .into()
164}
165166#[proc_macro_derive(IntoPyObjectRef, attributes(pyo3))]
167pub fn derive_into_py_object_ref(item: TokenStream) -> TokenStream {
168let ast = parse_macro_input!(item as syn::DeriveInput);
169let expanded =
170 pyo3_macros_backend::build_derive_into_pyobject::<true>(&ast).unwrap_or_compile_error();
171quote!(
172 #expanded
173 )
174 .into()
175}
176177#[proc_macro_derive(FromPyObject, attributes(pyo3))]
178pub fn derive_from_py_object(item: TokenStream) -> TokenStream {
179let ast = parse_macro_input!(item as syn::DeriveInput);
180let expanded = build_derive_from_pyobject(&ast).unwrap_or_compile_error();
181quote!(
182 #expanded
183 )
184 .into()
185}
186187fn pyclass_impl(
188 attrs: TokenStream,
189mut ast: syn::ItemStruct,
190 methods_type: PyClassMethodsType,
191) -> TokenStream {
192let args = parse_macro_input!(attrs with PyClassArgs::parse_struct_args);
193let expanded = build_py_class(&mut ast, args, methods_type).unwrap_or_compile_error();
194195quote!(
196 #ast
197 #expanded
198 )
199 .into()
200}
201202fn pyclass_enum_impl(
203 attrs: TokenStream,
204mut ast: syn::ItemEnum,
205 methods_type: PyClassMethodsType,
206) -> TokenStream {
207let args = parse_macro_input!(attrs with PyClassArgs::parse_enum_args);
208let expanded = build_py_enum(&mut ast, args, methods_type).unwrap_or_compile_error();
209210quote!(
211 #ast
212 #expanded
213 )
214 .into()
215}
216217fn pymethods_impl(
218 attr: TokenStream,
219 input: TokenStream,
220 methods_type: PyClassMethodsType,
221) -> TokenStream {
222let mut ast = parse_macro_input!(input as syn::ItemImpl);
223// Apply all options as a #[pyo3] attribute on the ItemImpl
224 // e.g. #[pymethods(crate = "crate")] impl Foo { }
225 // -> #[pyo3(crate = "crate")] impl Foo { }
226let attr: TokenStream2 = attr.into();
227 ast.attrs.push(syn::parse_quote!( #[pyo3(#attr)] ));
228let expanded = build_py_methods(&mut ast, methods_type).unwrap_or_compile_error();
229230quote!(
231 #ast
232 #expanded
233 )
234 .into()
235}
236237fn methods_type() -> PyClassMethodsType {
238if cfg!(feature = "multiple-pymethods") {
239 PyClassMethodsType::Inventory
240 } else {
241 PyClassMethodsType::Specialization
242 }
243}
244245trait UnwrapOrCompileError {
246fn unwrap_or_compile_error(self) -> TokenStream2;
247}
248249impl UnwrapOrCompileError for syn::Result<TokenStream2> {
250fn unwrap_or_compile_error(self) -> TokenStream2 {
251self.unwrap_or_else(|e| e.into_compile_error())
252 }
253}