convert PyGCProtocol
This commit is contained in:
parent
5e00e13212
commit
b84474155e
|
@ -42,10 +42,10 @@ fn main() {
|
|||
|
||||
fn hello(py: Python) -> PyResult<()> {
|
||||
let sys = py.import("sys")?;
|
||||
let version: String = sys.get(py, "version")?.extract(py)?;
|
||||
let version: String = sys.get("version")?.extract(py)?;
|
||||
|
||||
let locals = PyDict::new(py);
|
||||
locals.set_item(py, "os", py.import("os")?)?;
|
||||
locals.set_item("os", py.import("os")?)?;
|
||||
let user: String = py.eval("os.getenv('USER') or os.getenv('USERNAME')", None, Some(&locals))?.extract(py)?;
|
||||
|
||||
println!("Hello {}, I'm Python {}", user, version);
|
||||
|
|
|
@ -149,7 +149,18 @@ pub const CONTEXT: Proto = Proto {
|
|||
],
|
||||
};
|
||||
|
||||
|
||||
pub const GC: Proto = Proto {
|
||||
name: "GC",
|
||||
methods: &[
|
||||
MethodProto::Free{
|
||||
name: "__traverse__",
|
||||
proto: "::pyo3::class::gc::PyGCTraverseProtocol"},
|
||||
MethodProto::Free{
|
||||
name: "__clear__",
|
||||
proto: "::pyo3::class::gc::PyGCClearProtocol"},
|
||||
],
|
||||
py_methods: &[],
|
||||
};
|
||||
|
||||
pub const DESCR: Proto = Proto {
|
||||
name: "Descriptor",
|
||||
|
|
|
@ -7,6 +7,7 @@ use utils::print_err;
|
|||
|
||||
#[derive(Debug)]
|
||||
pub enum MethodProto {
|
||||
Free{name: &'static str, proto: &'static str, },
|
||||
Unary{name: &'static str, pyres: bool, proto: &'static str, },
|
||||
Binary{name: &'static str, arg: &'static str, pyres: bool, proto: &'static str},
|
||||
Ternary{name: &'static str,
|
||||
|
@ -23,6 +24,7 @@ impl MethodProto {
|
|||
|
||||
pub fn eq(&self, name: &str) -> bool {
|
||||
match *self {
|
||||
MethodProto::Free{name: n, proto: _} => n == name,
|
||||
MethodProto::Unary{name: n, pyres: _, proto: _} => n == name,
|
||||
MethodProto::Binary{name: n, arg: _, pyres: _, proto: _} => n == name,
|
||||
MethodProto::Ternary{name: n, arg1: _, arg2: _, pyres: _, proto: _} => n == name,
|
||||
|
@ -37,9 +39,20 @@ pub fn impl_method_proto(cls: &Box<syn::Ty>,
|
|||
meth: &MethodProto) -> Tokens {
|
||||
let decl = sig.decl.clone();
|
||||
|
||||
match *meth {
|
||||
MethodProto::Free{name: _, proto} => {
|
||||
let p = syn::Ident::from(proto);
|
||||
return quote! {
|
||||
impl<'p> #p<'p> for #cls {}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
};
|
||||
|
||||
match decl.output {
|
||||
syn::FunctionRetTy::Ty(ref ty) => {
|
||||
match *meth {
|
||||
MethodProto::Free{name: _, proto: _} => unreachable!(),
|
||||
MethodProto::Unary{name: _, pyres, proto} => {
|
||||
let p = syn::Ident::from(proto);
|
||||
let (ty, succ) = get_res_success(ty);
|
||||
|
@ -264,10 +277,8 @@ fn get_res_success(ty: &syn::Ty) -> (Tokens, syn::Ty) {
|
|||
},
|
||||
_ => panic!("fn result type is not supported"),
|
||||
},
|
||||
_ =>
|
||||
panic!("fn result type has to be PyResult or (), got {:?}",
|
||||
segment.ident.as_ref())
|
||||
|
||||
_ => panic!("fn result type has to be PyResult or (), got {:?}",
|
||||
segment.ident.as_ref())
|
||||
}
|
||||
} else {
|
||||
panic!("fn result is not supported {:?}", path)
|
||||
|
|
|
@ -9,15 +9,6 @@ use method::FnSpec;
|
|||
use func::impl_method_proto;
|
||||
|
||||
|
||||
struct Methods {
|
||||
methods: &'static [&'static str],
|
||||
}
|
||||
|
||||
static DEFAULT_METHODS: Methods = Methods {
|
||||
methods: &[],
|
||||
};
|
||||
|
||||
|
||||
pub fn build_py_proto(ast: &mut syn::Item) -> Tokens {
|
||||
match ast.node {
|
||||
syn::ItemKind::Impl(_, _, ref mut gen, ref mut path, ref ty, ref mut impl_items) => {
|
||||
|
@ -43,8 +34,7 @@ pub fn build_py_proto(ast: &mut syn::Item) -> Tokens {
|
|||
"PyBufferProtocol" =>
|
||||
impl_proto_impl(ty, impl_items, &defs::BUFFER),
|
||||
"PyGCProtocol" =>
|
||||
impl_protocol("_pyo3::class::gc::PyGCProtocolImpl",
|
||||
path.clone(), ty, impl_items, &DEFAULT_METHODS),
|
||||
impl_proto_impl(ty, impl_items, &defs::GC),
|
||||
_ => {
|
||||
warn!("#[proto] can not be used with this block");
|
||||
return Tokens::new()
|
||||
|
@ -144,72 +134,3 @@ fn impl_proto_impl(ty: &Box<syn::Ty>, impls: &mut Vec<syn::ImplItem>, proto: &de
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_protocol(name: &'static str,
|
||||
path: syn::Path, ty: &Box<syn::Ty>,
|
||||
impls: &mut Vec<syn::ImplItem>, methods: &Methods) -> Tokens {
|
||||
let mut py_methods = Vec::new();
|
||||
|
||||
// get method names in impl block
|
||||
let mut meth = Vec::new();
|
||||
for iimpl in impls.iter_mut() {
|
||||
match iimpl.node {
|
||||
syn::ImplItemKind::Method(ref mut sig, _) => {
|
||||
if methods.methods.contains(&iimpl.ident.as_ref()) {
|
||||
py_methods.push(py_method::gen_py_method(
|
||||
ty, &iimpl.ident, sig, &mut iimpl.attrs));
|
||||
} else {
|
||||
meth.push(String::from(iimpl.ident.as_ref()));
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
// set trait name
|
||||
let mut path = path;
|
||||
{
|
||||
let mut last = path.segments.last_mut().unwrap();
|
||||
last.ident = syn::Ident::from(name);
|
||||
}
|
||||
|
||||
let i = syn::Ident::from(name);
|
||||
let tokens = if py_methods.is_empty() {
|
||||
quote! {
|
||||
impl #i for #ty {
|
||||
fn methods() -> &'static [&'static str] {
|
||||
static METHODS: &'static [&'static str] = &[#(#meth),*];
|
||||
METHODS
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
impl #i for #ty {
|
||||
fn methods() -> &'static [&'static str] {
|
||||
static METHODS: &'static [&'static str] = &[#(#meth,),*];
|
||||
METHODS
|
||||
}
|
||||
|
||||
fn py_methods() -> &'static [pyo3::class::PyMethodDefType] {
|
||||
static METHODS: &'static [pyo3::class::PyMethodDefType] = &[
|
||||
#(#py_methods),*
|
||||
];
|
||||
METHODS
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
let name = name.split("::").last().unwrap();
|
||||
let dummy_const = syn::Ident::new(format!("_IMPL_PYO3_{}", name));
|
||||
quote! {
|
||||
#[feature(specialization)]
|
||||
#[allow(non_upper_case_globals, unused_attributes,
|
||||
unused_qualifications, unused_variables)]
|
||||
const #dummy_const: () = {
|
||||
extern crate pyo3 as _pyo3;
|
||||
|
||||
#tokens
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
127
src/class/gc.rs
127
src/class/gc.rs
|
@ -10,20 +10,25 @@ use ffi;
|
|||
use pyptr::Py;
|
||||
use python::{Python, ToPythonPointer};
|
||||
use callback::AbortOnDrop;
|
||||
use class::NO_METHODS;
|
||||
use typeob::PyTypeInfo;
|
||||
|
||||
pub struct PyTraverseError(c_int);
|
||||
|
||||
/// GC support
|
||||
#[allow(unused_variables)]
|
||||
pub trait PyGCProtocol<'p> : PyTypeInfo {
|
||||
|
||||
fn __traverse__(&'p self, py: Python<'p>, visit: PyVisit) -> Result<(), PyTraverseError>;
|
||||
fn __traverse__(&'p self, py: Python<'p>, visit: PyVisit)
|
||||
-> Result<(), PyTraverseError> { unimplemented!() }
|
||||
|
||||
fn __clear__(&'p mut self, py: Python<'p>);
|
||||
fn __clear__(&'p mut self, py: Python<'p>) { unimplemented!() }
|
||||
|
||||
}
|
||||
|
||||
pub trait PyGCTraverseProtocol<'p>: PyGCProtocol<'p> {}
|
||||
pub trait PyGCClearProtocol<'p>: PyGCProtocol<'p> {}
|
||||
|
||||
|
||||
impl<'p, T> PyGCProtocol<'p> for T where T: PyTypeInfo {
|
||||
default fn __traverse__(&'p self, _py: Python<'p>, _: PyVisit)
|
||||
-> Result<(), PyTraverseError> {
|
||||
|
@ -34,39 +39,35 @@ impl<'p, T> PyGCProtocol<'p> for T where T: PyTypeInfo {
|
|||
|
||||
#[doc(hidden)]
|
||||
pub trait PyGCProtocolImpl {
|
||||
fn methods() -> &'static [&'static str];
|
||||
|
||||
fn update_type_object(type_object: &mut ffi::PyTypeObject);
|
||||
}
|
||||
|
||||
impl<'p, T> PyGCProtocolImpl for T where T: PyGCProtocol<'p> {
|
||||
default fn methods() -> &'static [&'static str] {
|
||||
NO_METHODS
|
||||
}
|
||||
|
||||
impl<'p, T> PyGCProtocolImpl for T where T: PyGCProtocol<'p>
|
||||
{
|
||||
fn update_type_object(type_object: &mut ffi::PyTypeObject) {
|
||||
if <T as PyGCProtocolImpl>::methods().is_empty() {
|
||||
type_object.tp_flags = ffi::Py_TPFLAGS_DEFAULT
|
||||
} else {
|
||||
type_object.tp_traverse = Self::tp_traverse();
|
||||
type_object.tp_clear = Self::tp_clear();
|
||||
|
||||
if type_object.tp_traverse != None || type_object.tp_clear != None {
|
||||
type_object.tp_flags = ffi::Py_TPFLAGS_DEFAULT | ffi::Py_TPFLAGS_HAVE_GC;
|
||||
type_object.tp_traverse = Some(tp_traverse::<T>);
|
||||
type_object.tp_clear = Some(tp_clear::<T>);
|
||||
} else {
|
||||
type_object.tp_flags = ffi::Py_TPFLAGS_DEFAULT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct PyVisit<'a> {
|
||||
pub struct PyVisit<'p> {
|
||||
visit: ffi::visitproc,
|
||||
arg: *mut c_void,
|
||||
/// VisitProc contains a Python instance to ensure that
|
||||
/// 1) it is cannot be moved out of the traverse() call
|
||||
/// 2) it cannot be sent to other threads
|
||||
_py: Python<'a>
|
||||
_py: Python<'p>
|
||||
}
|
||||
|
||||
impl <'a> PyVisit<'a> {
|
||||
impl <'p> PyVisit<'p> {
|
||||
pub fn call<T>(&self, obj: &T) -> Result<(), PyTraverseError>
|
||||
where T: ToPythonPointer
|
||||
{
|
||||
|
@ -79,36 +80,76 @@ impl <'a> PyVisit<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
trait PyGCTraverseProtocolImpl {
|
||||
fn tp_traverse() -> Option<ffi::traverseproc>;
|
||||
}
|
||||
|
||||
impl<'p, T> PyGCTraverseProtocolImpl for T where T: PyGCProtocol<'p>
|
||||
{
|
||||
#[inline]
|
||||
default fn tp_traverse() -> Option<ffi::traverseproc> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
unsafe extern "C" fn tp_traverse<T>(slf: *mut ffi::PyObject,
|
||||
visit: ffi::visitproc,
|
||||
arg: *mut c_void) -> c_int
|
||||
where T: for<'p> PyGCProtocol<'p>
|
||||
impl<T> PyGCTraverseProtocolImpl for T where T: for<'p> PyGCTraverseProtocol<'p>
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!(T), ".__traverse__()");
|
||||
#[inline]
|
||||
fn tp_traverse() -> Option<ffi::traverseproc> {
|
||||
unsafe extern "C" fn tp_traverse<T>(slf: *mut ffi::PyObject,
|
||||
visit: ffi::visitproc,
|
||||
arg: *mut c_void) -> c_int
|
||||
where T: for<'p> PyGCTraverseProtocol<'p>
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!(T), ".__traverse__()");
|
||||
|
||||
let guard = AbortOnDrop(LOCATION);
|
||||
let py = Python::assume_gil_acquired();
|
||||
let visit = PyVisit { visit: visit, arg: arg, _py: py };
|
||||
let slf: Py<T> = Py::from_borrowed_ptr(py, slf);
|
||||
let guard = AbortOnDrop(LOCATION);
|
||||
let py = Python::assume_gil_acquired();
|
||||
let visit = PyVisit { visit: visit, arg: arg, _py: py };
|
||||
let slf: Py<T> = Py::from_borrowed_ptr(py, slf);
|
||||
|
||||
let ret = match T::__traverse__(&slf, py, visit) {
|
||||
Ok(()) => 0,
|
||||
Err(PyTraverseError(code)) => code
|
||||
};
|
||||
mem::forget(guard);
|
||||
ret
|
||||
let ret = match slf.as_ref().__traverse__(py, visit) {
|
||||
Ok(()) => 0,
|
||||
Err(PyTraverseError(code)) => code
|
||||
};
|
||||
mem::forget(guard);
|
||||
ret
|
||||
}
|
||||
|
||||
Some(tp_traverse::<T>)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn tp_clear<T>(slf: *mut ffi::PyObject) -> c_int
|
||||
where T: for<'p> PyGCProtocol<'p>
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!(T), ".__clear__()");
|
||||
|
||||
let guard = AbortOnDrop(LOCATION);
|
||||
let py = Python::assume_gil_acquired();
|
||||
let slf: Py<T> = Py::from_borrowed_ptr(py, slf);
|
||||
T::__clear__(slf.as_mut(), py);
|
||||
mem::forget(guard);
|
||||
0
|
||||
trait PyGCClearProtocolImpl {
|
||||
fn tp_clear() -> Option<ffi::inquiry>;
|
||||
}
|
||||
|
||||
impl<'p, T> PyGCClearProtocolImpl for T where T: PyGCProtocol<'p>
|
||||
{
|
||||
#[inline]
|
||||
default fn tp_clear() -> Option<ffi::inquiry> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PyGCClearProtocolImpl for T where T: for<'p> PyGCClearProtocol<'p>
|
||||
{
|
||||
#[inline]
|
||||
fn tp_clear() -> Option<ffi::inquiry> {
|
||||
unsafe extern "C" fn tp_clear<T>(slf: *mut ffi::PyObject) -> c_int
|
||||
where T: for<'p> PyGCClearProtocol<'p>
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!(T), ".__clear__()");
|
||||
|
||||
let guard = AbortOnDrop(LOCATION);
|
||||
let py = Python::assume_gil_acquired();
|
||||
let mut slf: Py<T> = Py::from_borrowed_ptr(py, slf);
|
||||
T::__clear__(&mut slf, py);
|
||||
mem::forget(guard);
|
||||
0
|
||||
}
|
||||
Some(tp_clear::<T>)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
use std::ffi::CString;
|
||||
|
||||
use ffi;
|
||||
use class::NO_PY_METHODS;
|
||||
|
||||
static NO_PY_METHODS: &'static [PyMethodDefType] = &[];
|
||||
|
||||
pub enum PyMethodDefType {
|
||||
New(PyMethodDef),
|
||||
|
|
|
@ -28,9 +28,6 @@ pub use self::gc::{PyVisit, PyGCProtocol, PyTraverseError};
|
|||
pub use self::methods::{PyMethodDef, PyMethodDefType, PyMethodType,
|
||||
PyGetterDef, PySetterDef};
|
||||
|
||||
pub static NO_METHODS: &'static [&'static str] = &[];
|
||||
pub static NO_PY_METHODS: &'static [PyMethodDefType] = &[];
|
||||
|
||||
use ffi;
|
||||
use typeob::PyTypeInfo;
|
||||
|
||||
|
|
Loading…
Reference in a new issue