Merge pull request #832 from gilescope/macro
Need to be able to create structs via macro_rules
This commit is contained in:
commit
3bf283d36c
|
@ -7,7 +7,7 @@ use crate::attributes::{
|
||||||
use crate::deprecations::Deprecations;
|
use crate::deprecations::Deprecations;
|
||||||
use crate::pyimpl::PyClassMethodsType;
|
use crate::pyimpl::PyClassMethodsType;
|
||||||
use crate::pymethod::{impl_py_getter_def, impl_py_setter_def, PropertyType};
|
use crate::pymethod::{impl_py_getter_def, impl_py_setter_def, PropertyType};
|
||||||
use crate::utils;
|
use crate::utils::{self, unwrap_group};
|
||||||
use proc_macro2::{Span, TokenStream};
|
use proc_macro2::{Span, TokenStream};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::ext::IdentExt;
|
use syn::ext::IdentExt;
|
||||||
|
@ -93,7 +93,7 @@ impl PyClassArgs {
|
||||||
// We allow arbitrary expressions here so you can e.g. use `8*64`
|
// We allow arbitrary expressions here so you can e.g. use `8*64`
|
||||||
self.freelist = Some(syn::Expr::clone(right));
|
self.freelist = Some(syn::Expr::clone(right));
|
||||||
}
|
}
|
||||||
"name" => match &**right {
|
"name" => match unwrap_group(&**right) {
|
||||||
syn::Expr::Lit(syn::ExprLit {
|
syn::Expr::Lit(syn::ExprLit {
|
||||||
lit: syn::Lit::Str(lit),
|
lit: syn::Lit::Str(lit),
|
||||||
..
|
..
|
||||||
|
@ -114,7 +114,7 @@ impl PyClassArgs {
|
||||||
}
|
}
|
||||||
_ => expected!("type name (e.g. \"Name\")"),
|
_ => expected!("type name (e.g. \"Name\")"),
|
||||||
},
|
},
|
||||||
"extends" => match &**right {
|
"extends" => match unwrap_group(&**right) {
|
||||||
syn::Expr::Path(exp) => {
|
syn::Expr::Path(exp) => {
|
||||||
self.base = syn::TypePath {
|
self.base = syn::TypePath {
|
||||||
path: exp.path.clone(),
|
path: exp.path.clone(),
|
||||||
|
@ -124,7 +124,7 @@ impl PyClassArgs {
|
||||||
}
|
}
|
||||||
_ => expected!("type path (e.g., my_mod::BaseClass)"),
|
_ => expected!("type path (e.g., my_mod::BaseClass)"),
|
||||||
},
|
},
|
||||||
"module" => match &**right {
|
"module" => match unwrap_group(&**right) {
|
||||||
syn::Expr::Lit(syn::ExprLit {
|
syn::Expr::Lit(syn::ExprLit {
|
||||||
lit: syn::Lit::Str(lit),
|
lit: syn::Lit::Str(lit),
|
||||||
..
|
..
|
||||||
|
|
|
@ -117,3 +117,10 @@ pub fn ensure_not_async_fn(sig: &syn::Signature) -> syn::Result<()> {
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn unwrap_group(mut expr: &syn::Expr) -> &syn::Expr {
|
||||||
|
while let syn::Expr::Group(g) = expr {
|
||||||
|
expr = &*g.expr;
|
||||||
|
}
|
||||||
|
expr
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
//! Ensure that pyo3 macros can be used inside macro_rules!
|
||||||
|
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
use pyo3::wrap_pyfunction;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod common;
|
||||||
|
|
||||||
|
macro_rules! make_struct_using_macro {
|
||||||
|
// Ensure that one doesn't need to fall back on the escape type: tt
|
||||||
|
// in order to macro create pyclass.
|
||||||
|
($class_name:ident, $py_name:literal) => {
|
||||||
|
#[pyclass(name=$py_name)]
|
||||||
|
struct $class_name {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
make_struct_using_macro!(MyBaseClass, "MyClass");
|
||||||
|
|
||||||
|
macro_rules! set_extends_via_macro {
|
||||||
|
($class_name:ident, $base_class:path) => {
|
||||||
|
// Try and pass a variable into the extends parameter
|
||||||
|
#[pyclass(extends=$base_class)]
|
||||||
|
struct $class_name {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
set_extends_via_macro!(MyClass2, MyBaseClass);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Check that pyfunctiona nd text_signature can be called with macro arguments.
|
||||||
|
//
|
||||||
|
|
||||||
|
macro_rules! fn_macro {
|
||||||
|
($sig:literal, $a_exp:expr, $b_exp:expr, $c_exp: expr) => {
|
||||||
|
// Try and pass a variable into the extends parameter
|
||||||
|
#[pyfunction($a_exp, $b_exp, "*", $c_exp)]
|
||||||
|
#[pyo3(text_signature = $sig)]
|
||||||
|
fn my_function_in_macro(a: i32, b: Option<i32>, c: i32) {
|
||||||
|
let _ = (a, b, c);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn_macro!("(a, b=None, *, c=42)", a, b = "None", c = 42);
|
||||||
|
|
||||||
|
macro_rules! property_rename_via_macro {
|
||||||
|
($prop_name:ident) => {
|
||||||
|
#[pyclass]
|
||||||
|
struct ClassWithProperty {
|
||||||
|
member: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pymethods]
|
||||||
|
impl ClassWithProperty {
|
||||||
|
#[getter($prop_name)]
|
||||||
|
fn get_member(&self) -> u64 {
|
||||||
|
self.member
|
||||||
|
}
|
||||||
|
|
||||||
|
#[setter($prop_name)]
|
||||||
|
fn set_member(&mut self, member: u64) {
|
||||||
|
self.member = member;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
property_rename_via_macro!(my_new_property_name);
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_macro_rules_interactions() {
|
||||||
|
Python::with_gil(|py| {
|
||||||
|
let my_base = py.get_type::<MyBaseClass>();
|
||||||
|
py_assert!(py, my_base, "my_base.__name__ == 'MyClass'");
|
||||||
|
|
||||||
|
let my_func = wrap_pyfunction!(my_function_in_macro, py).unwrap();
|
||||||
|
py_assert!(
|
||||||
|
py,
|
||||||
|
my_func,
|
||||||
|
"my_func.__text_signature__ == '(a, b=None, *, c=42)'"
|
||||||
|
);
|
||||||
|
|
||||||
|
let renamed_prop = py.get_type::<ClassWithProperty>();
|
||||||
|
py_assert!(
|
||||||
|
py,
|
||||||
|
renamed_prop,
|
||||||
|
"hasattr(renamed_prop, 'my_new_property_name')"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue