Merge branch 'master' into gc-default
This commit is contained in:
commit
cc469afc38
|
@ -16,7 +16,8 @@ rm -f target/debug/test_doc-*
|
||||||
|
|
||||||
# Note: On travis this is run with -P1 because it started failing with
|
# Note: On travis this is run with -P1 because it started failing with
|
||||||
# `-P $(nproc)`. kcov can probably be run in parallel if used with different CI
|
# `-P $(nproc)`. kcov can probably be run in parallel if used with different CI
|
||||||
FILES=$(find . -path ./target/debug/pyo3\* -or -path ./target/debug/test_\*)
|
# Exclude test_compile_error.rs (See https://github.com/PyO3/pyo3/pull/503)
|
||||||
|
FILES=$(find . -path ./target/debug/pyo3\* -or -path ./target/debug/test_\* -not -name '*test_compile_error*')
|
||||||
echo $FILES | xargs -n1 -P1 sh -c '
|
echo $FILES | xargs -n1 -P1 sh -c '
|
||||||
dir="target/cov/$(basename $@)"
|
dir="target/cov/$(basename $@)"
|
||||||
mkdir -p $dir
|
mkdir -p $dir
|
||||||
|
|
|
@ -85,16 +85,7 @@ impl<'a> FnSpec<'a> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let py = match ty {
|
let py = crate::utils::if_type_is_python(ty);
|
||||||
syn::Type::Path(syn::TypePath { ref path, .. }) => {
|
|
||||||
if let Some(segment) = path.segments.last() {
|
|
||||||
segment.value().ident == "Python"
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
let opt = check_arg_ty_and_optional(name, ty);
|
let opt = check_arg_ty_and_optional(name, ty);
|
||||||
arguments.push(FnArg {
|
arguments.push(FnArg {
|
||||||
|
|
|
@ -64,16 +64,7 @@ fn wrap_fn_argument<'a>(input: &'a syn::FnArg, name: &'a Ident) -> Option<method
|
||||||
_ => panic!("unsupported argument: {:?}", cap.pat),
|
_ => panic!("unsupported argument: {:?}", cap.pat),
|
||||||
};
|
};
|
||||||
|
|
||||||
let py = match cap.ty {
|
let py = crate::utils::if_type_is_python(&cap.ty);
|
||||||
syn::Type::Path(ref typath) => typath
|
|
||||||
.path
|
|
||||||
.segments
|
|
||||||
.last()
|
|
||||||
.map(|seg| seg.value().ident == "Python")
|
|
||||||
.unwrap_or(false),
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
let opt = method::check_arg_ty_and_optional(&name, &cap.ty);
|
let opt = method::check_arg_ty_and_optional(&name, &cap.ty);
|
||||||
Some(method::FnArg {
|
Some(method::FnArg {
|
||||||
name: ident,
|
name: ident,
|
||||||
|
@ -111,7 +102,7 @@ fn extract_pyfn_attrs(
|
||||||
}
|
}
|
||||||
_ => panic!("The first parameter of pyfn must be a MetaItem"),
|
_ => panic!("The first parameter of pyfn must be a MetaItem"),
|
||||||
}
|
}
|
||||||
// read Python fonction name
|
// read Python function name
|
||||||
match meta[1] {
|
match meta[1] {
|
||||||
syn::NestedMeta::Literal(syn::Lit::Str(ref lits)) => {
|
syn::NestedMeta::Literal(syn::Lit::Str(ref lits)) => {
|
||||||
fnname = Some(syn::Ident::new(&lits.value(), lits.span()));
|
fnname = Some(syn::Ident::new(&lits.value(), lits.span()));
|
||||||
|
|
|
@ -417,9 +417,12 @@ fn impl_descriptors(cls: &syn::Type, descriptors: Vec<(syn::Field, Vec<FnType>)>
|
||||||
|
|
||||||
let field_ty = &field.ty;
|
let field_ty = &field.ty;
|
||||||
match *desc {
|
match *desc {
|
||||||
FnType::Getter(ref getter) => {
|
FnType::Getter(ref getter) => impl_py_getter_def(
|
||||||
impl_py_getter_def(&name, doc, getter, &impl_wrap_getter(&cls, &name))
|
&name,
|
||||||
}
|
doc,
|
||||||
|
getter,
|
||||||
|
&impl_wrap_getter(&cls, &name, false),
|
||||||
|
),
|
||||||
FnType::Setter(ref setter) => {
|
FnType::Setter(ref setter) => {
|
||||||
let setter_name =
|
let setter_name =
|
||||||
syn::Ident::new(&format!("set_{}", name), Span::call_site());
|
syn::Ident::new(&format!("set_{}", name), Span::call_site());
|
||||||
|
|
|
@ -16,11 +16,11 @@ pub fn build_py_methods(ast: &mut syn::ItemImpl) -> syn::Result<TokenStream> {
|
||||||
"#[pymethods] can not be used with lifetime parameters or generics",
|
"#[pymethods] can not be used with lifetime parameters or generics",
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Ok(impl_methods(&ast.self_ty, &mut ast.items))
|
impl_methods(&ast.self_ty, &mut ast.items)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn impl_methods(ty: &syn::Type, impls: &mut Vec<syn::ImplItem>) -> TokenStream {
|
pub fn impl_methods(ty: &syn::Type, impls: &mut Vec<syn::ImplItem>) -> syn::Result<TokenStream> {
|
||||||
// get method names in impl block
|
// get method names in impl block
|
||||||
let mut methods = Vec::new();
|
let mut methods = Vec::new();
|
||||||
for iimpl in impls.iter_mut() {
|
for iimpl in impls.iter_mut() {
|
||||||
|
@ -31,16 +31,16 @@ pub fn impl_methods(ty: &syn::Type, impls: &mut Vec<syn::ImplItem>) -> TokenStre
|
||||||
&name,
|
&name,
|
||||||
&mut meth.sig,
|
&mut meth.sig,
|
||||||
&mut meth.attrs,
|
&mut meth.attrs,
|
||||||
));
|
)?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
quote! {
|
Ok(quote! {
|
||||||
pyo3::inventory::submit! {
|
pyo3::inventory::submit! {
|
||||||
#![crate = pyo3] {
|
#![crate = pyo3] {
|
||||||
type TyInventory = <#ty as pyo3::class::methods::PyMethodsInventoryDispatch>::InventoryType;
|
type TyInventory = <#ty as pyo3::class::methods::PyMethodsInventoryDispatch>::InventoryType;
|
||||||
<TyInventory as pyo3::class::methods::PyMethodsInventory>::new(&[#(#methods),*])
|
<TyInventory as pyo3::class::methods::PyMethodsInventory>::new(&[#(#methods),*])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,13 +9,13 @@ pub fn gen_py_method(
|
||||||
name: &syn::Ident,
|
name: &syn::Ident,
|
||||||
sig: &mut syn::MethodSig,
|
sig: &mut syn::MethodSig,
|
||||||
meth_attrs: &mut Vec<syn::Attribute>,
|
meth_attrs: &mut Vec<syn::Attribute>,
|
||||||
) -> TokenStream {
|
) -> syn::Result<TokenStream> {
|
||||||
check_generic(name, sig);
|
check_generic(name, sig)?;
|
||||||
|
|
||||||
let doc = utils::get_doc(&meth_attrs, true);
|
let doc = utils::get_doc(&meth_attrs, true);
|
||||||
let spec = FnSpec::parse(name, sig, meth_attrs).unwrap();
|
let spec = FnSpec::parse(name, sig, meth_attrs)?;
|
||||||
|
|
||||||
match spec.tp {
|
Ok(match spec.tp {
|
||||||
FnType::Fn => impl_py_method_def(name, doc, &spec, &impl_wrap(cls, name, &spec, true)),
|
FnType::Fn => impl_py_method_def(name, doc, &spec, &impl_wrap(cls, name, &spec, true)),
|
||||||
FnType::PySelf(ref self_ty) => impl_py_method_def(
|
FnType::PySelf(ref self_ty) => impl_py_method_def(
|
||||||
name,
|
name,
|
||||||
|
@ -31,28 +31,43 @@ pub fn gen_py_method(
|
||||||
impl_py_method_def_static(name, doc, &impl_wrap_static(cls, name, &spec))
|
impl_py_method_def_static(name, doc, &impl_wrap_static(cls, name, &spec))
|
||||||
}
|
}
|
||||||
FnType::Getter(ref getter) => {
|
FnType::Getter(ref getter) => {
|
||||||
impl_py_getter_def(name, doc, getter, &impl_wrap_getter(cls, name))
|
let takes_py = match &*spec.args {
|
||||||
|
[] => false,
|
||||||
|
[arg] if utils::if_type_is_python(arg.ty) => true,
|
||||||
|
_ => {
|
||||||
|
return Err(syn::Error::new_spanned(
|
||||||
|
spec.args[0].ty,
|
||||||
|
"Getter function can only have one argument of type pyo3::Python!",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
impl_py_getter_def(name, doc, getter, &impl_wrap_getter(cls, name, takes_py))
|
||||||
}
|
}
|
||||||
FnType::Setter(ref setter) => {
|
FnType::Setter(ref setter) => {
|
||||||
impl_py_setter_def(name, doc, setter, &impl_wrap_setter(cls, name, &spec))
|
impl_py_setter_def(name, doc, setter, &impl_wrap_setter(cls, name, &spec))
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_generic(name: &syn::Ident, sig: &syn::MethodSig) {
|
fn check_generic(name: &syn::Ident, sig: &syn::MethodSig) -> syn::Result<()> {
|
||||||
|
let err_msg = |typ| {
|
||||||
|
format!(
|
||||||
|
"A Python method can't have a generic {} parameter: {}",
|
||||||
|
name, typ
|
||||||
|
)
|
||||||
|
};
|
||||||
for param in &sig.decl.generics.params {
|
for param in &sig.decl.generics.params {
|
||||||
match param {
|
match param {
|
||||||
syn::GenericParam::Lifetime(_) => {}
|
syn::GenericParam::Lifetime(_) => {}
|
||||||
syn::GenericParam::Type(_) => panic!(
|
syn::GenericParam::Type(_) => {
|
||||||
"A Python method can't have a generic type parameter: {}",
|
return Err(syn::Error::new_spanned(param, err_msg("type")))
|
||||||
name
|
}
|
||||||
),
|
syn::GenericParam::Const(_) => {
|
||||||
syn::GenericParam::Const(_) => panic!(
|
return Err(syn::Error::new_spanned(param, err_msg("const")))
|
||||||
"A Python method can't have a const generic parameter: {}",
|
}
|
||||||
name
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate function wrapper (PyCFunction, PyCFunctionWithKeywords)
|
/// Generate function wrapper (PyCFunction, PyCFunctionWithKeywords)
|
||||||
|
@ -302,7 +317,12 @@ pub fn impl_wrap_static(cls: &syn::Type, name: &syn::Ident, spec: &FnSpec<'_>) -
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate functiona wrapper (PyCFunction, PyCFunctionWithKeywords)
|
/// Generate functiona wrapper (PyCFunction, PyCFunctionWithKeywords)
|
||||||
pub(crate) fn impl_wrap_getter(cls: &syn::Type, name: &syn::Ident) -> TokenStream {
|
pub(crate) fn impl_wrap_getter(cls: &syn::Type, name: &syn::Ident, takes_py: bool) -> TokenStream {
|
||||||
|
let fncall = if takes_py {
|
||||||
|
quote! { _slf.#name(_py) }
|
||||||
|
} else {
|
||||||
|
quote! { _slf.#name() }
|
||||||
|
};
|
||||||
quote! {
|
quote! {
|
||||||
unsafe extern "C" fn __wrap(
|
unsafe extern "C" fn __wrap(
|
||||||
_slf: *mut pyo3::ffi::PyObject, _: *mut ::std::os::raw::c_void) -> *mut pyo3::ffi::PyObject
|
_slf: *mut pyo3::ffi::PyObject, _: *mut ::std::os::raw::c_void) -> *mut pyo3::ffi::PyObject
|
||||||
|
@ -313,7 +333,7 @@ pub(crate) fn impl_wrap_getter(cls: &syn::Type, name: &syn::Ident) -> TokenStrea
|
||||||
let _py = pyo3::Python::assume_gil_acquired();
|
let _py = pyo3::Python::assume_gil_acquired();
|
||||||
let _slf = _py.mut_from_borrowed_ptr::<#cls>(_slf);
|
let _slf = _py.mut_from_borrowed_ptr::<#cls>(_slf);
|
||||||
|
|
||||||
let result = pyo3::derive_utils::IntoPyResult::into_py_result(_slf.#name());
|
let result = pyo3::derive_utils::IntoPyResult::into_py_result(#fncall);
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(val) => {
|
Ok(val) => {
|
||||||
|
|
|
@ -2,12 +2,24 @@
|
||||||
|
|
||||||
use proc_macro2::Span;
|
use proc_macro2::Span;
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use syn;
|
|
||||||
|
|
||||||
pub fn print_err(msg: String, t: TokenStream) {
|
pub fn print_err(msg: String, t: TokenStream) {
|
||||||
println!("Error: {} in '{}'", msg, t.to_string());
|
println!("Error: {} in '{}'", msg, t.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if the given type `ty` is `pyo3::Python`.
|
||||||
|
pub fn if_type_is_python(ty: &syn::Type) -> bool {
|
||||||
|
match ty {
|
||||||
|
syn::Type::Path(ref typath) => typath
|
||||||
|
.path
|
||||||
|
.segments
|
||||||
|
.last()
|
||||||
|
.map(|seg| seg.value().ident == "Python")
|
||||||
|
.unwrap_or(false),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME(althonos): not sure the docstring formatting is on par here.
|
// FIXME(althonos): not sure the docstring formatting is on par here.
|
||||||
pub fn get_doc(attrs: &[syn::Attribute], null_terminated: bool) -> syn::Lit {
|
pub fn get_doc(attrs: &[syn::Attribute], null_terminated: bool) -> syn::Lit {
|
||||||
let mut doc = Vec::new();
|
let mut doc = Vec::new();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(testkcovstopmarker)]
|
|
||||||
fn test_compile_errors() {
|
fn test_compile_errors() {
|
||||||
let t = trybuild::TestCases::new();
|
let t = trybuild::TestCases::new();
|
||||||
t.compile_fail("tests/ui/reject_generics.rs");
|
t.compile_fail("tests/ui/reject_generics.rs");
|
||||||
|
t.compile_fail("tests/ui/too_many_args_to_getter.rs");
|
||||||
}
|
}
|
||||||
|
|
|
@ -180,15 +180,19 @@ fn gc_integration() {
|
||||||
#[pyclass(gc)]
|
#[pyclass(gc)]
|
||||||
struct GCIntegration2 {}
|
struct GCIntegration2 {}
|
||||||
|
|
||||||
|
#[pyproto]
|
||||||
|
impl PyGCProtocol for GCIntegration2 {
|
||||||
|
fn __traverse__(&self, _visit: PyVisit) -> Result<(), PyTraverseError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn gc_integration2() {
|
fn gc_integration2() {
|
||||||
let gil = Python::acquire_gil();
|
let gil = Python::acquire_gil();
|
||||||
let py = gil.python();
|
let py = gil.python();
|
||||||
// Temporarily disable pythons garbage collector to avoid a race condition
|
|
||||||
py.run("import gc; gc.disable()", None, None).unwrap();
|
|
||||||
let inst = PyRef::new(py, GCIntegration2 {}).unwrap();
|
let inst = PyRef::new(py, GCIntegration2 {}).unwrap();
|
||||||
py_run!(py, inst, "assert inst in gc.get_objects()");
|
py_run!(py, inst, "import gc; assert inst in gc.get_objects()");
|
||||||
py.run("gc.enable()", None, None).unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyclass(weakref)]
|
#[pyclass(weakref)]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
use pyo3::py_run;
|
use pyo3::py_run;
|
||||||
use pyo3::types::IntoPyDict;
|
use pyo3::types::{IntoPyDict, PyList};
|
||||||
use std::isize;
|
use std::isize;
|
||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
|
@ -32,10 +32,16 @@ impl ClassWithProperties {
|
||||||
fn get_unwrapped(&self) -> i32 {
|
fn get_unwrapped(&self) -> i32 {
|
||||||
self.num
|
self.num
|
||||||
}
|
}
|
||||||
|
|
||||||
#[setter]
|
#[setter]
|
||||||
fn set_unwrapped(&mut self, value: i32) {
|
fn set_unwrapped(&mut self, value: i32) {
|
||||||
self.num = value;
|
self.num = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[getter]
|
||||||
|
fn get_data_list<'py>(&self, py: Python<'py>) -> &'py PyList {
|
||||||
|
PyList::new(py, &[self.num])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -48,12 +54,12 @@ fn class_with_properties() {
|
||||||
py_run!(py, inst, "assert inst.get_num() == 10");
|
py_run!(py, inst, "assert inst.get_num() == 10");
|
||||||
py_run!(py, inst, "assert inst.get_num() == inst.DATA");
|
py_run!(py, inst, "assert inst.get_num() == inst.DATA");
|
||||||
py_run!(py, inst, "inst.DATA = 20");
|
py_run!(py, inst, "inst.DATA = 20");
|
||||||
py_run!(py, inst, "assert inst.get_num() == 20");
|
py_run!(py, inst, "assert inst.get_num() == 20 == inst.DATA");
|
||||||
py_run!(py, inst, "assert inst.get_num() == inst.DATA");
|
|
||||||
|
|
||||||
py_run!(py, inst, "assert inst.get_num() == inst.unwrapped == 20");
|
py_run!(py, inst, "assert inst.get_num() == inst.unwrapped == 20");
|
||||||
py_run!(py, inst, "inst.unwrapped = 42");
|
py_run!(py, inst, "inst.unwrapped = 42");
|
||||||
py_run!(py, inst, "assert inst.get_num() == inst.unwrapped == 42");
|
py_run!(py, inst, "assert inst.get_num() == inst.unwrapped == 42");
|
||||||
|
py_run!(py, inst, "assert inst.data_list == [42]");
|
||||||
|
|
||||||
let d = [("C", py.get_type::<ClassWithProperties>())].into_py_dict(py);
|
let d = [("C", py.get_type::<ClassWithProperties>())].into_py_dict(py);
|
||||||
py.run(
|
py.run(
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
#[pyclass]
|
||||||
|
struct ClassWithGetter {
|
||||||
|
a: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pymethods]
|
||||||
|
impl ClassWithGetter {
|
||||||
|
#[getter]
|
||||||
|
fn get_num(&self, index: u32) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,5 @@
|
||||||
|
error: Getter function can only have one argument of type pyo3::Python!
|
||||||
|
--> $DIR/too_many_args_to_getter.rs:11:30
|
||||||
|
|
|
||||||
|
11 | fn get_num(&self, index: u32) {}
|
||||||
|
| ^^^
|
Loading…
Reference in New Issue