basic object customization interface
This commit is contained in:
parent
086f24e7a1
commit
34e4d956f1
|
@ -61,6 +61,7 @@ static NUM_METHODS: Methods = Methods {
|
|||
|
||||
|
||||
enum ImplType {
|
||||
Object,
|
||||
Async,
|
||||
Buffer,
|
||||
Context,
|
||||
|
@ -76,6 +77,9 @@ pub fn build_py_proto(ast: &mut syn::Item) -> Tokens {
|
|||
syn::ItemKind::Impl(_, _, _, ref path, ref ty, ref mut impl_items) => {
|
||||
if let &Some(ref path) = path {
|
||||
match process_path(path) {
|
||||
ImplType::Object =>
|
||||
impl_protocol("pyo3::class::async::PyObjectProtocolImpl",
|
||||
path.clone(), ty, impl_items, &DEFAULT_METHODS),
|
||||
ImplType::Async =>
|
||||
impl_protocol("pyo3::class::async::PyAsyncProtocolImpl",
|
||||
path.clone(), ty, impl_items, &DEFAULT_METHODS),
|
||||
|
@ -112,6 +116,7 @@ pub fn build_py_proto(ast: &mut syn::Item) -> Tokens {
|
|||
fn process_path(path: &syn::Path) -> ImplType {
|
||||
if let Some(segment) = path.segments.last() {
|
||||
match segment.ident.as_ref() {
|
||||
"PyObjectProtocol" => ImplType::Object,
|
||||
"PyAsyncProtocol" => ImplType::Async,
|
||||
"PyBufferProtocol" => ImplType::Buffer,
|
||||
"PyContextProtocol" => ImplType::Context,
|
||||
|
|
|
@ -5,14 +5,17 @@
|
|||
//! more information on python async support
|
||||
//! https://docs.python.org/3/reference/datamodel.html#basic-customization
|
||||
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use ::{CompareOp, Py_hash_t};
|
||||
use ffi;
|
||||
use err::{PyErr, PyResult};
|
||||
use python::{self, Python, PythonObject};
|
||||
use python::{Python, PythonObject, PyDrop};
|
||||
use conversion::ToPyObject;
|
||||
use objects::{PyObject, PyType, PyModule};
|
||||
use py_class::slots::UnitCallbackConverter;
|
||||
use objects::{exc, PyObject};
|
||||
use py_class::slots::{HashConverter, UnitCallbackConverter};
|
||||
use function::{handle_callback, PyObjectCallbackConverter};
|
||||
use class::NO_METHODS;
|
||||
use class::{NO_METHODS, NO_PY_METHODS};
|
||||
|
||||
// __new__
|
||||
// __init__
|
||||
|
@ -24,24 +27,184 @@ use class::NO_METHODS;
|
|||
/// Basic customization
|
||||
pub trait PyObjectProtocol {
|
||||
|
||||
// fn __getattr__()
|
||||
// fn __setattr__()
|
||||
// fn __delattr__()
|
||||
// fn __getattribute__
|
||||
// fn __setattribute__
|
||||
fn __getattr__(&self, py: Python, name: &PyObject) -> PyResult<PyObject>;
|
||||
|
||||
fn __setattr__(&self, py: Python, name: &PyObject, value: &PyObject) -> PyResult<()>;
|
||||
|
||||
fn __delattr__(&self, py: Python, name: &PyObject) -> PyResult<()>;
|
||||
|
||||
// __instancecheck__
|
||||
// __subclasscheck__
|
||||
// __iter__
|
||||
// __next__
|
||||
// __dir__
|
||||
|
||||
fn __str__(&self, py: Python) -> PyResult<PyString>;
|
||||
fn __str__(&self, py: Python) -> PyResult<PyObject>;
|
||||
|
||||
fn __repr__(&self, py: Python) -> PyResult<PyString>;
|
||||
fn __repr__(&self, py: Python) -> PyResult<PyObject>;
|
||||
|
||||
fn __hash__(&self, py: Python) -> PyResult<PyObject>;
|
||||
fn __hash__(&self, py: Python) -> PyResult<u64>;
|
||||
|
||||
fn __bool__(&self, py: Python) -> PyResult<bool>;
|
||||
|
||||
fn __richcmp__(&self, other: PyObject, op: pyo3::CompareOp) -> PyResult<bool>;
|
||||
fn __richcmp__(&self, py: Python, other: &PyObject, op: CompareOp) -> PyResult<PyObject>;
|
||||
|
||||
}
|
||||
|
||||
|
||||
impl<T> PyObjectProtocol for T {
|
||||
|
||||
default fn __getattr__(&self, py: Python, _: &PyObject) -> PyResult<PyObject> {
|
||||
Err(PyErr::new::<exc::NotImplementedError, _>(py, "Not implemented"))
|
||||
}
|
||||
default fn __setattr__(&self, py: Python, _: &PyObject, _: &PyObject) -> PyResult<()> {
|
||||
Err(PyErr::new::<exc::NotImplementedError, _>(py, "Not implemented"))
|
||||
}
|
||||
default fn __delattr__(&self, py: Python, _: &PyObject) -> PyResult<()> {
|
||||
Err(PyErr::new::<exc::NotImplementedError, _>(py, "Not implemented"))
|
||||
}
|
||||
|
||||
// __instancecheck__
|
||||
// __subclasscheck__
|
||||
// __iter__
|
||||
// __next__
|
||||
// __dir__
|
||||
|
||||
default fn __str__(&self, py: Python) -> PyResult<PyObject> {
|
||||
Err(PyErr::new::<exc::NotImplementedError, _>(py, "Not implemented"))
|
||||
}
|
||||
default fn __repr__(&self, py: Python) -> PyResult<PyObject> {
|
||||
Err(PyErr::new::<exc::NotImplementedError, _>(py, "Not implemented"))
|
||||
}
|
||||
default fn __hash__(&self, py: Python) -> PyResult<u64> {
|
||||
Err(PyErr::new::<exc::NotImplementedError, _>(py, "Not implemented"))
|
||||
}
|
||||
default fn __bool__(&self, py: Python) -> PyResult<bool> {
|
||||
Err(PyErr::new::<exc::NotImplementedError, _>(py, "Not implemented"))
|
||||
}
|
||||
default fn __richcmp__(&self, py: Python, _: &PyObject, _: CompareOp) -> PyResult<PyObject> {
|
||||
Err(PyErr::new::<exc::NotImplementedError, _>(py, "Not implemented"))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait PyObjectProtocolImpl {
|
||||
fn methods() -> &'static [&'static str];
|
||||
fn py_methods() -> &'static [::class::PyMethodDefType];
|
||||
}
|
||||
|
||||
impl<T> PyObjectProtocolImpl for T {
|
||||
default fn methods() -> &'static [&'static str] {
|
||||
NO_METHODS
|
||||
}
|
||||
default fn py_methods() -> &'static [::class::PyMethodDefType] {
|
||||
NO_PY_METHODS
|
||||
}
|
||||
}
|
||||
|
||||
pub fn py_object_proto_impl<T>(type_object: &mut ffi::PyTypeObject)
|
||||
where T: PyObjectProtocol + PyObjectProtocolImpl + PythonObject
|
||||
{
|
||||
let methods = T::methods();
|
||||
if methods.is_empty() {
|
||||
return
|
||||
}
|
||||
|
||||
for name in methods {
|
||||
match name {
|
||||
&"__str__" =>
|
||||
type_object.tp_str = py_unary_func!(
|
||||
PyObjectProtocol, T::__str__, PyObjectCallbackConverter),
|
||||
&"__repr__" =>
|
||||
type_object.tp_repr = py_unary_func!(
|
||||
PyObjectProtocol, T::__repr__, PyObjectCallbackConverter),
|
||||
&"__hash__" =>
|
||||
type_object.tp_hash = py_unary_func!(
|
||||
PyObjectProtocol, T::__hash__, HashConverter, Py_hash_t),
|
||||
&"__getattr__" =>
|
||||
type_object.tp_getattro = py_binary_func!(
|
||||
PyObjectProtocol, T::__getattr__, PyObjectCallbackConverter),
|
||||
&"__richcmp__" =>
|
||||
type_object.tp_richcompare = tp_richcompare::<T>(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
if methods.contains(&"__setattr__") || methods.contains(&"__getattr__") {
|
||||
type_object.tp_setattro = tp_setattro::<T>()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn tp_setattro<T>() -> Option<ffi::setattrofunc>
|
||||
where T: PyObjectProtocol + PythonObject
|
||||
{
|
||||
unsafe extern "C" fn wrap<T>(slf: *mut ffi::PyObject,
|
||||
key: *mut ffi::PyObject,
|
||||
value: *mut ffi::PyObject) -> c_int
|
||||
where T: PyObjectProtocol + PythonObject
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!(T), ".__setitem__()");
|
||||
|
||||
handle_callback(
|
||||
LOCATION, UnitCallbackConverter, |py|
|
||||
{
|
||||
let slf = PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<T>();
|
||||
let key = PyObject::from_borrowed_ptr(py, key);
|
||||
|
||||
// if value is none, then __delitem__
|
||||
let ret = if value.is_null() {
|
||||
slf.__delattr__(py, &key)
|
||||
} else {
|
||||
let value = PyObject::from_borrowed_ptr(py, value);
|
||||
let ret = slf.__setattr__(py, &key, &value);
|
||||
PyDrop::release_ref(value, py);
|
||||
ret
|
||||
};
|
||||
|
||||
PyDrop::release_ref(key, py);
|
||||
PyDrop::release_ref(slf, py);
|
||||
ret
|
||||
})
|
||||
}
|
||||
Some(wrap::<T>)
|
||||
}
|
||||
|
||||
fn extract_op(py: Python, op: c_int) -> PyResult<CompareOp> {
|
||||
match op {
|
||||
ffi::Py_LT => Ok(CompareOp::Lt),
|
||||
ffi::Py_LE => Ok(CompareOp::Le),
|
||||
ffi::Py_EQ => Ok(CompareOp::Eq),
|
||||
ffi::Py_NE => Ok(CompareOp::Ne),
|
||||
ffi::Py_GT => Ok(CompareOp::Gt),
|
||||
ffi::Py_GE => Ok(CompareOp::Ge),
|
||||
_ => Err(PyErr::new_lazy_init(
|
||||
py.get_type::<exc::ValueError>(),
|
||||
Some("tp_richcompare called with invalid comparison operator"
|
||||
.to_py_object(py).into_object())))
|
||||
}
|
||||
}
|
||||
|
||||
fn tp_richcompare<T>() -> Option<ffi::richcmpfunc>
|
||||
where T: PyObjectProtocol + PythonObject
|
||||
{
|
||||
unsafe extern "C" fn wrap<T>(slf: *mut ffi::PyObject,
|
||||
arg: *mut ffi::PyObject,
|
||||
op: c_int) -> *mut ffi::PyObject
|
||||
where T: PyObjectProtocol + PythonObject
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!(T), ".__richcmp__()");
|
||||
handle_callback(LOCATION, PyObjectCallbackConverter, |py| {
|
||||
let slf = PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<T>();
|
||||
let arg = PyObject::from_borrowed_ptr(py, arg);
|
||||
let ret = match extract_op(py, op) {
|
||||
Ok(op) => slf.__richcmp__(py, &arg, op),
|
||||
Err(_) => Ok(py.NotImplemented())
|
||||
};
|
||||
PyDrop::release_ref(arg, py);
|
||||
PyDrop::release_ref(slf, py);
|
||||
ret
|
||||
})
|
||||
}
|
||||
Some(wrap::<T>)
|
||||
}
|
||||
|
|
|
@ -29,19 +29,19 @@ pub trait PyDescrProtocol {
|
|||
}
|
||||
|
||||
impl<P> PyDescrProtocol for P {
|
||||
fn __get__(&self, py: Python, _: &PyObject, _: &PyObject) -> PyResult<PyObject> {
|
||||
default fn __get__(&self, py: Python, _: &PyObject, _: &PyObject) -> PyResult<PyObject> {
|
||||
Err(PyErr::new::<exc::NotImplementedError, _>(py, "Not implemented"))
|
||||
}
|
||||
|
||||
fn __set__(&self, py: Python, _: &PyObject, _: &PyObject) -> PyResult<()> {
|
||||
default fn __set__(&self, py: Python, _: &PyObject, _: &PyObject) -> PyResult<()> {
|
||||
Err(PyErr::new::<exc::NotImplementedError, _>(py, "Not implemented"))
|
||||
}
|
||||
|
||||
fn __delete__(&self, py: Python, _: &PyObject) -> PyResult<()> {
|
||||
default fn __delete__(&self, py: Python, _: &PyObject) -> PyResult<()> {
|
||||
Err(PyErr::new::<exc::NotImplementedError, _>(py, "Not implemented"))
|
||||
}
|
||||
|
||||
fn __set_name__(&self, py: Python, _: &PyObject) -> PyResult<()> {
|
||||
default fn __set_name__(&self, py: Python, _: &PyObject) -> PyResult<()> {
|
||||
Err(PyErr::new::<exc::NotImplementedError, _>(py, "Not implemented"))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
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
|
||||
($trait:ident, $class:ident :: $f:ident, $conv:expr) => {
|
||||
py_unary_func!($trait, $class::$f, $conv, *mut $crate::ffi::PyObject);
|
||||
};
|
||||
($trait:ident, $class:ident :: $f:ident, $conv:expr, $res_type:ty) => {{
|
||||
unsafe extern "C" fn wrap<T>(slf: *mut $crate::ffi::PyObject) -> $res_type
|
||||
where T: $trait + PythonObject
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#[macro_use] mod macros;
|
||||
|
||||
pub mod async;
|
||||
pub mod basic;
|
||||
pub mod buffer;
|
||||
pub mod context;
|
||||
pub mod descr;
|
||||
|
@ -13,6 +14,7 @@ pub mod gc;
|
|||
pub mod sequence;
|
||||
pub mod typeob;
|
||||
|
||||
pub use self::basic::PyObjectProtocol;
|
||||
pub use self::async::PyAsyncProtocol;
|
||||
pub use self::buffer::PyBufferProtocol;
|
||||
pub use self::context::PyContextProtocol;
|
||||
|
|
|
@ -3,13 +3,16 @@
|
|||
//! Python Number Interface
|
||||
//! Trait and support implementation for implementing number protocol
|
||||
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use ffi;
|
||||
use err::PyResult;
|
||||
use python::{Python, PythonObject};
|
||||
use objects::PyObject;
|
||||
use function::PyObjectCallbackConverter;
|
||||
use py_class::slots::BoolConverter;
|
||||
use class::{NO_METHODS, NO_PY_METHODS};
|
||||
|
||||
use class::basic::{PyObjectProtocol, PyObjectProtocolImpl};
|
||||
|
||||
/// Number interface
|
||||
pub trait PyNumberProtocol {
|
||||
|
@ -244,10 +247,14 @@ impl ffi::PyNumberMethods {
|
|||
|
||||
/// Construct PyNumberMethods struct for PyTypeObject.tp_as_number
|
||||
pub fn new<T>() -> Option<ffi::PyNumberMethods>
|
||||
where T: PyNumberProtocol + PyNumberProtocolImpl + PythonObject
|
||||
where T: PyNumberProtocol + PyNumberProtocolImpl
|
||||
+ PyObjectProtocol + PyObjectProtocolImpl
|
||||
+ PythonObject
|
||||
|
||||
{
|
||||
let methods = T::methods();
|
||||
if methods.is_empty() {
|
||||
let objm = <T as PyObjectProtocolImpl>::methods();
|
||||
let methods = <T as PyNumberProtocolImpl>::methods();
|
||||
if methods.is_empty() && ! objm.contains(&"__bool__") {
|
||||
return None
|
||||
}
|
||||
|
||||
|
@ -366,7 +373,6 @@ impl ffi::PyNumberMethods {
|
|||
&"__neg__" => {
|
||||
meth.nb_negative = py_unary_func!(
|
||||
PyNumberProtocol, T::__neg__, PyObjectCallbackConverter);
|
||||
|
||||
},
|
||||
&"__pos__" => {
|
||||
meth.nb_positive = py_unary_func!(
|
||||
|
@ -401,6 +407,11 @@ impl ffi::PyNumberMethods {
|
|||
}
|
||||
}
|
||||
|
||||
if objm.contains(&"__bool__") {
|
||||
meth.nb_bool = py_unary_func!(
|
||||
PyObjectProtocol, T::__bool__, BoolConverter, c_int);
|
||||
}
|
||||
|
||||
Some(meth)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@ pub use pyo3cls as cls;
|
|||
pub use pyo3cls::*;
|
||||
|
||||
pub mod ffi;
|
||||
pub use ffi::Py_ssize_t;
|
||||
pub use ffi::{Py_ssize_t, Py_hash_t};
|
||||
pub use err::{PyErr, PyResult};
|
||||
pub use objects::*;
|
||||
pub use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, PythonObjectDowncastError, PythonObjectWithTypeObject, PyClone, PyDrop};
|
||||
|
@ -100,7 +100,6 @@ pub use py_class::{CompareOp};
|
|||
pub use objectprotocol::{ObjectProtocol};
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type Py_hash_t = ffi::Py_hash_t;
|
||||
|
||||
use std::{ptr, mem};
|
||||
|
||||
|
|
Loading…
Reference in New Issue