convert PyGCProtocol

This commit is contained in:
Nikolay Kim 2017-05-30 12:42:07 -07:00
parent 5e00e13212
commit b84474155e
7 changed files with 116 additions and 134 deletions

View file

@ -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);

View file

@ -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",

View file

@ -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)

View file

@ -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
};
}
}

View file

@ -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>)
}
}

View file

@ -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),

View file

@ -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;