Encapsule slot_setter's behavior to an iterator

This commit is contained in:
kngwyu 2020-08-18 16:47:44 +09:00
parent 71a7a76227
commit 3e958bf607
4 changed files with 30 additions and 18 deletions

View File

@ -44,6 +44,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Improve lifetime elision in `#[pyproto]`. [#1093](https://github.com/PyO3/pyo3/pull/1093)
- Fix python configuration detection when cross-compiling. [#1095](https://github.com/PyO3/pyo3/pull/1095)
- Link against libpython on android with `extension-module` set. [#1095](https://github.com/PyO3/pyo3/pull/1095)
- Fix support for both `__add__` and `__radd__` in the `+` operator when both are defined in `PyNumberProtocol`
(and similar for all other reversible operators). [#1107](https://github.com/PyO3/pyo3/pull/1107)
## [0.11.1] - 2020-06-30
### Added

View File

@ -1,5 +1,6 @@
// Copyright (c) 2017-present PyO3 Project and Contributors
use crate::proto_method::MethodProto;
use std::collections::HashSet;
/// Predicates for `#[pyproto]`.
pub struct Proto {
@ -14,7 +15,7 @@ pub struct Proto {
/// All methods registered as normal methods like `#[pymethods]`.
pub py_methods: &'static [PyMethod],
/// All methods registered to the slot table.
pub slot_setters: &'static [SlotSetter],
slot_setters: &'static [SlotSetter],
}
impl Proto {
@ -30,6 +31,28 @@ impl Proto {
{
self.py_methods.iter().find(|m| query == m.name)
}
// Since the order matters, we expose only the iterator instead of the slice.
pub(crate) fn setters(
&self,
mut implemented_protocols: HashSet<String>,
) -> impl Iterator<Item = &'static str> {
self.slot_setters.iter().filter_map(move |setter| {
// If any required method is not implemented, we skip this setter.
if setter
.proto_names
.iter()
.any(|name| !implemented_protocols.contains(*name))
{
return None;
}
// To use 'paired' setter in priority, we remove used protocols.
// For example, if set_add_radd is already used, we shouldn't use set_add and set_radd.
for name in setter.proto_names {
implemented_protocols.remove(*name);
}
Some(setter.set_function)
})
}
}
/// Represents a method registered as a normal method like `#[pymethods]`.
@ -59,7 +82,7 @@ impl PyMethod {
}
/// Represents a setter used to register a method to the method table.
pub struct SlotSetter {
struct SlotSetter {
/// Protocols necessary for invoking this setter.
/// E.g., we need `__setattr__` and `__delattr__` for invoking `set_setdelitem`.
pub proto_names: &'static [&'static str],

View File

@ -134,24 +134,11 @@ fn slot_initialization(
ty: &syn::Type,
proto: &defs::Proto,
) -> syn::Result<TokenStream> {
// To skip used protocols.
// E.g., if __set__ and __del__ exist, we use set_set_del and skip set_set.
let mut used_protocols = HashSet::new();
// Collect initializers
let mut initializers: Vec<TokenStream> = vec![];
for m in proto.slot_setters {
// Skip if any required protocol are not implemented or already used.
if m.proto_names
.iter()
.any(|name| !method_names.contains(*name) || used_protocols.contains(name))
{
continue;
}
for name in m.proto_names {
used_protocols.insert(name);
}
for setter in proto.setters(method_names) {
// Add slot methods to PyProtoRegistry
let set = syn::Ident::new(m.set_function, Span::call_site());
let set = syn::Ident::new(setter, Span::call_site());
initializers.push(quote! { table.#set::<#ty>(); });
}
if initializers.is_empty() {

View File

@ -125,7 +125,7 @@ macro_rules! py_binary_fallback_num_func {
match (lhs.extract(), rhs.extract()) {
(Ok(l), Ok(r)) => $class::$lop(l, r).convert(py),
_ => {
// Next, try the right hand method (e.g., __add__)
// Next, try the right hand method (e.g., __radd__)
let slf: &$crate::PyCell<T> = extract_or_return_not_implemented!(rhs);
let arg = extract_or_return_not_implemented!(lhs);
$class::$rop(&*slf.try_borrow()?, arg).convert(py)