added sequence protocol

This commit is contained in:
Nikolay Kim 2017-05-16 01:27:59 -07:00
parent d8fff8e91a
commit 78d7d99bd8
9 changed files with 323 additions and 85 deletions

View File

@ -9,31 +9,37 @@ use py_method;
struct Methods {
methods: &'static [&'static str],
non_pyobj_result: &'static [&'static str],
no_adjust: bool,
}
static DEFAULT_METHODS: Methods = Methods {
methods: &[],
non_pyobj_result: &[],
no_adjust: true,
};
static BUFFER_METHODS: Methods = Methods {
methods: &[],
non_pyobj_result: &["bf_getbuffer", "bf_releasebuffer"],
no_adjust: false,
};
static GC_METHODS: Methods = Methods {
methods: &[],
non_pyobj_result: &["__traverse__", "__clear__"],
no_adjust: false,
};
static CONTEXT_METHODS: Methods = Methods {
methods: &["__enter__", "__exit__"],
non_pyobj_result: &[],
no_adjust: false,
};
static MAPPING_METHODS: Methods = Methods {
methods: &[],
non_pyobj_result: &["__setitem__", "__len__"],
no_adjust: false,
};
enum ImplType {
@ -42,6 +48,7 @@ enum ImplType {
Context,
GC,
Mapping,
Sequence,
}
pub fn build_py_proto(ast: &mut syn::Item) -> Tokens {
@ -68,6 +75,10 @@ pub fn build_py_proto(ast: &mut syn::Item) -> Tokens {
ImplType::Mapping => {
impl_protocol("pyo3::class::mapping::PyMappingProtocolImpl",
path.clone(), ty, impl_items, &MAPPING_METHODS)
},
ImplType::Sequence => {
impl_protocol("pyo3::class::mapping::PySequenceProtocolImpl",
path.clone(), ty, impl_items, &DEFAULT_METHODS)
}
}
} else {
@ -86,6 +97,7 @@ fn process_path(path: &syn::Path) -> ImplType {
"PyContextProtocol" => ImplType::Context,
"PyGCProtocol" => ImplType::GC,
"PyMappingProtocol" => ImplType::Mapping,
"PySequenceProtocol" => ImplType::Sequence,
_ => panic!("#[py_proto] can not be used with this block"),
}
} else {
@ -110,7 +122,8 @@ fn impl_protocol(name: &'static str,
meth.push(String::from(iimpl.ident.as_ref()));
// adjust return type
if !methods.non_pyobj_result.contains(&iimpl.ident.as_ref()) {
if !methods.non_pyobj_result.contains(&iimpl.ident.as_ref()) &&
!methods.no_adjust {
impl_adjust_result(sig, block);
}
}

View File

@ -83,19 +83,16 @@ impl ffi::PyAsyncMethods {
for name in methods {
match name {
&"__await__" => {
meth.am_await = py_unary_slot!(
PyAsyncProtocol, T::__await__,
*mut ffi::PyObject, PyObjectCallbackConverter);
meth.am_await = py_unary_func!(
PyAsyncProtocol, T::__await__, PyObjectCallbackConverter);
},
&"__aiter__" => {
meth.am_aiter = py_unary_slot!(
PyAsyncProtocol, T::__aiter__,
*mut ffi::PyObject, PyObjectCallbackConverter);
meth.am_aiter = py_unary_func!(
PyAsyncProtocol, T::__aiter__, PyObjectCallbackConverter);
},
&"__anext__" => {
meth.am_anext = py_unary_slot!(
PyAsyncProtocol, T::__anext__,
*mut ffi::PyObject, PyObjectCallbackConverter);
meth.am_anext = py_unary_func!(
PyAsyncProtocol, T::__anext__, PyObjectCallbackConverter);
},
_ => unreachable!(),
}

View File

@ -2,21 +2,82 @@
#[macro_export]
#[doc(hidden)]
macro_rules! py_unary_slot {
($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr) => {{
unsafe extern "C" fn wrap<T>(slf: *mut $crate::ffi::PyObject) -> $res_type
macro_rules! py_unary_func {
($trait:ident, $class:ident :: $f:ident, $conv:expr) => {{
unsafe extern "C" fn wrap<T>(slf: *mut $crate::ffi::PyObject)
-> *mut $crate::ffi::PyObject
where T: $trait + PythonObject
{
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
$crate::_detail::handle_callback(
LOCATION, $conv,
|py| {
let slf = $crate::PyObject::from_borrowed_ptr(
py, slf).unchecked_cast_into::<T>();
let ret = slf.$f(py);
$crate::PyDrop::release_ref(slf, py);
ret
})
$crate::_detail::handle_callback(LOCATION, $conv, |py| {
let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<T>();
let ret = slf.$f(py);
$crate::PyDrop::release_ref(slf, py);
ret
})
}
Some(wrap::<T>)
}}
}
#[macro_export]
#[doc(hidden)]
macro_rules! py_len_func {
($trait:ident, $class:ident :: $f:ident, $conv:expr) => {{
unsafe extern "C" fn wrap<T>(slf: *mut $crate::ffi::PyObject)
-> $crate::ffi::Py_ssize_t
where T: $trait + PythonObject
{
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
$crate::_detail::handle_callback(LOCATION, $conv, |py| {
let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<T>();
let ret = slf.$f(py);
$crate::PyDrop::release_ref(slf, py);
ret
})
}
Some(wrap::<T>)
}}
}
#[macro_export]
#[doc(hidden)]
macro_rules! py_binary_func {
($trait:ident, $class:ident :: $f:ident, $conv:expr) => {{
unsafe extern "C" fn wrap<T>(slf: *mut $crate::ffi::PyObject,
arg: *mut $crate::ffi::PyObject)
-> *mut $crate::ffi::PyObject
where T: $trait + PythonObject
{
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
$crate::_detail::handle_callback(LOCATION, $conv, |py| {
let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<T>();
let arg = $crate::PyObject::from_borrowed_ptr(py, arg);
let ret = slf.$f(py, &arg);
$crate::PyDrop::release_ref(arg, py);
$crate::PyDrop::release_ref(slf, py);
ret
})
}
Some(wrap::<T>)
}}
}
#[macro_export]
#[doc(hidden)]
macro_rules! py_ssizearg_func {
($trait:ident, $class:ident :: $f:ident, $conv:expr) => {{
unsafe extern "C" fn wrap<T>(slf: *mut $crate::ffi::PyObject,
arg: $crate::Py_ssize_t) -> *mut $crate::ffi::PyObject
where T: $trait + PythonObject
{
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
$crate::_detail::handle_callback(LOCATION, $conv, |py| {
let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<T>();
let ret = slf.$f(py, arg as isize);
$crate::PyDrop::release_ref(slf, py);
ret
})
}
Some(wrap::<T>)
}}
@ -25,24 +86,21 @@ macro_rules! py_unary_slot {
#[macro_export]
#[doc(hidden)]
macro_rules! py_binary_slot {
($trait:ident, $class:ident :: $f:ident, $arg_type:ty, $res_type:ty, $conv:expr) => {{
unsafe extern "C" fn wrap<T>(
slf: *mut $crate::ffi::PyObject, arg: *mut $crate::ffi::PyObject) -> $res_type
macro_rules! py_objobj_proc {
($trait:ident, $class:ident :: $f:ident, $conv:expr) => {{
unsafe extern "C" fn wrap<T>(slf: *mut $crate::ffi::PyObject,
arg: *mut $crate::ffi::PyObject) -> $crate::c_int
where T: $trait + PythonObject
{
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
$crate::_detail::handle_callback(
LOCATION, $conv, |py|
{
let slf = $crate::PyObject::from_borrowed_ptr(
py, slf).unchecked_cast_into::<T>();
let arg = $crate::PyObject::from_borrowed_ptr(py, arg);
let ret = slf.$f(py, &arg);
$crate::PyDrop::release_ref(arg, py);
$crate::PyDrop::release_ref(slf, py);
ret
})
$crate::_detail::handle_callback(LOCATION, $conv, |py| {
let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<T>();
let arg = PyObject::from_borrowed_ptr(py, arg);
let ret = slf.$f(py, &arg);
$crate::PyDrop::release_ref(arg, py);
$crate::PyDrop::release_ref(slf, py);
ret
})
}
Some(wrap::<T>)
}}

View File

@ -73,14 +73,12 @@ impl ffi::PyMappingMethods {
for name in methods {
match name {
&"__len__" => {
meth.mp_length = py_unary_slot!(
PyMappingProtocol, T::__len__,
ffi::Py_ssize_t, LenResultConverter);
meth.mp_length = py_len_func!(
PyMappingProtocol, T::__len__, LenResultConverter);
},
&"__getitem__" => {
meth.mp_subscript = py_binary_slot!(
PyMappingProtocol, T::__getitem__,
*mut ffi::PyObject, *mut ffi::PyObject, PyObjectCallbackConverter);
meth.mp_subscript = py_binary_func!(
PyMappingProtocol, T::__getitem__, PyObjectCallbackConverter);
},
_ => unreachable!(),
}

View File

@ -81,6 +81,15 @@ impl<T> PyClassInit for T where T: PythonObject + py_class::BaseObject {
type_object.tp_as_mapping = 0 as *mut ffi::PyMappingMethods;
}
// sequence methods
if let Some(buf) = ffi::PySequenceMethods::new::<T>() {
static mut SQ_METHODS: ffi::PySequenceMethods = ffi::PySequenceMethods_INIT;
*(unsafe { &mut SQ_METHODS }) = buf;
type_object.tp_as_sequence = unsafe { &mut SQ_METHODS };
} else {
type_object.tp_as_sequence = 0 as *mut ffi::PySequenceMethods;
}
// async methods
if let Some(buf) = ffi::PyAsyncMethods::new::<T>() {
static mut ASYNC_METHODS: ffi::PyAsyncMethods = ffi::PyAsyncMethods_INIT;

View File

@ -8,12 +8,15 @@ pub mod context;
pub mod mapping;
pub mod methods;
pub mod gc;
pub mod sequence;
pub use self::async::*;
pub use self::buffer::*;
pub use self::context::*;
pub use self::gc::{PyVisit, PyGCProtocol, PyTraverseError};
pub use self::mapping::{PyMappingProtocol};
pub use self::mapping::PyMappingProtocol;
pub use self::sequence::PySequenceProtocol;
pub use self::methods::{PyMethodDef, PyMethodType};
use self::gc::PyGCProtocolImpl;

176
src/class/sequence.rs Normal file
View File

@ -0,0 +1,176 @@
// Copyright (c) 2017-present PyO3 Project and Contributors
//! Python Sequence Interface
//! Trait and support implementation for implementing sequence
use std::os::raw::c_int;
use ffi;
use err::{PyErr, PyResult};
use python::{self, Python, PythonObject, PyDrop};
use conversion::ToPyObject;
use objects::{exc, PyObject, PyType, PyModule};
use py_class::slots::{LenResultConverter, UnitCallbackConverter, BoolConverter};
use function::{handle_callback, PyObjectCallbackConverter};
use class::NO_METHODS;
/// Mapping interface
pub trait PySequenceProtocol {
fn __len__(&self, py: Python) -> PyResult<usize>;
fn __getitem__(&self, py: Python, key: isize) -> PyResult<PyObject>;
fn __setitem__(&self, py: Python, key: isize, value: &PyObject) -> PyResult<()>;
fn __delitem__(&self, py: Python, key: isize) -> PyResult<()>;
fn __contains__(&self, py: Python, value: &PyObject) -> PyResult<bool>;
fn __concat__(&self, py: Python, other: &PyObject) -> PyResult<PyObject>;
fn __repeat__(&self, py: Python, count: isize) -> PyResult<PyObject>;
fn __inplace_concat__(&self, py: Python, other: &PyObject) -> PyResult<PyObject>;
fn __inplace_repeat__(&self, py: Python, count: isize) -> PyResult<PyObject>;
}
impl<T> PySequenceProtocol for T where T: PythonObject {
default fn __len__(&self, py: Python) -> PyResult<usize> {
Err(PyErr::new::<exc::NotImplementedError, _>(py, "Not implemented"))
}
default fn __getitem__(&self, py: Python, _: isize) -> PyResult<PyObject> {
Err(PyErr::new::<exc::NotImplementedError, _>(py, "Not implemented"))
}
default fn __setitem__(&self, py: Python, _: isize, _: &PyObject) -> PyResult<()> {
Err(PyErr::new::<exc::NotImplementedError, _>(
py, format!("Subscript assignment not supported by {:?}", self.as_object())))
}
default fn __delitem__(&self, py: Python, _: isize) -> PyResult<()> {
Err(PyErr::new::<exc::NotImplementedError, _>(
py, format!("Subscript deletion not supported by {:?}", self.as_object())))
}
default fn __contains__(&self, py: Python, _: &PyObject) -> PyResult<bool> {
Err(PyErr::new::<exc::NotImplementedError, _>(py, "Not implemented"))
}
default fn __concat__(&self, py: Python, _: &PyObject) -> PyResult<PyObject> {
Err(PyErr::new::<exc::NotImplementedError, _>(py, "Not implemented"))
}
default fn __repeat__(&self, py: Python, _: isize) -> PyResult<PyObject> {
Err(PyErr::new::<exc::NotImplementedError, _>(py, "Not implemented"))
}
default fn __inplace_concat__(&self, py: Python, _: &PyObject) -> PyResult<PyObject> {
Err(PyErr::new::<exc::NotImplementedError, _>(py, "Not implemented"))
}
default fn __inplace_repeat__(&self, py: Python, _: isize) -> PyResult<PyObject> {
Err(PyErr::new::<exc::NotImplementedError, _>(py, "Not implemented"))
}
}
#[doc(hidden)]
pub trait PySequenceProtocolImpl {
fn methods() -> &'static [&'static str];
}
impl<T> PySequenceProtocolImpl for T {
default fn methods() -> &'static [&'static str] {
NO_METHODS
}
}
impl ffi::PySequenceMethods {
/// Construct PySequenceMethods struct for PyTypeObject.tp_as_sequence
pub fn new<T>() -> Option<ffi::PySequenceMethods>
where T: PySequenceProtocol + PySequenceProtocolImpl + PythonObject
{
let methods = T::methods();
if methods.is_empty() {
return None
}
let mut meth: ffi::PySequenceMethods = ffi::PySequenceMethods_INIT;
for name in methods {
match name {
&"__len__" => {
meth.sq_length = py_len_func!(
PySequenceProtocol, T::__len__, LenResultConverter);
},
&"__getitem__" => {
meth.sq_item = py_ssizearg_func!(
PySequenceProtocol, T::__getitem__, PyObjectCallbackConverter);
},
&"__repeat__" => {
meth.sq_repeat = py_ssizearg_func!(
PySequenceProtocol, T::__repeat__, PyObjectCallbackConverter);
},
&"__contains__" => {
meth.sq_contains = py_objobj_proc!(
PySequenceProtocol, T::__contains__, BoolConverter);
},
&"__concat__" => {
meth.sq_concat = py_binary_func!(
PySequenceProtocol, T::__concat__, PyObjectCallbackConverter);
},
&"__inplace_concat__" => {
meth.sq_inplace_concat = py_binary_func!(
PySequenceProtocol, T::__inplace_concat__, PyObjectCallbackConverter);
},
&"__inplace_repeat__" => {
meth.sq_inplace_repeat = py_ssizearg_func!(
PySequenceProtocol, T::__inplace_repeat__, PyObjectCallbackConverter);
},
_ => unreachable!(),
}
}
// always set
meth.sq_ass_item = Some(sq_ass_subscript::<T>());
Some(meth)
}
}
fn sq_ass_subscript<T>() -> ffi::ssizeobjargproc
where T: PySequenceProtocol + PythonObject
{
unsafe extern "C" fn wrap<T>(slf: *mut ffi::PyObject,
key: ffi::Py_ssize_t,
value: *mut ffi::PyObject) -> c_int
where T: PySequenceProtocol + PythonObject
{
const LOCATION: &'static str = concat!(stringify!($class), ".__setitem__()");
handle_callback(
LOCATION, UnitCallbackConverter, |py|
{
let slf = PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<T>();
// if value is none, then __delitem__
let ret = if value.is_null() {
slf.__delitem__(py, key as isize)
} else {
let value = PyObject::from_borrowed_ptr(py, value);
let ret = slf.__setitem__(py, key as isize, &value);
PyDrop::release_ref(value, py);
ret
};
PyDrop::release_ref(slf, py);
ret
})
}
wrap::<T>
}

View File

@ -50,41 +50,42 @@ pub unsafe fn Py_SIZE(ob : *mut PyObject) -> Py_ssize_t {
}
pub type unaryfunc =
unsafe extern "C" fn(arg1: *mut PyObject)
-> *mut PyObject;
unsafe extern "C" fn(arg1: *mut PyObject) -> *mut PyObject;
pub type binaryfunc =
unsafe extern "C" fn
(arg1: *mut PyObject, arg2: *mut PyObject)
-> *mut PyObject;
unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject)
-> *mut PyObject;
pub type ternaryfunc =
unsafe extern "C" fn
(arg1: *mut PyObject, arg2: *mut PyObject,
arg3: *mut PyObject) -> *mut PyObject;
unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject,
arg3: *mut PyObject) -> *mut PyObject;
pub type inquiry =
unsafe extern "C" fn(arg1: *mut PyObject)
-> c_int;
unsafe extern "C" fn(arg1: *mut PyObject) -> c_int;
pub type lenfunc =
unsafe extern "C" fn(arg1: *mut PyObject) -> Py_ssize_t;
pub type ssizeargfunc =
unsafe extern "C" fn(arg1: *mut PyObject, arg2: Py_ssize_t)
-> *mut PyObject;
-> *mut PyObject;
pub type ssizessizeargfunc =
unsafe extern "C" fn
(arg1: *mut PyObject, arg2: Py_ssize_t,
arg3: Py_ssize_t) -> *mut PyObject;
unsafe extern "C" fn(arg1: *mut PyObject, arg2: Py_ssize_t,
arg3: Py_ssize_t) -> *mut PyObject;
pub type ssizeobjargproc =
unsafe extern "C" fn
(arg1: *mut PyObject, arg2: Py_ssize_t,
arg3: *mut PyObject) -> c_int;
unsafe extern "C" fn(arg1: *mut PyObject, arg2: Py_ssize_t,
arg3: *mut PyObject) -> c_int;
pub type ssizessizeobjargproc =
unsafe extern "C" fn
(arg1: *mut PyObject, arg2: Py_ssize_t,
arg3: Py_ssize_t, arg4: *mut PyObject)
-> c_int;
unsafe extern "C" fn(arg1: *mut PyObject, arg2: Py_ssize_t,
arg3: Py_ssize_t, arg4: *mut PyObject)
-> c_int;
pub type objobjargproc =
unsafe extern "C" fn
(arg1: *mut PyObject, arg2: *mut PyObject,
arg3: *mut PyObject) -> c_int;
unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject,
arg3: *mut PyObject) -> c_int;
#[cfg(not(Py_LIMITED_API))]
mod bufferinfo {

View File

@ -69,7 +69,6 @@ macro_rules! py_class_type_object_dynamic_init {
}
) => {
// call slot macros outside of unsafe block
*(unsafe { &mut $type_object.tp_as_sequence }) = py_class_as_sequence!($as_sequence);
*(unsafe { &mut $type_object.tp_as_number }) = py_class_as_number!($as_number);
}
}
@ -102,22 +101,6 @@ macro_rules! py_class_wrap_newfunc {
}}
}
#[macro_export]
#[doc(hidden)]
macro_rules! py_class_as_sequence {
([]) => (0 as *mut $crate::_detail::ffi::PySequenceMethods);
([$( $slot_name:ident : $slot_value:expr ,)+]) => {{
static mut SEQUENCE_METHODS : $crate::_detail::ffi::PySequenceMethods
= $crate::_detail::ffi::PySequenceMethods {
$( $slot_name : $slot_value, )*
..
$crate::_detail::ffi::PySequenceMethods_INIT
};
unsafe { &mut SEQUENCE_METHODS }
}}
}
#[macro_export]
#[doc(hidden)]
macro_rules! py_class_as_number {