drop py_class
This commit is contained in:
parent
34e4d956f1
commit
c9aefd7e5f
|
@ -7,7 +7,6 @@ readme = "README.md"
|
|||
keywords = [
|
||||
"pyo3",
|
||||
"python",
|
||||
"cpython",
|
||||
]
|
||||
homepage = "https://github.com/pyo3/pyo3"
|
||||
repository = "https://github.com/pyo3/pyo3.git"
|
||||
|
|
5
Makefile
5
Makefile
|
@ -23,10 +23,7 @@ CARGO_FLAGS := --features "$(FEATURES)" --no-default-features
|
|||
|
||||
default: test
|
||||
|
||||
src/py_class/py_class_impl.rs: src/py_class/py_class_impl.py
|
||||
PY=3 python $< >$@
|
||||
|
||||
build: src/py_class/py_class_impl.rs
|
||||
build:
|
||||
cargo build $(CARGO_FLAGS)
|
||||
|
||||
test: build
|
||||
|
|
|
@ -5,16 +5,36 @@ use quote::{Tokens, ToTokens};
|
|||
|
||||
|
||||
pub fn build_py_class(ast: &mut syn::DeriveInput) -> Tokens {
|
||||
if let syn::Body::Enum(_) = ast.body {
|
||||
panic!("#[py_class] can only be used with structs")
|
||||
}
|
||||
let base = syn::Ident::from("pyo3::PyObject");
|
||||
|
||||
let mut tokens = Tokens::new();
|
||||
|
||||
match ast.body {
|
||||
syn::Body::Struct(syn::VariantData::Struct(ref mut data)) => {
|
||||
impl_storage(&ast.ident, &base, data).to_tokens(&mut tokens);
|
||||
|
||||
let tt = quote! {
|
||||
struct Test {
|
||||
_unsafe_inner: PyObject
|
||||
}
|
||||
};
|
||||
let t = syn::parse_item(tt.as_str()).unwrap();
|
||||
match t.node {
|
||||
syn::ItemKind::Struct(syn::VariantData::Struct(fields), _) => {
|
||||
data.clear();
|
||||
data.extend(fields);
|
||||
}
|
||||
_ => panic!("something is worng"),
|
||||
}
|
||||
},
|
||||
_ =>
|
||||
panic!("#[class] can only be used with notmal structs"),
|
||||
}
|
||||
|
||||
impl_to_py_object(&ast.ident).to_tokens(&mut tokens);
|
||||
impl_from_py_object(&ast.ident).to_tokens(&mut tokens);
|
||||
impl_python_object(&ast.ident).to_tokens(&mut tokens);
|
||||
impl_checked_downcast(&ast.ident).to_tokens(&mut tokens);
|
||||
impl_class_init(&ast.ident).to_tokens(&mut tokens);
|
||||
|
||||
let dummy_const = syn::Ident::new(format!("_IMPL_PYO3_CLS_{}", ast.ident));
|
||||
quote! {
|
||||
|
@ -23,13 +43,140 @@ pub fn build_py_class(ast: &mut syn::DeriveInput) -> Tokens {
|
|||
const #dummy_const: () = {
|
||||
extern crate pyo3;
|
||||
use std;
|
||||
use pyo3::ffi;
|
||||
use pyo3::class::BaseObject;
|
||||
use pyo3::{ffi, Python, PyObject, PyType, PyResult, PyModule};
|
||||
|
||||
#tokens
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_storage(cls: &syn::Ident, base: &syn::Ident, fields: &Vec<syn::Field>) -> Tokens {
|
||||
let names: Vec<syn::Ident> = fields.iter()
|
||||
.map(|f| f.ident.as_ref().unwrap().clone()).collect();
|
||||
let values: Vec<syn::Ident> = fields.iter()
|
||||
.map(|f| f.ident.as_ref().unwrap().clone()).collect();
|
||||
//let types: Vec<syn::Ty> = fields.iter().map(|f| f.ty.clone()).collect();
|
||||
|
||||
let mut accessors = Tokens::new();
|
||||
for field in fields.iter() {
|
||||
let name = &field.ident.as_ref().unwrap();
|
||||
let name_mut = syn::Ident::from(format!("{}_mut", name.as_ref()));
|
||||
let ty = &field.ty;
|
||||
|
||||
let accessor = quote!{
|
||||
impl #cls {
|
||||
fn #name<'a>(&'a self, py: Python<'a>) -> &'a #ty {
|
||||
unsafe {
|
||||
let ptr = (self._unsafe_inner.as_ptr() as *const u8)
|
||||
.offset(base_offset() as isize) as *const Storage;
|
||||
&(*ptr).#name
|
||||
}
|
||||
}
|
||||
fn #name_mut<'a>(&'a self, py: Python<'a>) -> &'a mut #ty {
|
||||
unsafe {
|
||||
let ptr = (self._unsafe_inner.as_ptr() as *const u8)
|
||||
.offset(base_offset() as isize) as *mut Storage;
|
||||
&mut (*ptr).#name
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
accessor.to_tokens(&mut accessors);
|
||||
}
|
||||
|
||||
quote! {
|
||||
struct Storage {
|
||||
#(#fields),*
|
||||
}
|
||||
|
||||
impl #cls {
|
||||
fn create_instance(py: Python, #(#fields),*) -> PyResult<#cls> {
|
||||
let obj = try!(unsafe {
|
||||
<#cls as BaseObject>::alloc(
|
||||
py, &py.get_type::<#cls>(),
|
||||
Storage { #(#names: #values),*})});
|
||||
|
||||
return Ok(#cls { _unsafe_inner: obj });
|
||||
}
|
||||
}
|
||||
|
||||
#accessors
|
||||
|
||||
impl pyo3::PythonObjectWithTypeObject for #cls {
|
||||
#[inline]
|
||||
fn type_object(py: Python) -> PyType {
|
||||
unsafe { #cls::initialized(py, None) }
|
||||
}
|
||||
}
|
||||
|
||||
impl pyo3::class::PyTypeObject for #cls {
|
||||
|
||||
fn add_to_module(py: Python, module: &PyModule) -> PyResult<()> {
|
||||
let ty = unsafe { #cls::initialized(py, module.name(py).ok()) };
|
||||
module.add(py, stringify!(#cls), ty)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn type_obj() -> &'static mut ffi::PyTypeObject {
|
||||
static mut TYPE_OBJECT: ffi::PyTypeObject = ffi::PyTypeObject_INIT;
|
||||
&mut TYPE_OBJECT
|
||||
}
|
||||
|
||||
unsafe fn initialized(py: Python, module_name: Option<&str>) -> PyType {
|
||||
let mut ty = #cls::type_obj();
|
||||
|
||||
if (ty.tp_flags & ffi::Py_TPFLAGS_READY) != 0 {
|
||||
PyType::from_type_ptr(py, ty)
|
||||
} else {
|
||||
// automatically initialize the class on-demand
|
||||
pyo3::class::typeob::initialize_type::<#cls>(
|
||||
py, module_name, ty).expect(
|
||||
concat!("An error occurred while initializing class ",
|
||||
stringify!(#cls)));
|
||||
PyType::from_type_ptr(py, ty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn base_offset() -> usize {
|
||||
let align = std::mem::align_of::<Storage>();
|
||||
let bs = <#base as BaseObject>::size();
|
||||
|
||||
// round base_size up to next multiple of align
|
||||
(bs + align - 1) / align * align
|
||||
}
|
||||
|
||||
impl BaseObject for #cls {
|
||||
type Type = Storage;
|
||||
|
||||
#[inline]
|
||||
fn size() -> usize {
|
||||
base_offset() + std::mem::size_of::<Self::Type>()
|
||||
}
|
||||
|
||||
unsafe fn alloc(py: Python, ty: &PyType, value: Self::Type) -> PyResult<PyObject>
|
||||
{
|
||||
let obj = try!(<#base as BaseObject>::alloc(py, ty, ()));
|
||||
|
||||
let ptr = (obj.as_ptr() as *mut u8)
|
||||
.offset(base_offset() as isize) as *mut Self::Type;
|
||||
std::ptr::write(ptr, value);
|
||||
|
||||
Ok(obj)
|
||||
}
|
||||
|
||||
unsafe fn dealloc(py: Python, obj: *mut ffi::PyObject) {
|
||||
let ptr = (obj as *mut u8).offset(base_offset() as isize) as *mut Self::Type;
|
||||
std::ptr::drop_in_place(ptr);
|
||||
|
||||
<#base as BaseObject>::dealloc(py, obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_to_py_object(cls: &syn::Ident) -> Tokens {
|
||||
quote! {
|
||||
/// Identity conversion: allows using existing `PyObject` instances where
|
||||
|
@ -130,13 +277,3 @@ fn impl_checked_downcast(cls: &syn::Ident) -> Tokens {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_class_init(cls: &syn::Ident) -> Tokens {
|
||||
quote! {
|
||||
impl pyo3::class::typeob::PyClassInit for #cls {
|
||||
fn init() -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -220,8 +220,8 @@ fn impl_wrap(cls: &Box<syn::Ty>, name: &syn::Ident, args: Vec<Arg>) -> Tokens {
|
|||
{
|
||||
const LOCATION: &'static str = concat!(
|
||||
stringify!(#cls), ".", stringify!(#name), "()");
|
||||
pyo3::_detail::handle_callback(
|
||||
LOCATION, pyo3::_detail::PyObjectCallbackConverter, |py|
|
||||
pyo3::callback::handle_callback(
|
||||
LOCATION, pyo3::callback::PyObjectCallbackConverter, |py|
|
||||
{
|
||||
let args: pyo3::PyTuple =
|
||||
pyo3::PyObject::from_borrowed_ptr(py, args).unchecked_cast_into();
|
||||
|
@ -248,8 +248,8 @@ fn impl_wrap_getter(cls: &Box<syn::Ty>, name: &syn::Ident, _args: Vec<Arg>) -> T
|
|||
{
|
||||
const LOCATION: &'static str = concat!(
|
||||
stringify!(#cls), ".getter_", stringify!(#name), "()");
|
||||
pyo3::_detail::handle_callback(
|
||||
LOCATION, pyo3::_detail::PyObjectCallbackConverter, |py|
|
||||
pyo3::callback::handle_callback(
|
||||
LOCATION, pyo3::callback::PyObjectCallbackConverter, |py|
|
||||
{
|
||||
let slf = pyo3::PyObject::from_borrowed_ptr(
|
||||
py, slf).unchecked_cast_into::<#cls>();
|
||||
|
@ -270,8 +270,8 @@ fn impl_wrap_setter(cls: &Box<syn::Ty>, name: &syn::Ident, _args: Vec<Arg>) -> T
|
|||
{
|
||||
const LOCATION: &'static str = concat!(
|
||||
stringify!(#cls), ".setter", stringify!(#name), "()");
|
||||
pyo3::_detail::handle_callback(
|
||||
LOCATION, pyo3::py_class::slots::UnitCallbackConverter, |py|
|
||||
pyo3::callback::handle_callback(
|
||||
LOCATION, pyo3::callback::UnitCallbackConverter, |py|
|
||||
{
|
||||
let slf = pyo3::PyObject::from_borrowed_ptr(py, slf)
|
||||
.unchecked_cast_into::<#cls>();
|
||||
|
|
|
@ -0,0 +1,242 @@
|
|||
/// Utilities for a Python callable object that invokes a Rust function.
|
||||
|
||||
use std::os::raw::c_int;
|
||||
use std::{any, mem, ptr, isize, io, marker, panic};
|
||||
|
||||
use libc;
|
||||
use python::{Python, PythonObject};
|
||||
use objects::exc;
|
||||
use conversion::ToPyObject;
|
||||
use ffi::{self, Py_hash_t};
|
||||
use err::{PyErr, PyResult};
|
||||
|
||||
|
||||
pub trait CallbackConverter<S> {
|
||||
type R;
|
||||
|
||||
fn convert(S, Python) -> Self::R;
|
||||
fn error_value() -> Self::R;
|
||||
}
|
||||
|
||||
pub struct PyObjectCallbackConverter;
|
||||
|
||||
impl <S> CallbackConverter<S> for PyObjectCallbackConverter
|
||||
where S: ToPyObject
|
||||
{
|
||||
type R = *mut ffi::PyObject;
|
||||
|
||||
fn convert(val: S, py: Python) -> *mut ffi::PyObject {
|
||||
val.into_py_object(py).into_object().steal_ptr()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn error_value() -> *mut ffi::PyObject {
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PythonObjectCallbackConverter<T>(pub marker::PhantomData<T>);
|
||||
|
||||
impl <T, S> CallbackConverter<S> for PythonObjectCallbackConverter<T>
|
||||
where T: PythonObject,
|
||||
S: ToPyObject
|
||||
{
|
||||
type R = *mut ffi::PyObject;
|
||||
|
||||
fn convert(val: S, py: Python) -> *mut ffi::PyObject {
|
||||
val.into_py_object(py).into_object().steal_ptr()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn error_value() -> *mut ffi::PyObject {
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BoolConverter;
|
||||
|
||||
impl CallbackConverter<bool> for BoolConverter {
|
||||
type R = c_int;
|
||||
|
||||
#[inline]
|
||||
fn convert(val: bool, _py: Python) -> c_int {
|
||||
val as c_int
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn error_value() -> c_int {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LenResultConverter;
|
||||
|
||||
impl CallbackConverter<usize> for LenResultConverter {
|
||||
type R = isize;
|
||||
|
||||
fn convert(val: usize, py: Python) -> isize {
|
||||
if val <= (isize::MAX as usize) {
|
||||
val as isize
|
||||
} else {
|
||||
PyErr::new_lazy_init(py.get_type::<exc::OverflowError>(), None).restore(py);
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn error_value() -> isize {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct UnitCallbackConverter;
|
||||
|
||||
impl CallbackConverter<()> for UnitCallbackConverter {
|
||||
type R = c_int;
|
||||
|
||||
#[inline]
|
||||
fn convert(_: (), _: Python) -> c_int {
|
||||
0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn error_value() -> c_int {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VoidCallbackConverter;
|
||||
|
||||
impl CallbackConverter<()> for VoidCallbackConverter {
|
||||
type R = ();
|
||||
|
||||
#[inline]
|
||||
fn convert(_: (), _: Python) -> () {
|
||||
()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn error_value() -> () {
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IterNextResultConverter;
|
||||
|
||||
impl <T> CallbackConverter<Option<T>>
|
||||
for IterNextResultConverter
|
||||
where T: ToPyObject
|
||||
{
|
||||
type R = *mut ffi::PyObject;
|
||||
|
||||
fn convert(val: Option<T>, py: Python) -> *mut ffi::PyObject {
|
||||
match val {
|
||||
Some(val) => val.into_py_object(py).into_object().steal_ptr(),
|
||||
None => unsafe {
|
||||
ffi::PyErr_SetNone(ffi::PyExc_StopIteration);
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn error_value() -> *mut ffi::PyObject {
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WrappingCastTo<T> {
|
||||
fn wrapping_cast(self) -> T;
|
||||
}
|
||||
|
||||
macro_rules! wrapping_cast {
|
||||
($from:ty, $to:ty) => {
|
||||
impl WrappingCastTo<$to> for $from {
|
||||
#[inline]
|
||||
fn wrapping_cast(self) -> $to {
|
||||
self as $to
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
wrapping_cast!(u8, Py_hash_t);
|
||||
wrapping_cast!(u16, Py_hash_t);
|
||||
wrapping_cast!(u32, Py_hash_t);
|
||||
wrapping_cast!(usize, Py_hash_t);
|
||||
wrapping_cast!(u64, Py_hash_t);
|
||||
wrapping_cast!(i8, Py_hash_t);
|
||||
wrapping_cast!(i16, Py_hash_t);
|
||||
wrapping_cast!(i32, Py_hash_t);
|
||||
wrapping_cast!(isize, Py_hash_t);
|
||||
wrapping_cast!(i64, Py_hash_t);
|
||||
|
||||
pub struct HashConverter;
|
||||
|
||||
impl <T> CallbackConverter<T> for HashConverter
|
||||
where T: WrappingCastTo<Py_hash_t>
|
||||
{
|
||||
type R = Py_hash_t;
|
||||
|
||||
#[inline]
|
||||
fn convert(val: T, _py: Python) -> Py_hash_t {
|
||||
let hash = val.wrapping_cast();
|
||||
if hash == -1 {
|
||||
-2
|
||||
} else {
|
||||
hash
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn error_value() -> Py_hash_t {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub unsafe fn handle_callback<F, T, C>(location: &str, _c: C, f: F) -> C::R
|
||||
where F: FnOnce(Python) -> PyResult<T>,
|
||||
F: panic::UnwindSafe,
|
||||
C: CallbackConverter<T>
|
||||
{
|
||||
let guard = AbortOnDrop(location);
|
||||
let ret = panic::catch_unwind(|| {
|
||||
let py = Python::assume_gil_acquired();
|
||||
match f(py) {
|
||||
Ok(val) => {
|
||||
C::convert(val, py)
|
||||
}
|
||||
Err(e) => {
|
||||
e.restore(py);
|
||||
C::error_value()
|
||||
}
|
||||
}
|
||||
});
|
||||
let ret = match ret {
|
||||
Ok(r) => r,
|
||||
Err(ref err) => {
|
||||
handle_panic(Python::assume_gil_acquired(), err);
|
||||
C::error_value()
|
||||
}
|
||||
};
|
||||
mem::forget(guard);
|
||||
ret
|
||||
}
|
||||
|
||||
fn handle_panic(_py: Python, _panic: &any::Any) {
|
||||
let msg = cstr!("Rust panic");
|
||||
unsafe {
|
||||
ffi::PyErr_SetString(ffi::PyExc_SystemError, msg.as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AbortOnDrop<'a>(pub &'a str);
|
||||
|
||||
impl <'a> Drop for AbortOnDrop<'a> {
|
||||
fn drop(&mut self) {
|
||||
use std::io::Write;
|
||||
let _ = writeln!(&mut io::stderr(), "Cannot unwind out of {}", self.0);
|
||||
unsafe { libc::abort() }
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ use ffi;
|
|||
use err::PyResult;
|
||||
use python::{Python, PythonObject};
|
||||
use objects::PyObject;
|
||||
use function::PyObjectCallbackConverter;
|
||||
use callback::PyObjectCallbackConverter;
|
||||
use class::NO_METHODS;
|
||||
|
||||
|
||||
|
|
|
@ -11,10 +11,9 @@ use ::{CompareOp, Py_hash_t};
|
|||
use ffi;
|
||||
use err::{PyErr, PyResult};
|
||||
use python::{Python, PythonObject, PyDrop};
|
||||
use conversion::ToPyObject;
|
||||
use objects::{exc, PyObject};
|
||||
use py_class::slots::{HashConverter, UnitCallbackConverter};
|
||||
use function::{handle_callback, PyObjectCallbackConverter};
|
||||
use conversion::ToPyObject;
|
||||
use callback::{handle_callback, PyObjectCallbackConverter, HashConverter, UnitCallbackConverter};
|
||||
use class::{NO_METHODS, NO_PY_METHODS};
|
||||
|
||||
// __new__
|
||||
|
|
|
@ -11,8 +11,7 @@ use ffi;
|
|||
use err::PyResult;
|
||||
use python::{Python, PythonObject};
|
||||
use objects::PyObject;
|
||||
use py_class::slots::UnitCallbackConverter;
|
||||
use function::handle_callback;
|
||||
use callback::{handle_callback, UnitCallbackConverter};
|
||||
use class::NO_METHODS;
|
||||
|
||||
|
||||
|
|
|
@ -12,8 +12,7 @@ use err::{PyErr, PyResult};
|
|||
use python::{Python, PythonObject};
|
||||
use objects::{exc, PyObject};
|
||||
use class::{NO_METHODS, NO_PY_METHODS};
|
||||
use function::PyObjectCallbackConverter;
|
||||
use py_class::slots::UnitCallbackConverter;
|
||||
use callback::{PyObjectCallbackConverter, UnitCallbackConverter};
|
||||
|
||||
/// Descriptor interface
|
||||
pub trait PyDescrProtocol {
|
||||
|
|
|
@ -9,7 +9,7 @@ use std::os::raw::{c_int, c_void};
|
|||
use ffi;
|
||||
use python::{Python, PythonObject, PyDrop, ToPythonPointer};
|
||||
use objects::PyObject;
|
||||
use function::AbortOnDrop;
|
||||
use callback::AbortOnDrop;
|
||||
use class::NO_METHODS;
|
||||
|
||||
pub struct PyTraverseError(c_int);
|
||||
|
|
|
@ -11,7 +11,7 @@ macro_rules! py_unary_func {
|
|||
where T: $trait + PythonObject
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
|
||||
$crate::_detail::handle_callback(LOCATION, $conv, |py| {
|
||||
$crate::callback::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);
|
||||
|
@ -31,7 +31,7 @@ macro_rules! py_unary_func {
|
|||
where T: $trait + PythonObject
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
|
||||
$crate::_detail::handle_callback(LOCATION, $conv, |py| {
|
||||
$crate::callback::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);
|
||||
|
@ -52,7 +52,7 @@ macro_rules! py_binary_func {
|
|||
where T: $trait + PythonObject
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
|
||||
$crate::_detail::handle_callback(LOCATION, $conv, |py| {
|
||||
$crate::callback::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);
|
||||
|
@ -78,7 +78,7 @@ macro_rules! py_ternary_func {
|
|||
where T: $trait + PythonObject
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
|
||||
$crate::_detail::handle_callback(LOCATION, $conv, |py| {
|
||||
$crate::callback::handle_callback(LOCATION, $conv, |py| {
|
||||
let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<T>();
|
||||
let arg1 = $crate::PyObject::from_borrowed_ptr(py, arg1);
|
||||
let arg2 = $crate::PyObject::from_borrowed_ptr(py, arg2);
|
||||
|
@ -102,7 +102,7 @@ macro_rules! py_ssizearg_func {
|
|||
where T: $trait + PythonObject
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
|
||||
$crate::_detail::handle_callback(LOCATION, $conv, |py| {
|
||||
$crate::callback::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);
|
||||
|
@ -123,7 +123,7 @@ macro_rules! py_objobj_proc {
|
|||
where T: $trait + PythonObject
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
|
||||
$crate::_detail::handle_callback(LOCATION, $conv, |py| {
|
||||
$crate::callback::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);
|
||||
|
@ -148,7 +148,7 @@ macro_rules! py_ternary_slot {
|
|||
where T: $trait + PythonObject
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
|
||||
$crate::_detail::handle_callback(
|
||||
$crate::callback::handle_callback(
|
||||
LOCATION, $conv, |py|
|
||||
{
|
||||
let slf = $crate::PyObject::from_borrowed_ptr(
|
||||
|
|
|
@ -9,8 +9,8 @@ use ffi;
|
|||
use err::{PyErr, PyResult};
|
||||
use python::{Python, PythonObject, PyDrop};
|
||||
use objects::{exc, PyObject};
|
||||
use py_class::slots::{LenResultConverter, UnitCallbackConverter};
|
||||
use function::{handle_callback, PyObjectCallbackConverter};
|
||||
use callback::{handle_callback, PyObjectCallbackConverter,
|
||||
LenResultConverter, UnitCallbackConverter};
|
||||
use class::NO_METHODS;
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
use std::mem;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
#[macro_use] mod macros;
|
||||
|
||||
pub mod async;
|
||||
|
@ -23,9 +26,77 @@ pub use self::number::PyNumberProtocol;
|
|||
pub use self::mapping::PyMappingProtocol;
|
||||
pub use self::sequence::PySequenceProtocol;
|
||||
|
||||
pub use self::typeob::PyTypeObject;
|
||||
pub use self::gc::{PyVisit, PyGCProtocol, PyTraverseError};
|
||||
pub use self::methods::{PyMethodDef, PyMethodDefType, PyMethodType,
|
||||
PyGetterDef, PySetterDef};
|
||||
|
||||
pub static NO_METHODS: &'static [&'static str] = &[];
|
||||
pub static NO_PY_METHODS: &'static [PyMethodDefType] = &[];
|
||||
|
||||
use ffi;
|
||||
use err::{self, PyResult};
|
||||
use objects::{PyObject, PyType};
|
||||
use python::{Python, PythonObject};
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CompareOp {
|
||||
Lt = ffi::Py_LT as isize,
|
||||
Le = ffi::Py_LE as isize,
|
||||
Eq = ffi::Py_EQ as isize,
|
||||
Ne = ffi::Py_NE as isize,
|
||||
Gt = ffi::Py_GT as isize,
|
||||
Ge = ffi::Py_GE as isize
|
||||
}
|
||||
|
||||
|
||||
/// A PythonObject that is usable as a base type for #[class]
|
||||
pub trait BaseObject : PythonObject {
|
||||
/// Gets the size of the object, in bytes.
|
||||
fn size() -> usize;
|
||||
|
||||
type Type;
|
||||
|
||||
/// Allocates a new object (usually by calling ty->tp_alloc),
|
||||
/// and initializes it using init_val.
|
||||
/// `ty` must be derived from the Self type, and the resulting object
|
||||
/// must be of type `ty`.
|
||||
unsafe fn alloc(py: Python, ty: &PyType, val: Self::Type) -> PyResult<PyObject>;
|
||||
|
||||
/// Calls the rust destructor for the object and frees the memory
|
||||
/// (usually by calling ptr->ob_type->tp_free).
|
||||
/// This function is used as tp_dealloc implementation.
|
||||
unsafe fn dealloc(py: Python, obj: *mut ffi::PyObject);
|
||||
}
|
||||
|
||||
|
||||
impl BaseObject for PyObject {
|
||||
#[inline]
|
||||
fn size() -> usize {
|
||||
mem::size_of::<ffi::PyObject>()
|
||||
}
|
||||
|
||||
type Type = ();
|
||||
|
||||
unsafe fn alloc(py: Python, ty: &PyType, _init_val: ()) -> PyResult<PyObject> {
|
||||
let ptr = ffi::PyType_GenericAlloc(ty.as_type_ptr(), 0);
|
||||
err::result_from_owned_ptr(py, ptr)
|
||||
}
|
||||
|
||||
unsafe fn dealloc(_py: Python, obj: *mut ffi::PyObject) {
|
||||
// Unfortunately, there is no PyType_GenericFree, so
|
||||
// we have to manually un-do the work of PyType_GenericAlloc:
|
||||
let ty = ffi::Py_TYPE(obj);
|
||||
if ffi::PyType_IS_GC(ty) != 0 {
|
||||
ffi::PyObject_GC_Del(obj as *mut c_void);
|
||||
} else {
|
||||
ffi::PyObject_Free(obj as *mut c_void);
|
||||
}
|
||||
// For heap types, PyType_GenericAlloc calls INCREF on the type objects,
|
||||
// so we need to call DECREF here:
|
||||
if ffi::PyType_HasFeature(ty, ffi::Py_TPFLAGS_HEAPTYPE) != 0 {
|
||||
ffi::Py_DECREF(ty as *mut ffi::PyObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,7 @@ use ffi;
|
|||
use err::PyResult;
|
||||
use python::{Python, PythonObject};
|
||||
use objects::PyObject;
|
||||
use function::PyObjectCallbackConverter;
|
||||
use py_class::slots::BoolConverter;
|
||||
use callback::{BoolConverter, PyObjectCallbackConverter};
|
||||
use class::{NO_METHODS, NO_PY_METHODS};
|
||||
use class::basic::{PyObjectProtocol, PyObjectProtocolImpl};
|
||||
|
||||
|
|
|
@ -9,8 +9,8 @@ use ffi;
|
|||
use err::{PyErr, PyResult};
|
||||
use python::{Python, PythonObject, PyDrop};
|
||||
use objects::{exc, PyObject};
|
||||
use py_class::slots::{LenResultConverter, UnitCallbackConverter, BoolConverter};
|
||||
use function::{handle_callback, PyObjectCallbackConverter};
|
||||
use callback::{handle_callback, PyObjectCallbackConverter,
|
||||
LenResultConverter, UnitCallbackConverter, BoolConverter};
|
||||
use class::NO_METHODS;
|
||||
|
||||
|
||||
|
|
|
@ -4,144 +4,130 @@ use std::mem;
|
|||
use std::ffi::CString;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use ::{ffi, class, py_class, PyErr, Python, PyResult, PythonObject};
|
||||
use objects::PyType;
|
||||
use function::AbortOnDrop;
|
||||
use class::PyMethodDefType;
|
||||
use ::{ffi, class, PyErr, Python, PyResult, PythonObject};
|
||||
use objects::{PyType, PyModule};
|
||||
use callback::AbortOnDrop;
|
||||
use class::{BaseObject, PyMethodDefType};
|
||||
|
||||
|
||||
pub trait PyClassInit {
|
||||
pub trait PyTypeObject : BaseObject + PythonObject {
|
||||
|
||||
fn init() -> bool;
|
||||
fn add_to_module(py: Python, module: &PyModule) -> PyResult<()>;
|
||||
|
||||
fn type_object() -> &'static mut ffi::PyTypeObject;
|
||||
unsafe fn type_obj() -> &'static mut ffi::PyTypeObject;
|
||||
|
||||
fn build_type(py: Python,
|
||||
module_name: Option<&str>,
|
||||
type_object: &mut ffi::PyTypeObject) -> PyResult<PyType>;
|
||||
unsafe fn initialized(py: Python, module_name: Option<&str>) -> PyType;
|
||||
|
||||
}
|
||||
|
||||
impl<T> PyClassInit for T
|
||||
where T: PythonObject + py_class::BaseObject {
|
||||
pub fn initialize_type<T>(py: Python, module_name: Option<&str>,
|
||||
type_object: &mut ffi::PyTypeObject) -> PyResult<PyType>
|
||||
where T: BaseObject + PythonObject
|
||||
{
|
||||
// type name
|
||||
let name = match module_name {
|
||||
Some(module_name) => CString::new(
|
||||
format!("{}.{}", module_name, stringify!(type_name))),
|
||||
None => CString::new(stringify!(type_name))
|
||||
};
|
||||
let name = name.expect(
|
||||
"Module name/type name must not contain NUL byte").into_raw();
|
||||
|
||||
default fn init() -> bool { false }
|
||||
type_object.tp_name = name;
|
||||
|
||||
default fn type_object() -> &'static mut ffi::PyTypeObject {
|
||||
static mut TYPE_OBJECT: ffi::PyTypeObject = ffi::PyTypeObject_INIT;
|
||||
unsafe {
|
||||
&mut TYPE_OBJECT
|
||||
}
|
||||
// dealloc
|
||||
type_object.tp_dealloc = Some(tp_dealloc_callback::<T>);
|
||||
|
||||
// GC support
|
||||
<T as class::gc::PyGCProtocolImpl>::update_type_object(type_object);
|
||||
|
||||
// type size
|
||||
type_object.tp_basicsize = <T as BaseObject>::size() as ffi::Py_ssize_t;
|
||||
|
||||
// descriptor protocol
|
||||
type_object.tp_descr_get = class::descr::get_descrfunc::<T>();
|
||||
type_object.tp_descr_set = class::descr::set_descrfunc::<T>();
|
||||
|
||||
// number methods
|
||||
if let Some(meth) = ffi::PyNumberMethods::new::<T>() {
|
||||
static mut NB_METHODS: ffi::PyNumberMethods = ffi::PyNumberMethods_INIT;
|
||||
*(unsafe { &mut NB_METHODS }) = meth;
|
||||
type_object.tp_as_number = unsafe { &mut NB_METHODS };
|
||||
} else {
|
||||
type_object.tp_as_number = 0 as *mut ffi::PyNumberMethods;
|
||||
}
|
||||
|
||||
default fn build_type(py: Python, module_name: Option<&str>,
|
||||
type_object: &mut ffi::PyTypeObject) -> PyResult<PyType> {
|
||||
// type name
|
||||
let name = match module_name {
|
||||
Some(module_name) => CString::new(
|
||||
format!("{}.{}", module_name, stringify!(type_name))),
|
||||
None => CString::new(stringify!(type_name))
|
||||
};
|
||||
let name = name.expect(
|
||||
"Module name/type name must not contain NUL byte").into_raw();
|
||||
// mapping methods
|
||||
if let Some(meth) = ffi::PyMappingMethods::new::<T>() {
|
||||
static mut MP_METHODS: ffi::PyMappingMethods = ffi::PyMappingMethods_INIT;
|
||||
*(unsafe { &mut MP_METHODS }) = meth;
|
||||
type_object.tp_as_mapping = unsafe { &mut MP_METHODS };
|
||||
} else {
|
||||
type_object.tp_as_mapping = 0 as *mut ffi::PyMappingMethods;
|
||||
}
|
||||
|
||||
type_object.tp_name = name;
|
||||
// sequence methods
|
||||
if let Some(meth) = ffi::PySequenceMethods::new::<T>() {
|
||||
static mut SQ_METHODS: ffi::PySequenceMethods = ffi::PySequenceMethods_INIT;
|
||||
*(unsafe { &mut SQ_METHODS }) = meth;
|
||||
type_object.tp_as_sequence = unsafe { &mut SQ_METHODS };
|
||||
} else {
|
||||
type_object.tp_as_sequence = 0 as *mut ffi::PySequenceMethods;
|
||||
}
|
||||
|
||||
// dealloc
|
||||
type_object.tp_dealloc = Some(tp_dealloc_callback::<T>);
|
||||
// async methods
|
||||
if let Some(meth) = ffi::PyAsyncMethods::new::<T>() {
|
||||
static mut ASYNC_METHODS: ffi::PyAsyncMethods = ffi::PyAsyncMethods_INIT;
|
||||
*(unsafe { &mut ASYNC_METHODS }) = meth;
|
||||
type_object.tp_as_async = unsafe { &mut ASYNC_METHODS };
|
||||
} else {
|
||||
type_object.tp_as_async = 0 as *mut ffi::PyAsyncMethods;
|
||||
}
|
||||
|
||||
// GC support
|
||||
<T as class::gc::PyGCProtocolImpl>::update_type_object(type_object);
|
||||
// buffer protocol
|
||||
if let Some(meth) = ffi::PyBufferProcs::new::<T>() {
|
||||
static mut BUFFER_PROCS: ffi::PyBufferProcs = ffi::PyBufferProcs_INIT;
|
||||
*(unsafe { &mut BUFFER_PROCS }) = meth;
|
||||
type_object.tp_as_buffer = unsafe { &mut BUFFER_PROCS };
|
||||
} else {
|
||||
type_object.tp_as_buffer = 0 as *mut ffi::PyBufferProcs;
|
||||
}
|
||||
|
||||
// type size
|
||||
type_object.tp_basicsize = <T as py_class::BaseObject>::size() as ffi::Py_ssize_t;
|
||||
// normal methods
|
||||
let mut methods = py_class_method_defs::<T>();
|
||||
if !methods.is_empty() {
|
||||
methods.push(ffi::PyMethodDef_INIT);
|
||||
type_object.tp_methods = methods.as_ptr() as *mut _;
|
||||
|
||||
// descriptor protocol
|
||||
type_object.tp_descr_get = class::descr::get_descrfunc::<T>();
|
||||
type_object.tp_descr_set = class::descr::set_descrfunc::<T>();
|
||||
static mut METHODS: *const ffi::PyMethodDef = 0 as *const _;
|
||||
*(unsafe { &mut METHODS }) = methods.as_ptr();
|
||||
}
|
||||
|
||||
// number methods
|
||||
if let Some(meth) = ffi::PyNumberMethods::new::<T>() {
|
||||
static mut NB_METHODS: ffi::PyNumberMethods = ffi::PyNumberMethods_INIT;
|
||||
*(unsafe { &mut NB_METHODS }) = meth;
|
||||
type_object.tp_as_number = unsafe { &mut NB_METHODS };
|
||||
// properties
|
||||
let mut props = py_class_properties::<T>();
|
||||
if !props.is_empty() {
|
||||
props.push(ffi::PyGetSetDef_INIT);
|
||||
let props = props.into_boxed_slice();
|
||||
type_object.tp_getset = props.as_ptr() as *mut _;
|
||||
|
||||
static mut PROPS: *const ffi::PyGetSetDef = 0 as *const _;
|
||||
*(unsafe { &mut PROPS }) = props.as_ptr();
|
||||
|
||||
// strange
|
||||
mem::forget(props);
|
||||
}
|
||||
|
||||
// register type object
|
||||
unsafe {
|
||||
if ffi::PyType_Ready(type_object) == 0 {
|
||||
Ok(PyType::from_type_ptr(py, type_object))
|
||||
} else {
|
||||
type_object.tp_as_number = 0 as *mut ffi::PyNumberMethods;
|
||||
}
|
||||
|
||||
// mapping methods
|
||||
if let Some(meth) = ffi::PyMappingMethods::new::<T>() {
|
||||
static mut MP_METHODS: ffi::PyMappingMethods = ffi::PyMappingMethods_INIT;
|
||||
*(unsafe { &mut MP_METHODS }) = meth;
|
||||
type_object.tp_as_mapping = unsafe { &mut MP_METHODS };
|
||||
} else {
|
||||
type_object.tp_as_mapping = 0 as *mut ffi::PyMappingMethods;
|
||||
}
|
||||
|
||||
// sequence methods
|
||||
if let Some(meth) = ffi::PySequenceMethods::new::<T>() {
|
||||
static mut SQ_METHODS: ffi::PySequenceMethods = ffi::PySequenceMethods_INIT;
|
||||
*(unsafe { &mut SQ_METHODS }) = meth;
|
||||
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(meth) = ffi::PyAsyncMethods::new::<T>() {
|
||||
static mut ASYNC_METHODS: ffi::PyAsyncMethods = ffi::PyAsyncMethods_INIT;
|
||||
*(unsafe { &mut ASYNC_METHODS }) = meth;
|
||||
type_object.tp_as_async = unsafe { &mut ASYNC_METHODS };
|
||||
} else {
|
||||
type_object.tp_as_async = 0 as *mut ffi::PyAsyncMethods;
|
||||
}
|
||||
|
||||
// buffer protocol
|
||||
if let Some(meth) = ffi::PyBufferProcs::new::<T>() {
|
||||
static mut BUFFER_PROCS: ffi::PyBufferProcs = ffi::PyBufferProcs_INIT;
|
||||
*(unsafe { &mut BUFFER_PROCS }) = meth;
|
||||
type_object.tp_as_buffer = unsafe { &mut BUFFER_PROCS };
|
||||
} else {
|
||||
type_object.tp_as_buffer = 0 as *mut ffi::PyBufferProcs;
|
||||
}
|
||||
|
||||
// normal methods
|
||||
let mut methods = py_class_method_defs::<T>();
|
||||
if !methods.is_empty() {
|
||||
methods.push(ffi::PyMethodDef_INIT);
|
||||
type_object.tp_methods = methods.as_ptr() as *mut _;
|
||||
|
||||
static mut METHODS: *const ffi::PyMethodDef = 0 as *const _;
|
||||
*(unsafe { &mut METHODS }) = methods.as_ptr();
|
||||
}
|
||||
|
||||
// properties
|
||||
let mut props = py_class_properties::<T>();
|
||||
if !props.is_empty() {
|
||||
props.push(ffi::PyGetSetDef_INIT);
|
||||
let props = props.into_boxed_slice();
|
||||
type_object.tp_getset = props.as_ptr() as *mut _;
|
||||
|
||||
static mut PROPS: *const ffi::PyGetSetDef = 0 as *const _;
|
||||
*(unsafe { &mut PROPS }) = props.as_ptr();
|
||||
|
||||
// strange
|
||||
mem::forget(props);
|
||||
}
|
||||
|
||||
// register type object
|
||||
unsafe {
|
||||
if ffi::PyType_Ready(type_object) == 0 {
|
||||
Ok(PyType::from_type_ptr(py, type_object))
|
||||
} else {
|
||||
Err(PyErr::fetch(py))
|
||||
}
|
||||
Err(PyErr::fetch(py))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn tp_dealloc_callback<T>(obj: *mut ffi::PyObject)
|
||||
where T: py_class::BaseObject
|
||||
unsafe extern "C" fn tp_dealloc_callback<T>(obj: *mut ffi::PyObject) where T: BaseObject
|
||||
{
|
||||
let guard = AbortOnDrop("Cannot unwind out of tp_dealloc");
|
||||
let py = Python::assume_gil_acquired();
|
||||
|
|
|
@ -100,7 +100,7 @@ macro_rules! py_exception {
|
|||
#[inline]
|
||||
fn type_object(py: $crate::Python) -> $crate::PyType {
|
||||
unsafe {
|
||||
static mut type_object: *mut $crate::_detail::ffi::PyTypeObject = 0 as *mut $crate::_detail::ffi::PyTypeObject;
|
||||
static mut type_object: *mut $crate::ffi::PyTypeObject = 0 as *mut $crate::ffi::PyTypeObject;
|
||||
|
||||
if type_object.is_null() {
|
||||
type_object = $crate::PyErr::new_type(
|
||||
|
|
128
src/function.rs
128
src/function.rs
|
@ -16,30 +16,27 @@
|
|||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use libc;
|
||||
use std::{mem, ptr, io, any, marker};
|
||||
use std::panic;
|
||||
use python::{Python, PythonObject};
|
||||
use std::ptr;
|
||||
use python::Python;
|
||||
use objects::PyObject;
|
||||
use conversion::ToPyObject;
|
||||
use ffi;
|
||||
use err::{self, PyResult};
|
||||
use err;
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_method_def {
|
||||
($name: expr, $flags: expr, $wrap: expr) => {{
|
||||
static mut METHOD_DEF: $crate::_detail::ffi::PyMethodDef = $crate::_detail::ffi::PyMethodDef {
|
||||
static mut METHOD_DEF: $crate::ffi::PyMethodDef = $crate::ffi::PyMethodDef {
|
||||
//ml_name: bytes!(stringify!($name), "\0"),
|
||||
ml_name: 0 as *const $crate::_detail::libc::c_char,
|
||||
ml_name: 0 as *const $crate::c_char,
|
||||
ml_meth: None,
|
||||
ml_flags: $crate::_detail::ffi::METH_VARARGS | $crate::_detail::ffi::METH_KEYWORDS | $flags,
|
||||
ml_doc: 0 as *const $crate::_detail::libc::c_char
|
||||
ml_flags: $crate::ffi::METH_VARARGS | $crate:::METH_KEYWORDS | $flags,
|
||||
ml_doc: 0 as *const $crate::c_char
|
||||
};
|
||||
METHOD_DEF.ml_name = concat!($name, "\0").as_ptr() as *const _;
|
||||
METHOD_DEF.ml_meth = Some(
|
||||
::std::mem::transmute::<$crate::_detail::ffi::PyCFunctionWithKeywords,
|
||||
$crate::_detail::ffi::PyCFunction>($wrap)
|
||||
::std::mem::transmute::<$crate::ffi::PyCFunctionWithKeywords,
|
||||
$crate::ffi::PyCFunction>($wrap)
|
||||
);
|
||||
&mut METHOD_DEF
|
||||
}}
|
||||
|
@ -112,13 +109,13 @@ macro_rules! py_fn_impl {
|
|||
// Form 1: reference existing function
|
||||
{ $py:expr, $f:ident [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ] } => {{
|
||||
unsafe extern "C" fn wrap(
|
||||
_slf: *mut $crate::_detail::ffi::PyObject,
|
||||
args: *mut $crate::_detail::ffi::PyObject,
|
||||
kwargs: *mut $crate::_detail::ffi::PyObject)
|
||||
-> *mut $crate::_detail::ffi::PyObject
|
||||
_slf: *mut $crate::ffi::PyObject,
|
||||
args: *mut $crate::ffi::PyObject,
|
||||
kwargs: *mut $crate::ffi::PyObject)
|
||||
-> *mut $crate::ffi::PyObject
|
||||
{
|
||||
$crate::_detail::handle_callback(
|
||||
stringify!($f), $crate::_detail::PyObjectCallbackConverter,
|
||||
$crate::callback::handle_callback(
|
||||
stringify!($f), $crate::callback::PyObjectCallbackConverter,
|
||||
|py| {
|
||||
py_argparse_raw!(py, Some(stringify!($f)), args, kwargs,
|
||||
[ $( { $pname : $ptype = $detail } )* ]
|
||||
|
@ -128,7 +125,7 @@ macro_rules! py_fn_impl {
|
|||
})
|
||||
}
|
||||
unsafe {
|
||||
$crate::_detail::py_fn_impl($py,
|
||||
$crate::function::py_fn_impl($py,
|
||||
py_method_def!(stringify!($f), 0, wrap))
|
||||
}
|
||||
}};
|
||||
|
@ -139,97 +136,8 @@ macro_rules! py_fn_impl {
|
|||
}}
|
||||
}
|
||||
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub unsafe fn py_fn_impl(py: Python, method_def: *mut ffi::PyMethodDef) -> PyObject {
|
||||
err::from_owned_ptr_or_panic(py, ffi::PyCFunction_New(method_def, ptr::null_mut()))
|
||||
}
|
||||
|
||||
pub trait CallbackConverter<S> {
|
||||
type R;
|
||||
|
||||
fn convert(S, Python) -> Self::R;
|
||||
fn error_value() -> Self::R;
|
||||
}
|
||||
|
||||
pub struct PyObjectCallbackConverter;
|
||||
|
||||
impl <S> CallbackConverter<S> for PyObjectCallbackConverter
|
||||
where S: ToPyObject
|
||||
{
|
||||
type R = *mut ffi::PyObject;
|
||||
|
||||
fn convert(val: S, py: Python) -> *mut ffi::PyObject {
|
||||
val.into_py_object(py).into_object().steal_ptr()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn error_value() -> *mut ffi::PyObject {
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PythonObjectCallbackConverter<T>(pub marker::PhantomData<T>);
|
||||
|
||||
impl <T, S> CallbackConverter<S> for PythonObjectCallbackConverter<T>
|
||||
where T: PythonObject,
|
||||
S: ToPyObject
|
||||
{
|
||||
type R = *mut ffi::PyObject;
|
||||
|
||||
fn convert(val: S, py: Python) -> *mut ffi::PyObject {
|
||||
val.into_py_object(py).into_object().steal_ptr()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn error_value() -> *mut ffi::PyObject {
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn handle_callback<F, T, C>(location: &str, _c: C, f: F) -> C::R
|
||||
where F: FnOnce(Python) -> PyResult<T>,
|
||||
F: panic::UnwindSafe,
|
||||
C: CallbackConverter<T>
|
||||
{
|
||||
let guard = AbortOnDrop(location);
|
||||
let ret = panic::catch_unwind(|| {
|
||||
let py = Python::assume_gil_acquired();
|
||||
match f(py) {
|
||||
Ok(val) => {
|
||||
C::convert(val, py)
|
||||
}
|
||||
Err(e) => {
|
||||
e.restore(py);
|
||||
C::error_value()
|
||||
}
|
||||
}
|
||||
});
|
||||
let ret = match ret {
|
||||
Ok(r) => r,
|
||||
Err(ref err) => {
|
||||
handle_panic(Python::assume_gil_acquired(), err);
|
||||
C::error_value()
|
||||
}
|
||||
};
|
||||
mem::forget(guard);
|
||||
ret
|
||||
}
|
||||
|
||||
fn handle_panic(_py: Python, _panic: &any::Any) {
|
||||
let msg = cstr!("Rust panic");
|
||||
unsafe {
|
||||
ffi::PyErr_SetString(ffi::PyExc_SystemError, msg.as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AbortOnDrop<'a>(pub &'a str);
|
||||
|
||||
impl <'a> Drop for AbortOnDrop<'a> {
|
||||
fn drop(&mut self) {
|
||||
use std::io::Write;
|
||||
let _ = writeln!(&mut io::stderr(), "Cannot unwind out of {}", self.0);
|
||||
unsafe { libc::abort() }
|
||||
}
|
||||
}
|
||||
|
||||
// Tests for this file are in tests/test_function.rs
|
||||
|
||||
|
|
24
src/lib.rs
24
src/lib.rs
|
@ -96,7 +96,7 @@ pub use objects::*;
|
|||
pub use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, PythonObjectDowncastError, PythonObjectWithTypeObject, PyClone, PyDrop};
|
||||
pub use pythonrun::{GILGuard, GILProtected, prepare_freethreaded_python};
|
||||
pub use conversion::{FromPyObject, RefFromPyObject, ToPyObject, ToPyTuple};
|
||||
pub use py_class::{CompareOp};
|
||||
pub use class::{CompareOp};
|
||||
pub use objectprotocol::{ObjectProtocol};
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
|
@ -142,7 +142,7 @@ macro_rules! py_impl_to_py_object_for_python_object {
|
|||
|
||||
#[inline]
|
||||
fn with_borrowed_ptr<F, R>(&self, _py: $crate::Python, f: F) -> R
|
||||
where F: FnOnce(*mut $crate::_detail::ffi::PyObject) -> R
|
||||
where F: FnOnce(*mut $crate::ffi::PyObject) -> R
|
||||
{
|
||||
f($crate::PythonObject::as_object(self).as_ptr())
|
||||
}
|
||||
|
@ -170,7 +170,6 @@ macro_rules! py_impl_from_py_object_for_python_object {
|
|||
}
|
||||
}
|
||||
|
||||
pub mod py_class;
|
||||
mod python;
|
||||
mod err;
|
||||
mod conversion;
|
||||
|
@ -182,24 +181,11 @@ mod function;
|
|||
pub mod buffer;
|
||||
pub mod class;
|
||||
pub use class::*;
|
||||
pub mod callback;
|
||||
|
||||
// re-export for simplicity
|
||||
pub use std::os::raw::*;
|
||||
|
||||
/// Private re-exports for macros. Do not use.
|
||||
#[doc(hidden)]
|
||||
pub mod _detail {
|
||||
pub mod ffi {
|
||||
pub use ::ffi::*;
|
||||
}
|
||||
pub mod libc {
|
||||
pub use std::os::raw::{c_char, c_void, c_int};
|
||||
}
|
||||
pub use err::{from_owned_ptr_or_panic, result_from_owned_ptr};
|
||||
pub use function::{handle_callback, py_fn_impl, AbortOnDrop,
|
||||
PyObjectCallbackConverter, PythonObjectCallbackConverter};
|
||||
}
|
||||
|
||||
/// Expands to an `extern "C"` function that allows Python to load
|
||||
/// the rust code as a Python extension module.
|
||||
///
|
||||
|
@ -264,7 +250,7 @@ macro_rules! py_module_init {
|
|||
fn init($py_id: $crate::Python, $m_id: &$crate::PyModule) -> $crate::PyResult<()> {
|
||||
$body
|
||||
}
|
||||
static mut MODULE_DEF: $crate::ffi::PyModuleDef = $crate::_detail::ffi::PyModuleDef_INIT;
|
||||
static mut MODULE_DEF: $crate::ffi::PyModuleDef = $crate::ffi::PyModuleDef_INIT;
|
||||
// We can't convert &'static str to *const c_char within a static initializer,
|
||||
// so we'll do it here in the module initialization:
|
||||
MODULE_DEF.m_name = concat!(stringify!($name), "\0").as_ptr() as *const _;
|
||||
|
@ -278,7 +264,7 @@ pub unsafe fn py_module_init_impl(
|
|||
def: *mut ffi::PyModuleDef,
|
||||
init: fn(Python, &PyModule) -> PyResult<()>) -> *mut ffi::PyObject
|
||||
{
|
||||
let guard = function::AbortOnDrop("py_module_init");
|
||||
let guard = callback::AbortOnDrop("py_module_init");
|
||||
let py = Python::assume_gil_acquired();
|
||||
ffi::PyEval_InitThreads();
|
||||
let module = ffi::PyModule_Create(def);
|
||||
|
|
|
@ -23,7 +23,7 @@ use python::{Python, PythonObject};
|
|||
use objectprotocol::ObjectProtocol;
|
||||
use conversion::{ToPyObject, ToPyTuple};
|
||||
use objects::{PyObject, PyDict, exc};
|
||||
use py_class::PythonObjectFromPyClassMacro;
|
||||
use class::PyTypeObject;
|
||||
use err::{self, PyResult, PyErr};
|
||||
use std::ffi::{CStr, CString};
|
||||
|
||||
|
@ -107,11 +107,11 @@ impl PyModule {
|
|||
|
||||
/// Adds a new extension type to the module.
|
||||
///
|
||||
/// This is a convenience function that initializes the `py_class!()`,
|
||||
/// This is a convenience function that initializes the `class`,
|
||||
/// sets `new_type.__module__` to this module's name,
|
||||
/// and adds the type to this module.
|
||||
pub fn add_class<'p, T>(&self, py: Python<'p>) -> PyResult<()>
|
||||
where T: PythonObjectFromPyClassMacro
|
||||
where T: PyTypeObject
|
||||
{
|
||||
T::add_to_module(py, self)
|
||||
}
|
||||
|
|
|
@ -1,194 +0,0 @@
|
|||
// Copyright (c) 2016 Daniel Grunwald
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use std::marker;
|
||||
use python::{Python, PythonObject};
|
||||
use conversion::ToPyObject;
|
||||
use objects::PyObject;
|
||||
use err::{self, PyResult};
|
||||
use ffi;
|
||||
|
||||
/// Represents something that can be added as a member to a Python class/type.
|
||||
///
|
||||
/// T: type of rust class used for instances of the Python class/type.
|
||||
pub trait TypeMember<T> where T: PythonObject {
|
||||
/// Convert the type member into a python object
|
||||
/// that can be stored in the type dict.
|
||||
///
|
||||
/// Because the member may expect `self` values to be of type `T`,
|
||||
/// `ty` must be T::type_object() or a derived class.
|
||||
/// (otherwise the behavior is undefined)
|
||||
unsafe fn into_descriptor(self, py: Python, ty: *mut ffi::PyTypeObject) -> PyResult<PyObject>;
|
||||
}
|
||||
|
||||
impl <T, S> TypeMember<T> for S where T: PythonObject, S: ToPyObject {
|
||||
#[inline]
|
||||
unsafe fn into_descriptor(self, py: Python, _ty: *mut ffi::PyTypeObject) -> PyResult<PyObject> {
|
||||
Ok(self.into_py_object(py).into_object())
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_init_members {
|
||||
($class:ident, $py:ident, $type_object: ident, { }) => {{}};
|
||||
($class:ident, $py:ident, $type_object: ident, { $( $name:ident = $init:expr; )+ }) => {{
|
||||
let dict = $crate::PyDict::new($py);
|
||||
$( {
|
||||
// keep $init out of unsafe block; it might contain user code
|
||||
let init = $init;
|
||||
let descriptor = try!(unsafe {
|
||||
$crate::py_class::members::TypeMember::<$class>::into_descriptor(init, $py, &mut $type_object)
|
||||
});
|
||||
try!(dict.set_item($py, stringify!($name), descriptor));
|
||||
})*
|
||||
unsafe {
|
||||
assert!($type_object.tp_dict.is_null());
|
||||
$type_object.tp_dict = $crate::PythonObject::into_object(dict).steal_ptr();
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_instance_method {
|
||||
($py:ident, $class:ident :: $f:ident [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ]) => {{
|
||||
unsafe extern "C" fn wrap_instance_method(
|
||||
slf: *mut $crate::_detail::ffi::PyObject,
|
||||
args: *mut $crate::_detail::ffi::PyObject,
|
||||
kwargs: *mut $crate::_detail::ffi::PyObject)
|
||||
-> *mut $crate::_detail::ffi::PyObject
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
|
||||
$crate::_detail::handle_callback(
|
||||
LOCATION, $crate::_detail::PyObjectCallbackConverter,
|
||||
|py| {
|
||||
py_argparse_raw!(py, Some(LOCATION), args, kwargs,
|
||||
[ $( { $pname : $ptype = $detail } )* ]
|
||||
{
|
||||
let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<$class>();
|
||||
let ret = slf.$f(py $(, $pname )* );
|
||||
$crate::PyDrop::release_ref(slf, py);
|
||||
ret
|
||||
})
|
||||
})
|
||||
}
|
||||
unsafe {
|
||||
let method_def = py_method_def!(stringify!($f), 0, wrap_instance_method);
|
||||
$crate::py_class::members::create_instance_method_descriptor::<$class>(method_def)
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
||||
pub struct InstanceMethodDescriptor<T>(*mut ffi::PyMethodDef, marker::PhantomData<fn(&T)>);
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn create_instance_method_descriptor<T>(method_def: *mut ffi::PyMethodDef)
|
||||
-> InstanceMethodDescriptor<T>
|
||||
{
|
||||
InstanceMethodDescriptor(method_def, marker::PhantomData)
|
||||
}
|
||||
|
||||
impl <T> TypeMember<T> for InstanceMethodDescriptor<T> where T: PythonObject {
|
||||
#[inline]
|
||||
unsafe fn into_descriptor(self, py: Python, ty: *mut ffi::PyTypeObject) -> PyResult<PyObject> {
|
||||
err::result_from_owned_ptr(py, ffi::PyDescr_NewMethod(ty, self.0))
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_class_method {
|
||||
($py:ident, $class:ident :: $f:ident [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ]) => {{
|
||||
unsafe extern "C" fn wrap_class_method(
|
||||
cls: *mut $crate::_detail::ffi::PyObject,
|
||||
args: *mut $crate::_detail::ffi::PyObject,
|
||||
kwargs: *mut $crate::_detail::ffi::PyObject)
|
||||
-> *mut $crate::_detail::ffi::PyObject
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
|
||||
$crate::_detail::handle_callback(
|
||||
LOCATION, $crate::_detail::PyObjectCallbackConverter,
|
||||
|py| {
|
||||
py_argparse_raw!(py, Some(LOCATION), args, kwargs,
|
||||
[ $( { $pname : $ptype = $detail } )* ]
|
||||
{
|
||||
let cls = $crate::PyObject::from_borrowed_ptr(py, cls).unchecked_cast_into::<$crate::PyType>();
|
||||
let ret = $class::$f(&cls, py $(, $pname )* );
|
||||
$crate::PyDrop::release_ref(cls, py);
|
||||
ret
|
||||
})
|
||||
})
|
||||
}
|
||||
unsafe {
|
||||
let method_def = py_method_def!(stringify!($f),
|
||||
$crate::_detail::ffi::METH_CLASS,
|
||||
wrap_class_method);
|
||||
$crate::py_class::members::create_class_method_descriptor(method_def)
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
||||
pub struct ClassMethodDescriptor(*mut ffi::PyMethodDef);
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn create_class_method_descriptor(method_def: *mut ffi::PyMethodDef)
|
||||
-> ClassMethodDescriptor
|
||||
{
|
||||
ClassMethodDescriptor(method_def)
|
||||
}
|
||||
|
||||
impl <T> TypeMember<T> for ClassMethodDescriptor where T: PythonObject {
|
||||
#[inline]
|
||||
unsafe fn into_descriptor(self, py: Python, ty: *mut ffi::PyTypeObject) -> PyResult<PyObject> {
|
||||
err::result_from_owned_ptr(py, ffi::PyDescr_NewClassMethod(ty, self.0))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_static_method {
|
||||
($py:ident, $class:ident :: $f:ident [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ]) => {{
|
||||
unsafe extern "C" fn wrap_static_method(
|
||||
_slf: *mut $crate::_detail::ffi::PyObject,
|
||||
args: *mut $crate::_detail::ffi::PyObject,
|
||||
kwargs: *mut $crate::_detail::ffi::PyObject)
|
||||
-> *mut $crate::_detail::ffi::PyObject
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
|
||||
$crate::_detail::handle_callback(
|
||||
LOCATION, $crate::_detail::PyObjectCallbackConverter,
|
||||
|py| {
|
||||
py_argparse_raw!(py, Some(LOCATION), args, kwargs,
|
||||
[ $( { $pname : $ptype = $detail } )* ]
|
||||
{
|
||||
$class::$f(py $(, $pname )* )
|
||||
})
|
||||
})
|
||||
}
|
||||
unsafe {
|
||||
let method_def = py_method_def!(stringify!($f),
|
||||
$crate::_detail::ffi::METH_STATIC,
|
||||
wrap_static_method);
|
||||
$crate::_detail::py_fn_impl($py, method_def)
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
||||
|
|
@ -1,148 +0,0 @@
|
|||
// Copyright (c) 2016 Daniel Grunwald
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
mod py_class;
|
||||
mod py_class_impl;
|
||||
#[doc(hidden)] pub mod slots;
|
||||
#[doc(hidden)] pub mod members;
|
||||
|
||||
use std::os::raw::c_void;
|
||||
use std::{mem, ptr};
|
||||
use python::{self, Python, PythonObject};
|
||||
use objects::{PyObject, PyType, PyModule};
|
||||
use err::{self, PyResult};
|
||||
use ffi;
|
||||
|
||||
// TODO: consider moving CompareOp to a different module, so that it isn't exported via two paths
|
||||
#[derive(Debug)]
|
||||
pub enum CompareOp {
|
||||
Lt = ffi::Py_LT as isize,
|
||||
Le = ffi::Py_LE as isize,
|
||||
Eq = ffi::Py_EQ as isize,
|
||||
Ne = ffi::Py_NE as isize,
|
||||
Gt = ffi::Py_GT as isize,
|
||||
Ge = ffi::Py_GE as isize
|
||||
}
|
||||
|
||||
/// Trait implemented by the types produced by the `py_class!()` macro.
|
||||
///
|
||||
/// This is an unstable implementation detail; do not implement manually!
|
||||
pub trait PythonObjectFromPyClassMacro : python::PythonObjectWithTypeObject {
|
||||
/// Initializes the class.
|
||||
///
|
||||
/// module_name: the name of the parent module into which the class will be placed.
|
||||
fn initialize(py: Python, module_name: Option<&str>) -> PyResult<PyType>;
|
||||
|
||||
/// Initializes the class and adds it to the module.
|
||||
fn add_to_module(py: Python, module: &PyModule) -> PyResult<()>;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[doc(hidden)]
|
||||
pub fn data_offset<T>(base_size: usize) -> usize {
|
||||
let align = mem::align_of::<T>();
|
||||
// round base_size up to next multiple of align
|
||||
(base_size + align - 1) / align * align
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[doc(hidden)]
|
||||
pub fn data_new_size<T>(base_size: usize) -> usize {
|
||||
data_offset::<T>(base_size) + mem::size_of::<T>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[doc(hidden)]
|
||||
pub unsafe fn data_get<'a, T>(_py: Python<'a>, obj: &'a PyObject, offset: usize) -> &'a T {
|
||||
let ptr = (obj.as_ptr() as *const u8).offset(offset as isize) as *const T;
|
||||
&*ptr
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[doc(hidden)]
|
||||
pub unsafe fn data_init<'a, T>(_py: Python<'a>, obj: &'a PyObject, offset: usize, value: T)
|
||||
where T: Send + 'static
|
||||
{
|
||||
let ptr = (obj.as_ptr() as *mut u8).offset(offset as isize) as *mut T;
|
||||
ptr::write(ptr, value)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[doc(hidden)]
|
||||
pub unsafe fn data_drop<'a, T>(_py: Python<'a>, obj: *mut ffi::PyObject, offset: usize) {
|
||||
let ptr = (obj as *mut u8).offset(offset as isize) as *mut T;
|
||||
ptr::drop_in_place(ptr)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[doc(hidden)]
|
||||
pub fn is_ready(_py: Python, ty: &ffi::PyTypeObject) -> bool {
|
||||
(ty.tp_flags & ffi::Py_TPFLAGS_READY) != 0
|
||||
}
|
||||
|
||||
/// A PythonObject that is usable as a base type with the `py_class!()` macro.
|
||||
pub trait BaseObject : PythonObject {
|
||||
/// Gets the size of the object, in bytes.
|
||||
fn size() -> usize;
|
||||
|
||||
type InitType;
|
||||
|
||||
/// Allocates a new object (usually by calling ty->tp_alloc),
|
||||
/// and initializes it using init_val.
|
||||
/// `ty` must be derived from the Self type, and the resulting object
|
||||
/// must be of type `ty`.
|
||||
unsafe fn alloc(py: Python, ty: &PyType, init_val: Self::InitType) -> PyResult<PyObject>;
|
||||
|
||||
/// Calls the rust destructor for the object and frees the memory
|
||||
/// (usually by calling ptr->ob_type->tp_free).
|
||||
/// This function is used as tp_dealloc implementation.
|
||||
unsafe fn dealloc(py: Python, obj: *mut ffi::PyObject);
|
||||
}
|
||||
|
||||
impl BaseObject for PyObject {
|
||||
#[inline]
|
||||
fn size() -> usize {
|
||||
mem::size_of::<ffi::PyObject>()
|
||||
}
|
||||
|
||||
type InitType = ();
|
||||
|
||||
unsafe fn alloc(py: Python, ty: &PyType, _init_val: ()) -> PyResult<PyObject> {
|
||||
let ptr = ffi::PyType_GenericAlloc(ty.as_type_ptr(), 0);
|
||||
//println!("BaseObject::alloc({:?}) = {:?}", ty.as_type_ptr(), ptr);
|
||||
err::result_from_owned_ptr(py, ptr)
|
||||
}
|
||||
|
||||
unsafe fn dealloc(_py: Python, obj: *mut ffi::PyObject) {
|
||||
//println!("BaseObject::dealloc({:?})", ptr);
|
||||
// Unfortunately, there is no PyType_GenericFree, so
|
||||
// we have to manually un-do the work of PyType_GenericAlloc:
|
||||
let ty = ffi::Py_TYPE(obj);
|
||||
if ffi::PyType_IS_GC(ty) != 0 {
|
||||
ffi::PyObject_GC_Del(obj as *mut c_void);
|
||||
} else {
|
||||
ffi::PyObject_Free(obj as *mut c_void);
|
||||
}
|
||||
// For heap types, PyType_GenericAlloc calls INCREF on the type objects,
|
||||
// so we need to call DECREF here:
|
||||
if ffi::PyType_HasFeature(ty, ffi::Py_TPFLAGS_HEAPTYPE) != 0 {
|
||||
ffi::Py_DECREF(ty as *mut ffi::PyObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,480 +0,0 @@
|
|||
// Copyright (c) 2016 Daniel Grunwald
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
/**
|
||||
Defines new python extension class.
|
||||
A `py_class!` macro invocation generates code that declares a new Python class.
|
||||
Additionally, it generates a Rust struct of the same name, which allows accessing
|
||||
instances of that Python class from Rust.
|
||||
|
||||
# Syntax
|
||||
`py_class!(pub class MyType |py| { ... })`
|
||||
|
||||
* `pub` makes the generated Rust struct visible outside the current module. It has no effect on the visibility from Python.
|
||||
* `MyType` is the name of the Python class.
|
||||
* `py` is an identifier that will be made available as a variable of type `Python`
|
||||
in all function bodies.
|
||||
* `{ ... }` is the class body, described in more detail below.
|
||||
|
||||
# Example
|
||||
```
|
||||
#[macro_use] extern crate pyo3;
|
||||
use pyo3::{Python, PyResult, PyDict};
|
||||
|
||||
py_class!(class MyType |py| {
|
||||
data number: i32;
|
||||
def __new__(_cls, arg: i32) -> PyResult<MyType> {
|
||||
MyType::create_instance(py, arg)
|
||||
}
|
||||
def half(&self) -> PyResult<i32> {
|
||||
println!("half() was called with self={:?}", self.number(py));
|
||||
Ok(self.number(py) / 2)
|
||||
}
|
||||
});
|
||||
|
||||
fn main() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let dict = PyDict::new(py);
|
||||
dict.set_item(py, "MyType", py.get_type::<MyType>()).unwrap();
|
||||
py.run("assert MyType(42).half() == 21", None, Some(&dict)).unwrap();
|
||||
}
|
||||
```
|
||||
|
||||
# Generated Rust type
|
||||
|
||||
The above example generates the following Rust type:
|
||||
|
||||
```ignore
|
||||
struct MyType { ... }
|
||||
|
||||
impl ToPythonObject for MyType { ... }
|
||||
impl PythonObject for MyType { ... }
|
||||
impl PythonObjectWithCheckedDowncast for MyType { ... }
|
||||
impl PythonObjectWithTypeObject for MyType { ... }
|
||||
impl PythonObjectFromPyClassMacro for MyType { ... }
|
||||
|
||||
impl MyType {
|
||||
fn create_instance(py: Python, number: i32) -> PyResult<MyType> { ... }
|
||||
|
||||
// data accessors
|
||||
fn number<'a>(&'a self, py: Python<'a>) -> &'a i32 { ... }
|
||||
|
||||
// functions callable from python
|
||||
pub fn __new__(_cls: &PyType, py: Python, arg: i32) -> PyResult<MyType> {
|
||||
MyType::create_instance(py, arg)
|
||||
}
|
||||
|
||||
pub fn half(&self, py: Python) -> PyResult<i32> {
|
||||
println!("half() was called with self={:?}", self.number(py));
|
||||
Ok(self.number(py) / 2)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* The generated type implements a number of traits from the `pyo3` crate.
|
||||
* The inherent `create_instance` method can create new Python objects
|
||||
given the values for the data fields.
|
||||
* Private accessors functions are created for the data fields.
|
||||
* All functions callable from Python are also exposed as public Rust functions.
|
||||
* To convert from `MyType` to `PyObject`, use `as_object()` or `into_object()` (from the `PythonObject` trait).
|
||||
* To convert `PyObject` to `MyType`, use `obj.cast_as::<MyType>(py)` or `obj.cast_into::<MyType>(py)`.
|
||||
|
||||
# py_class body
|
||||
The body of a `py_class!` supports the following definitions:
|
||||
|
||||
## Data declarations
|
||||
`data data_name: data_type;`
|
||||
|
||||
Declares a data field within the Python class.
|
||||
Used to store Rust data directly in the Python object instance.
|
||||
|
||||
Because Python code can pass all Python objects to other threads,
|
||||
`data_type` must be `Send + 'static`.
|
||||
|
||||
Because Python object instances can be freely shared (Python has no concept of "ownership"),
|
||||
data fields cannot be declared as `mut`.
|
||||
If mutability is required, you have to use interior mutability (`Cell` or `RefCell`).
|
||||
|
||||
If data members are used to store references to other Python objects, make sure
|
||||
to read the section "Garbage Collector Integration".
|
||||
|
||||
Data declarations are not accessible from Python.
|
||||
On the Rust side, data is accessed through the automatically generated accessor functions:
|
||||
```ignore
|
||||
impl MyType {
|
||||
fn data_name<'a>(&'a self, py: Python<'a>) -> &'a data_type { ... }
|
||||
}
|
||||
```
|
||||
|
||||
## Instance methods
|
||||
`def method_name(&self, parameter-list) -> PyResult<...> { ... }`
|
||||
|
||||
Declares an instance method callable from Python.
|
||||
|
||||
* Because Python objects are potentially shared, the self parameter must always
|
||||
be a shared reference (`&self`).
|
||||
* For details on `parameter-list`, see the documentation of `py_argparse!()`.
|
||||
* The return type must be `PyResult<T>` for some `T` that implements `ToPyObject`.
|
||||
|
||||
## Class methods
|
||||
`@classmethod def method_name(cls, parameter-list) -> PyResult<...> { ... }`
|
||||
|
||||
Declares a class method callable from Python.
|
||||
|
||||
* The first parameter is the type object of the class on which the method is called.
|
||||
This may be the type object of a derived class.
|
||||
* The first parameter implicitly has type `&PyType`. This type must not be explicitly specified.
|
||||
* For details on `parameter-list`, see the documentation of `py_argparse!()`.
|
||||
* The return type must be `PyResult<T>` for some `T` that implements `ToPyObject`.
|
||||
|
||||
## Static methods
|
||||
`@staticmethod def method_name(parameter-list) -> PyResult<...> { ... }`
|
||||
|
||||
Declares a static method callable from Python.
|
||||
|
||||
* For details on `parameter-list`, see the documentation of `py_argparse!()`.
|
||||
* The return type must be `PyResult<T>` for some `T` that implements `ToPyObject`.
|
||||
|
||||
## __new__
|
||||
`def __new__(cls, parameter-list) -> PyResult<...> { ... }`
|
||||
|
||||
Declares a constructor method callable from Python.
|
||||
|
||||
* If no `__new__` method is declared, object instances can only be created from Rust (via `MyType::create_instance`),
|
||||
but not from Python.
|
||||
* The first parameter is the type object of the class to create.
|
||||
This may be the type object of a derived class declared in Python.
|
||||
* The first parameter implicitly has type `&PyType`. This type must not be explicitly specified.
|
||||
* For details on `parameter-list`, see the documentation of `py_argparse!()`.
|
||||
* The return type must be `PyResult<T>` for some `T` that implements `ToPyObject`.
|
||||
Usually, `T` will be `MyType`.
|
||||
|
||||
## Garbage Collector Integration
|
||||
|
||||
If your type owns references to other python objects, you will need to
|
||||
integrate with Python's garbage collector so that the GC is aware of
|
||||
those references.
|
||||
To do this, implement the special member functions `__traverse__` and `__clear__`.
|
||||
These correspond to the slots `tp_traverse` and `tp_clear` in the Python C API.
|
||||
|
||||
`__traverse__` must call `visit.call()` for each reference to another python object.
|
||||
|
||||
`__clear__` must clear out any mutable references to other python objects
|
||||
(thus breaking reference cycles). Immutable references do not have to be cleared,
|
||||
as every cycle must contain at least one mutable reference.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
#[macro_use] extern crate pyo3;
|
||||
use std::{mem, cell};
|
||||
use pyo3::{PyObject, PyDrop};
|
||||
|
||||
py_class!(class ClassWithGCSupport |py| {
|
||||
data obj: cell::RefCell<Option<PyObject>>;
|
||||
|
||||
def __traverse__(&self, visit) {
|
||||
if let Some(ref obj) = *self.obj(py).borrow() {
|
||||
try!(visit.call(obj))
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
def __clear__(&self) {
|
||||
let old_obj = mem::replace(&mut *self.obj(py).borrow_mut(), None);
|
||||
// Release reference only after the mutable borrow has expired,
|
||||
// see Caution note below.
|
||||
old_obj.release_ref(py);
|
||||
}
|
||||
});
|
||||
# fn main() {}
|
||||
```
|
||||
|
||||
Caution: `__traverse__` may be called by the garbage collector:
|
||||
|
||||
* during any python operation that takes a `Python` token as argument
|
||||
* indirectly from the `PyObject` (or derived type) `Drop` implementation
|
||||
* if your code releases the GIL, at any time by other threads.
|
||||
|
||||
If you are using `RefCell<PyObject>`, you must not perform any of the above
|
||||
operations while your code holds a mutable borrow, or you may cause the borrow
|
||||
in `__traverse__` to panic.
|
||||
|
||||
This is why the example above uses the `mem::replace`/`release_ref` dance:
|
||||
`release_ref` (or the implicit `Drop`) can only be called safely in a separate
|
||||
statement, after the mutable borrow on the `RefCell` has expired.
|
||||
|
||||
Note that this restriction applies not only to `__clear__`, but to all methods
|
||||
that use `RefCell::borrow_mut`.
|
||||
|
||||
## Iterator Types
|
||||
|
||||
Iterators can be defined using the Python special methods `__iter__` and `__next__`:
|
||||
|
||||
* `def __iter__(&self) -> PyResult<impl ToPyObject>`
|
||||
* `def __next__(&self) -> PyResult<Option<impl ToPyObject>>`
|
||||
|
||||
Returning `Ok(None)` from `__next__` indicates that that there are no further items.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
#[macro_use] extern crate pyo3;
|
||||
use std::cell::RefCell;
|
||||
use pyo3::{PyObject, PyClone, PyResult};
|
||||
|
||||
py_class!(class MyIterator |py| {
|
||||
data iter: RefCell<Box<Iterator<Item=PyObject> + Send>>;
|
||||
|
||||
def __iter__(&self) -> PyResult<Self> {
|
||||
Ok(self.clone_ref(py))
|
||||
}
|
||||
|
||||
def __next__(&self) -> PyResult<Option<PyObject>> {
|
||||
Ok(self.iter(py).borrow_mut().next())
|
||||
}
|
||||
});
|
||||
# fn main() {}
|
||||
```
|
||||
|
||||
## String Conversions
|
||||
|
||||
* `def __repr__(&self) -> PyResult<impl ToPyObject<ObjectType=PyString>>`
|
||||
* `def __str__(&self) -> PyResult<impl ToPyObject<ObjectType=PyString>>`
|
||||
|
||||
Possible return types for `__str__` and `__repr__` are `PyResult<String>` or `PyResult<PyString>`.
|
||||
|
||||
In Python 2.7, Unicode strings returned by `__str__` and `__repr__` will be converted to byte strings
|
||||
by the Python runtime, which results in an exception if the string contains non-ASCII characters.
|
||||
|
||||
* `def __bytes__(&self) -> PyResult<PyBytes>`
|
||||
|
||||
On Python 3.x, provides the conversion to `bytes`.
|
||||
On Python 2.7, `__bytes__` is allowed but has no effect.
|
||||
|
||||
* `def __unicode__(&self) -> PyResult<PyUnicode>`
|
||||
|
||||
On Python 2.7, provides the conversion to `unicode`.
|
||||
On Python 3.x, `__unicode__` is allowed but has no effect.
|
||||
|
||||
* `def __format__(&self, format_spec: &str) -> PyResult<impl ToPyObject<ObjectType=PyString>>`
|
||||
|
||||
Special method that is used by the `format()` builtin and the `str.format()` method.
|
||||
Possible return types are `PyResult<String>` or `PyResult<PyString>`.
|
||||
|
||||
## Comparison operators
|
||||
|
||||
* `def __richcmp__(&self, other: impl FromPyObject, op: CompareOp) -> PyResult<impl ToPyObject>`
|
||||
|
||||
Overloads Python comparison operations (`==`, `!=`, `<`, `<=`, `>`, and `>=`).
|
||||
The `op` argument indicates the comparison operation being performed.
|
||||
The return type will normally be `PyResult<bool>`, but any Python object can be returned.
|
||||
|
||||
If `other` is not of the type specified in the signature, the generated code will
|
||||
automatically `return NotImplemented`.
|
||||
|
||||
* `def __hash__(&self) -> PyResult<impl PrimInt>`
|
||||
|
||||
Objects that compare equal must have the same hash value.
|
||||
The return type must be `PyResult<T>` where `T` is one of Rust's primitive integer types.
|
||||
|
||||
## Emulating Container Types
|
||||
|
||||
* `def __len__(&self) -> PyResult<usize>`
|
||||
|
||||
Called by the built-in Python function `len()`.
|
||||
|
||||
* `def __length_hint__(&self) -> PyResult<usize>`
|
||||
|
||||
Should return an estimated length for the object.
|
||||
This method is purely an optimization and is never required for correctness.
|
||||
|
||||
`__length_hint__` is new in Python 3.4; older versions will ignore the method.
|
||||
|
||||
* `def __getitem__(&self, key: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||
|
||||
Called by the Python subscript operator `self[key]`.
|
||||
|
||||
* `def __setitem__(&self, key: impl FromPyObject, value: impl FromPyObject) -> PyResult<()>`
|
||||
|
||||
Called by Python `self[key] = value`.
|
||||
|
||||
* `def __delitem__(&self, key: impl FromPyObject) -> PyResult<()>`
|
||||
|
||||
Called by Python `del self[key]`.
|
||||
|
||||
* `def __reversed__(&self) -> PyResult<impl ToPyObject>`
|
||||
|
||||
Called by the `reversed()` built-in.
|
||||
It should return a new iterator object that iterates over all the objects in the container in reverse order.
|
||||
|
||||
* `def __contains__(&self, item: impl FromPyObject) -> PyResult<bool>`
|
||||
|
||||
Called by Python `item in self`.
|
||||
For mapping types, this should consider the keys of the mapping rather than the values
|
||||
or the key-item pairs.
|
||||
|
||||
If extraction of the `item` parameter fails with `TypeError`,
|
||||
`__contains__` will return `Ok(false)`.
|
||||
|
||||
## Arithmetic methods
|
||||
|
||||
* `def __add__(lhs, rhs) -> PyResult<impl ToPyObject>`
|
||||
* `def __sub__(lhs, rhs) -> PyResult<impl ToPyObject>`
|
||||
* `def __mul__(lhs, rhs) -> PyResult<impl ToPyObject>`
|
||||
* `def __lshift__(lhs, rhs) -> PyResult<impl ToPyObject>`
|
||||
* `def __rshift__(lhs, rhs) -> PyResult<impl ToPyObject>`
|
||||
* `def __and__(lhs, rhs) -> PyResult<impl ToPyObject>`
|
||||
* `def __xor__(lhs, rhs) -> PyResult<impl ToPyObject>`
|
||||
* `def __or__(lhs, rhs) -> PyResult<impl ToPyObject>`
|
||||
|
||||
The parameters `lhs` and `rhs` must not be given an explicit type.
|
||||
Within the method bodies, both parameters will implicitly have type `&PyObject`.
|
||||
|
||||
There are no separate "reversed" versions of these methods (`__radd__()`, etc.)
|
||||
Instead, if the first operand cannot perform the operation,
|
||||
the same method of the second operand is called, with the operands in the same order.
|
||||
|
||||
This means that you can't rely on the first parameter of these methods being `self`
|
||||
or being the correct type, and you should test the types of both operands before deciding what to do.
|
||||
If you can't handle the combination of types you've been given,
|
||||
you should return `Ok(py.NotImplemented())`.
|
||||
|
||||
* `def __iadd__(&self, other: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||
* `def __isub__(&self, other: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||
* `def __imul__(&self, other: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||
* `def __imatmul__(&self, other: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||
* `def __itruediv__(&self, other: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||
* `def __ifloordiv__(&self, other: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||
* `def __imod__(&self, other: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||
* `def __ilshift__(&self, other: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||
* `def __irshift__(&self, other: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||
* `def __iand__(&self, other: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||
* `def __ixor__(&self, other: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||
* `def __ior__(&self, other: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||
|
||||
Handles inplace operations if possible, falling back to the non-inplace versions.
|
||||
These methods must return a new reference! In the common case of returning the
|
||||
same (mutated) object, you will want to return `Ok(self.clone_ref(py))`.
|
||||
|
||||
If you can't handle the combination of types you've been given,
|
||||
you should return `Ok(py.NotImplemented())`.
|
||||
|
||||
## Context Manager
|
||||
|
||||
* `def __enter__(&self) -> PyResult<impl ToPyObject>`
|
||||
* `def __exit__(&self, ty: Option<PyType>, value: PyObject, traceback: PyObject) -> PyResult<bool>`
|
||||
|
||||
## Other Special Methods
|
||||
|
||||
* `def __bool__(&self) -> PyResult<bool>`
|
||||
|
||||
Determines the "truthyness" of the object.
|
||||
|
||||
Note that `py_class!` always expects this member to be called `__bool__`,
|
||||
even on Python 2.7 where the Python spelling was `__nonzero__`.
|
||||
|
||||
* `def __call__(&self, parameter-list) -> PyResult<impl ToPyObject>`
|
||||
|
||||
For details on `parameter-list`, see the documentation of `py_argparse!()`.
|
||||
The return type must be `PyResult<T>` for some `T` that implements `ToPyObject`.
|
||||
|
||||
|
||||
*/
|
||||
#[macro_export]
|
||||
macro_rules! py_class {
|
||||
(class $class:ident |$py: ident| { $( $body:tt )* }) => (
|
||||
py_class_impl! {
|
||||
{ $( $body )* }
|
||||
$class $py
|
||||
/* info: */ {
|
||||
/* base_type: */ $crate::PyObject,
|
||||
/* size: */ <$crate::PyObject as $crate::py_class::BaseObject>::size(),
|
||||
/* class_visibility: */ {},
|
||||
/* gc: */ {
|
||||
/* traverse_proc: */ None,
|
||||
/* traverse_data: */ [ /*name*/ ]
|
||||
},
|
||||
/* data: */ [ /* { offset, name, type } */ ]
|
||||
// TODO: base type, documentation, ...
|
||||
}
|
||||
/* slots: */ {
|
||||
/* type_slots */ [ /* slot: expr, */ ]
|
||||
/* as_async */ [ /* slot: expr, */ ]
|
||||
/* as_number */ [ /* slot: expr, */ ]
|
||||
/* as_sequence */ [ /* slot: expr, */ ]
|
||||
/* as_mapping */ [ /* slot: expr, */ ]
|
||||
/* as_buffer */ [ /* slot: expr, */ ]
|
||||
/* setitem_delitem */ [
|
||||
sdi_setitem: {},
|
||||
sdi_delitem: {},
|
||||
]
|
||||
}
|
||||
/* impls: */ { /* impl body */ }
|
||||
/* members: */ { /* ident = expr; */ }
|
||||
/* properties: */ { /* expr; */ }
|
||||
}
|
||||
);
|
||||
(pub class $class:ident |$py: ident| { $( $body:tt )* }) => (
|
||||
py_class_impl! {
|
||||
{ $( $body )* }
|
||||
$class $py
|
||||
/* info: */ {
|
||||
/* base_type: */ $crate::PyObject,
|
||||
/* size: */ <$crate::PyObject as $crate::py_class::BaseObject>::size(),
|
||||
/* class_visibility: */ {pub},
|
||||
/* gc: */ {
|
||||
/* traverse_proc: */ None,
|
||||
/* traverse_data: */ [ /*name*/ ]
|
||||
},
|
||||
/* data: */ [ /* { offset, name, type } */ ]
|
||||
// TODO: base type, documentation, ...
|
||||
}
|
||||
/* slots: */ {
|
||||
/* type_slots */ [ /* slot: expr, */ ]
|
||||
/* as_async */ [ /* slot: expr, */ ]
|
||||
/* as_number */ [ /* slot: expr, */ ]
|
||||
/* as_sequence */ [ /* slot: expr, */ ]
|
||||
/* as_mapping */ [ /* slot: expr, */ ]
|
||||
/* as_buffer */ [ /* slot: expr, */ ]
|
||||
/* setitem_delitem */ [
|
||||
sdi_setitem: {},
|
||||
sdi_delitem: {},
|
||||
]
|
||||
}
|
||||
/* impls: */ { /* impl body */ }
|
||||
/* members: */ { /* ident = expr; */ }
|
||||
/* properties: */ { /* expr; */ }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_impl_item {
|
||||
{ $class:ident, $py:ident, $name:ident( $( $selfarg:tt )* )
|
||||
$res_type:ty; $body:block [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ]
|
||||
} => { py_coerce_item! {
|
||||
impl $class {
|
||||
pub fn $name($( $selfarg )* $py: $crate::Python $( , $pname: $ptype )* )
|
||||
-> $res_type $body
|
||||
}
|
||||
}}
|
||||
}
|
|
@ -1,827 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
This python script generates the py_class_impl! macro.
|
||||
"""
|
||||
|
||||
from collections import namedtuple
|
||||
import sys, os
|
||||
|
||||
PY2 = (os.getenv('PY') == '2')
|
||||
|
||||
header = '''
|
||||
// Copyright (c) 2016 Daniel Grunwald
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
'''
|
||||
|
||||
macro_start = '''
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_impl {
|
||||
// TT muncher macro. Results are accumulated in $info $slots $impls and $members.
|
||||
'''
|
||||
|
||||
base_case = '''
|
||||
// Base case: we're done munching and can start producing code:
|
||||
{ {}
|
||||
$class:ident $py:ident
|
||||
/* info: */ {
|
||||
$base_type:ty,
|
||||
$size:expr,
|
||||
{ $( $class_visibility:tt )* },
|
||||
$gc:tt,
|
||||
/* data: */ [ $( { $data_offset:expr, $data_name:ident, $data_ty:ty } )* ]
|
||||
}
|
||||
$slots:tt { $( $imp:item )* } $members:tt $properties:tt
|
||||
} => {
|
||||
py_coerce_item! {
|
||||
$($class_visibility)* struct $class { _unsafe_inner: $crate::PyObject }
|
||||
}
|
||||
|
||||
py_impl_to_py_object_for_python_object!($class);
|
||||
py_impl_from_py_object_for_python_object!($class);
|
||||
|
||||
impl $crate::PythonObject for $class {
|
||||
#[inline]
|
||||
fn as_object(&self) -> &$crate::PyObject {
|
||||
&self._unsafe_inner
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_object(self) -> $crate::PyObject {
|
||||
self._unsafe_inner
|
||||
}
|
||||
|
||||
/// Unchecked downcast from PyObject to Self.
|
||||
/// Undefined behavior if the input object does not have the expected type.
|
||||
#[inline]
|
||||
unsafe fn unchecked_downcast_from(obj: $crate::PyObject) -> Self {
|
||||
$class { _unsafe_inner: obj }
|
||||
}
|
||||
|
||||
/// Unchecked downcast from PyObject to Self.
|
||||
/// Undefined behavior if the input object does not have the expected type.
|
||||
#[inline]
|
||||
unsafe fn unchecked_downcast_borrow_from<'a>(obj: &'a $crate::PyObject) -> &'a Self {
|
||||
::std::mem::transmute(obj)
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::PythonObjectWithCheckedDowncast for $class {
|
||||
#[inline]
|
||||
fn downcast_from<'p>(py: $crate::Python<'p>, obj: $crate::PyObject) -> Result<$class, $crate::PythonObjectDowncastError<'p>> {
|
||||
if py.get_type::<$class>().is_instance(py, &obj) {
|
||||
Ok($class { _unsafe_inner: obj })
|
||||
} else {
|
||||
Err($crate::PythonObjectDowncastError(py))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn downcast_borrow_from<'a, 'p>(py: $crate::Python<'p>, obj: &'a $crate::PyObject) -> Result<&'a $class, $crate::PythonObjectDowncastError<'p>> {
|
||||
if py.get_type::<$class>().is_instance(py, obj) {
|
||||
unsafe { Ok(::std::mem::transmute(obj)) }
|
||||
} else {
|
||||
Err($crate::PythonObjectDowncastError(py))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
py_coerce_item! {
|
||||
impl $crate::py_class::BaseObject for $class {
|
||||
type InitType = ( $( $data_ty, )* );
|
||||
|
||||
#[inline]
|
||||
fn size() -> usize {
|
||||
$size
|
||||
}
|
||||
|
||||
unsafe fn alloc(
|
||||
py: $crate::Python,
|
||||
ty: &$crate::PyType,
|
||||
( $( $data_name, )* ): Self::InitType
|
||||
) -> $crate::PyResult<$crate::PyObject>
|
||||
{
|
||||
let obj = try!(<$base_type as $crate::py_class::BaseObject>::alloc(py, ty, ()));
|
||||
$( $crate::py_class::data_init::<$data_ty>(py, &obj, $data_offset, $data_name); )*
|
||||
Ok(obj)
|
||||
}
|
||||
|
||||
unsafe fn dealloc(py: $crate::Python, obj: *mut $crate::_detail::ffi::PyObject) {
|
||||
$( $crate::py_class::data_drop::<$data_ty>(py, obj, $data_offset); )*
|
||||
<$base_type as $crate::py_class::BaseObject>::dealloc(py, obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
$($imp)*
|
||||
py_coerce_item! {
|
||||
impl $class {
|
||||
fn create_instance(py: $crate::Python $( , $data_name : $data_ty )* ) -> $crate::PyResult<$class> {
|
||||
let obj = try!(unsafe {
|
||||
<$class as $crate::py_class::BaseObject>::alloc(
|
||||
py, &py.get_type::<$class>(), ( $($data_name,)* )
|
||||
)
|
||||
});
|
||||
return Ok($class { _unsafe_inner: obj });
|
||||
|
||||
// hide statics in create_instance to avoid name conflicts
|
||||
static mut TYPE_OBJECT : $crate::_detail::ffi::PyTypeObject
|
||||
= py_class_type_object_static_init!($class, $gc, $slots);
|
||||
static mut INIT_ACTIVE: bool = false;
|
||||
|
||||
// trait implementations that need direct access to TYPE_OBJECT
|
||||
impl $crate::PythonObjectWithTypeObject for $class {
|
||||
fn type_object(py: $crate::Python) -> $crate::PyType {
|
||||
unsafe {
|
||||
if $crate::py_class::is_ready(py, &TYPE_OBJECT) {
|
||||
$crate::PyType::from_type_ptr(py, &mut TYPE_OBJECT)
|
||||
} else {
|
||||
// automatically initialize the class on-demand
|
||||
<$class as $crate::py_class::PythonObjectFromPyClassMacro>::initialize(py, None)
|
||||
.expect(concat!("An error occurred while initializing class ", stringify!($class)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::py_class::PythonObjectFromPyClassMacro for $class {
|
||||
fn initialize(py: $crate::Python, module_name: Option<&str>) -> $crate::PyResult<$crate::PyType> {
|
||||
unsafe {
|
||||
if $crate::py_class::is_ready(py, &TYPE_OBJECT) {
|
||||
return Ok($crate::PyType::from_type_ptr(py, &mut TYPE_OBJECT));
|
||||
}
|
||||
assert!(!INIT_ACTIVE,
|
||||
concat!("Reentrancy detected: already initializing class ",
|
||||
stringify!($class)));
|
||||
INIT_ACTIVE = true;
|
||||
let res = init(py, module_name);
|
||||
INIT_ACTIVE = false;
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
fn add_to_module(py: $crate::Python, module: &$crate::PyModule) -> $crate::PyResult<()> {
|
||||
let ty = <$class as $crate::py_class::PythonObjectFromPyClassMacro>::initialize(py, module.name(py).ok())?;
|
||||
module.add(py, stringify!($class), ty)
|
||||
}
|
||||
}
|
||||
|
||||
fn init($py: $crate::Python, module_name: Option<&str>) -> $crate::PyResult<$crate::PyType> {
|
||||
py_class_type_object_dynamic_init!($class, $py, TYPE_OBJECT, module_name, $slots);
|
||||
py_class_init_members!($class, $py, TYPE_OBJECT, $members);
|
||||
py_class_init_properties!($class, $py, TYPE_OBJECT, $properties);
|
||||
unsafe {
|
||||
if $crate::_detail::ffi::PyType_Ready(&mut TYPE_OBJECT) == 0 {
|
||||
Ok($crate::PyType::from_type_ptr($py, &mut TYPE_OBJECT))
|
||||
} else {
|
||||
Err($crate::PyErr::fetch($py))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
{ { property $name:ident { $($body:tt)* } $($tail:tt)* }
|
||||
$class:ident $py:ident $info:tt $slots:tt { $( $imp:item )* } $members:tt
|
||||
{ $( $properties:expr; )* }
|
||||
} => { py_class_impl! {
|
||||
{ $($tail)* }
|
||||
$class $py $info $slots { $($imp)* } $members
|
||||
/* properties: */ {
|
||||
$( $properties; )*
|
||||
py_class_property_impl! { { $($body)* } $class $py $name { } };
|
||||
}
|
||||
}};
|
||||
'''
|
||||
|
||||
indentation = [' ']
|
||||
last_char = '\n'
|
||||
|
||||
def write(text):
|
||||
global last_char
|
||||
for line in text.splitlines(True):
|
||||
line = line.lstrip(' ')
|
||||
if len(line.strip()) == 0 and last_char == '\n':
|
||||
continue
|
||||
if last_char == '\n':
|
||||
initial_closing = 0
|
||||
for c in line:
|
||||
if c in ']}':
|
||||
initial_closing += 1
|
||||
else:
|
||||
break
|
||||
if initial_closing:
|
||||
sys.stdout.write(''.join(indentation[:-initial_closing]))
|
||||
else:
|
||||
sys.stdout.write(''.join(indentation))
|
||||
elif last_char not in ' \n' and len(line) > 0 and line[0] not in ' \n;':
|
||||
sys.stdout.write(' ')
|
||||
sys.stdout.write(line)
|
||||
min_indent_level = len(indentation)
|
||||
for c in line:
|
||||
if c in '[{':
|
||||
if len(indentation) > min_indent_level:
|
||||
indentation.append('')
|
||||
else:
|
||||
indentation.append(' ')
|
||||
elif c in ']}':
|
||||
indentation.pop()
|
||||
if len(indentation) < min_indent_level:
|
||||
min_indent_level = len(indentation)
|
||||
last_char = line[-1]
|
||||
|
||||
slot_groups = (
|
||||
('tp', 'type_slots', None),
|
||||
('am', 'as_async', None),
|
||||
('nb', 'as_number', None),
|
||||
('sq', 'as_sequence', None),
|
||||
('mp', 'as_mapping', None),
|
||||
('bf', 'as_buffer', None),
|
||||
('sdi', 'setdelitem', ['sdi_setitem', 'sdi_delitem'])
|
||||
)
|
||||
|
||||
def generate_case(pattern, old_info=None, new_info=None, new_impl=None, new_slots=None, new_members=None):
|
||||
write('{ { %s $($tail:tt)* }\n' % pattern)
|
||||
write('$class:ident $py:ident')
|
||||
if old_info is not None:
|
||||
write(old_info)
|
||||
elif new_info is not None:
|
||||
write('\n/* info: */ {\n')
|
||||
write('$base_type: ty,\n')
|
||||
write('$size: expr,\n')
|
||||
write('$class_visibility: tt,\n')
|
||||
write('$gc: tt,\n')
|
||||
write('[ $( $data:tt )* ]\n')
|
||||
write('}\n')
|
||||
else:
|
||||
write('$info:tt')
|
||||
if new_slots:
|
||||
write('\n/* slots: */ {\n')
|
||||
for prefix, group_name, explicit_slots in slot_groups:
|
||||
if any(s.startswith(prefix) for s, v in new_slots):
|
||||
if explicit_slots is None:
|
||||
write('\n/* %s */ [ $( $%s_slot_name:ident : $%s_slot_value:expr, )* ]\n'
|
||||
% (group_name, prefix, prefix))
|
||||
else:
|
||||
write('\n/* %s */ [\n' % group_name)
|
||||
for slot in explicit_slots:
|
||||
if any(s == slot for s, v in new_slots):
|
||||
write('%s: {},\n' % slot)
|
||||
else:
|
||||
write('%s: $%s_slot_value:tt,\n' % (slot, slot))
|
||||
write(']\n')
|
||||
else:
|
||||
write('$%s:tt' % group_name)
|
||||
write('\n}\n')
|
||||
else:
|
||||
write('$slots:tt')
|
||||
if new_impl is not None:
|
||||
write('\n{ $( $imp:item )* }\n')
|
||||
else:
|
||||
write('$impls:tt')
|
||||
if new_members:
|
||||
write('\n{ $( $member_name:ident = $member_expr:expr; )* }')
|
||||
else:
|
||||
write('$members:tt')
|
||||
|
||||
write('$properties:tt')
|
||||
|
||||
write('\n} => { py_class_impl! {\n')
|
||||
write('{ $($tail)* }\n')
|
||||
write('$class $py')
|
||||
write(new_info or '$info')
|
||||
if new_slots:
|
||||
write('\n/* slots: */ {\n')
|
||||
for prefix, group_name, explicit_slots in slot_groups:
|
||||
if any(s.startswith(prefix) for s, v in new_slots):
|
||||
write('\n/* %s */ [\n' % group_name)
|
||||
if explicit_slots is None:
|
||||
write('$( $%s_slot_name : $%s_slot_value, )*\n' % (prefix, prefix))
|
||||
for s, v in new_slots:
|
||||
if s.startswith(prefix):
|
||||
write('%s: %s,\n' % (s, v))
|
||||
else:
|
||||
for slot in explicit_slots:
|
||||
slot_value = next((v for s, v in new_slots if s == slot), None)
|
||||
if slot_value is None:
|
||||
write('%s: $%s_slot_value,\n' % (slot, slot))
|
||||
else:
|
||||
write('%s: { %s },\n' % (slot, slot_value))
|
||||
write(']\n')
|
||||
else:
|
||||
write('$%s' % group_name)
|
||||
write('\n}\n')
|
||||
else:
|
||||
write('$slots')
|
||||
if new_impl is not None:
|
||||
write('\n/* impl: */ {\n')
|
||||
write('$($imp)*\n')
|
||||
write(new_impl)
|
||||
write('\n}\n')
|
||||
else:
|
||||
write('$impls')
|
||||
if new_members:
|
||||
write('\n/* members: */ {\n')
|
||||
write('$( $member_name = $member_expr; )*\n')
|
||||
for name, val in new_members:
|
||||
write('%s = %s;\n' % (name, val))
|
||||
write('}')
|
||||
else:
|
||||
write('$members')
|
||||
write('$properties')
|
||||
write('\n}};\n')
|
||||
|
||||
def data_decl():
|
||||
generate_case('data $data_name:ident : $data_type:ty;',
|
||||
new_info = '''
|
||||
/* info: */ {
|
||||
$base_type,
|
||||
/* size: */ $crate::py_class::data_new_size::<$data_type>($size),
|
||||
$class_visibility,
|
||||
$gc,
|
||||
/* data: */ [
|
||||
$($data)*
|
||||
{
|
||||
$crate::py_class::data_offset::<$data_type>($size),
|
||||
$data_name,
|
||||
$data_type
|
||||
}
|
||||
]
|
||||
}
|
||||
''',
|
||||
new_impl='''
|
||||
impl $class {
|
||||
fn $data_name<'a>(&'a self, py: $crate::Python<'a>) -> &'a $data_type {
|
||||
unsafe {
|
||||
$crate::py_class::data_get::<$data_type>(
|
||||
py,
|
||||
&self._unsafe_inner,
|
||||
$crate::py_class::data_offset::<$data_type>($size)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
''')
|
||||
|
||||
def generate_class_method(special_name=None, decoration='',
|
||||
slot=None, add_member=False, value_macro=None, value_args=None):
|
||||
name_pattern = special_name or '$name:ident'
|
||||
name_use = special_name or '$name'
|
||||
def impl(with_params):
|
||||
if with_params:
|
||||
param_pattern = ', $($p:tt)+'
|
||||
impl = '''py_argparse_parse_plist_impl!{
|
||||
py_class_impl_item { $class, $py, %s($cls: &$crate::PyType,) $res_type; { $($body)* } }
|
||||
[] ($($p)+,)
|
||||
}''' % name_use
|
||||
value = 'py_argparse_parse_plist_impl!{%s {%s} [] ($($p)+,)}' \
|
||||
% (value_macro, value_args)
|
||||
else:
|
||||
param_pattern = ''
|
||||
impl = 'py_class_impl_item! { $class, $py,%s($cls: &$crate::PyType,) $res_type; { $($body)* } [] }' \
|
||||
% name_use
|
||||
value = '%s!{%s []}' % (value_macro, value_args)
|
||||
pattern = '%s def %s ($cls:ident%s) -> $res_type:ty { $( $body:tt )* }' \
|
||||
% (decoration, name_pattern, param_pattern)
|
||||
slots = []
|
||||
if slot is not None:
|
||||
slots.append((slot, value))
|
||||
members = []
|
||||
if add_member:
|
||||
members.append((name_use, value))
|
||||
generate_case(pattern, new_impl=impl, new_slots=slots, new_members=members)
|
||||
impl(False) # without parameters
|
||||
impl(True) # with parameters
|
||||
|
||||
def traverse_and_clear():
|
||||
generate_case('def __traverse__(&$slf:tt, $visit:ident) $body:block',
|
||||
old_info = '''
|
||||
/* info: */ {
|
||||
$base_type: ty,
|
||||
$size: expr,
|
||||
$class_visibility: tt,
|
||||
/* gc: */ {
|
||||
/* traverse_proc: */ None,
|
||||
$traverse_data: tt
|
||||
},
|
||||
$datas: tt
|
||||
}
|
||||
''',
|
||||
new_info='''
|
||||
/* info: */ {
|
||||
$base_type,
|
||||
$size,
|
||||
$class_visibility,
|
||||
/* gc: */ {
|
||||
/* traverse_proc: */ $class::__traverse__,
|
||||
$traverse_data
|
||||
},
|
||||
$datas
|
||||
}
|
||||
''',
|
||||
new_impl='''
|
||||
py_coerce_item!{
|
||||
impl $class {
|
||||
fn __traverse__(&$slf,
|
||||
$py: $crate::Python,
|
||||
$visit: $crate::py_class::gc::VisitProc)
|
||||
-> Result<(), $crate::py_class::gc::TraverseError>
|
||||
$body
|
||||
}
|
||||
}
|
||||
''')
|
||||
generate_case('def __clear__ (&$slf:ident) $body:block',
|
||||
new_slots=[('tp_clear', 'py_class_tp_clear!($class)')],
|
||||
new_impl='''
|
||||
py_coerce_item!{
|
||||
impl $class {
|
||||
fn __clear__(&$slf, $py: $crate::Python) $body
|
||||
}
|
||||
}
|
||||
''')
|
||||
|
||||
def generate_instance_method(special_name=None, decoration='',
|
||||
slot=None, add_member=False, value_macro=None, value_args=None):
|
||||
name_pattern = special_name or '$name:ident'
|
||||
name_use = special_name or '$name'
|
||||
def impl(with_params):
|
||||
if with_params:
|
||||
param_pattern = ', $($p:tt)+'
|
||||
impl = '''py_argparse_parse_plist_impl!{
|
||||
py_class_impl_item { $class, $py, %s(&$slf,) $res_type; { $($body)* } }
|
||||
[] ($($p)+,)
|
||||
}''' % name_use
|
||||
value = 'py_argparse_parse_plist_impl!{%s {%s} [] ($($p)+,)}' \
|
||||
% (value_macro, value_args)
|
||||
else:
|
||||
param_pattern = ''
|
||||
impl = 'py_class_impl_item! { $class, $py, %s(&$slf,) $res_type; { $($body)* } [] }' \
|
||||
% name_use
|
||||
value = '%s!{%s []}' % (value_macro, value_args)
|
||||
pattern = '%s def %s (&$slf:ident%s) -> $res_type:ty { $( $body:tt )* }' \
|
||||
% (decoration, name_pattern, param_pattern)
|
||||
slots = []
|
||||
if slot is not None:
|
||||
slots.append((slot, value))
|
||||
members = []
|
||||
if add_member:
|
||||
members.append((name_use, value))
|
||||
generate_case(pattern, new_impl=impl, new_slots=slots, new_members=members)
|
||||
impl(False) # without parameters
|
||||
impl(True) # with parameters
|
||||
|
||||
def static_method():
|
||||
generate_case(
|
||||
'@staticmethod def $name:ident ($($p:tt)*) -> $res_type:ty { $( $body:tt )* }',
|
||||
new_impl='''
|
||||
py_argparse_parse_plist!{
|
||||
py_class_impl_item { $class, $py, $name() $res_type; { $($body)* } }
|
||||
($($p)*)
|
||||
}
|
||||
''',
|
||||
new_members=[('$name', '''
|
||||
py_argparse_parse_plist!{
|
||||
py_class_static_method {$py, $class::$name}
|
||||
($($p)*)
|
||||
}
|
||||
''')])
|
||||
|
||||
def static_data():
|
||||
generate_case('static $name:ident = $init:expr;',
|
||||
new_members=[('$name', '$init')])
|
||||
|
||||
macro_end = '''
|
||||
}
|
||||
'''
|
||||
|
||||
def special_method(decorated_function):
|
||||
def wrap1(*args, **kwargs):
|
||||
def wrap2(special_name):
|
||||
return decorated_function(special_name, *args, **kwargs)
|
||||
return wrap2
|
||||
return wrap1
|
||||
|
||||
@special_method
|
||||
def error(special_name, msg):
|
||||
print('''
|
||||
{ { def %s $($tail:tt)* } $( $stuff:tt )* } => {
|
||||
py_error! { "%s" }
|
||||
};''' % (special_name, msg))
|
||||
|
||||
@special_method
|
||||
def unimplemented(special_name):
|
||||
return error('%s is not supported by py_class! yet.' % special_name)(special_name)
|
||||
|
||||
@special_method
|
||||
def normal_method(special_name):
|
||||
pass
|
||||
|
||||
@special_method
|
||||
def special_class_method(special_name, *args, **kwargs):
|
||||
generate_class_method(special_name=special_name, *args, **kwargs)
|
||||
|
||||
|
||||
class Argument(object):
|
||||
def __init__(self, name, arg_type=None):
|
||||
self.name = name
|
||||
|
||||
if arg_type is not None:
|
||||
if arg_type == 'int':
|
||||
arg_type = '$crate::_detail::libc::c_int'
|
||||
|
||||
self.type = arg_type
|
||||
|
||||
def get_type(self):
|
||||
if self.type is not None:
|
||||
return self.type
|
||||
else:
|
||||
return '$%s_type' % self.name
|
||||
|
||||
def get_arg_pattern(self):
|
||||
if self.type is not None:
|
||||
return ', ${0}:ident '.format(self.name)
|
||||
else:
|
||||
return ', ${0}:ident : ${0}_type:ty'.format(self.name)
|
||||
|
||||
def get_arg_param_item(self):
|
||||
return '{{ ${0} : {1} = {{}} }}'.format(self.name, self.get_type())
|
||||
|
||||
|
||||
@special_method
|
||||
def operator(special_name, slot,
|
||||
args=(),
|
||||
wrapper=None,
|
||||
res_type='PyObject',
|
||||
res_conv=None,
|
||||
res_ffi_type='*mut $crate::_detail::ffi::PyObject',
|
||||
additional_slots=()
|
||||
):
|
||||
if res_conv is None:
|
||||
if res_type == 'void':
|
||||
res_conv = '$crate::py_class::slots::VoidCallbackConverter'
|
||||
if res_type == '()':
|
||||
res_conv = '$crate::py_class::slots::UnitCallbackConverter'
|
||||
res_ffi_type = '$crate::_detail::libc::c_int'
|
||||
elif res_type == 'bool':
|
||||
res_conv = '$crate::py_class::slots::BoolConverter'
|
||||
res_ffi_type = '$crate::_detail::libc::c_int'
|
||||
elif res_type == 'PyObject':
|
||||
res_conv = '$crate::_detail::PyObjectCallbackConverter'
|
||||
else:
|
||||
res_conv = '$crate::_detail::PythonObjectCallbackConverter::<$crate::%s>(::std::marker::PhantomData)' % res_type
|
||||
arg_pattern = ''
|
||||
param_list = []
|
||||
for arg in args:
|
||||
arg_pattern += arg.get_arg_pattern()
|
||||
param_list.append(arg.get_arg_param_item())
|
||||
if slot == 'sq_contains':
|
||||
new_slots = [(slot, 'py_class_contains_slot!($class::%s, $%s_type)' % (special_name, args[0].name))]
|
||||
elif slot == 'tp_richcompare':
|
||||
new_slots = [(slot, 'py_class_richcompare_slot!($class::%s, $%s_type, %s, %s)'
|
||||
% (special_name, args[0].name, res_ffi_type, res_conv))]
|
||||
elif len(args) == 0:
|
||||
new_slots = [(slot, 'py_class_unary_slot!($class::%s, %s, %s)'
|
||||
% (special_name, res_ffi_type, res_conv))]
|
||||
elif len(args) == 1:
|
||||
new_slots = [(slot, 'py_class_binary_slot!($class::%s, %s, %s, %s)'
|
||||
% (special_name, args[0].get_type(), res_ffi_type, res_conv))]
|
||||
elif len(args) == 2:
|
||||
if wrapper is None:
|
||||
wrapper = 'py_class_ternary_slot'
|
||||
new_slots = [(slot, '%s!($class::%s, %s, %s, %s, %s)'
|
||||
% (wrapper, special_name, args[0].get_type(), args[1].get_type(), res_ffi_type, res_conv))]
|
||||
else:
|
||||
raise ValueError('Unsupported argument count')
|
||||
generate_case(
|
||||
pattern='def %s(&$slf:ident%s) -> $res_type:ty { $($body:tt)* }' % (special_name, arg_pattern),
|
||||
new_impl='py_class_impl_item! { $class, $py, %s(&$slf,) $res_type; { $($body)* } [%s] }'
|
||||
% (special_name, ' '.join(param_list)),
|
||||
new_slots=new_slots + list(additional_slots)
|
||||
)
|
||||
# Generate fall-back matcher that produces an error
|
||||
# when using the wrong method signature
|
||||
error('Invalid signature for operator %s' % special_name)(special_name)
|
||||
|
||||
@special_method
|
||||
def call_operator(special_name, slot):
|
||||
generate_instance_method(
|
||||
special_name=special_name,
|
||||
slot=slot,
|
||||
value_macro='py_class_call_slot',
|
||||
value_args='$class::%s' % special_name)
|
||||
|
||||
@special_method
|
||||
def binary_numeric_operator(special_name, slot):
|
||||
generate_case(
|
||||
pattern='def %s($left:ident, $right:ident) -> $res_type:ty { $($body:tt)* }'
|
||||
% special_name,
|
||||
new_impl='py_class_impl_item! { $class, $py, %s() $res_type; { $($body)* } ' % special_name
|
||||
+'[ { $left : &$crate::PyObject = {} } { $right : &$crate::PyObject = {} } ] }',
|
||||
new_slots=[(slot, 'py_class_binary_numeric_slot!($class::%s)' % special_name)]
|
||||
)
|
||||
error('Invalid signature for binary numeric operator %s' % special_name)(special_name)
|
||||
|
||||
@special_method
|
||||
def reflected_numeric_operator(special_name):
|
||||
error('Reflected numeric operator %s is not supported by py_class! Use __%s__ instead!'
|
||||
% (special_name, special_name[3:-2]))(special_name)
|
||||
|
||||
@special_method
|
||||
def inplace_numeric_operator(special_name, slot):
|
||||
operator(slot=slot,
|
||||
args=[Argument('other')])(special_name)
|
||||
|
||||
special_names = {
|
||||
'__init__': error('__init__ is not supported by py_class!; use __new__ instead.'),
|
||||
'__new__': special_class_method(
|
||||
slot='tp_new',
|
||||
value_macro='py_class_wrap_newfunc',
|
||||
value_args='$class::__new__'),
|
||||
'__del__': error('__del__ is not supported by py_class!; Use a data member with a Drop impl instead.'),
|
||||
'__repr__': operator('tp_repr', res_type="PyString"),
|
||||
'__str__': operator('tp_str', res_type="PyString"),
|
||||
'__unicode__': normal_method(),
|
||||
'__bytes__': normal_method(),
|
||||
'__format__': normal_method(),
|
||||
# Comparison Operators
|
||||
'__lt__': error('__lt__ is not supported by py_class! use __richcmp__ instead.'),
|
||||
'__le__': error('__le__ is not supported by py_class! use __richcmp__ instead.'),
|
||||
'__gt__': error('__gt__ is not supported by py_class! use __richcmp__ instead.'),
|
||||
'__ge__': error('__ge__ is not supported by py_class! use __richcmp__ instead.'),
|
||||
'__eq__': error('__eq__ is not supported by py_class! use __richcmp__ instead.'),
|
||||
'__ne__': error('__ne__ is not supported by py_class! use __richcmp__ instead.'),
|
||||
'__cmp__': error('__cmp__ is not supported by py_class! use __richcmp__ instead.'),
|
||||
'__richcmp__': operator('tp_richcompare',
|
||||
res_type='PyObject',
|
||||
args=[Argument('other'), Argument('op')]),
|
||||
'__hash__': operator('tp_hash',
|
||||
res_conv='$crate::py_class::slots::HashConverter',
|
||||
res_ffi_type='$crate::Py_hash_t'),
|
||||
'__nonzero__': error('__nonzero__ is not supported by py_class!; use the Python 3 spelling __bool__ instead.'),
|
||||
'__bool__': operator('nb_nonzero' if PY2 else 'nb_bool',
|
||||
res_type='bool'),
|
||||
# Customizing attribute access
|
||||
'__getattr__': unimplemented(),
|
||||
'__getattribute__': unimplemented(),
|
||||
'__setattr__': unimplemented(),
|
||||
'__delattr__': unimplemented(),
|
||||
'__dir__': unimplemented(),
|
||||
|
||||
# Implementing Descriptors
|
||||
'__get__': unimplemented(),
|
||||
'__set__': unimplemented(),
|
||||
'__delete__': unimplemented(),
|
||||
|
||||
# Customizing instance and subclass checks
|
||||
'__instancecheck__': unimplemented(),
|
||||
'__subclasscheck__': unimplemented(),
|
||||
|
||||
# Emulating callable objects
|
||||
'__call__': call_operator('tp_call'),
|
||||
|
||||
# Emulating container types
|
||||
'__len__': operator('sq_length',
|
||||
res_ffi_type='$crate::_detail::ffi::Py_ssize_t',
|
||||
res_conv='$crate::py_class::slots::LenResultConverter',
|
||||
additional_slots=[
|
||||
# Use PySequence_Size to forward mp_length calls to sq_length.
|
||||
('mp_length', 'Some($crate::_detail::ffi::PySequence_Size)')
|
||||
]),
|
||||
'__length_hint__': normal_method(),
|
||||
'__getitem__': operator('mp_subscript',
|
||||
args=[Argument('key')],
|
||||
additional_slots=[
|
||||
('sq_item', 'Some($crate::py_class::slots::sq_item)')
|
||||
]),
|
||||
'__missing__': normal_method(),
|
||||
'__setitem__': operator('sdi_setitem',
|
||||
args=[Argument('key'), Argument('value')],
|
||||
res_type='()'),
|
||||
'__delitem__': operator('sdi_delitem',
|
||||
args=[Argument('key')],
|
||||
res_type='()'),
|
||||
'__iter__': operator('tp_iter'),
|
||||
'__next__': operator('tp_iternext',
|
||||
res_conv='$crate::py_class::slots::IterNextResultConverter'),
|
||||
'__reversed__': normal_method(),
|
||||
'__contains__': operator('sq_contains', args=[Argument('item')]),
|
||||
|
||||
# Emulating numeric types
|
||||
'__add__': binary_numeric_operator('nb_add'),
|
||||
'__sub__': binary_numeric_operator('nb_subtract'),
|
||||
'__mul__': binary_numeric_operator('nb_multiply'),
|
||||
'__matmul__': unimplemented(),
|
||||
'__div__': unimplemented(),
|
||||
'__truediv__': unimplemented(),
|
||||
'__floordiv__': unimplemented(),
|
||||
'__mod__': unimplemented(),
|
||||
'__divmod__': unimplemented(),
|
||||
'__pow__': unimplemented(),
|
||||
'__lshift__': binary_numeric_operator('nb_lshift'),
|
||||
'__rshift__': binary_numeric_operator('nb_rshift'),
|
||||
'__and__': binary_numeric_operator('nb_and'),
|
||||
'__xor__': binary_numeric_operator('nb_xor'),
|
||||
'__or__': binary_numeric_operator('nb_or'),
|
||||
|
||||
# Emulating numeric types - reflected
|
||||
'__radd__': reflected_numeric_operator(),
|
||||
'__rsub__': reflected_numeric_operator(),
|
||||
'__rmul__': reflected_numeric_operator(),
|
||||
'__rmatmul__': reflected_numeric_operator(),
|
||||
'__rdiv__': reflected_numeric_operator(),
|
||||
'__rtruediv__': reflected_numeric_operator(),
|
||||
'__rfloordiv__': reflected_numeric_operator(),
|
||||
'__rmod__': reflected_numeric_operator(),
|
||||
'__rdivmod__': reflected_numeric_operator(),
|
||||
'__rpow__': reflected_numeric_operator(),
|
||||
'__rlshift__': reflected_numeric_operator(),
|
||||
'__rrshift__': reflected_numeric_operator(),
|
||||
'__rand__': reflected_numeric_operator(),
|
||||
'__rxor__': reflected_numeric_operator(),
|
||||
'__ror__': reflected_numeric_operator(),
|
||||
|
||||
# Emulating numeric types - in-place
|
||||
'__iadd__': inplace_numeric_operator('nb_inplace_add'),
|
||||
'__isub__': inplace_numeric_operator('nb_inplace_subtract'),
|
||||
'__imul__': inplace_numeric_operator('nb_inplace_multiply'),
|
||||
'__imatmul__': inplace_numeric_operator('nb_inplace_matrix_multiply'),
|
||||
'__idiv__': unimplemented(),
|
||||
'__itruediv__': inplace_numeric_operator('nb_inplace_true_divide'),
|
||||
'__ifloordiv__': inplace_numeric_operator('nb_inplace_floor_divide'),
|
||||
'__imod__': inplace_numeric_operator('nb_inplace_remainder'),
|
||||
'__ipow__': unimplemented(),
|
||||
'__ilshift__': inplace_numeric_operator('nb_inplace_lshift'),
|
||||
'__irshift__': inplace_numeric_operator('nb_inplace_rshift'),
|
||||
'__iand__': inplace_numeric_operator('nb_inplace_and'),
|
||||
'__ixor__': inplace_numeric_operator('nb_inplace_xor'),
|
||||
'__ior__': inplace_numeric_operator('nb_inplace_or'),
|
||||
|
||||
# Unary arithmetic
|
||||
'__neg__': operator('nb_negative'),
|
||||
'__pos__': operator('nb_positive'),
|
||||
'__abs__': operator('nb_absolute'),
|
||||
'__invert__': operator('nb_invert'),
|
||||
'__complex__': unimplemented(),
|
||||
'__int__': unimplemented(),
|
||||
'__long__': unimplemented(),
|
||||
'__float__': unimplemented(),
|
||||
'__round__': unimplemented(),
|
||||
'__index__': unimplemented(),
|
||||
'__coerce__': unimplemented(),
|
||||
|
||||
# With statement context managers
|
||||
'__enter__': normal_method(),
|
||||
'__exit__': normal_method(),
|
||||
}
|
||||
|
||||
def main():
|
||||
if sys.argv[1:] == ['--format']:
|
||||
while True:
|
||||
line = sys.stdin.readline()
|
||||
if len(line) == 0:
|
||||
return
|
||||
write(line)
|
||||
print(header)
|
||||
print('')
|
||||
print('// !!!!!!!!!!!!!!!!!!!!!!!!!!!')
|
||||
print('// THIS IS A GENERATED FILE !!')
|
||||
print('// DO NOT MODIFY !!')
|
||||
print('// !!!!!!!!!!!!!!!!!!!!!!!!!!!')
|
||||
print(macro_start)
|
||||
print(base_case)
|
||||
data_decl()
|
||||
traverse_and_clear()
|
||||
for name, f in sorted(special_names.items()):
|
||||
f(name)
|
||||
generate_instance_method(
|
||||
add_member=True,
|
||||
value_macro='py_class_instance_method',
|
||||
value_args='$py, $class::$name')
|
||||
generate_class_method(decoration='@classmethod',
|
||||
add_member=True,
|
||||
value_macro='py_class_class_method',
|
||||
value_args='$py, $class::$name')
|
||||
static_method()
|
||||
static_data()
|
||||
print(macro_end)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -1,531 +0,0 @@
|
|||
// Copyright (c) 2016 Daniel Grunwald
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use ffi;
|
||||
use std::{isize, ptr};
|
||||
use std::os::raw::{c_int};
|
||||
use python::{Python, PythonObject};
|
||||
use conversion::ToPyObject;
|
||||
use function::CallbackConverter;
|
||||
use err::{PyErr, PyResult};
|
||||
use py_class::{CompareOp};
|
||||
use exc;
|
||||
use Py_hash_t;
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_type_object_static_init {
|
||||
($class_name:ident,
|
||||
$gc:tt,
|
||||
/* slots: */ {
|
||||
/* type_slots */ [ $( $slot_name:ident : $slot_value:expr, )* ]
|
||||
$as_async:tt
|
||||
$as_number:tt
|
||||
$as_sequence:tt
|
||||
$as_mapping:tt
|
||||
$as_buffer:tt
|
||||
$setdelitem:tt
|
||||
}) => (
|
||||
$crate::_detail::ffi::PyTypeObject {
|
||||
$( $slot_name : $slot_value, )*
|
||||
..
|
||||
$crate::_detail::ffi::PyTypeObject_INIT
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_wrap_newfunc {
|
||||
($class:ident :: $f:ident [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ]) => {{
|
||||
unsafe extern "C" fn wrap_newfunc(
|
||||
cls: *mut $crate::_detail::ffi::PyTypeObject,
|
||||
args: *mut $crate::_detail::ffi::PyObject,
|
||||
kwargs: *mut $crate::_detail::ffi::PyObject)
|
||||
-> *mut $crate::_detail::ffi::PyObject
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
|
||||
$crate::_detail::handle_callback(
|
||||
LOCATION, $crate::_detail::PyObjectCallbackConverter,
|
||||
|py| {
|
||||
py_argparse_raw!(py, Some(LOCATION), args, kwargs,
|
||||
[ $( { $pname : $ptype = $detail } )* ]
|
||||
{
|
||||
let cls = $crate::PyType::from_type_ptr(py, cls);
|
||||
let ret = $class::$f(&cls, py $(, $pname )* );
|
||||
$crate::PyDrop::release_ref(cls, py);
|
||||
ret
|
||||
})
|
||||
})
|
||||
}
|
||||
Some(wrap_newfunc)
|
||||
}}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_as_number {
|
||||
([]) => (0 as *mut $crate::_detail::ffi::PyNumberMethods);
|
||||
([$( $slot_name:ident : $slot_value:expr ,)+]) => {{
|
||||
static mut NUMBER_METHODS : $crate::_detail::ffi::PyNumberMethods
|
||||
= $crate::_detail::ffi::PyNumberMethods {
|
||||
$( $slot_name : $slot_value, )*
|
||||
..
|
||||
$crate::_detail::ffi::PyNumberMethods_INIT
|
||||
};
|
||||
unsafe { &mut NUMBER_METHODS }
|
||||
}}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_unary_slot {
|
||||
($class:ident :: $f:ident, $res_type:ty, $conv:expr) => {{
|
||||
unsafe extern "C" fn wrap_unary(
|
||||
slf: *mut $crate::_detail::ffi::PyObject)
|
||||
-> $res_type
|
||||
{
|
||||
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::<$class>();
|
||||
let ret = slf.$f(py);
|
||||
$crate::PyDrop::release_ref(slf, py);
|
||||
ret
|
||||
})
|
||||
}
|
||||
Some(wrap_unary)
|
||||
}}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_binary_slot {
|
||||
($class:ident :: $f:ident, $arg_type:ty, $res_type:ty, $conv:expr) => {{
|
||||
unsafe extern "C" fn wrap_binary(
|
||||
slf: *mut $crate::_detail::ffi::PyObject,
|
||||
arg: *mut $crate::_detail::ffi::PyObject)
|
||||
-> $res_type
|
||||
{
|
||||
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::<$class>();
|
||||
let arg = $crate::PyObject::from_borrowed_ptr(py, arg);
|
||||
let ret = match <$arg_type as $crate::FromPyObject>::extract(py, &arg) {
|
||||
Ok(arg) => slf.$f(py, arg),
|
||||
Err(e) => Err(e)
|
||||
};
|
||||
$crate::PyDrop::release_ref(arg, py);
|
||||
$crate::PyDrop::release_ref(slf, py);
|
||||
ret
|
||||
})
|
||||
}
|
||||
Some(wrap_binary)
|
||||
}}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_ternary_slot {
|
||||
($class:ident :: $f:ident, $arg1_type:ty, $arg2_type:ty, $res_type:ty, $conv:expr) => {{
|
||||
unsafe extern "C" fn wrap_binary(
|
||||
slf: *mut $crate::_detail::ffi::PyObject,
|
||||
arg1: *mut $crate::_detail::ffi::PyObject,
|
||||
arg2: *mut $crate::_detail::ffi::PyObject)
|
||||
-> $res_type
|
||||
{
|
||||
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::<$class>();
|
||||
let arg1 = $crate::PyObject::from_borrowed_ptr(py, arg1);
|
||||
let arg2 = $crate::PyObject::from_borrowed_ptr(py, arg2);
|
||||
let ret = match <$arg1_type as $crate::FromPyObject>::extract(py, &arg1) {
|
||||
Ok(arg1) => match <$arg2_type as $crate::FromPyObject>::extract(py, &arg2) {
|
||||
Ok(arg2) => slf.$f(py, arg1, arg2),
|
||||
Err(e) => Err(e)
|
||||
},
|
||||
Err(e) => Err(e)
|
||||
};
|
||||
$crate::PyDrop::release_ref(arg1, py);
|
||||
$crate::PyDrop::release_ref(arg2, py);
|
||||
$crate::PyDrop::release_ref(slf, py);
|
||||
ret
|
||||
})
|
||||
}
|
||||
Some(wrap_binary)
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_binary_internal {
|
||||
($class:ident :: $f:ident, $arg1_type:ty, $res_type:ty, $conv:expr) => {{
|
||||
unsafe extern "C" fn wrap_binary(
|
||||
slf: *mut $crate::_detail::ffi::PyObject,
|
||||
arg1: $arg1_type)
|
||||
-> $res_type {
|
||||
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::<$class>();
|
||||
Ok(slf.$f(py, arg1))
|
||||
})
|
||||
}
|
||||
Some(wrap_binary)
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_ternary_internal {
|
||||
($class:ident :: $f:ident, $arg1_type:ty, $arg2_type:ty, $res_type:ty, $conv:expr) => {{
|
||||
unsafe extern "C" fn wrap(
|
||||
slf: *mut $crate::_detail::ffi::PyObject,
|
||||
arg1: $arg1_type,
|
||||
arg2: $arg2_type)
|
||||
-> $res_type
|
||||
{
|
||||
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::<$class>();
|
||||
Ok(slf.$f(py, arg1, arg2))
|
||||
})
|
||||
}
|
||||
Some(wrap)
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
pub 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())))
|
||||
}
|
||||
}
|
||||
|
||||
// sq_richcompare is special-cased slot
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_richcompare_slot {
|
||||
($class:ident :: $f:ident, $arg_type:ty, $res_type:ty, $conv:expr) => {{
|
||||
unsafe extern "C" fn tp_richcompare(
|
||||
slf: *mut $crate::_detail::ffi::PyObject,
|
||||
arg: *mut $crate::_detail::ffi::PyObject,
|
||||
op: $crate::_detail::libc::c_int)
|
||||
-> $res_type
|
||||
{
|
||||
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::<$class>();
|
||||
let arg = $crate::PyObject::from_borrowed_ptr(py, arg);
|
||||
let ret = match $crate::py_class::slots::extract_op(py, op) {
|
||||
Ok(op) => match <$arg_type as $crate::FromPyObject>::extract(py, &arg) {
|
||||
Ok(arg) => slf.$f(py, arg, op).map(|res| { res.into_py_object(py).into_object() }),
|
||||
Err(_) => Ok(py.NotImplemented())
|
||||
},
|
||||
Err(_) => Ok(py.NotImplemented())
|
||||
};
|
||||
$crate::PyDrop::release_ref(arg, py);
|
||||
$crate::PyDrop::release_ref(slf, py);
|
||||
ret
|
||||
})
|
||||
}
|
||||
Some(tp_richcompare)
|
||||
}}
|
||||
}
|
||||
|
||||
// sq_contains is special-cased slot because it converts type errors to Ok(false)
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_contains_slot {
|
||||
($class:ident :: $f:ident, $arg_type:ty) => {{
|
||||
unsafe extern "C" fn sq_contains(
|
||||
slf: *mut $crate::_detail::ffi::PyObject,
|
||||
arg: *mut $crate::_detail::ffi::PyObject)
|
||||
-> $crate::_detail::libc::c_int
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
|
||||
$crate::_detail::handle_callback(
|
||||
LOCATION, $crate::py_class::slots::BoolConverter,
|
||||
|py| {
|
||||
let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<$class>();
|
||||
let arg = $crate::PyObject::from_borrowed_ptr(py, arg);
|
||||
let ret = match <$arg_type as $crate::FromPyObject>::extract(py, &arg) {
|
||||
Ok(arg) => slf.$f(py, arg),
|
||||
Err(e) => $crate::py_class::slots::type_error_to_false(py, e)
|
||||
};
|
||||
$crate::PyDrop::release_ref(arg, py);
|
||||
$crate::PyDrop::release_ref(slf, py);
|
||||
ret
|
||||
})
|
||||
}
|
||||
Some(sq_contains)
|
||||
}}
|
||||
}
|
||||
|
||||
pub fn type_error_to_false(py: Python, e: PyErr) -> PyResult<bool> {
|
||||
if e.matches(py, py.get_type::<exc::TypeError>()) {
|
||||
Ok(false)
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_binary_numeric_slot {
|
||||
($class:ident :: $f:ident) => {{
|
||||
unsafe extern "C" fn binary_numeric(
|
||||
lhs: *mut $crate::_detail::ffi::PyObject,
|
||||
rhs: *mut $crate::_detail::ffi::PyObject)
|
||||
-> *mut $crate::_detail::ffi::PyObject
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
|
||||
$crate::_detail::handle_callback(
|
||||
LOCATION, $crate::_detail::PyObjectCallbackConverter,
|
||||
|py| {
|
||||
let lhs = $crate::PyObject::from_borrowed_ptr(py, lhs);
|
||||
let rhs = $crate::PyObject::from_borrowed_ptr(py, rhs);
|
||||
let ret = $class::$f(py, &lhs, &rhs);
|
||||
$crate::PyDrop::release_ref(lhs, py);
|
||||
$crate::PyDrop::release_ref(rhs, py);
|
||||
ret
|
||||
})
|
||||
}
|
||||
Some(binary_numeric)
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
pub struct VoidCallbackConverter;
|
||||
|
||||
impl CallbackConverter<()> for VoidCallbackConverter {
|
||||
type R = ();
|
||||
|
||||
#[inline]
|
||||
fn convert(_: (), _: Python) -> () {
|
||||
()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn error_value() -> () {
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct UnitCallbackConverter;
|
||||
|
||||
impl CallbackConverter<()> for UnitCallbackConverter {
|
||||
type R = c_int;
|
||||
|
||||
#[inline]
|
||||
fn convert(_: (), _: Python) -> c_int {
|
||||
0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn error_value() -> c_int {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LenResultConverter;
|
||||
|
||||
impl CallbackConverter<usize> for LenResultConverter {
|
||||
type R = isize;
|
||||
|
||||
fn convert(val: usize, py: Python) -> isize {
|
||||
if val <= (isize::MAX as usize) {
|
||||
val as isize
|
||||
} else {
|
||||
PyErr::new_lazy_init(py.get_type::<exc::OverflowError>(), None).restore(py);
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn error_value() -> isize {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IterNextResultConverter;
|
||||
|
||||
impl <T> CallbackConverter<Option<T>>
|
||||
for IterNextResultConverter
|
||||
where T: ToPyObject
|
||||
{
|
||||
type R = *mut ffi::PyObject;
|
||||
|
||||
fn convert(val: Option<T>, py: Python) -> *mut ffi::PyObject {
|
||||
match val {
|
||||
Some(val) => val.into_py_object(py).into_object().steal_ptr(),
|
||||
None => unsafe {
|
||||
ffi::PyErr_SetNone(ffi::PyExc_StopIteration);
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn error_value() -> *mut ffi::PyObject {
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WrappingCastTo<T> {
|
||||
fn wrapping_cast(self) -> T;
|
||||
}
|
||||
|
||||
macro_rules! wrapping_cast {
|
||||
($from:ty, $to:ty) => {
|
||||
impl WrappingCastTo<$to> for $from {
|
||||
#[inline]
|
||||
fn wrapping_cast(self) -> $to {
|
||||
self as $to
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
wrapping_cast!(u8, Py_hash_t);
|
||||
wrapping_cast!(u16, Py_hash_t);
|
||||
wrapping_cast!(u32, Py_hash_t);
|
||||
wrapping_cast!(usize, Py_hash_t);
|
||||
wrapping_cast!(u64, Py_hash_t);
|
||||
wrapping_cast!(i8, Py_hash_t);
|
||||
wrapping_cast!(i16, Py_hash_t);
|
||||
wrapping_cast!(i32, Py_hash_t);
|
||||
wrapping_cast!(isize, Py_hash_t);
|
||||
wrapping_cast!(i64, Py_hash_t);
|
||||
|
||||
pub struct HashConverter;
|
||||
|
||||
impl <T> CallbackConverter<T> for HashConverter
|
||||
where T: WrappingCastTo<Py_hash_t>
|
||||
{
|
||||
type R = Py_hash_t;
|
||||
|
||||
#[inline]
|
||||
fn convert(val: T, _py: Python) -> Py_hash_t {
|
||||
let hash = val.wrapping_cast();
|
||||
if hash == -1 {
|
||||
-2
|
||||
} else {
|
||||
hash
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn error_value() -> Py_hash_t {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BoolConverter;
|
||||
|
||||
impl CallbackConverter<bool> for BoolConverter {
|
||||
type R = c_int;
|
||||
|
||||
#[inline]
|
||||
fn convert(val: bool, _py: Python) -> c_int {
|
||||
val as c_int
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn error_value() -> c_int {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct SuccessConverter;
|
||||
|
||||
impl CallbackConverter<bool> for SuccessConverter {
|
||||
type R = c_int;
|
||||
|
||||
#[inline]
|
||||
fn convert(val: bool, _py: Python) -> c_int {
|
||||
if val { 0 } else { -1 }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn error_value() -> c_int {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_call_slot {
|
||||
($class:ident :: $f:ident [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ]) => {{
|
||||
unsafe extern "C" fn wrap_call(
|
||||
slf: *mut $crate::_detail::ffi::PyObject,
|
||||
args: *mut $crate::_detail::ffi::PyObject,
|
||||
kwargs: *mut $crate::_detail::ffi::PyObject)
|
||||
-> *mut $crate::_detail::ffi::PyObject
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
|
||||
$crate::_detail::handle_callback(
|
||||
LOCATION, $crate::_detail::PyObjectCallbackConverter,
|
||||
|py| {
|
||||
py_argparse_raw!(py, Some(LOCATION), args, kwargs,
|
||||
[ $( { $pname : $ptype = $detail } )* ]
|
||||
{
|
||||
let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<$class>();
|
||||
let ret = slf.$f(py $(, $pname )* );
|
||||
$crate::PyDrop::release_ref(slf, py);
|
||||
ret
|
||||
})
|
||||
})
|
||||
}
|
||||
Some(wrap_call)
|
||||
}}
|
||||
}
|
||||
|
||||
/// Used as implementation in the `sq_item` slot to forward calls to the `mp_subscript` slot.
|
||||
pub unsafe extern "C" fn sq_item(obj: *mut ffi::PyObject, index: ffi::Py_ssize_t) -> *mut ffi::PyObject {
|
||||
let arg = ffi::PyLong_FromSsize_t(index);
|
||||
if arg.is_null() {
|
||||
return arg;
|
||||
}
|
||||
let ret = ffi::PyObject_GetItem(obj, arg);
|
||||
ffi::Py_DECREF(arg);
|
||||
ret
|
||||
}
|
||||
|
Loading…
Reference in New Issue