From 452a4e8d7f1b1ac4d91e5b73cf23b1c9d683d1d2 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sat, 20 May 2017 10:40:33 -0700 Subject: [PATCH] more mapping protocol methods; start working on class tests --- pyo3cls/Cargo.toml | 2 + pyo3cls/src/func.rs | 57 +++-- pyo3cls/src/lib.rs | 1 + pyo3cls/src/method.rs | 4 +- pyo3cls/src/py_method.rs | 4 +- pyo3cls/src/py_proto.rs | 48 +++- src/class/basic.rs | 30 +-- src/class/context.rs | 3 +- src/class/mapping.rs | 90 +++++++ src/class/sequence.rs | 2 +- src/objects/bytearray.rs | 3 +- tests/test_class.rs | 539 +++++++++++++++++++++++---------------- 12 files changed, 510 insertions(+), 273 deletions(-) diff --git a/pyo3cls/Cargo.toml b/pyo3cls/Cargo.toml index 9845b904..b0561344 100644 --- a/pyo3cls/Cargo.toml +++ b/pyo3cls/Cargo.toml @@ -8,6 +8,8 @@ proc-macro = true [dependencies] quote="0.3" +log="0.3" +env_logger = "0.4" [dependencies.syn] version="0.11" diff --git a/pyo3cls/src/func.rs b/pyo3cls/src/func.rs index 94476cb8..7cf8d93b 100644 --- a/pyo3cls/src/func.rs +++ b/pyo3cls/src/func.rs @@ -5,9 +5,8 @@ use quote::Tokens; // Add lifetime support for args with Rptr pub enum MethodProto { - Len{name: &'static str, proto: &'static str}, - Unary{name: &'static str, proto: &'static str}, - Binary{name: &'static str, arg: &'static str, proto: &'static str}, + Unary{name: &'static str, pyres: bool, proto: &'static str, }, + Binary{name: &'static str, arg: &'static str, pyres: bool, proto: &'static str}, Ternary{name: &'static str, arg1: &'static str, arg2: &'static str, proto: &'static str}, Quaternary{name: &'static str, arg1: &'static str, @@ -19,9 +18,8 @@ impl MethodProto { pub fn eq(&self, name: &str) -> bool { match *self { - MethodProto::Len{name: n, proto: _} => n == name, - MethodProto::Unary{name: n, proto: _} => n == name, - MethodProto::Binary{name: n, arg: _, proto: _} => n == name, + MethodProto::Unary{name: n, pyres: _, proto: _} => n == name, + MethodProto::Binary{name: n, arg: _, pyres: _, proto: _} => n == name, MethodProto::Ternary{name: n, arg1: _, arg2: _, proto: _} => n == name, MethodProto::Quaternary{name: n, arg1: _, arg2: _, arg3: _, proto: _} => n == name, } @@ -35,36 +33,45 @@ pub fn impl_method_proto(cls: &Box, match sig.decl.output { syn::FunctionRetTy::Ty(ref ty) => { match *meth { - MethodProto::Len{name: _, proto} => { - let p = syn::Ident::from(proto); - quote! { - impl #p for #cls { - type Result = #ty; - } - } - }, - MethodProto::Unary{name: _, proto} => { + MethodProto::Unary{name: _, pyres, proto} => { let p = syn::Ident::from(proto); let succ = get_res_success(ty); - quote! { - impl #p for #cls { - type Success = #succ; - type Result = #ty; + if pyres { + quote! { + impl #p for #cls { + type Success = #succ; + type Result = #ty; + } + } + } else { + quote! { + impl #p for #cls { + type Result = #ty; + } } } }, - MethodProto::Binary{name: _, arg, proto} => { + MethodProto::Binary{name: _, arg, pyres, proto} => { let p = syn::Ident::from(proto); let arg_name = syn::Ident::from(arg); let arg_ty = get_arg_ty(sig, 2); let succ = get_res_success(ty); - quote! { - impl #p for #cls { - type #arg_name = #arg_ty; - type Success = #succ; - type Result = #ty; + if pyres { + quote! { + impl #p for #cls { + type #arg_name = #arg_ty; + type Success = #succ; + type Result = #ty; + } + } + } else { + quote! { + impl #p for #cls { + type #arg_name = #arg_ty; + type Result = #ty; + } } } }, diff --git a/pyo3cls/src/lib.rs b/pyo3cls/src/lib.rs index 7b69359f..004912d2 100644 --- a/pyo3cls/src/lib.rs +++ b/pyo3cls/src/lib.rs @@ -6,6 +6,7 @@ extern crate proc_macro; extern crate syn; #[macro_use] extern crate quote; +#[macro_use] extern crate log; use std::str::FromStr; use proc_macro::TokenStream; diff --git a/pyo3cls/src/method.rs b/pyo3cls/src/method.rs index a4fbb974..a54cdde4 100644 --- a/pyo3cls/src/method.rs +++ b/pyo3cls/src/method.rs @@ -192,10 +192,10 @@ fn parse_attributes(attrs: &mut Vec) -> (FnType, Vec) { match attr.value { syn::MetaItem::Word(ref name) => { match name.as_ref() { - "new" => { + "new" | "__new__" => { res = Some(FnType::FnNew) }, - "call" => { + "call" | "__call__" => { res = Some(FnType::FnCall) }, "setter" | "getter" => { diff --git a/pyo3cls/src/py_method.rs b/pyo3cls/src/py_method.rs index 98a2040d..37077a58 100644 --- a/pyo3cls/src/py_method.rs +++ b/pyo3cls/src/py_method.rs @@ -2,8 +2,6 @@ use syn; use quote::{Tokens, ToTokens}; -use utils::for_err_msg; - use method::{FnArg, FnSpec, FnType}; @@ -207,7 +205,7 @@ fn impl_arg_params(spec: &FnSpec, body: Tokens) -> Tokens { let mut rargs = spec.args.clone(); rargs.reverse(); let mut body = body; - for arg in spec.args.iter() { + for arg in rargs.iter() { body = impl_arg_param(&arg, &spec, &body); } diff --git a/pyo3cls/src/py_proto.rs b/pyo3cls/src/py_proto.rs index 99787e2f..3cf0885e 100644 --- a/pyo3cls/src/py_proto.rs +++ b/pyo3cls/src/py_proto.rs @@ -45,15 +45,19 @@ static ASYNC: Proto = Proto { methods: &[ MethodProto::Unary { name: "__await__", + pyres: true, proto: "_pyo3::class::async::PyAsyncAwaitProtocol"}, MethodProto::Unary{ name: "__aiter__", + pyres: true, proto: "_pyo3::class::async::PyAsyncAiterProtocol"}, MethodProto::Unary{ name: "__anext__", + pyres: true, proto: "_pyo3::class::async::PyAsyncAnextProtocol"}, MethodProto::Unary{ name: "__aenter__", + pyres: true, proto: "_pyo3::class::async::PyAsyncAenterProtocol"}, MethodProto::Quaternary { name: "__aexit__", @@ -77,6 +81,7 @@ static CONTEXT: Proto = Proto { methods: &[ MethodProto::Unary{ name: "__enter__", + pyres: true, proto: "_pyo3::class::context::PyContextEnterProtocol"}, MethodProto::Quaternary { name: "__exit__", @@ -101,9 +106,11 @@ static ITER: Proto = Proto { methods: &[ MethodProto::Unary{ name: "__iter__", + pyres: true, proto: "_pyo3::class::iter::PyIterIterProtocol"}, MethodProto::Unary{ name: "__next__", + pyres: true, proto: "_pyo3::class::iter::PyIterNextProtocol"}, ], }; @@ -111,14 +118,15 @@ static ITER: Proto = Proto { static MAPPING: Proto = Proto { name: "Mapping", - py_methods: &[], methods: &[ - MethodProto::Len{ + MethodProto::Unary{ name: "__len__", + pyres: false, proto: "_pyo3::class::mapping::PyMappingLenProtocol"}, MethodProto::Binary{ name: "__getitem__", arg: "Key", + pyres: true, proto: "_pyo3::class::mapping::PyMappingGetItemProtocol"}, MethodProto::Ternary{ name: "__setitem__", @@ -128,8 +136,37 @@ static MAPPING: Proto = Proto { MethodProto::Binary{ name: "__delitem__", arg: "Key", + pyres: true, proto: "_pyo3::class::mapping::PyMappingDelItemProtocol"}, + MethodProto::Binary{ + name: "__contains__", + arg: "Value", + pyres: false, + proto: "_pyo3::class::mapping::PyMappingContainsProtocol"}, + MethodProto::Unary{ + name: "__reversed__", + pyres: true, + proto: "_pyo3::class::mapping::PyMappingReversedProtocol"}, + MethodProto::Unary{ + name: "__iter__", + pyres: true, + proto: "_pyo3::class::mapping::PyMappingIterProtocol"}, ], + py_methods: &[ + PyMethod { + name: "__iter__", + proto: "_pyo3::class::mapping::PyMappingIterProtocolImpl", + }, + PyMethod { + name: "__contains__", + proto: "_pyo3::class::mapping::PyMappingContainsProtocolImpl", + }, + PyMethod { + name: "__reversed__", + proto: "_pyo3::class::mapping::PyMappingReversedProtocolImpl", + }, + ], + }; @@ -160,12 +197,15 @@ pub fn build_py_proto(ast: &mut syn::Item) -> Tokens { impl_protocol("_pyo3::class::gc::PyGCProtocolImpl", path.clone(), ty, impl_items, &DEFAULT_METHODS), "PySequenceProtocol" => - impl_protocol("_pyo3::class::mapping::PySequenceProtocolImpl", + impl_protocol("_pyo3::class::sequence::PySequenceProtocolImpl", path.clone(), ty, impl_items, &DEFAULT_METHODS), "PyNumberProtocol" => impl_protocol("_pyo3::class::number::PyNumberProtocolImpl", path.clone(), ty, impl_items, &NUM_METHODS), - _ => panic!("#[proto] can not be used with this block"), + _ => { + warn!("#[proto] can not be used with this block"); + Tokens::new() + } } } else { panic!("#[proto] can only be used with protocol trait implementations") diff --git a/src/class/basic.rs b/src/class/basic.rs index b31e2dc1..2c762edd 100644 --- a/src/class/basic.rs +++ b/src/class/basic.rs @@ -16,11 +16,10 @@ use conversion::ToPyObject; use callback::{handle_callback, PyObjectCallbackConverter, HashConverter, UnitCallbackConverter}; use class::{NO_METHODS, NO_PY_METHODS}; -// __new__ -// __init__ -// __call__ // classmethod // staticmethod +// __instancecheck__ +// __subclasscheck__ /// Object customization @@ -32,18 +31,18 @@ pub trait PyObjectProtocol { fn __delattr__(&self, py: Python, name: &PyObject) -> PyResult<()>; - // __instancecheck__ - // __subclasscheck__ - // __dir__ - fn __str__(&self, py: Python) -> PyResult; fn __repr__(&self, py: Python) -> PyResult; - fn __hash__(&self, py: Python) -> PyResult; + fn __format__(&self, py: Python, format_spec: &str) -> PyResult; + + fn __hash__(&self, py: Python) -> PyResult; fn __bool__(&self, py: Python) -> PyResult; + fn __bytes__(&self, py: Python) -> PyResult; + fn __richcmp__(&self, py: Python, other: &PyObject, op: CompareOp) -> PyResult; } @@ -60,25 +59,24 @@ impl PyObjectProtocol for T { default fn __delattr__(&self, py: Python, _: &PyObject) -> PyResult<()> { Err(PyErr::new::(py, "Not implemented")) } - - // __instancecheck__ - // __subclasscheck__ - // __iter__ - // __next__ - // __dir__ - default fn __str__(&self, py: Python) -> PyResult { Err(PyErr::new::(py, "Not implemented")) } default fn __repr__(&self, py: Python) -> PyResult { Err(PyErr::new::(py, "Not implemented")) } - default fn __hash__(&self, py: Python) -> PyResult { + default fn __format__(&self, py: Python, format_spec: &str) -> PyResult { + Err(PyErr::new::(py, "Not implemented")) + } + default fn __hash__(&self, py: Python) -> PyResult { Err(PyErr::new::(py, "Not implemented")) } default fn __bool__(&self, py: Python) -> PyResult { Err(PyErr::new::(py, "Not implemented")) } + default fn __bytes__(&self, py: Python) -> PyResult { + Err(PyErr::new::(py, "Not implemented")) + } default fn __richcmp__(&self, py: Python, _: &PyObject, _: CompareOp) -> PyResult { Err(PyErr::new::(py, "Not implemented")) } diff --git a/src/class/context.rs b/src/class/context.rs index bf8cd03a..7b542ef2 100644 --- a/src/class/context.rs +++ b/src/class/context.rs @@ -65,7 +65,7 @@ impl PyContextProtocolImpl for T where T: PyContextProtocol { } #[doc(hidden)] -trait PyContextEnterProtocolImpl { +pub trait PyContextEnterProtocolImpl { fn __enter__() -> Option; } @@ -78,6 +78,7 @@ impl PyContextEnterProtocolImpl for T } } +#[doc(hidden)] pub trait PyContextExitProtocolImpl { fn __exit__() -> Option; } diff --git a/src/class/mapping.rs b/src/class/mapping.rs index 237be0bd..ddeb2bfe 100644 --- a/src/class/mapping.rs +++ b/src/class/mapping.rs @@ -11,6 +11,7 @@ use python::{Python, PythonObject, PyDrop}; use objects::{exc, PyObject}; use callback::{PyObjectCallbackConverter, LenResultConverter, UnitCallbackConverter}; use conversion::{ToPyObject, FromPyObject}; +use class::methods::PyMethodDef; /// Mapping interface @@ -29,6 +30,15 @@ pub trait PyMappingProtocol: PythonObject { fn __delitem__(&self, py: Python, key: Self::Key) -> Self::Result where Self: PyMappingDelItemProtocol { unimplemented!() } + fn __iter__(&self, py: Python) + -> Self::Result where Self: PyMappingIterProtocol { unimplemented!() } + + fn __contains__(&self, py: Python, value: Self::Value) + -> Self::Result where Self: PyMappingContainsProtocol { unimplemented!() } + + fn __reversed__(&self, py: Python) + -> Self::Result where Self: PyMappingReversedProtocol { unimplemented!() } + } @@ -58,9 +68,25 @@ pub trait PyMappingDelItemProtocol: PyMappingProtocol { type Result: Into>; } +pub trait PyMappingIterProtocol: PyMappingProtocol { + type Success: ToPyObject; + type Result: Into>; +} + +pub trait PyMappingContainsProtocol: PyMappingProtocol { + type Value: for<'a> FromPyObject<'a>; + type Result: Into>; +} + +pub trait PyMappingReversedProtocol: PyMappingProtocol { + type Success: ToPyObject; + type Result: Into>; +} + #[doc(hidden)] pub trait PyMappingProtocolImpl { fn tp_as_mapping() -> Option; + fn methods() -> Vec; } impl PyMappingProtocolImpl for T { @@ -68,6 +94,10 @@ impl PyMappingProtocolImpl for T { default fn tp_as_mapping() -> Option { None } + #[inline] + default fn methods() -> Vec { + Vec::new() + } } impl PyMappingProtocolImpl for T where T: PyMappingProtocol { @@ -85,6 +115,23 @@ impl PyMappingProtocolImpl for T where T: PyMappingProtocol { mp_ass_subscript: f, }) } + + #[inline] + fn methods() -> Vec { + let mut methods = Vec::new(); + + if let Some(def) = ::__iter__() { + methods.push(def) + } + if let Some(def) = ::__contains__() { + methods.push(def) + } + if let Some(def) = ::__reversed__() { + methods.push(def) + } + + methods + } } trait PyMappingLenProtocolImpl { @@ -279,3 +326,46 @@ impl PyMappingDelItemProtocolImpl for T Some(wrap::) } } + + +#[doc(hidden)] +pub trait PyMappingContainsProtocolImpl { + fn __contains__() -> Option; +} + +impl PyMappingContainsProtocolImpl for T + where T: PyMappingProtocol +{ + #[inline] + default fn __contains__() -> Option { + None + } +} + +#[doc(hidden)] +pub trait PyMappingReversedProtocolImpl { + fn __reversed__() -> Option; +} + +impl PyMappingReversedProtocolImpl for T + where T: PyMappingProtocol +{ + #[inline] + default fn __reversed__() -> Option { + None + } +} + +#[doc(hidden)] +pub trait PyMappingIterProtocolImpl { + fn __iter__() -> Option; +} + +impl PyMappingIterProtocolImpl for T + where T: PyMappingProtocol +{ + #[inline] + default fn __iter__() -> Option { + None + } +} diff --git a/src/class/sequence.rs b/src/class/sequence.rs index 81b0fc49..7717b24e 100644 --- a/src/class/sequence.rs +++ b/src/class/sequence.rs @@ -14,7 +14,7 @@ use callback::{handle_callback, PyObjectCallbackConverter, use class::NO_METHODS; -/// Mapping interface +/// Sequece interface pub trait PySequenceProtocol { fn __len__(&self, py: Python) -> PyResult; diff --git a/src/objects/bytearray.rs b/src/objects/bytearray.rs index 9c2b36ae..0a43766e 100644 --- a/src/objects/bytearray.rs +++ b/src/objects/bytearray.rs @@ -75,7 +75,8 @@ impl PyByteArray { #[cfg(test)] mod test { use exc; - use python::{Python, PythonObject, PythonObjectWithTypeObject}; + use class::PyTypeObject; + use python::{Python, PythonObject}; use objects::PyByteArray; #[test] diff --git a/tests/test_class.rs b/tests/test_class.rs index c969bf04..ea5cfb9c 100644 --- a/tests/test_class.rs +++ b/tests/test_class.rs @@ -1,10 +1,11 @@ +#![feature(proc_macro, specialization)] #![allow(dead_code, unused_variables)] -#[macro_use] extern crate pyo3; +extern crate pyo3; use pyo3::*; use std::{mem, isize, iter}; -use std::cell::{Cell, RefCell}; +use std::cell::RefCell; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use pyo3::ffi; @@ -13,6 +14,7 @@ macro_rules! py_run { ($py:expr, $val:ident, $code:expr) => {{ let d = PyDict::new($py); d.set_item($py, stringify!($val), &$val).unwrap(); + //$py.run($code, None, Some(&d)).map_err(|e| e.print($py)).expect($code); $py.run($code, None, Some(&d)).expect($code); }} } @@ -34,7 +36,8 @@ macro_rules! py_expect_exception { } -py_class!(class EmptyClass |py| { }); +#[py::class] +struct EmptyClass { } #[test] fn empty_class() { @@ -47,7 +50,8 @@ fn empty_class() { py_assert!(py, typeobj, "typeobj.__name__ == 'EmptyClass'"); } -py_class!(class EmptyClassInModule |py| { }); +#[py::class] +struct EmptyClassInModule { } #[test] fn empty_class_in_module() { @@ -61,11 +65,16 @@ fn empty_class_in_module() { assert_eq!(ty.getattr(py, "__module__").unwrap().extract::(py).unwrap(), "test_module.nested"); } -py_class!(class EmptyClassWithNew |py| { - def __new__(_cls) -> PyResult { +#[py::class] +struct EmptyClassWithNew { } + +#[py::methods] +impl EmptyClassWithNew { + #[__new__] + fn __new__(_cls: &PyType, py: Python) -> PyResult { EmptyClassWithNew::create_instance(py) } -}); +} #[test] fn empty_class_with_new() { @@ -75,12 +84,17 @@ fn empty_class_with_new() { assert!(typeobj.call(py, NoArgs, None).unwrap().cast_into::(py).is_ok()); } -py_class!(class NewWithOneArg |py| { - data _data: i32; - def __new__(_cls, arg: i32) -> PyResult { +#[py::class] +struct NewWithOneArg { + _data: i32, +} +#[py::methods] +impl NewWithOneArg { + #[new] + fn __new__(_cls: &PyType, py: Python, arg: i32) -> PyResult { NewWithOneArg::create_instance(py, arg) } -}); +} #[test] fn new_with_one_arg() { @@ -91,13 +105,19 @@ fn new_with_one_arg() { assert_eq!(*obj._data(py), 42); } -py_class!(class NewWithTwoArgs |py| { - data _data1: i32; - data _data2: i32; - def __new__(_cls, arg1: i32, arg2: i32) -> PyResult { +#[py::class] +struct NewWithTwoArgs { + _data1: i32, + _data2: i32, +} + +#[py::methods] +impl NewWithTwoArgs { + #[new] + fn __new__(_cls: &PyType, py: Python, arg1: i32, arg2: i32) -> PyResult { NewWithTwoArgs::create_instance(py, arg1, arg2) } -}); +} #[test] fn new_with_two_args() { @@ -118,10 +138,11 @@ impl Drop for TestDropCall { } } -py_class!(class DataIsDropped |py| { - data member1: TestDropCall; - data member2: TestDropCall; -}); +#[py::class] +struct DataIsDropped { + member1: TestDropCall, + member2: TestDropCall, +} #[test] fn data_is_dropped() { @@ -140,13 +161,17 @@ fn data_is_dropped() { assert!(drop_called2.load(Ordering::Relaxed) == true); } -py_class!(class InstanceMethod |py| { - data member: i32; +#[py::class] +struct InstanceMethod { + member: i32, +} - def method(&self) -> PyResult { +#[py::methods] +impl InstanceMethod { + fn method(&self, py: Python) -> PyResult { Ok(*self.member(py)) } -}); +} #[test] fn instance_method() { @@ -160,13 +185,16 @@ fn instance_method() { py.run("assert obj.method() == 42", None, Some(&d)).unwrap(); } -py_class!(class InstanceMethodWithArgs |py| { - data member: i32; - - def method(&self, multiplier: i32) -> PyResult { +#[py::class] +struct InstanceMethodWithArgs { + member: i32 +} +#[py::methods] +impl InstanceMethodWithArgs { + fn method(&self, py: Python, multiplier: i32) -> PyResult { Ok(*self.member(py) * multiplier) } -}); +} #[test] fn instance_method_with_args() { @@ -181,18 +209,22 @@ fn instance_method_with_args() { py.run("assert obj.method(multiplier=6) == 42", None, Some(&d)).unwrap(); } -py_class!(class ClassMethod |py| { - def __new__(cls) -> PyResult { +#[py::class] +struct ClassMethod {} +#[py::methods] +impl ClassMethod { + #[new] + fn __new__(cls: &PyType, py: Python) -> PyResult { ClassMethod::create_instance(py) } - @classmethod - def method(cls) -> PyResult { - Ok(format!("{}.method()!", cls.name(py))) - } -}); + //#[classmethod] + //def method(cls) -> PyResult { + // Ok(format!("{}.method()!", cls.name(py))) + //} +} -#[test] +//#[test] fn class_method() { let gil = Python::acquire_gil(); let py = gil.python(); @@ -203,36 +235,41 @@ fn class_method() { py.run("assert C().method() == 'ClassMethod.method()!'", None, Some(&d)).unwrap(); } -py_class!(class ClassMethodWithArgs |py| { - @classmethod - def method(cls, input: &str) -> PyResult { - Ok(format!("{}.method({})", cls.name(py), input)) - } -}); +//py_class!(class ClassMethodWithArgs |py| { +// @classmethod +// def method(cls, input: &str) -> PyResult { +// Ok(format!("{}.method({})", cls.name(py), input)) +// } +//}); -#[test] -fn class_method_with_args() { +//#[test] +/*fn class_method_with_args() { let gil = Python::acquire_gil(); let py = gil.python(); let d = PyDict::new(py); d.set_item(py, "C", py.get_type::()).unwrap(); py.run("assert C.method('abc') == 'ClassMethodWithArgs.method(abc)'", None, Some(&d)).unwrap(); -} +}*/ -py_class!(class StaticMethod |py| { - def __new__(cls) -> PyResult { +#[py::class] +struct StaticMethod {} + +#[py::methods] +impl StaticMethod { + #[new] + fn __new__(cls: &PyType, py: Python) -> PyResult { StaticMethod::create_instance(py) } - @staticmethod - def method() -> PyResult<&'static str> { - Ok("StaticMethod.method()!") - } -}); + //#[staticmethod] + //fn method(py: Python) -> PyResult<&'static str> { + // Ok("StaticMethod.method()!") + //} +} -#[test] -fn static_method() { +//#[test] +/*fn static_method() { let gil = Python::acquire_gil(); let py = gil.python(); @@ -241,57 +278,44 @@ fn static_method() { d.set_item(py, "C", py.get_type::()).unwrap(); py.run("assert C.method() == 'StaticMethod.method()!'", None, Some(&d)).unwrap(); py.run("assert C().method() == 'StaticMethod.method()!'", None, Some(&d)).unwrap(); +}*/ + +//py_class!(class StaticMethodWithArgs |py| { +// @staticmethod +// def method(input: i32) -> PyResult { +// Ok(format!("0x{:x}", input)) +// } +//}); + +//#[test] +//fn static_method_with_args() { +// let gil = Python::acquire_gil(); +// let py = gil.python(); + +// assert_eq!(StaticMethodWithArgs::method(py, 1234).unwrap(), "0x4d2"); +// let d = PyDict::new(py); +// d.set_item(py, "C", py.get_type::()).unwrap(); +// py.run("assert C.method(1337) == '0x539'", None, Some(&d)).unwrap(); +//} + +#[py::class] +struct GCIntegration { + self_ref: RefCell, + dropped: TestDropCall, } -py_class!(class StaticMethodWithArgs |py| { - @staticmethod - def method(input: i32) -> PyResult { - Ok(format!("0x{:x}", input)) - } -}); - -#[test] -fn static_method_with_args() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - assert_eq!(StaticMethodWithArgs::method(py, 1234).unwrap(), "0x4d2"); - let d = PyDict::new(py); - d.set_item(py, "C", py.get_type::()).unwrap(); - py.run("assert C.method(1337) == '0x539'", None, Some(&d)).unwrap(); -} - -py_class!(class StaticData |py| { - static VAL1 = 123; - static VAL2 = py.None(); -}); - -#[test] -fn static_data() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let d = PyDict::new(py); - d.set_item(py, "C", py.get_type::()).unwrap(); - py.run("assert C.VAL1 == 123", None, Some(&d)).unwrap(); - py.run("assert C.VAL2 is None", None, Some(&d)).unwrap(); - assert!(py.run("C.VAL1 = 124", None, Some(&d)).is_err()); -} - -py_class!(class GCIntegration |py| { - data self_ref: RefCell; - data dropped: TestDropCall; - - def __traverse__(&self, visit) { +#[py::proto] +impl PyGCProtocol for GCIntegration { + fn __traverse__(&self, py: Python, visit: PyVisit) -> Result<(), PyTraverseError> { visit.call(&*self.self_ref(py).borrow()) } - def __clear__(&self) { + fn __clear__(&self, py: Python) { let old_ref = mem::replace(&mut *self.self_ref(py).borrow_mut(), py.None()); // Release reference only after the mutable borrow has expired. old_ref.release_ref(py); } -}); +} #[test] fn gc_integration() { @@ -310,13 +334,17 @@ fn gc_integration() { assert!(drop_called.load(Ordering::Relaxed)); } -py_class!(class Len |py| { - data l: usize; +#[py::class] +pub struct Len { + l: usize +} - def __len__(&self) -> PyResult { +#[py::proto] +impl PyMappingProtocol for Len { + fn __len__(&self, py: Python) -> PyResult { Ok(*self.l(py)) } -}); +} #[test] fn len() { @@ -334,7 +362,8 @@ fn len() { py_expect_exception!(py, inst, "len(inst)", OverflowError); } -py_class!(class Iterator |py| { + +/*py_class!(class Iterator |py| { data iter: RefCell + Send>>; def __iter__(&self) -> PyResult { @@ -354,29 +383,34 @@ fn iterator() { let inst = Iterator::create_instance(py, RefCell::new(Box::new(5..8))).unwrap(); py_assert!(py, inst, "iter(inst) is inst"); py_assert!(py, inst, "list(inst) == [5, 6, 7]"); -} +}*/ -py_class!(class StringMethods |py| { - def __str__(&self) -> PyResult<&'static str> { +/* +#[py::class] +struct StringMethods {} + +#[py::proto] +impl PyObjectProtocol for StringMethods { + fn __str__(&self, py: Python) -> PyResult<&'static str> { Ok("str") } - def __repr__(&self) -> PyResult<&'static str> { + fn __repr__(&self, py: Python) -> PyResult<&'static str> { Ok("repr") } - def __format__(&self, format_spec: &str) -> PyResult { + fn __format__(&self, py: Python, format_spec: &str) -> PyResult { Ok(format!("format({})", format_spec)) } - def __unicode__(&self) -> PyResult { - Ok(PyString::new(py, "unicode")) - } + //fn __unicode__(&self) -> PyResult { + // Ok(PyString::new(py, "unicode")) + //} - def __bytes__(&self) -> PyResult { + fn __bytes__(&self, py: Python) -> PyResult { Ok(PyBytes::new(py, b"bytes")) } -}); +} #[test] fn string_methods() { @@ -387,8 +421,8 @@ fn string_methods() { py_assert!(py, obj, "str(obj) == 'str'"); py_assert!(py, obj, "repr(obj) == 'repr'"); py_assert!(py, obj, "'{0:x}'.format(obj) == 'format(x)'"); -} - +}*/ +/* #[test] fn python3_string_methods() { let gil = Python::acquire_gil(); @@ -396,20 +430,24 @@ fn python3_string_methods() { let obj = StringMethods::create_instance(py).unwrap(); py_assert!(py, obj, "bytes(obj) == b'bytes'"); +}*/ + + +#[py::class] +struct Comparisons { + val: i32, } - -py_class!(class Comparisons |py| { - data val: i32; - - def __hash__(&self) -> PyResult { - Ok(*self.val(py)) +#[py::proto] +impl PyObjectProtocol for Comparisons { + fn __hash__(&self, py: Python) -> PyResult { + Ok(*self.val(py) as usize) } - def __bool__(&self) -> PyResult { + fn __bool__(&self, py: Python) -> PyResult { Ok(*self.val(py) != 0) } -}); +} #[test] @@ -430,20 +468,22 @@ fn comparisons() { } -py_class!(class Sequence |py| { - def __len__(&self) -> PyResult { +/*#[py::class] +struct Sequence {} + +#[py::proto] +impl PySequenceProtocol for Sequence { + fn __len__(&self, py: Python) -> PyResult { Ok(5) } - def __getitem__(&self, key: PyObject) -> PyResult { - if let Ok(index) = key.extract::(py) { - if index == 5 { - return Err(PyErr::new::(py, NoArgs)); - } + fn __getitem__(&self, py: Python, key: isize) -> PyResult { + if key == 5 { + return Err(PyErr::new::(py, NoArgs)); } Ok(key) } -}); +} #[test] fn sequence() { @@ -453,14 +493,20 @@ fn sequence() { let c = Sequence::create_instance(py).unwrap(); py_assert!(py, c, "list(c) == [0, 1, 2, 3, 4]"); py_assert!(py, c, "c['abc'] == 'abc'"); -} +}*/ -py_class!(class Callable |py| { - def __call__(&self, arg: i32) -> PyResult { +#[py::class] +struct Callable {} + +#[py::methods] +impl Callable { + + #[__call__] + fn __call__(&self, py: Python, arg: i32) -> PyResult { Ok(arg * 6) } -}); +} #[test] fn callable() { @@ -475,80 +521,97 @@ fn callable() { py_assert!(py, nc, "not callable(nc)"); } -py_class!(class SetItem |py| { - data key: Cell; - data val: Cell; +#[py::class] +struct SetItem { + key: i32, + val: i32, +} - def __setitem__(&self, key: i32, val: i32) -> PyResult<()> { - self.key(py).set(key); - self.val(py).set(val); +#[py::proto] +impl PyMappingProtocol for SetItem { + fn __setitem__(&self, py: Python, key: i32, val: i32) -> PyResult<()> { + *self.key_mut(py) = key; + *self.val_mut(py) = val; Ok(()) } -}); +} #[test] fn setitem() { let gil = Python::acquire_gil(); let py = gil.python(); - let c = SetItem::create_instance(py, Cell::new(0), Cell::new(0)).unwrap(); + let c = SetItem::create_instance(py, 0, 0).unwrap(); py_run!(py, c, "c[1] = 2"); - assert_eq!(c.key(py).get(), 1); - assert_eq!(c.val(py).get(), 2); + assert_eq!(*c.key(py), 1); + assert_eq!(*c.val(py), 2); py_expect_exception!(py, c, "del c[1]", NotImplementedError); } -py_class!(class DelItem |py| { - data key: Cell; +#[py::class] +struct DelItem { + key: i32, +} - def __delitem__(&self, key: i32) -> PyResult<()> { - self.key(py).set(key); +#[py::proto] +impl PyMappingProtocol for DelItem { + fn __delitem__(&self, py: Python, key: i32) -> PyResult<()> { + *self.key_mut(py) = key; Ok(()) } -}); +} #[test] fn delitem() { let gil = Python::acquire_gil(); let py = gil.python(); - let c = DelItem::create_instance(py, Cell::new(0)).unwrap(); + let c = DelItem::create_instance(py, 0).unwrap(); py_run!(py, c, "del c[1]"); - assert_eq!(c.key(py).get(), 1); + assert_eq!(*c.key(py), 1); py_expect_exception!(py, c, "c[1] = 2", NotImplementedError); } -py_class!(class SetDelItem |py| { - data val: Cell>; +#[py::class] +struct SetDelItem { + val: Option, +} - def __setitem__(&self, key: i32, val: i32) -> PyResult<()> { - self.val(py).set(Some(val)); +#[py::proto] +impl PyMappingProtocol for SetDelItem { + fn __setitem__(&self, py: Python, key: i32, val: i32) -> PyResult<()> { + *self.val_mut(py) = Some(val); Ok(()) } - def __delitem__(&self, key: i32) -> PyResult<()> { - self.val(py).set(None); + fn __delitem__(&self, py: Python, key: i32) -> PyResult<()> { + *self.val_mut(py) = None; Ok(()) } -}); +} #[test] fn setdelitem() { let gil = Python::acquire_gil(); let py = gil.python(); - let c = SetDelItem::create_instance(py, Cell::new(None)).unwrap(); + let c = SetDelItem::create_instance(py, None).unwrap(); py_run!(py, c, "c[1] = 2"); - assert_eq!(c.val(py).get(), Some(2)); + assert_eq!(*c.val(py), Some(2)); py_run!(py, c, "del c[1]"); - assert_eq!(c.val(py).get(), None); + assert_eq!(*c.val(py), None); } -py_class!(class Reversed |py| { - def __reversed__(&self) -> PyResult<&'static str> { +#[py::class] +struct Reversed {} + +#[py::proto] +impl PyMappingProtocol for Reversed{ + fn __reversed__(&self, py: Python) -> PyResult<&'static str> { + println!("__reversed__"); Ok("I am reversed") } -}); +} #[test] fn reversed() { @@ -559,11 +622,15 @@ fn reversed() { py_run!(py, c, "assert reversed(c) == 'I am reversed'"); } -py_class!(class Contains |py| { - def __contains__(&self, item: i32) -> PyResult { +/*#[py::class] +struct Contains {} + +#[py::proto] +impl PyMappingProtocol for Contains { + fn __contains__(&self, py: Python, item: i32) -> PyResult { Ok(item >= 0) } -}); +} #[test] fn contains() { @@ -574,8 +641,9 @@ fn contains() { py_run!(py, c, "assert 1 in c"); py_run!(py, c, "assert -1 not in c"); py_run!(py, c, "assert 'wrong type' not in c"); -} +}*/ +/* py_class!(class UnaryArithmetic |py| { def __neg__(&self) -> PyResult<&'static str> { Ok("neg") @@ -604,45 +672,53 @@ fn unary_arithmetic() { py_run!(py, c, "assert +c == 'pos'"); py_run!(py, c, "assert abs(c) == 'abs'"); py_run!(py, c, "assert ~c == 'invert'"); -} +}*/ -py_class!(class BinaryArithmetic |py| { +/* +#[py::class] +struct BinaryArithmetic {} + +#[py::proto] +impl PyObjectProtocol for BinaryArithmetic { def __repr__(&self) -> PyResult<&'static str> { Ok("BA") } +} - def __add__(lhs, rhs) -> PyResult { +#[py::proto] +impl PyNumberProtocol for BinaryArithmetic { + fn __add__(lhs, rhs) -> PyResult { Ok(format!("{:?} + {:?}", lhs, rhs)) } - def __sub__(lhs, rhs) -> PyResult { + fn __sub__(lhs, rhs) -> PyResult { Ok(format!("{:?} - {:?}", lhs, rhs)) } - def __mul__(lhs, rhs) -> PyResult { + fn __mul__(lhs, rhs) -> PyResult { Ok(format!("{:?} * {:?}", lhs, rhs)) } - def __lshift__(lhs, rhs) -> PyResult { + fn __lshift__(lhs, rhs) -> PyResult { Ok(format!("{:?} << {:?}", lhs, rhs)) } - def __rshift__(lhs, rhs) -> PyResult { + fn __rshift__(lhs, rhs) -> PyResult { Ok(format!("{:?} >> {:?}", lhs, rhs)) } - def __and__(lhs, rhs) -> PyResult { + fn __and__(lhs, rhs) -> PyResult { Ok(format!("{:?} & {:?}", lhs, rhs)) } - def __xor__(lhs, rhs) -> PyResult { + fn __xor__(lhs, rhs) -> PyResult { Ok(format!("{:?} ^ {:?}", lhs, rhs)) } - def __or__(lhs, rhs) -> PyResult { + fn __or__(lhs, rhs) -> PyResult { Ok(format!("{:?} | {:?}", lhs, rhs)) } -}); +} #[test] fn binary_arithmetic() { @@ -668,8 +744,9 @@ fn binary_arithmetic() { py_run!(py, c, "assert 1 ^ c == '1 ^ BA'"); py_run!(py, c, "assert c | 1 == 'BA | 1'"); py_run!(py, c, "assert 1 | c == '1 | BA'"); -} +}*/ +/* py_class!(class RichComparisons |py| { def __repr__(&self) -> PyResult<&'static str> { Ok("RC") @@ -753,54 +830,62 @@ fn rich_comparisons_python_3_type_error() { py_expect_exception!(py, c2, "c2 >= 1", TypeError); py_expect_exception!(py, c2, "1 >= c2", TypeError); } + */ +/* +#[py::class] +struct InPlaceOperations { + value: u32 +} -py_class!(class InPlaceOperations |py| { - data value: Cell; - - def __repr__(&self) -> PyResult { +#[py::proto] +impl PyObjectProtocol for InPlaceOperations { + fn __repr__(&self, py: Python) -> PyResult { Ok(format!("IPO({:?})", self.value(py).get())) } +} - def __iadd__(&self, other: u32) -> PyResult { +#[py::proto] +impl PyNumberProtocol for InPlaceOperations { + fn __iadd__(&self, py: Python, other: u32) -> PyResult { self.value(py).set(self.value(py).get() + other); Ok(self.clone_ref(py)) } - def __isub__(&self, other: u32) -> PyResult { + fn __isub__(&self, py: Python, other: u32) -> PyResult { self.value(py).set(self.value(py).get() - other); Ok(self.clone_ref(py)) } - def __imul__(&self, other: u32) -> PyResult { + fn __imul__(&self, py: Python, other: u32) -> PyResult { self.value(py).set(self.value(py).get() * other); Ok(self.clone_ref(py)) } - def __ilshift__(&self, other: u32) -> PyResult { + fn __ilshift__(&self, py: Python, other: u32) -> PyResult { self.value(py).set(self.value(py).get() << other); Ok(self.clone_ref(py)) } - def __irshift__(&self, other: u32) -> PyResult { + fn __irshift__(&self, py: Python, other: u32) -> PyResult { self.value(py).set(self.value(py).get() >> other); Ok(self.clone_ref(py)) } - def __iand__(&self, other: u32) -> PyResult { + fn __iand__(&self, py: Python, other: u32) -> PyResult { self.value(py).set(self.value(py).get() & other); Ok(self.clone_ref(py)) } - def __ixor__(&self, other: u32) -> PyResult { + fn __ixor__(&self, py: Python, other: u32) -> PyResult { self.value(py).set(self.value(py).get() ^ other); Ok(self.clone_ref(py)) } - def __ior__(&self, other: u32) -> PyResult { + fn __ior__(&self, py: Python, other: u32) -> PyResult { self.value(py).set(self.value(py).get() | other); Ok(self.clone_ref(py)) } -}); +} #[test] fn inplace_operations() { @@ -831,60 +916,74 @@ fn inplace_operations() { let c = InPlaceOperations::create_instance(py, Cell::new(12)).unwrap(); py_run!(py, c, "d = c; c ^= 5; assert repr(c) == repr(d) == 'IPO(9)'"); } +*/ -py_class!(class ContextManager |py| { - data exit_called : Cell; +#[py::class] +struct ContextManager { + exit_called: bool +} - def __enter__(&self) -> PyResult { +#[py::proto] +impl PyContextProtocol for ContextManager { + + fn __enter__(&self, py: Python) -> PyResult { Ok(42) } - def __exit__(&self, ty: Option, value: PyObject, traceback: PyObject) -> PyResult { - self.exit_called(py).set(true); + fn __exit__(&self, py: Python, + ty: Option, + value: Option, + traceback: Option) -> PyResult { + *self.exit_called_mut(py) = true; if ty == Some(py.get_type::()) { Ok(true) } else { - Ok(false) + Ok(false) } } -}); +} #[test] fn context_manager() { let gil = Python::acquire_gil(); let py = gil.python(); - let c = ContextManager::create_instance(py, Cell::new(false)).unwrap(); + let c = ContextManager::create_instance(py, false).unwrap(); py_run!(py, c, "with c as x:\n assert x == 42"); - assert!(c.exit_called(py).get()); + assert!(*c.exit_called(py)); - c.exit_called(py).set(false); + *c.exit_called_mut(py) = false; py_run!(py, c, "with c as x:\n raise ValueError"); - assert!(c.exit_called(py).get()); + assert!(*c.exit_called(py)); - c.exit_called(py).set(false); + *c.exit_called_mut(py) = false; py_expect_exception!(py, c, "with c as x:\n raise NotImplementedError", NotImplementedError); - assert!(c.exit_called(py).get()); + assert!(*c.exit_called(py)); } -py_class!(class ClassWithProperties |py| { - data num: Cell; +#[py::class] +struct ClassWithProperties { + num: i32 +} - def get_num(&self) -> PyResult { - Ok(self.num(py).get()) +#[py::methods] +impl ClassWithProperties { + + fn get_num(&self, py: Python) -> PyResult { + Ok(*self.num(py)) } - property DATA { - get(&slf) -> PyResult { - Ok(slf.num(py).get()) - } - set(&slf, value: i32) -> PyResult<()> { - slf.num(py).set(value); - Ok(()) - } + #[getter(DATA)] + fn get_data(&self, py: Python) -> PyResult { + Ok(*self.num(py)) } -}); + #[setter(DATA)] + fn set(&self, py: Python, value: i32) -> PyResult<()> { + *self.num_mut(py) = value; + Ok(()) + } +} #[test] @@ -892,7 +991,7 @@ fn class_with_properties() { let gil = Python::acquire_gil(); let py = gil.python(); - let inst = ClassWithProperties::create_instance(py, Cell::new(10)).unwrap(); + let inst = ClassWithProperties::create_instance(py, 10).unwrap(); py_run!(py, inst, "assert inst.get_num() == 10"); py_run!(py, inst, "assert inst.get_num() == inst.DATA");