Merge pull request #1093 from kngwyu/iterator-example

Improve lifetime insertions for #[pyproto]
This commit is contained in:
Yuji Kanagawa 2020-08-11 18:16:53 +09:00 committed by GitHub
commit b17d4fffe2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 80 additions and 67 deletions

View file

@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
[#1058](https://github.com/PyO3/pyo3/pull/1058). [#1059](https://github.com/PyO3/pyo3/pull/1059) [#1058](https://github.com/PyO3/pyo3/pull/1058). [#1059](https://github.com/PyO3/pyo3/pull/1059)
- Allows `&Self` as a `#[pymethods]` argument again. [#1071](https://github.com/PyO3/pyo3/pull/1071) - Allows `&Self` as a `#[pymethods]` argument again. [#1071](https://github.com/PyO3/pyo3/pull/1071)
- Fix best-effort build against PyPy 3.6. #[1092](https://github.com/PyO3/pyo3/pull/1092) - Fix best-effort build against PyPy 3.6. #[1092](https://github.com/PyO3/pyo3/pull/1092)
- Improve lifetime elision in `#[pyproto]`. [#1093](https://github.com/PyO3/pyo3/pull/1093)
## [0.11.1] - 2020-06-30 ## [0.11.1] - 2020-06-30
### Added ### Added

View file

@ -923,8 +923,8 @@ struct MyIterator {
#[pyproto] #[pyproto]
impl PyIterProtocol for MyIterator { impl PyIterProtocol for MyIterator {
fn __iter__(slf: PyRef<Self>) -> Py<MyIterator> { fn __iter__(slf: PyRef<Self>) -> PyRef<Self> {
slf.into() slf
} }
fn __next__(mut slf: PyRefMut<Self>) -> Option<PyObject> { fn __next__(mut slf: PyRefMut<Self>) -> Option<PyObject> {
slf.iter.next() slf.iter.next()
@ -948,8 +948,8 @@ struct Iter {
#[pyproto] #[pyproto]
impl PyIterProtocol for Iter { impl PyIterProtocol for Iter {
fn __iter__(slf: PyRefMut<Self>) -> Py<Iter> { fn __iter__(slf: PyRef<Self>) -> PyRef<Self> {
slf.into() slf
} }
fn __next__(mut slf: PyRefMut<Self>) -> Option<usize> { fn __next__(mut slf: PyRefMut<Self>) -> Option<usize> {
@ -964,7 +964,7 @@ struct Container {
#[pyproto] #[pyproto]
impl PyIterProtocol for Container { impl PyIterProtocol for Container {
fn __iter__(slf: PyRefMut<Self>) -> PyResult<Py<Iter>> { fn __iter__(slf: PyRef<Self>) -> PyResult<Py<Iter>> {
let iter = Iter { let iter = Iter {
inner: slf.iter.clone().into_iter(), inner: slf.iter.clone().into_iter(),
}; };

View file

@ -1,5 +1,5 @@
// Copyright (c) 2017-present PyO3 Project and Contributors // Copyright (c) 2017-present PyO3 Project and Contributors
use crate::func::MethodProto; use crate::proto_method::MethodProto;
/// Predicates for `#[pyproto]`. /// Predicates for `#[pyproto]`.
pub struct Proto { pub struct Proto {

View file

@ -4,10 +4,10 @@
#![recursion_limit = "1024"] #![recursion_limit = "1024"]
mod defs; mod defs;
mod func;
mod konst; mod konst;
mod method; mod method;
mod module; mod module;
mod proto_method;
mod pyclass; mod pyclass;
mod pyfunction; mod pyfunction;
mod pyimpl; mod pyimpl;

View file

@ -6,7 +6,6 @@ use syn::Token;
// TODO: // TODO:
// Add lifetime support for args with Rptr // Add lifetime support for args with Rptr
#[derive(Debug)] #[derive(Debug)]
pub enum MethodProto { pub enum MethodProto {
Free { Free {
@ -77,7 +76,11 @@ pub(crate) fn impl_method_proto(
) -> TokenStream { ) -> TokenStream {
let ret_ty = match &sig.output { let ret_ty = match &sig.output {
syn::ReturnType::Default => quote! { () }, syn::ReturnType::Default => quote! { () },
syn::ReturnType::Type(_, ty) => ty.to_token_stream(), syn::ReturnType::Type(_, ty) => {
let mut ty = ty.clone();
insert_lifetime(&mut ty);
ty.to_token_stream()
}
}; };
match *meth { match *meth {
@ -106,22 +109,7 @@ pub(crate) fn impl_method_proto(
let p: syn::Path = syn::parse_str(proto).unwrap(); let p: syn::Path = syn::parse_str(proto).unwrap();
let slf_name = syn::Ident::new(arg, Span::call_site()); let slf_name = syn::Ident::new(arg, Span::call_site());
let mut slf_ty = get_arg_ty(sig, 0); let slf_ty = get_arg_ty(sig, 0);
// update the type if no lifetime was given:
// PyRef<Self> --> PyRef<'p, Self>
if let syn::Type::Path(ref mut path) = slf_ty {
if let syn::PathArguments::AngleBracketed(ref mut args) =
path.path.segments[0].arguments
{
if let syn::GenericArgument::Lifetime(_) = args.args[0] {
} else {
let lt = syn::parse_quote! {'p};
args.args.insert(0, lt);
}
}
}
let tmp: syn::ItemFn = syn::parse_quote! { let tmp: syn::ItemFn = syn::parse_quote! {
fn test(&self) -> <#cls as #p<'p>>::Result {} fn test(&self) -> <#cls as #p<'p>>::Result {}
}; };
@ -336,40 +324,64 @@ pub(crate) fn impl_method_proto(
} }
} }
// TODO: better arg ty detection /// Some hacks for arguments: get `T` from `Option<T>` and insert lifetime
fn get_arg_ty(sig: &syn::Signature, idx: usize) -> syn::Type { fn get_arg_ty(sig: &syn::Signature, idx: usize) -> syn::Type {
let mut ty = match sig.inputs[idx] { fn get_option_ty(path: &syn::Path) -> Option<syn::Type> {
syn::FnArg::Typed(ref cap) => { let seg = path.segments.last()?;
match *cap.ty { if seg.ident == "Option" {
syn::Type::Path(ref ty) => { if let syn::PathArguments::AngleBracketed(ref data) = seg.arguments {
// use only last path segment for Option<> if let Some(syn::GenericArgument::Type(ref ty)) = data.args.last() {
let seg = ty.path.segments.last().unwrap().clone(); return Some(ty.to_owned());
if seg.ident == "Option" {
if let syn::PathArguments::AngleBracketed(ref data) = seg.arguments {
if let Some(pair) = data.args.last() {
match pair {
syn::GenericArgument::Type(ref ty) => return ty.clone(),
_ => panic!("Option only accepted for concrete types"),
}
};
}
}
*cap.ty.clone()
} }
_ => *cap.ty.clone(),
} }
} }
_ => panic!("fn arg type is not supported"), None
};
// Add a lifetime if there is none
if let syn::Type::Reference(ref mut r) = ty {
r.lifetime.get_or_insert(syn::parse_quote! {'p});
} }
let mut ty = match &sig.inputs[idx] {
syn::FnArg::Typed(ref cap) => match &*cap.ty {
// For `Option<T>`, we use `T` as an associated type for the protocol.
syn::Type::Path(ref ty) => get_option_ty(&ty.path).unwrap_or_else(|| *cap.ty.clone()),
_ => *cap.ty.clone(),
},
ty => panic!("Unsupported argument type: {:?}", ty),
};
insert_lifetime(&mut ty);
ty ty
} }
/// Insert lifetime `'p` to `PyRef<Self>` or references (e.g., `&PyType`).
fn insert_lifetime(ty: &mut syn::Type) {
fn insert_lifetime_for_path(path: &mut syn::TypePath) {
if let Some(seg) = path.path.segments.last_mut() {
if let syn::PathArguments::AngleBracketed(ref mut args) = seg.arguments {
let mut has_lifetime = false;
for arg in &mut args.args {
match arg {
// Insert `'p` recursively for `Option<PyRef<Self>>` or so.
syn::GenericArgument::Type(ref mut ty) => insert_lifetime(ty),
syn::GenericArgument::Lifetime(_) => has_lifetime = true,
_ => {}
}
}
// Insert lifetime to PyRef (i.e., PyRef<Self> -> PyRef<'p, Self>)
if !has_lifetime && (seg.ident == "PyRef" || seg.ident == "PyRefMut") {
args.args.insert(0, syn::parse_quote! {'p});
}
}
}
}
match ty {
syn::Type::Reference(ref mut r) => {
r.lifetime.get_or_insert(syn::parse_quote! {'p});
insert_lifetime(&mut *r.elem);
}
syn::Type::Path(ref mut path) => insert_lifetime_for_path(path),
_ => {}
}
}
fn extract_decl(spec: syn::Item) -> syn::Signature { fn extract_decl(spec: syn::Item) -> syn::Signature {
match spec { match spec {
syn::Item::Fn(f) => f.sig, syn::Item::Fn(f) => f.sig,

View file

@ -1,8 +1,8 @@
// Copyright (c) 2017-present PyO3 Project and Contributors // Copyright (c) 2017-present PyO3 Project and Contributors
use crate::defs; use crate::defs;
use crate::func::impl_method_proto;
use crate::method::{FnSpec, FnType}; use crate::method::{FnSpec, FnType};
use crate::proto_method::impl_method_proto;
use crate::pymethod; use crate::pymethod;
use proc_macro2::{Span, TokenStream}; use proc_macro2::{Span, TokenStream};
use quote::quote; use quote::quote;

View file

@ -51,12 +51,12 @@ struct Iterator {
} }
#[pyproto] #[pyproto]
impl<'p> PyIterProtocol for Iterator { impl PyIterProtocol for Iterator {
fn __iter__(slf: PyRef<'p, Self>) -> Py<Iterator> { fn __iter__(slf: PyRef<Self>) -> PyRef<Self> {
slf.into() slf
} }
fn __next__(mut slf: PyRefMut<'p, Self>) -> Option<i32> { fn __next__(mut slf: PyRefMut<Self>) -> Option<i32> {
slf.iter.next() slf.iter.next()
} }
} }
@ -81,7 +81,7 @@ fn iterator() {
struct StringMethods {} struct StringMethods {}
#[pyproto] #[pyproto]
impl<'p> PyObjectProtocol<'p> for StringMethods { impl PyObjectProtocol for StringMethods {
fn __str__(&self) -> &'static str { fn __str__(&self) -> &'static str {
"str" "str"
} }
@ -236,7 +236,7 @@ struct SetItem {
} }
#[pyproto] #[pyproto]
impl PyMappingProtocol<'a> for SetItem { impl PyMappingProtocol for SetItem {
fn __setitem__(&mut self, key: i32, val: i32) { fn __setitem__(&mut self, key: i32, val: i32) {
self.key = key; self.key = key;
self.val = val; self.val = val;
@ -362,16 +362,16 @@ struct ContextManager {
} }
#[pyproto] #[pyproto]
impl<'p> PyContextProtocol<'p> for ContextManager { impl PyContextProtocol for ContextManager {
fn __enter__(&mut self) -> i32 { fn __enter__(&mut self) -> i32 {
42 42
} }
fn __exit__( fn __exit__(
&mut self, &mut self,
ty: Option<&'p PyType>, ty: Option<&PyType>,
_value: Option<&'p PyAny>, _value: Option<&PyAny>,
_traceback: Option<&'p PyAny>, _traceback: Option<&PyAny>,
) -> bool { ) -> bool {
let gil = Python::acquire_gil(); let gil = Python::acquire_gil();
self.exit_called = true; self.exit_called = true;
@ -564,14 +564,14 @@ impl OnceFuture {
#[pyproto] #[pyproto]
impl PyAsyncProtocol for OnceFuture { impl PyAsyncProtocol for OnceFuture {
fn __await__(slf: PyRef<'p, Self>) -> PyRef<'p, Self> { fn __await__(slf: PyRef<Self>) -> PyRef<Self> {
slf slf
} }
} }
#[pyproto] #[pyproto]
impl PyIterProtocol for OnceFuture { impl PyIterProtocol for OnceFuture {
fn __iter__(slf: PyRef<'p, Self>) -> PyRef<'p, Self> { fn __iter__(slf: PyRef<Self>) -> PyRef<Self> {
slf slf
} }
fn __next__(mut slf: PyRefMut<Self>) -> Option<PyObject> { fn __next__(mut slf: PyRefMut<Self>) -> Option<PyObject> {
@ -632,14 +632,14 @@ impl DescrCounter {
#[pyproto] #[pyproto]
impl PyDescrProtocol for DescrCounter { impl PyDescrProtocol for DescrCounter {
fn __get__( fn __get__(
mut slf: PyRefMut<'p, Self>, mut slf: PyRefMut<Self>,
_instance: &PyAny, _instance: &PyAny,
_owner: Option<&'p PyType>, _owner: Option<&PyType>,
) -> PyRefMut<'p, Self> { ) -> PyRefMut<Self> {
slf.count += 1; slf.count += 1;
slf slf
} }
fn __set__(_slf: PyRef<'p, Self>, _instance: &PyAny, mut new_value: PyRefMut<'p, Self>) { fn __set__(_slf: PyRef<Self>, _instance: &PyAny, mut new_value: PyRefMut<Self>) {
new_value.count = _slf.count; new_value.count = _slf.count;
} }
} }