Add tuple and unit struct support for pyclass macro

This commit is contained in:
messense 2021-03-18 00:04:25 +08:00
parent 20452a7c09
commit 773a371ba5
5 changed files with 117 additions and 7 deletions

View File

@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Add FFI definition `PyCFunction_CheckExact` for Python 3.9 and later. [#1425](https://github.com/PyO3/pyo3/pull/1425)
- Add FFI definition `Py_IS_TYPE`. [#1429](https://github.com/PyO3/pyo3/pull/1429)
- Add FFI definition `_Py_InitializeMain`. [#1473](https://github.com/PyO3/pyo3/pull/1473)
- Add tuple and unit struct support for `#[pyclass]` macro. [#1504](https://github.com/PyO3/pyo3/pull/1504)
### Changed
- Change `PyTimeAcces::get_fold()` to return a `bool` instead of a `u8`. [#1397](https://github.com/PyO3/pyo3/pull/1397)

View File

@ -179,15 +179,24 @@ pub fn build_py_class(
class.generics.span() => "#[pyclass] cannot have generic parameters"
);
if let syn::Fields::Named(fields) = &mut class.fields {
for field in fields.named.iter_mut() {
let field_descs = parse_descriptors(field)?;
if !field_descs.is_empty() {
descriptors.push((field.clone(), field_descs));
match &mut class.fields {
syn::Fields::Named(fields) => {
for field in fields.named.iter_mut() {
let field_descs = parse_descriptors(field)?;
if !field_descs.is_empty() {
descriptors.push((field.clone(), field_descs));
}
}
}
} else {
bail_spanned!(class.fields.span() => "#[pyclass] can only be used with C-style structs");
syn::Fields::Unnamed(fields) => {
for field in fields.unnamed.iter_mut() {
let field_descs = parse_descriptors(field)?;
if !field_descs.is_empty() {
descriptors.push((field.clone(), field_descs));
}
}
}
syn::Fields::Unit => { /* No fields for unit struct */ }
}
impl_class(&class.ident, &attr, doc, descriptors, methods_type)

View File

@ -18,6 +18,20 @@ fn empty_class() {
py_assert!(py, typeobj, "typeobj.__name__ == 'EmptyClass'");
}
#[pyclass]
struct UnitClass;
#[test]
fn unit_class() {
let gil = Python::acquire_gil();
let py = gil.python();
let typeobj = py.get_type::<UnitClass>();
// By default, don't allow creating instances from python.
assert!(typeobj.call((), None).is_err());
py_assert!(py, typeobj, "typeobj.__name__ == 'UnitClass'");
}
/// Line1
///Line2
/// Line3
@ -289,3 +303,16 @@ fn test_pymethods_from_py_with() {
);
})
}
#[pyclass]
struct TupleClass(i32);
#[test]
fn test_tuple_struct_class() {
let gil = Python::acquire_gil();
let py = gil.python();
let typeobj = py.get_type::<TupleClass>();
assert!(typeobj.call((), None).is_err());
py_assert!(py, typeobj, "typeobj.__name__ == 'TupleClass'");
}

View File

@ -24,6 +24,51 @@ fn empty_class_with_new() {
.is_ok());
}
#[pyclass]
struct UnitClassWithNew;
#[pymethods]
impl UnitClassWithNew {
#[new]
fn new() -> Self {
Self
}
}
#[test]
fn unit_class_with_new() {
let gil = Python::acquire_gil();
let py = gil.python();
let typeobj = py.get_type::<UnitClassWithNew>();
assert!(typeobj
.call((), None)
.unwrap()
.cast_as::<PyCell<UnitClassWithNew>>()
.is_ok());
}
#[pyclass]
struct TupleClassWithNew(i32);
#[pymethods]
impl TupleClassWithNew {
#[new]
fn new(arg: i32) -> Self {
Self(arg)
}
}
#[test]
fn tuple_class_with_new() {
let gil = Python::acquire_gil();
let py = gil.python();
let typeobj = py.get_type::<TupleClassWithNew>();
let wrp = typeobj.call((42,), None).unwrap();
let obj = wrp.cast_as::<PyCell<TupleClassWithNew>>().unwrap();
let obj_ref = obj.borrow();
assert_eq!(obj_ref.0, 42);
}
#[pyclass]
#[derive(Debug)]
struct NewWithOneArg {

View File

@ -130,3 +130,31 @@ fn ref_getter_setter() {
py_run!(py, inst, "assert inst.num == 10");
py_run!(py, inst, "inst.num = 20; assert inst.num == 20");
}
#[pyclass]
struct TupleClassGetterSetter(i32);
#[pymethods]
impl TupleClassGetterSetter {
#[getter(num)]
fn get_num(&self) -> i32 {
self.0
}
#[setter(num)]
fn set_num(&mut self, value: i32) {
self.0 = value;
}
}
#[test]
fn tuple_struct_getter_setter() {
let gil = Python::acquire_gil();
let py = gil.python();
let inst = Py::new(py, TupleClassGetterSetter(10)).unwrap();
py_run!(py, inst, "assert inst.num == 10");
py_run!(py, inst, "inst.num = 20");
py_run!(py, inst, "assert inst.num == 20");
}