drop py_class
This commit is contained in:
parent
34e4d956f1
commit
c9aefd7e5f
|
@ -7,7 +7,6 @@ readme = "README.md"
|
||||||
keywords = [
|
keywords = [
|
||||||
"pyo3",
|
"pyo3",
|
||||||
"python",
|
"python",
|
||||||
"cpython",
|
|
||||||
]
|
]
|
||||||
homepage = "https://github.com/pyo3/pyo3"
|
homepage = "https://github.com/pyo3/pyo3"
|
||||||
repository = "https://github.com/pyo3/pyo3.git"
|
repository = "https://github.com/pyo3/pyo3.git"
|
||||||
|
|
5
Makefile
5
Makefile
|
@ -23,10 +23,7 @@ CARGO_FLAGS := --features "$(FEATURES)" --no-default-features
|
||||||
|
|
||||||
default: test
|
default: test
|
||||||
|
|
||||||
src/py_class/py_class_impl.rs: src/py_class/py_class_impl.py
|
build:
|
||||||
PY=3 python $< >$@
|
|
||||||
|
|
||||||
build: src/py_class/py_class_impl.rs
|
|
||||||
cargo build $(CARGO_FLAGS)
|
cargo build $(CARGO_FLAGS)
|
||||||
|
|
||||||
test: build
|
test: build
|
||||||
|
|
|
@ -5,16 +5,36 @@ use quote::{Tokens, ToTokens};
|
||||||
|
|
||||||
|
|
||||||
pub fn build_py_class(ast: &mut syn::DeriveInput) -> Tokens {
|
pub fn build_py_class(ast: &mut syn::DeriveInput) -> Tokens {
|
||||||
if let syn::Body::Enum(_) = ast.body {
|
let base = syn::Ident::from("pyo3::PyObject");
|
||||||
panic!("#[py_class] can only be used with structs")
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut tokens = Tokens::new();
|
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_to_py_object(&ast.ident).to_tokens(&mut tokens);
|
||||||
impl_from_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_python_object(&ast.ident).to_tokens(&mut tokens);
|
||||||
impl_checked_downcast(&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));
|
let dummy_const = syn::Ident::new(format!("_IMPL_PYO3_CLS_{}", ast.ident));
|
||||||
quote! {
|
quote! {
|
||||||
|
@ -23,13 +43,140 @@ pub fn build_py_class(ast: &mut syn::DeriveInput) -> Tokens {
|
||||||
const #dummy_const: () = {
|
const #dummy_const: () = {
|
||||||
extern crate pyo3;
|
extern crate pyo3;
|
||||||
use std;
|
use std;
|
||||||
use pyo3::ffi;
|
use pyo3::class::BaseObject;
|
||||||
|
use pyo3::{ffi, Python, PyObject, PyType, PyResult, PyModule};
|
||||||
|
|
||||||
#tokens
|
#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 {
|
fn impl_to_py_object(cls: &syn::Ident) -> Tokens {
|
||||||
quote! {
|
quote! {
|
||||||
/// Identity conversion: allows using existing `PyObject` instances where
|
/// 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!(
|
const LOCATION: &'static str = concat!(
|
||||||
stringify!(#cls), ".", stringify!(#name), "()");
|
stringify!(#cls), ".", stringify!(#name), "()");
|
||||||
pyo3::_detail::handle_callback(
|
pyo3::callback::handle_callback(
|
||||||
LOCATION, pyo3::_detail::PyObjectCallbackConverter, |py|
|
LOCATION, pyo3::callback::PyObjectCallbackConverter, |py|
|
||||||
{
|
{
|
||||||
let args: pyo3::PyTuple =
|
let args: pyo3::PyTuple =
|
||||||
pyo3::PyObject::from_borrowed_ptr(py, args).unchecked_cast_into();
|
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!(
|
const LOCATION: &'static str = concat!(
|
||||||
stringify!(#cls), ".getter_", stringify!(#name), "()");
|
stringify!(#cls), ".getter_", stringify!(#name), "()");
|
||||||
pyo3::_detail::handle_callback(
|
pyo3::callback::handle_callback(
|
||||||
LOCATION, pyo3::_detail::PyObjectCallbackConverter, |py|
|
LOCATION, pyo3::callback::PyObjectCallbackConverter, |py|
|
||||||
{
|
{
|
||||||
let slf = pyo3::PyObject::from_borrowed_ptr(
|
let slf = pyo3::PyObject::from_borrowed_ptr(
|
||||||
py, slf).unchecked_cast_into::<#cls>();
|
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!(
|
const LOCATION: &'static str = concat!(
|
||||||
stringify!(#cls), ".setter", stringify!(#name), "()");
|
stringify!(#cls), ".setter", stringify!(#name), "()");
|
||||||
pyo3::_detail::handle_callback(
|
pyo3::callback::handle_callback(
|
||||||
LOCATION, pyo3::py_class::slots::UnitCallbackConverter, |py|
|
LOCATION, pyo3::callback::UnitCallbackConverter, |py|
|
||||||
{
|
{
|
||||||
let slf = pyo3::PyObject::from_borrowed_ptr(py, slf)
|
let slf = pyo3::PyObject::from_borrowed_ptr(py, slf)
|
||||||
.unchecked_cast_into::<#cls>();
|
.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 err::PyResult;
|
||||||
use python::{Python, PythonObject};
|
use python::{Python, PythonObject};
|
||||||
use objects::PyObject;
|
use objects::PyObject;
|
||||||
use function::PyObjectCallbackConverter;
|
use callback::PyObjectCallbackConverter;
|
||||||
use class::NO_METHODS;
|
use class::NO_METHODS;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,10 +11,9 @@ use ::{CompareOp, Py_hash_t};
|
||||||
use ffi;
|
use ffi;
|
||||||
use err::{PyErr, PyResult};
|
use err::{PyErr, PyResult};
|
||||||
use python::{Python, PythonObject, PyDrop};
|
use python::{Python, PythonObject, PyDrop};
|
||||||
use conversion::ToPyObject;
|
|
||||||
use objects::{exc, PyObject};
|
use objects::{exc, PyObject};
|
||||||
use py_class::slots::{HashConverter, UnitCallbackConverter};
|
use conversion::ToPyObject;
|
||||||
use function::{handle_callback, PyObjectCallbackConverter};
|
use callback::{handle_callback, PyObjectCallbackConverter, HashConverter, UnitCallbackConverter};
|
||||||
use class::{NO_METHODS, NO_PY_METHODS};
|
use class::{NO_METHODS, NO_PY_METHODS};
|
||||||
|
|
||||||
// __new__
|
// __new__
|
||||||
|
|
|
@ -11,8 +11,7 @@ use ffi;
|
||||||
use err::PyResult;
|
use err::PyResult;
|
||||||
use python::{Python, PythonObject};
|
use python::{Python, PythonObject};
|
||||||
use objects::PyObject;
|
use objects::PyObject;
|
||||||
use py_class::slots::UnitCallbackConverter;
|
use callback::{handle_callback, UnitCallbackConverter};
|
||||||
use function::handle_callback;
|
|
||||||
use class::NO_METHODS;
|
use class::NO_METHODS;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,7 @@ use err::{PyErr, PyResult};
|
||||||
use python::{Python, PythonObject};
|
use python::{Python, PythonObject};
|
||||||
use objects::{exc, PyObject};
|
use objects::{exc, PyObject};
|
||||||
use class::{NO_METHODS, NO_PY_METHODS};
|
use class::{NO_METHODS, NO_PY_METHODS};
|
||||||
use function::PyObjectCallbackConverter;
|
use callback::{PyObjectCallbackConverter, UnitCallbackConverter};
|
||||||
use py_class::slots::UnitCallbackConverter;
|
|
||||||
|
|
||||||
/// Descriptor interface
|
/// Descriptor interface
|
||||||
pub trait PyDescrProtocol {
|
pub trait PyDescrProtocol {
|
||||||
|
|
|
@ -9,7 +9,7 @@ use std::os::raw::{c_int, c_void};
|
||||||
use ffi;
|
use ffi;
|
||||||
use python::{Python, PythonObject, PyDrop, ToPythonPointer};
|
use python::{Python, PythonObject, PyDrop, ToPythonPointer};
|
||||||
use objects::PyObject;
|
use objects::PyObject;
|
||||||
use function::AbortOnDrop;
|
use callback::AbortOnDrop;
|
||||||
use class::NO_METHODS;
|
use class::NO_METHODS;
|
||||||
|
|
||||||
pub struct PyTraverseError(c_int);
|
pub struct PyTraverseError(c_int);
|
||||||
|
|
|
@ -11,7 +11,7 @@ macro_rules! py_unary_func {
|
||||||
where T: $trait + PythonObject
|
where T: $trait + PythonObject
|
||||||
{
|
{
|
||||||
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
|
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 slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<T>();
|
||||||
let ret = slf.$f(py);
|
let ret = slf.$f(py);
|
||||||
$crate::PyDrop::release_ref(slf, py);
|
$crate::PyDrop::release_ref(slf, py);
|
||||||
|
@ -31,7 +31,7 @@ macro_rules! py_unary_func {
|
||||||
where T: $trait + PythonObject
|
where T: $trait + PythonObject
|
||||||
{
|
{
|
||||||
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
|
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 slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<T>();
|
||||||
let ret = slf.$f(py);
|
let ret = slf.$f(py);
|
||||||
$crate::PyDrop::release_ref(slf, py);
|
$crate::PyDrop::release_ref(slf, py);
|
||||||
|
@ -52,7 +52,7 @@ macro_rules! py_binary_func {
|
||||||
where T: $trait + PythonObject
|
where T: $trait + PythonObject
|
||||||
{
|
{
|
||||||
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
|
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 slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<T>();
|
||||||
let arg = $crate::PyObject::from_borrowed_ptr(py, arg);
|
let arg = $crate::PyObject::from_borrowed_ptr(py, arg);
|
||||||
let ret = slf.$f(py, &arg);
|
let ret = slf.$f(py, &arg);
|
||||||
|
@ -78,7 +78,7 @@ macro_rules! py_ternary_func {
|
||||||
where T: $trait + PythonObject
|
where T: $trait + PythonObject
|
||||||
{
|
{
|
||||||
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
|
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 slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<T>();
|
||||||
let arg1 = $crate::PyObject::from_borrowed_ptr(py, arg1);
|
let arg1 = $crate::PyObject::from_borrowed_ptr(py, arg1);
|
||||||
let arg2 = $crate::PyObject::from_borrowed_ptr(py, arg2);
|
let arg2 = $crate::PyObject::from_borrowed_ptr(py, arg2);
|
||||||
|
@ -102,7 +102,7 @@ macro_rules! py_ssizearg_func {
|
||||||
where T: $trait + PythonObject
|
where T: $trait + PythonObject
|
||||||
{
|
{
|
||||||
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
|
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 slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<T>();
|
||||||
let ret = slf.$f(py, arg as isize);
|
let ret = slf.$f(py, arg as isize);
|
||||||
$crate::PyDrop::release_ref(slf, py);
|
$crate::PyDrop::release_ref(slf, py);
|
||||||
|
@ -123,7 +123,7 @@ macro_rules! py_objobj_proc {
|
||||||
where T: $trait + PythonObject
|
where T: $trait + PythonObject
|
||||||
{
|
{
|
||||||
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
|
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 slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<T>();
|
||||||
let arg = PyObject::from_borrowed_ptr(py, arg);
|
let arg = PyObject::from_borrowed_ptr(py, arg);
|
||||||
let ret = slf.$f(py, &arg);
|
let ret = slf.$f(py, &arg);
|
||||||
|
@ -148,7 +148,7 @@ macro_rules! py_ternary_slot {
|
||||||
where T: $trait + PythonObject
|
where T: $trait + PythonObject
|
||||||
{
|
{
|
||||||
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
|
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
|
||||||
$crate::_detail::handle_callback(
|
$crate::callback::handle_callback(
|
||||||
LOCATION, $conv, |py|
|
LOCATION, $conv, |py|
|
||||||
{
|
{
|
||||||
let slf = $crate::PyObject::from_borrowed_ptr(
|
let slf = $crate::PyObject::from_borrowed_ptr(
|
||||||
|
|
|
@ -9,8 +9,8 @@ use ffi;
|
||||||
use err::{PyErr, PyResult};
|
use err::{PyErr, PyResult};
|
||||||
use python::{Python, PythonObject, PyDrop};
|
use python::{Python, PythonObject, PyDrop};
|
||||||
use objects::{exc, PyObject};
|
use objects::{exc, PyObject};
|
||||||
use py_class::slots::{LenResultConverter, UnitCallbackConverter};
|
use callback::{handle_callback, PyObjectCallbackConverter,
|
||||||
use function::{handle_callback, PyObjectCallbackConverter};
|
LenResultConverter, UnitCallbackConverter};
|
||||||
use class::NO_METHODS;
|
use class::NO_METHODS;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||||
|
|
||||||
|
use std::mem;
|
||||||
|
use std::os::raw::c_void;
|
||||||
|
|
||||||
#[macro_use] mod macros;
|
#[macro_use] mod macros;
|
||||||
|
|
||||||
pub mod async;
|
pub mod async;
|
||||||
|
@ -23,9 +26,77 @@ pub use self::number::PyNumberProtocol;
|
||||||
pub use self::mapping::PyMappingProtocol;
|
pub use self::mapping::PyMappingProtocol;
|
||||||
pub use self::sequence::PySequenceProtocol;
|
pub use self::sequence::PySequenceProtocol;
|
||||||
|
|
||||||
|
pub use self::typeob::PyTypeObject;
|
||||||
pub use self::gc::{PyVisit, PyGCProtocol, PyTraverseError};
|
pub use self::gc::{PyVisit, PyGCProtocol, PyTraverseError};
|
||||||
pub use self::methods::{PyMethodDef, PyMethodDefType, PyMethodType,
|
pub use self::methods::{PyMethodDef, PyMethodDefType, PyMethodType,
|
||||||
PyGetterDef, PySetterDef};
|
PyGetterDef, PySetterDef};
|
||||||
|
|
||||||
pub static NO_METHODS: &'static [&'static str] = &[];
|
pub static NO_METHODS: &'static [&'static str] = &[];
|
||||||
pub static NO_PY_METHODS: &'static [PyMethodDefType] = &[];
|
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 err::PyResult;
|
||||||
use python::{Python, PythonObject};
|
use python::{Python, PythonObject};
|
||||||
use objects::PyObject;
|
use objects::PyObject;
|
||||||
use function::PyObjectCallbackConverter;
|
use callback::{BoolConverter, PyObjectCallbackConverter};
|
||||||
use py_class::slots::BoolConverter;
|
|
||||||
use class::{NO_METHODS, NO_PY_METHODS};
|
use class::{NO_METHODS, NO_PY_METHODS};
|
||||||
use class::basic::{PyObjectProtocol, PyObjectProtocolImpl};
|
use class::basic::{PyObjectProtocol, PyObjectProtocolImpl};
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,8 @@ use ffi;
|
||||||
use err::{PyErr, PyResult};
|
use err::{PyErr, PyResult};
|
||||||
use python::{Python, PythonObject, PyDrop};
|
use python::{Python, PythonObject, PyDrop};
|
||||||
use objects::{exc, PyObject};
|
use objects::{exc, PyObject};
|
||||||
use py_class::slots::{LenResultConverter, UnitCallbackConverter, BoolConverter};
|
use callback::{handle_callback, PyObjectCallbackConverter,
|
||||||
use function::{handle_callback, PyObjectCallbackConverter};
|
LenResultConverter, UnitCallbackConverter, BoolConverter};
|
||||||
use class::NO_METHODS;
|
use class::NO_METHODS;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,144 +4,130 @@ use std::mem;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use ::{ffi, class, py_class, PyErr, Python, PyResult, PythonObject};
|
use ::{ffi, class, PyErr, Python, PyResult, PythonObject};
|
||||||
use objects::PyType;
|
use objects::{PyType, PyModule};
|
||||||
use function::AbortOnDrop;
|
use callback::AbortOnDrop;
|
||||||
use class::PyMethodDefType;
|
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,
|
unsafe fn initialized(py: Python, module_name: Option<&str>) -> PyType;
|
||||||
module_name: Option<&str>,
|
|
||||||
type_object: &mut ffi::PyTypeObject) -> PyResult<PyType>;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> PyClassInit for T
|
pub fn initialize_type<T>(py: Python, module_name: Option<&str>,
|
||||||
where T: PythonObject + py_class::BaseObject {
|
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 {
|
// dealloc
|
||||||
static mut TYPE_OBJECT: ffi::PyTypeObject = ffi::PyTypeObject_INIT;
|
type_object.tp_dealloc = Some(tp_dealloc_callback::<T>);
|
||||||
unsafe {
|
|
||||||
&mut TYPE_OBJECT
|
// 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>,
|
// mapping methods
|
||||||
type_object: &mut ffi::PyTypeObject) -> PyResult<PyType> {
|
if let Some(meth) = ffi::PyMappingMethods::new::<T>() {
|
||||||
// type name
|
static mut MP_METHODS: ffi::PyMappingMethods = ffi::PyMappingMethods_INIT;
|
||||||
let name = match module_name {
|
*(unsafe { &mut MP_METHODS }) = meth;
|
||||||
Some(module_name) => CString::new(
|
type_object.tp_as_mapping = unsafe { &mut MP_METHODS };
|
||||||
format!("{}.{}", module_name, stringify!(type_name))),
|
} else {
|
||||||
None => CString::new(stringify!(type_name))
|
type_object.tp_as_mapping = 0 as *mut ffi::PyMappingMethods;
|
||||||
};
|
}
|
||||||
let name = name.expect(
|
|
||||||
"Module name/type name must not contain NUL byte").into_raw();
|
|
||||||
|
|
||||||
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
|
// async methods
|
||||||
type_object.tp_dealloc = Some(tp_dealloc_callback::<T>);
|
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
|
// buffer protocol
|
||||||
<T as class::gc::PyGCProtocolImpl>::update_type_object(type_object);
|
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
|
// normal methods
|
||||||
type_object.tp_basicsize = <T as py_class::BaseObject>::size() as ffi::Py_ssize_t;
|
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
|
static mut METHODS: *const ffi::PyMethodDef = 0 as *const _;
|
||||||
type_object.tp_descr_get = class::descr::get_descrfunc::<T>();
|
*(unsafe { &mut METHODS }) = methods.as_ptr();
|
||||||
type_object.tp_descr_set = class::descr::set_descrfunc::<T>();
|
}
|
||||||
|
|
||||||
// number methods
|
// properties
|
||||||
if let Some(meth) = ffi::PyNumberMethods::new::<T>() {
|
let mut props = py_class_properties::<T>();
|
||||||
static mut NB_METHODS: ffi::PyNumberMethods = ffi::PyNumberMethods_INIT;
|
if !props.is_empty() {
|
||||||
*(unsafe { &mut NB_METHODS }) = meth;
|
props.push(ffi::PyGetSetDef_INIT);
|
||||||
type_object.tp_as_number = unsafe { &mut NB_METHODS };
|
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 {
|
} else {
|
||||||
type_object.tp_as_number = 0 as *mut ffi::PyNumberMethods;
|
Err(PyErr::fetch(py))
|
||||||
}
|
|
||||||
|
|
||||||
// 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))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe extern "C" fn tp_dealloc_callback<T>(obj: *mut ffi::PyObject)
|
unsafe extern "C" fn tp_dealloc_callback<T>(obj: *mut ffi::PyObject) where T: BaseObject
|
||||||
where T: py_class::BaseObject
|
|
||||||
{
|
{
|
||||||
let guard = AbortOnDrop("Cannot unwind out of tp_dealloc");
|
let guard = AbortOnDrop("Cannot unwind out of tp_dealloc");
|
||||||
let py = Python::assume_gil_acquired();
|
let py = Python::assume_gil_acquired();
|
||||||
|
|
|
@ -100,7 +100,7 @@ macro_rules! py_exception {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn type_object(py: $crate::Python) -> $crate::PyType {
|
fn type_object(py: $crate::Python) -> $crate::PyType {
|
||||||
unsafe {
|
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() {
|
if type_object.is_null() {
|
||||||
type_object = $crate::PyErr::new_type(
|
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
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
// DEALINGS IN THE SOFTWARE.
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
use libc;
|
use std::ptr;
|
||||||
use std::{mem, ptr, io, any, marker};
|
use python::Python;
|
||||||
use std::panic;
|
|
||||||
use python::{Python, PythonObject};
|
|
||||||
use objects::PyObject;
|
use objects::PyObject;
|
||||||
use conversion::ToPyObject;
|
|
||||||
use ffi;
|
use ffi;
|
||||||
use err::{self, PyResult};
|
use err;
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
macro_rules! py_method_def {
|
macro_rules! py_method_def {
|
||||||
($name: expr, $flags: expr, $wrap: expr) => {{
|
($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: 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_meth: None,
|
||||||
ml_flags: $crate::_detail::ffi::METH_VARARGS | $crate::_detail::ffi::METH_KEYWORDS | $flags,
|
ml_flags: $crate::ffi::METH_VARARGS | $crate:::METH_KEYWORDS | $flags,
|
||||||
ml_doc: 0 as *const $crate::_detail::libc::c_char
|
ml_doc: 0 as *const $crate::c_char
|
||||||
};
|
};
|
||||||
METHOD_DEF.ml_name = concat!($name, "\0").as_ptr() as *const _;
|
METHOD_DEF.ml_name = concat!($name, "\0").as_ptr() as *const _;
|
||||||
METHOD_DEF.ml_meth = Some(
|
METHOD_DEF.ml_meth = Some(
|
||||||
::std::mem::transmute::<$crate::_detail::ffi::PyCFunctionWithKeywords,
|
::std::mem::transmute::<$crate::ffi::PyCFunctionWithKeywords,
|
||||||
$crate::_detail::ffi::PyCFunction>($wrap)
|
$crate::ffi::PyCFunction>($wrap)
|
||||||
);
|
);
|
||||||
&mut METHOD_DEF
|
&mut METHOD_DEF
|
||||||
}}
|
}}
|
||||||
|
@ -112,13 +109,13 @@ macro_rules! py_fn_impl {
|
||||||
// Form 1: reference existing function
|
// Form 1: reference existing function
|
||||||
{ $py:expr, $f:ident [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ] } => {{
|
{ $py:expr, $f:ident [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ] } => {{
|
||||||
unsafe extern "C" fn wrap(
|
unsafe extern "C" fn wrap(
|
||||||
_slf: *mut $crate::_detail::ffi::PyObject,
|
_slf: *mut $crate::ffi::PyObject,
|
||||||
args: *mut $crate::_detail::ffi::PyObject,
|
args: *mut $crate::ffi::PyObject,
|
||||||
kwargs: *mut $crate::_detail::ffi::PyObject)
|
kwargs: *mut $crate::ffi::PyObject)
|
||||||
-> *mut $crate::_detail::ffi::PyObject
|
-> *mut $crate::ffi::PyObject
|
||||||
{
|
{
|
||||||
$crate::_detail::handle_callback(
|
$crate::callback::handle_callback(
|
||||||
stringify!($f), $crate::_detail::PyObjectCallbackConverter,
|
stringify!($f), $crate::callback::PyObjectCallbackConverter,
|
||||||
|py| {
|
|py| {
|
||||||
py_argparse_raw!(py, Some(stringify!($f)), args, kwargs,
|
py_argparse_raw!(py, Some(stringify!($f)), args, kwargs,
|
||||||
[ $( { $pname : $ptype = $detail } )* ]
|
[ $( { $pname : $ptype = $detail } )* ]
|
||||||
|
@ -128,7 +125,7 @@ macro_rules! py_fn_impl {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
unsafe {
|
unsafe {
|
||||||
$crate::_detail::py_fn_impl($py,
|
$crate::function::py_fn_impl($py,
|
||||||
py_method_def!(stringify!($f), 0, wrap))
|
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 {
|
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()))
|
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 python::{Python, PythonObject, PythonObjectWithCheckedDowncast, PythonObjectDowncastError, PythonObjectWithTypeObject, PyClone, PyDrop};
|
||||||
pub use pythonrun::{GILGuard, GILProtected, prepare_freethreaded_python};
|
pub use pythonrun::{GILGuard, GILProtected, prepare_freethreaded_python};
|
||||||
pub use conversion::{FromPyObject, RefFromPyObject, ToPyObject, ToPyTuple};
|
pub use conversion::{FromPyObject, RefFromPyObject, ToPyObject, ToPyTuple};
|
||||||
pub use py_class::{CompareOp};
|
pub use class::{CompareOp};
|
||||||
pub use objectprotocol::{ObjectProtocol};
|
pub use objectprotocol::{ObjectProtocol};
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
|
@ -142,7 +142,7 @@ macro_rules! py_impl_to_py_object_for_python_object {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_borrowed_ptr<F, R>(&self, _py: $crate::Python, f: F) -> R
|
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())
|
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 python;
|
||||||
mod err;
|
mod err;
|
||||||
mod conversion;
|
mod conversion;
|
||||||
|
@ -182,24 +181,11 @@ mod function;
|
||||||
pub mod buffer;
|
pub mod buffer;
|
||||||
pub mod class;
|
pub mod class;
|
||||||
pub use class::*;
|
pub use class::*;
|
||||||
|
pub mod callback;
|
||||||
|
|
||||||
// re-export for simplicity
|
// re-export for simplicity
|
||||||
pub use std::os::raw::*;
|
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
|
/// Expands to an `extern "C"` function that allows Python to load
|
||||||
/// the rust code as a Python extension module.
|
/// 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<()> {
|
fn init($py_id: $crate::Python, $m_id: &$crate::PyModule) -> $crate::PyResult<()> {
|
||||||
$body
|
$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,
|
// We can't convert &'static str to *const c_char within a static initializer,
|
||||||
// so we'll do it here in the module initialization:
|
// so we'll do it here in the module initialization:
|
||||||
MODULE_DEF.m_name = concat!(stringify!($name), "\0").as_ptr() as *const _;
|
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,
|
def: *mut ffi::PyModuleDef,
|
||||||
init: fn(Python, &PyModule) -> PyResult<()>) -> *mut ffi::PyObject
|
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();
|
let py = Python::assume_gil_acquired();
|
||||||
ffi::PyEval_InitThreads();
|
ffi::PyEval_InitThreads();
|
||||||
let module = ffi::PyModule_Create(def);
|
let module = ffi::PyModule_Create(def);
|
||||||
|
|
|
@ -23,7 +23,7 @@ use python::{Python, PythonObject};
|
||||||
use objectprotocol::ObjectProtocol;
|
use objectprotocol::ObjectProtocol;
|
||||||
use conversion::{ToPyObject, ToPyTuple};
|
use conversion::{ToPyObject, ToPyTuple};
|
||||||
use objects::{PyObject, PyDict, exc};
|
use objects::{PyObject, PyDict, exc};
|
||||||
use py_class::PythonObjectFromPyClassMacro;
|
use class::PyTypeObject;
|
||||||
use err::{self, PyResult, PyErr};
|
use err::{self, PyResult, PyErr};
|
||||||
use std::ffi::{CStr, CString};
|
use std::ffi::{CStr, CString};
|
||||||
|
|
||||||
|
@ -107,11 +107,11 @@ impl PyModule {
|
||||||
|
|
||||||
/// Adds a new extension type to the module.
|
/// 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,
|
/// sets `new_type.__module__` to this module's name,
|
||||||
/// and adds the type to this module.
|
/// and adds the type to this module.
|
||||||
pub fn add_class<'p, T>(&self, py: Python<'p>) -> PyResult<()>
|
pub fn add_class<'p, T>(&self, py: Python<'p>) -> PyResult<()>
|
||||||
where T: PythonObjectFromPyClassMacro
|
where T: PyTypeObject
|
||||||
{
|
{
|
||||||
T::add_to_module(py, self)
|
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