2018-05-19 15:27:26 +00:00
//! This crate declares only the proc macro attributes, as a crate defining proc macro attributes
2018-07-18 11:08:05 +00:00
//! must not contain any other public items.
2017-05-16 05:24:06 +00:00
2021-12-22 11:09:16 +00:00
#![ cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg)) ]
2021-02-10 15:07:25 +00:00
extern crate proc_macro ;
2019-02-01 13:01:18 +00:00
use proc_macro ::TokenStream ;
2021-12-28 01:25:01 +00:00
use proc_macro2 ::TokenStream as TokenStream2 ;
2020-12-13 11:29:39 +00:00
use pyo3_macros_backend ::{
2021-11-16 19:31:30 +00:00
build_derive_from_pyobject , build_py_class , build_py_enum , build_py_function , build_py_methods ,
2022-05-10 07:17:10 +00:00
get_doc , process_functions_in_module , pymodule_impl , PyClassArgs , PyClassMethodsType ,
PyFunctionOptions , PyModuleOptions ,
2019-02-18 19:07:41 +00:00
} ;
2019-02-01 13:01:18 +00:00
use quote ::quote ;
2021-11-19 13:33:56 +00:00
use syn ::{ parse ::Nothing , parse_macro_input } ;
2018-05-13 22:49:15 +00:00
2021-04-29 22:13:19 +00:00
/// A proc macro used to implement Python modules.
///
2021-05-24 06:48:22 +00:00
/// The name of the module will be taken from the function name, unless `#[pyo3(name = "my_name")]`
/// is also annotated on the function to override the name. **Important**: the module name should
/// match the `lib.name` setting in `Cargo.toml`, so that Python is able to import the module
/// without needing a custom import loader.
///
/// Functions annotated with `#[pymodule]` can also be annotated with the following:
///
/// | Annotation | Description |
/// | :- | :- |
/// | `#[pyo3(name = "...")]` | Defines the name of the module in Python. |
///
/// For more on creating Python modules see the [module section of the guide][1].
///
2022-09-27 06:55:21 +00:00
/// Due to technical limitations on how `#[pymodule]` is implemented, a function marked
/// `#[pymodule]` cannot have a module with the same name in the same scope. (The
/// `#[pymodule]` implementation generates a hidden module with the same name containing
/// metadata about the module, which is used by `wrap_pymodule!`).
///
2021-07-24 07:47:02 +00:00
/// [1]: https://pyo3.rs/latest/module.html
2017-06-11 23:35:24 +00:00
#[ proc_macro_attribute ]
2021-11-19 13:33:56 +00:00
pub fn pymodule ( args : TokenStream , input : TokenStream ) -> TokenStream {
parse_macro_input! ( args as Nothing ) ;
2021-05-24 06:48:22 +00:00
2021-11-19 13:33:56 +00:00
let mut ast = parse_macro_input! ( input as syn ::ItemFn ) ;
let options = match PyModuleOptions ::from_attrs ( & mut ast . attrs ) {
2021-05-24 06:48:22 +00:00
Ok ( options ) = > options ,
2022-03-21 23:53:08 +00:00
Err ( e ) = > return e . into_compile_error ( ) . into ( ) ,
2019-02-18 19:07:41 +00:00
} ;
2017-06-11 23:35:24 +00:00
2022-05-10 07:17:10 +00:00
if let Err ( err ) = process_functions_in_module ( & options , & mut ast ) {
2022-03-21 23:53:08 +00:00
return err . into_compile_error ( ) . into ( ) ;
2020-02-18 03:31:45 +00:00
}
2018-04-30 21:17:09 +00:00
2021-07-30 23:21:13 +00:00
let doc = get_doc ( & ast . attrs , None ) ;
2019-11-24 14:00:21 +00:00
2021-12-28 01:25:01 +00:00
let expanded = pymodule_impl ( & ast . sig . ident , options , doc , & ast . vis ) ;
2017-06-11 23:35:24 +00:00
2018-09-02 21:13:35 +00:00
quote! (
2018-07-03 19:11:56 +00:00
#ast
#expanded
2018-09-28 21:34:57 +00:00
)
. into ( )
2017-05-16 05:24:06 +00:00
}
#[ proc_macro_attribute ]
2019-02-01 13:01:18 +00:00
pub fn pyclass ( attr : TokenStream , input : TokenStream ) -> TokenStream {
2021-11-16 19:31:30 +00:00
use syn ::Item ;
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. " )
2022-03-21 23:53:08 +00:00
. into_compile_error ( )
2021-11-16 19:31:30 +00:00
. into ( )
}
}
2017-05-14 19:52:30 +00:00
}
2017-05-16 18:58:18 +00:00
2021-04-29 22:13:19 +00:00
/// A proc macro used to expose methods to Python.
///
2021-11-07 12:57:40 +00:00
/// Methods within a `#[pymethods]` block can be annotated with as well as the following:
2021-04-29 23:23:20 +00:00
///
2021-04-29 22:13:19 +00:00
/// | Annotation | Description |
/// | :- | :- |
/// | [`#[new]`][4] | Defines the class constructor, like Python's `__new__` method. |
2021-04-29 22:31:09 +00:00
/// | [`#[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).|
2021-04-29 22:13:19 +00:00
/// | [`#[staticmethod]`][6]| Defines the method as a staticmethod, like Python's `@staticmethod` decorator.|
/// | [`#[classmethod]`][7] | Defines the method as a classmethod, like Python's `@classmethod` decorator.|
/// | [`#[classattr]`][9] | Defines a class variable. |
2022-10-25 06:23:21 +00:00
/// | [`#[args]`][10] | Deprecated way to define a method's default arguments and allows the function to receive `*args` and `**kwargs`. Use `#[pyo3(signature = (...))]` instead. |
/// | <nobr>[`#[pyo3(<option> = <value>)`][pyo3-method-options]</nobr> | Any of the `#[pyo3]` options supported on [`macro@pyfunction`]. |
2021-04-29 22:13:19 +00:00
///
/// For more on creating class methods,
/// see the [class section of the guide][1].
///
/// If the [`multiple-pymethods`][2] feature is enabled, it is possible to implement
2021-04-29 23:23:20 +00:00
/// multiple `#[pymethods]` blocks for a single `#[pyclass]`.
/// This will add a transitive dependency on the [`inventory`][3] crate.
2021-04-29 22:13:19 +00:00
///
2021-07-24 07:47:02 +00:00
/// [1]: https://pyo3.rs/latest/class.html#instance-methods
/// [2]: https://pyo3.rs/latest/features.html#multiple-pymethods
2021-04-29 22:13:19 +00:00
/// [3]: https://docs.rs/inventory/
2021-07-24 07:47:02 +00:00
/// [4]: https://pyo3.rs/latest/class.html#constructor
/// [5]: https://pyo3.rs/latest/class.html#object-properties-using-getter-and-setter
/// [6]: https://pyo3.rs/latest/class.html#static-methods
/// [7]: https://pyo3.rs/latest/class.html#class-methods
/// [8]: https://pyo3.rs/latest/class.html#callable-objects
/// [9]: https://pyo3.rs/latest/class.html#class-attributes
/// [10]: https://pyo3.rs/latest/class.html#method-arguments
/// [11]: https://pyo3.rs/latest/class.html#object-properties-using-pyo3get-set
2017-05-16 18:58:18 +00:00
#[ proc_macro_attribute ]
2023-01-27 06:34:12 +00:00
pub fn pymethods ( attr : TokenStream , input : TokenStream ) -> TokenStream {
2021-08-19 20:55:39 +00:00
let methods_type = if cfg! ( feature = " multiple-pymethods " ) {
PyClassMethodsType ::Inventory
} else {
PyClassMethodsType ::Specialization
} ;
2023-01-27 06:34:12 +00:00
pymethods_impl ( attr , input , methods_type )
2017-05-16 18:58:18 +00:00
}
2018-04-30 21:17:09 +00:00
2021-04-29 22:13:19 +00:00
/// A proc macro used to expose Rust functions to Python.
///
2022-01-06 23:21:06 +00:00
/// Functions annotated with `#[pyfunction]` can also be annotated with the following `#[pyo3]`
/// options:
2021-05-07 21:43:57 +00:00
///
/// | Annotation | Description |
/// | :- | :- |
/// | `#[pyo3(name = "...")]` | Defines the name of the function in Python. |
2021-06-05 15:28:31 +00:00
/// | `#[pyo3(text_signature = "...")]` | Defines the `__text_signature__` attribute of the function in Python. |
2021-06-05 15:39:54 +00:00
/// | `#[pyo3(pass_module)]` | Passes the module containing the function as a `&PyModule` first argument to the function. |
2021-05-07 21:43:57 +00:00
///
/// For more on exposing functions see the [function section of the guide][1].
2021-04-29 22:13:19 +00:00
///
2022-01-06 23:21:06 +00:00
/// Due to technical limitations on how `#[pyfunction]` is implemented, a function marked
/// `#[pyfunction]` cannot have a module with the same name in the same scope. (The
/// `#[pyfunction]` implementation generates a hidden module with the same name containing
/// metadata about the function, which is used by `wrap_pyfunction!`).
///
2021-07-24 07:47:02 +00:00
/// [1]: https://pyo3.rs/latest/function.html
2018-04-30 21:17:09 +00:00
#[ proc_macro_attribute ]
2019-02-18 19:07:41 +00:00
pub fn pyfunction ( attr : TokenStream , input : TokenStream ) -> TokenStream {
2019-11-24 14:00:21 +00:00
let mut ast = parse_macro_input! ( input as syn ::ItemFn ) ;
2021-04-17 21:22:06 +00:00
let options = parse_macro_input! ( attr as PyFunctionOptions ) ;
2018-04-30 21:17:09 +00:00
2021-12-28 01:25:01 +00:00
let expanded = build_py_function ( & mut ast , options ) . unwrap_or_compile_error ( ) ;
2018-04-30 21:17:09 +00:00
2018-09-02 21:13:35 +00:00
quote! (
2018-07-03 19:11:56 +00:00
#ast
#expanded
2018-09-28 21:34:57 +00:00
)
. into ( )
2018-04-30 21:17:09 +00:00
}
2020-08-24 22:00:12 +00:00
2021-01-09 17:33:28 +00:00
#[ proc_macro_derive(FromPyObject, attributes(pyo3)) ]
2020-08-24 22:00:12 +00:00
pub fn derive_from_py_object ( item : TokenStream ) -> TokenStream {
2020-08-26 20:13:14 +00:00
let ast = parse_macro_input! ( item as syn ::DeriveInput ) ;
2021-12-28 01:25:01 +00:00
let expanded = build_derive_from_pyobject ( & ast ) . unwrap_or_compile_error ( ) ;
2020-08-24 22:00:12 +00:00
quote! (
#expanded
)
. into ( )
}
2021-03-02 22:34:25 +00:00
fn pyclass_impl (
2021-11-16 19:31:30 +00:00
attrs : TokenStream ,
mut ast : syn ::ItemStruct ,
2021-03-02 22:34:25 +00:00
methods_type : PyClassMethodsType ,
) -> TokenStream {
2021-11-16 19:31:30 +00:00
let args = parse_macro_input! ( attrs with PyClassArgs ::parse_stuct_args ) ;
2022-03-18 14:58:44 +00:00
let expanded = build_py_class ( & mut ast , args , methods_type ) . unwrap_or_compile_error ( ) ;
2021-03-02 22:34:25 +00:00
quote! (
#ast
#expanded
)
. into ( )
}
2021-11-16 19:31:30 +00:00
fn pyclass_enum_impl (
2021-11-23 20:21:54 +00:00
attrs : TokenStream ,
mut ast : syn ::ItemEnum ,
2021-11-16 19:31:30 +00:00
methods_type : PyClassMethodsType ,
) -> TokenStream {
2021-11-23 20:21:54 +00:00
let args = parse_macro_input! ( attrs with PyClassArgs ::parse_enum_args ) ;
2022-03-18 14:58:44 +00:00
let expanded = build_py_enum ( & mut ast , args , methods_type ) . unwrap_or_compile_error ( ) ;
2021-11-16 19:31:30 +00:00
quote! (
2021-11-23 20:21:54 +00:00
#ast
2021-11-16 19:31:30 +00:00
#expanded
)
. into ( )
}
2023-01-27 06:34:12 +00:00
fn pymethods_impl (
attr : TokenStream ,
input : TokenStream ,
methods_type : PyClassMethodsType ,
) -> TokenStream {
2021-03-02 22:34:25 +00:00
let mut ast = parse_macro_input! ( input as syn ::ItemImpl ) ;
2023-01-27 06:34:12 +00:00
// Apply all options as a #[pyo3] attribute on the ItemImpl
// e.g. #[pymethods(crate = "crate")] impl Foo { }
// -> #[pyo3(crate = "crate")] impl Foo { }
let attr : TokenStream2 = attr . into ( ) ;
ast . attrs . push ( syn ::parse_quote! ( #[ pyo3(#attr) ] ) ) ;
2021-12-28 01:25:01 +00:00
let expanded = build_py_methods ( & mut ast , methods_type ) . unwrap_or_compile_error ( ) ;
2021-03-02 22:34:25 +00:00
quote! (
#ast
#expanded
)
. into ( )
}
2021-11-16 19:31:30 +00:00
fn methods_type ( ) -> PyClassMethodsType {
if cfg! ( feature = " multiple-pymethods " ) {
PyClassMethodsType ::Inventory
} else {
PyClassMethodsType ::Specialization
}
}
2021-12-28 01:25:01 +00:00
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 ( ) )
}
}