Merge pull request #961 from kngwyu/slot-provider

Object protocols without specialization
This commit is contained in:
Yuji Kanagawa 2020-06-18 17:19:39 +09:00 committed by GitHub
commit 390ff5f17f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 1427 additions and 2297 deletions

View File

@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Change return type of `PyTuple::as_slice` to `&[&PyAny]`. [#971](https://github.com/PyO3/pyo3/pull/971)
- Update `num-complex` optional dependendency from `0.2` to `0.3`. [#977](https://github.com/PyO3/pyo3/pull/977)
- Update `num-bigint` optional dependendency from `0.2` to `0.3`. [#978](https://github.com/PyO3/pyo3/pull/978)
- `#[pyproto]` is re-implemented without specialization. [#961](https://github.com/PyO3/pyo3/pull/961)
### Removed
- Remove `ManagedPyRef` (unused, and needs specialization) [#930](https://github.com/PyO3/pyo3/pull/930)

View File

@ -19,6 +19,7 @@ travis-ci = { repository = "PyO3/pyo3", branch = "master" }
appveyor = { repository = "fafhrd91/pyo3" }
[dependencies]
ctor = { version = "0.1", optional = true }
indoc = { version = "0.3.4", optional = true }
inventory = { version = "0.1.4", optional = true }
libc = "0.2.62"
@ -38,7 +39,7 @@ version_check = "0.9.1"
[features]
default = ["macros"]
macros = ["indoc", "inventory", "paste", "pyo3cls", "unindent"]
macros = ["ctor", "indoc", "inventory", "paste", "pyo3cls", "unindent"]
# this is no longer needed internally, but setuptools-rust assumes this feature
python3 = []

View File

@ -956,6 +956,14 @@ impl pyo3::class::methods::HasMethodsInventory for MyClass {
type Methods = Pyo3MethodsInventoryForMyClass;
}
pyo3::inventory::collect!(Pyo3MethodsInventoryForMyClass);
impl pyo3::class::proto_methods::HasProtoRegistry for MyClass {
fn registry() -> &'static pyo3::class::proto_methods::PyProtoRegistry {
static REGISTRY: pyo3::class::proto_methods::PyProtoRegistry
= pyo3::class::proto_methods::PyProtoRegistry::new();
&REGISTRY
}
}
# let gil = Python::acquire_gil();
# let py = gil.python();
# let cls = py.get_type::<MyClass>();

View File

@ -1,10 +1,20 @@
// Copyright (c) 2017-present PyO3 Project and Contributors
use crate::func::MethodProto;
/// Predicates for `#[pyproto]`.
pub struct Proto {
/// The name of this protocol. E.g., Iter.
pub name: &'static str,
/// The name of slot table. E.g., PyIterMethods.
pub slot_table: &'static str,
/// The name of the setter used to set the table to `PyProtoRegistry`.
pub set_slot_table: &'static str,
/// All methods.
pub methods: &'static [MethodProto],
/// 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],
}
impl Proto {
@ -22,6 +32,7 @@ impl Proto {
}
}
/// Represents a method registered as a normal method like `#[pymethods]`.
// TODO(kngwyu): Currently only __radd__-like methods use METH_COEXIST to prevent
// __add__-like methods from overriding them.
pub struct PyMethod {
@ -47,8 +58,33 @@ impl PyMethod {
}
}
/// Represents a setter used to register a method to the method table.
pub 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],
/// The name of the setter called to the method table.
pub set_function: &'static str,
/// Represents a set of setters disabled by this setter.
/// E.g., `set_setdelitem` have to disable `set_setitem` and `set_delitem`.
pub skipped_setters: &'static [&'static str],
}
impl SlotSetter {
const EMPTY_SETTERS: &'static [&'static str] = &[];
const fn new(names: &'static [&'static str], set_function: &'static str) -> Self {
SlotSetter {
proto_names: names,
set_function,
skipped_setters: Self::EMPTY_SETTERS,
}
}
}
pub const OBJECT: Proto = Proto {
name: "Object",
slot_table: "pyo3::class::basic::PyObjectMethods",
set_slot_table: "set_basic_methods",
methods: &[
MethodProto::Binary {
name: "__getattr__",
@ -95,40 +131,60 @@ pub const OBJECT: Proto = Proto {
pyres: true,
proto: "pyo3::class::basic::PyObjectBytesProtocol",
},
MethodProto::Unary {
name: "__bool__",
pyres: false,
proto: "pyo3::class::basic::PyObjectBoolProtocol",
},
MethodProto::Binary {
name: "__richcmp__",
arg: "Other",
pyres: true,
proto: "pyo3::class::basic::PyObjectRichcmpProtocol",
},
MethodProto::Unary {
name: "__bool__",
pyres: false,
proto: "pyo3::class::basic::PyObjectBoolProtocol",
},
],
py_methods: &[
PyMethod::new("__format__", "pyo3::class::basic::FormatProtocolImpl"),
PyMethod::new("__bytes__", "pyo3::class::basic::BytesProtocolImpl"),
PyMethod::new("__unicode__", "pyo3::class::basic::UnicodeProtocolImpl"),
],
slot_setters: &[
SlotSetter::new(&["__str__"], "set_str"),
SlotSetter::new(&["__repr__"], "set_repr"),
SlotSetter::new(&["__hash__"], "set_hash"),
SlotSetter::new(&["__getattr__"], "set_getattr"),
SlotSetter::new(&["__richcmp__"], "set_richcompare"),
SlotSetter {
proto_names: &["__setattr__", "__delattr__"],
set_function: "set_setdelattr",
skipped_setters: &["set_setattr", "set_delattr"],
},
SlotSetter::new(&["__setattr__"], "set_setattr"),
SlotSetter::new(&["__delattr__"], "set_delattr"),
SlotSetter::new(&["__bool__"], "set_bool"),
],
};
pub const ASYNC: Proto = Proto {
name: "Async",
slot_table: "pyo3::ffi::PyAsyncMethods",
set_slot_table: "set_async_methods",
methods: &[
MethodProto::Unary {
MethodProto::UnaryS {
name: "__await__",
arg: "Receiver",
pyres: true,
proto: "pyo3::class::pyasync::PyAsyncAwaitProtocol",
},
MethodProto::Unary {
MethodProto::UnaryS {
name: "__aiter__",
arg: "Receiver",
pyres: true,
proto: "pyo3::class::pyasync::PyAsyncAiterProtocol",
},
MethodProto::Unary {
MethodProto::UnaryS {
name: "__anext__",
arg: "Receiver",
pyres: true,
proto: "pyo3::class::pyasync::PyAsyncAnextProtocol",
},
@ -155,10 +211,17 @@ pub const ASYNC: Proto = Proto {
"pyo3::class::pyasync::PyAsyncAexitProtocolImpl",
),
],
slot_setters: &[
SlotSetter::new(&["__await__"], "set_await"),
SlotSetter::new(&["__aiter__"], "set_aiter"),
SlotSetter::new(&["__anext__"], "set_anext"),
],
};
pub const BUFFER: Proto = Proto {
name: "Buffer",
slot_table: "pyo3::ffi::PyBufferProcs",
set_slot_table: "set_buffer_methods",
methods: &[
MethodProto::Unary {
name: "bf_getbuffer",
@ -172,10 +235,16 @@ pub const BUFFER: Proto = Proto {
},
],
py_methods: &[],
slot_setters: &[
SlotSetter::new(&["bf_getbuffer"], "set_getbuffer"),
SlotSetter::new(&["bf_releasebuffer"], "set_releasebuffer"),
],
};
pub const CONTEXT: Proto = Proto {
name: "Context",
slot_table: "",
set_slot_table: "",
methods: &[
MethodProto::Unary {
name: "__enter__",
@ -200,10 +269,13 @@ pub const CONTEXT: Proto = Proto {
"pyo3::class::context::PyContextExitProtocolImpl",
),
],
slot_setters: &[],
};
pub const GC: Proto = Proto {
name: "GC",
slot_table: "pyo3::class::gc::PyGCMethods",
set_slot_table: "set_gc_methods",
methods: &[
MethodProto::Free {
name: "__traverse__",
@ -215,23 +287,31 @@ pub const GC: Proto = Proto {
},
],
py_methods: &[],
slot_setters: &[
SlotSetter::new(&["__traverse__"], "set_traverse"),
SlotSetter::new(&["__clear__"], "set_clear"),
],
};
pub const DESCR: Proto = Proto {
name: "Descriptor",
slot_table: "pyo3::class::descr::PyDescrMethods",
set_slot_table: "set_descr_methods",
methods: &[
MethodProto::Ternary {
MethodProto::TernaryS {
name: "__get__",
arg1: "Inst",
arg2: "Owner",
arg1: "Receiver",
arg2: "Inst",
arg3: "Owner",
pyres: true,
proto: "pyo3::class::descr::PyDescrGetProtocol",
},
MethodProto::Ternary {
MethodProto::TernaryS {
name: "__set__",
arg1: "Inst",
arg2: "Value",
pyres: true,
arg1: "Receiver",
arg2: "Inst",
arg3: "Value",
pyres: false,
proto: "pyo3::class::descr::PyDescrSetProtocol",
},
MethodProto::Binary {
@ -254,10 +334,16 @@ pub const DESCR: Proto = Proto {
"pyo3::class::context::PyDescrNameProtocolImpl",
),
],
slot_setters: &[
SlotSetter::new(&["__get__"], "set_descr_get"),
SlotSetter::new(&["__set__"], "set_descr_set"),
],
};
pub const ITER: Proto = Proto {
name: "Iter",
slot_table: "pyo3::class::iter::PyIterMethods",
set_slot_table: "set_iter_methods",
py_methods: &[],
methods: &[
MethodProto::UnaryS {
@ -273,10 +359,16 @@ pub const ITER: Proto = Proto {
proto: "pyo3::class::iter::PyIterNextProtocol",
},
],
slot_setters: &[
SlotSetter::new(&["__iter__"], "set_iter"),
SlotSetter::new(&["__next__"], "set_iternext"),
],
};
pub const MAPPING: Proto = Proto {
name: "Mapping",
slot_table: "pyo3::ffi::PyMappingMethods",
set_slot_table: "set_mapping_methods",
methods: &[
MethodProto::Unary {
name: "__len__",
@ -312,10 +404,23 @@ pub const MAPPING: Proto = Proto {
"__reversed__",
"pyo3::class::mapping::PyMappingReversedProtocolImpl",
)],
slot_setters: &[
SlotSetter::new(&["__len__"], "set_length"),
SlotSetter::new(&["__getitem__"], "set_getitem"),
SlotSetter {
proto_names: &["__setitem__", "__delitem__"],
set_function: "set_setdelitem",
skipped_setters: &["set_setitem", "set_delitem"],
},
SlotSetter::new(&["__setitem__"], "set_setitem"),
SlotSetter::new(&["__delitem__"], "set_delitem"),
],
};
pub const SEQ: Proto = Proto {
name: "Sequence",
slot_table: "pyo3::ffi::PySequenceMethods",
set_slot_table: "set_sequence_methods",
methods: &[
MethodProto::Unary {
name: "__len__",
@ -373,10 +478,28 @@ pub const SEQ: Proto = Proto {
},
],
py_methods: &[],
slot_setters: &[
SlotSetter::new(&["__len__"], "set_len"),
SlotSetter::new(&["__concat__"], "set_concat"),
SlotSetter::new(&["__repeat__"], "set_repeat"),
SlotSetter::new(&["__getitem__"], "set_getitem"),
SlotSetter {
proto_names: &["__setitem__", "__delitem__"],
set_function: "set_setdelitem",
skipped_setters: &["set_setitem", "set_delitem"],
},
SlotSetter::new(&["__setitem__"], "set_setitem"),
SlotSetter::new(&["__delitem__"], "set_delitem"),
SlotSetter::new(&["__contains__"], "set_contains"),
SlotSetter::new(&["__inplace_concat__"], "set_inplace_concat"),
SlotSetter::new(&["__inplace_repeat__"], "set_inplace_repeat"),
],
};
pub const NUM: Proto = Proto {
name: "Number",
slot_table: "pyo3::ffi::PyNumberMethods",
set_slot_table: "set_number_methods",
methods: &[
MethodProto::BinaryS {
name: "__add__",
@ -729,4 +852,106 @@ pub const NUM: Proto = Proto {
"pyo3::class::number::PyNumberRoundProtocolImpl",
),
],
slot_setters: &[
SlotSetter {
proto_names: &["__add__"],
set_function: "set_add",
skipped_setters: &["set_radd"],
},
SlotSetter::new(&["__radd__"], "set_radd"),
SlotSetter {
proto_names: &["__sub__"],
set_function: "set_sub",
skipped_setters: &["set_rsub"],
},
SlotSetter::new(&["__rsub__"], "set_rsub"),
SlotSetter {
proto_names: &["__mul__"],
set_function: "set_mul",
skipped_setters: &["set_rmul"],
},
SlotSetter::new(&["__rmul__"], "set_rmul"),
SlotSetter::new(&["__mod__"], "set_mod"),
SlotSetter {
proto_names: &["__divmod__"],
set_function: "set_divmod",
skipped_setters: &["set_rdivmod"],
},
SlotSetter::new(&["__rdivmod__"], "set_rdivmod"),
SlotSetter {
proto_names: &["__pow__"],
set_function: "set_pow",
skipped_setters: &["set_rpow"],
},
SlotSetter::new(&["__rpow__"], "set_rpow"),
SlotSetter::new(&["__neg__"], "set_neg"),
SlotSetter::new(&["__pos__"], "set_pos"),
SlotSetter::new(&["__abs__"], "set_abs"),
SlotSetter::new(&["__invert__"], "set_invert"),
SlotSetter::new(&["__rdivmod__"], "set_rdivmod"),
SlotSetter {
proto_names: &["__lshift__"],
set_function: "set_lshift",
skipped_setters: &["set_rlshift"],
},
SlotSetter::new(&["__rlshift__"], "set_rlshift"),
SlotSetter {
proto_names: &["__rshift__"],
set_function: "set_rshift",
skipped_setters: &["set_rrshift"],
},
SlotSetter::new(&["__rrshift__"], "set_rrshift"),
SlotSetter {
proto_names: &["__and__"],
set_function: "set_and",
skipped_setters: &["set_rand"],
},
SlotSetter::new(&["__rand__"], "set_rand"),
SlotSetter {
proto_names: &["__xor__"],
set_function: "set_xor",
skipped_setters: &["set_rxor"],
},
SlotSetter::new(&["__rxor__"], "set_rxor"),
SlotSetter {
proto_names: &["__or__"],
set_function: "set_or",
skipped_setters: &["set_ror"],
},
SlotSetter::new(&["__ror__"], "set_ror"),
SlotSetter::new(&["__int__"], "set_int"),
SlotSetter::new(&["__float__"], "set_float"),
SlotSetter::new(&["__iadd__"], "set_iadd"),
SlotSetter::new(&["__isub__"], "set_isub"),
SlotSetter::new(&["__imul__"], "set_imul"),
SlotSetter::new(&["__imod__"], "set_imod"),
SlotSetter::new(&["__ipow__"], "set_ipow"),
SlotSetter::new(&["__ilshift__"], "set_ilshift"),
SlotSetter::new(&["__irshift__"], "set_irshift"),
SlotSetter::new(&["__iand__"], "set_iand"),
SlotSetter::new(&["__ixor__"], "set_ixor"),
SlotSetter::new(&["__ior__"], "set_ior"),
SlotSetter {
proto_names: &["__floordiv__"],
set_function: "set_floordiv",
skipped_setters: &["set_rfloordiv"],
},
SlotSetter::new(&["__rfloordiv__"], "set_rfloordiv"),
SlotSetter {
proto_names: &["__truediv__"],
set_function: "set_truediv",
skipped_setters: &["set_rtruediv"],
},
SlotSetter::new(&["__rtruediv__"], "set_rtruediv"),
SlotSetter::new(&["__ifloordiv__"], "set_ifloordiv"),
SlotSetter::new(&["__itruediv__"], "set_itruediv"),
SlotSetter::new(&["__index__"], "set_index"),
SlotSetter {
proto_names: &["__matmul__"],
set_function: "set_matmul",
skipped_setters: &["set_rmatmul"],
},
SlotSetter::new(&["__rmatmul__"], "set_rmatmul"),
SlotSetter::new(&["__imatmul__"], "set_imatmul"),
],
};

View File

@ -236,6 +236,19 @@ fn impl_methods_inventory(cls: &syn::Ident) -> TokenStream {
}
}
/// Implement `HasProtoRegistry` for the class for lazy protocol initialization.
fn impl_proto_registry(cls: &syn::Ident) -> TokenStream {
quote! {
impl pyo3::class::proto_methods::HasProtoRegistry for #cls {
fn registry() -> &'static pyo3::class::proto_methods::PyProtoRegistry {
static REGISTRY: pyo3::class::proto_methods::PyProtoRegistry
= pyo3::class::proto_methods::PyProtoRegistry::new();
&REGISTRY
}
}
}
}
fn get_class_python_name(cls: &syn::Ident, attr: &PyClassArgs) -> TokenStream {
match &attr.name {
Some(name) => quote! { #name },
@ -340,6 +353,7 @@ fn impl_class(
};
let impl_inventory = impl_methods_inventory(&cls);
let impl_proto_registry = impl_proto_registry(&cls);
let base = &attr.base;
let flags = &attr.flags;
@ -414,6 +428,8 @@ fn impl_class(
#impl_inventory
#impl_proto_registry
#extra
#gc_impl

View File

@ -4,9 +4,10 @@ use crate::defs;
use crate::func::impl_method_proto;
use crate::method::FnSpec;
use crate::pymethod;
use proc_macro2::TokenStream;
use proc_macro2::{Span, TokenStream};
use quote::quote;
use quote::ToTokens;
use std::collections::HashSet;
pub fn build_py_proto(ast: &mut syn::ItemImpl) -> syn::Result<TokenStream> {
if let Some((_, ref mut path, _)) = ast.trait_ {
@ -60,12 +61,17 @@ fn impl_proto_impl(
) -> syn::Result<TokenStream> {
let mut trait_impls = TokenStream::new();
let mut py_methods = Vec::new();
let mut method_names = HashSet::new();
for iimpl in impls.iter_mut() {
if let syn::ImplItem::Method(ref mut met) = iimpl {
// impl Py~Protocol<'p> { type = ... }
if let Some(m) = proto.get_proto(&met.sig.ident) {
impl_method_proto(ty, &mut met.sig, m).to_tokens(&mut trait_impls);
// Insert the method to the HashSet
method_names.insert(met.sig.ident.to_string());
}
// Add non-slot methods to inventory like `#[pymethods]`
if let Some(m) = proto.get_method(&met.sig.ident) {
let name = &met.sig.ident;
let fn_spec = FnSpec::parse(&met.sig, &mut met.attrs, false)?;
@ -76,7 +82,7 @@ fn impl_proto_impl(
} else {
quote!(0)
};
// TODO(kngwyu): doc
// TODO(kngwyu): Set ml_doc
py_methods.push(quote! {
pyo3::class::PyMethodDefType::Method({
#method
@ -91,20 +97,78 @@ fn impl_proto_impl(
}
}
}
let inventory_submission = inventory_submission(py_methods, ty);
let slot_initialization = slot_initialization(method_names, ty, proto)?;
Ok(quote! {
#trait_impls
#inventory_submission
#slot_initialization
})
}
fn inventory_submission(py_methods: Vec<TokenStream>, ty: &syn::Type) -> TokenStream {
if py_methods.is_empty() {
return Ok(quote! { #trait_impls });
return quote! {};
}
let inventory_submission = quote! {
quote! {
pyo3::inventory::submit! {
#![crate = pyo3] {
type Inventory = <#ty as pyo3::class::methods::HasMethodsInventory>::Methods;
<Inventory as pyo3::class::methods::PyMethodsInventory>::new(&[#(#py_methods),*])
}
}
};
}
}
fn slot_initialization(
method_names: HashSet<String>,
ty: &syn::Type,
proto: &defs::Proto,
) -> syn::Result<TokenStream> {
// Some setters cannot coexist.
// E.g., if we have `__add__`, we need to skip `set_radd`.
let mut skipped_setters = Vec::new();
// Collect initializers
let mut initializers: Vec<TokenStream> = vec![];
'outer_loop: for m in proto.slot_setters {
if skipped_setters.contains(&m.set_function) {
continue;
}
for name in m.proto_names {
// If this `#[pyproto]` block doesn't provide all required methods,
// let's skip implementing this method.
if !method_names.contains(*name) {
continue 'outer_loop;
}
}
skipped_setters.extend_from_slice(m.skipped_setters);
// Add slot methods to PyProtoRegistry
let set = syn::Ident::new(m.set_function, Span::call_site());
initializers.push(quote! { table.#set::<#ty>(); });
}
if initializers.is_empty() {
return Ok(quote! {});
}
let table: syn::Path = syn::parse_str(proto.slot_table)?;
let set = syn::Ident::new(proto.set_slot_table, Span::call_site());
let ty_hash = typename_hash(ty);
let init = syn::Ident::new(
&format!("__init_{}_{}", proto.name, ty_hash),
Span::call_site(),
);
Ok(quote! {
#trait_impls
#inventory_submission
#[pyo3::ctor::ctor]
fn #init() {
let mut table = #table::default();
#(#initializers)*
<#ty as pyo3::class::proto_methods::HasProtoRegistry>::registry().#set(table);
}
})
}
fn typename_hash(ty: &syn::Type) -> u64 {
use std::hash::{Hash, Hasher};
let mut hasher = std::collections::hash_map::DefaultHasher::new();
ty.hash(&mut hasher);
hasher.finish()
}

View File

@ -77,13 +77,6 @@ pub trait PyObjectProtocol<'p>: PyClass {
unimplemented!()
}
fn __bool__(&'p self) -> Self::Result
where
Self: PyObjectBoolProtocol<'p>,
{
unimplemented!()
}
fn __bytes__(&'p self) -> Self::Result
where
Self: PyObjectBytesProtocol<'p>,
@ -97,6 +90,12 @@ pub trait PyObjectProtocol<'p>: PyClass {
{
unimplemented!()
}
fn __bool__(&'p self) -> Self::Result
where
Self: PyObjectBoolProtocol<'p>,
{
unimplemented!()
}
}
pub trait PyObjectGetAttrProtocol<'p>: PyObjectProtocol<'p> {
@ -142,311 +141,160 @@ pub trait PyObjectRichcmpProtocol<'p>: PyObjectProtocol<'p> {
type Result: Into<PyResult<Self::Success>>;
}
/// All FFI functions for basic protocols.
#[derive(Default)]
pub struct PyObjectMethods {
pub tp_str: Option<ffi::reprfunc>,
pub tp_repr: Option<ffi::reprfunc>,
pub tp_hash: Option<ffi::hashfunc>,
pub tp_getattro: Option<ffi::getattrofunc>,
pub tp_richcompare: Option<ffi::richcmpfunc>,
pub tp_setattro: Option<ffi::setattrofunc>,
pub nb_bool: Option<ffi::inquiry>,
}
#[doc(hidden)]
pub trait PyObjectProtocolImpl {
fn tp_as_object(_type_object: &mut ffi::PyTypeObject);
fn nb_bool_fn() -> Option<ffi::inquiry>;
}
impl<T> PyObjectProtocolImpl for T {
default fn tp_as_object(_type_object: &mut ffi::PyTypeObject) {}
default fn nb_bool_fn() -> Option<ffi::inquiry> {
None
impl PyObjectMethods {
pub(crate) fn update_typeobj(&self, type_object: &mut ffi::PyTypeObject) {
type_object.tp_str = self.tp_str;
type_object.tp_repr = self.tp_repr;
type_object.tp_hash = self.tp_hash;
type_object.tp_getattro = self.tp_getattro;
type_object.tp_richcompare = self.tp_richcompare;
type_object.tp_setattro = self.tp_setattro;
}
}
impl<'p, T> PyObjectProtocolImpl for T
where
T: PyObjectProtocol<'p>,
{
fn tp_as_object(type_object: &mut ffi::PyTypeObject) {
type_object.tp_str = Self::tp_str();
type_object.tp_repr = Self::tp_repr();
type_object.tp_hash = Self::tp_hash();
type_object.tp_getattro = Self::tp_getattro();
type_object.tp_richcompare = Self::tp_richcompare();
type_object.tp_setattro = tp_setattro_impl::tp_setattro::<Self>();
}
fn nb_bool_fn() -> Option<ffi::inquiry> {
Self::nb_bool()
}
}
trait GetAttrProtocolImpl {
fn tp_getattro() -> Option<ffi::binaryfunc>;
}
impl<'p, T> GetAttrProtocolImpl for T
where
T: PyObjectProtocol<'p>,
{
default fn tp_getattro() -> Option<ffi::binaryfunc> {
None
}
}
impl<T> GetAttrProtocolImpl for T
where
T: for<'p> PyObjectGetAttrProtocol<'p>,
{
fn tp_getattro() -> Option<ffi::binaryfunc> {
#[allow(unused_mut)]
unsafe extern "C" fn wrap<T>(
slf: *mut ffi::PyObject,
arg: *mut ffi::PyObject,
) -> *mut ffi::PyObject
where
T: for<'p> PyObjectGetAttrProtocol<'p>,
{
crate::callback_body!(py, {
// Behave like python's __getattr__ (as opposed to __getattribute__) and check
// for existing fields and methods first
let existing = ffi::PyObject_GenericGetAttr(slf, arg);
if existing.is_null() {
// PyObject_HasAttr also tries to get an object and clears the error if it fails
ffi::PyErr_Clear();
} else {
return Ok(existing);
}
let slf = py.from_borrowed_ptr::<PyCell<T>>(slf);
let arg = py.from_borrowed_ptr::<PyAny>(arg);
call_ref!(slf, __getattr__, arg)
})
}
Some(wrap::<T>)
}
}
/// An object may support setting attributes (by implementing PyObjectSetAttrProtocol)
/// and may support deleting attributes (by implementing PyObjectDelAttrProtocol).
/// We need to generate a single "extern C" function that supports only setting, only deleting
/// or both, and return None in case none of the two is supported.
mod tp_setattro_impl {
use super::*;
/// setattrofunc PyTypeObject.tp_setattro
///
/// An optional pointer to the function for setting and deleting attributes.
///
/// The signature is the same as for PyObject_SetAttr(), but setting v to NULL to delete an
/// attribute must be supported. It is usually convenient to set this field to
/// PyObject_GenericSetAttr(), which implements the normal way of setting object attributes.
pub(super) fn tp_setattro<'p, T: PyObjectProtocol<'p>>() -> Option<ffi::setattrofunc> {
if let Some(set_del) = T::set_del_attr() {
Some(set_del)
} else if let Some(set) = T::set_attr() {
Some(set)
} else if let Some(del) = T::del_attr() {
Some(del)
} else {
None
}
}
trait SetAttr {
fn set_attr() -> Option<ffi::setattrofunc>;
}
impl<'p, T: PyObjectProtocol<'p>> SetAttr for T {
default fn set_attr() -> Option<ffi::setattrofunc> {
None
}
}
impl<T> SetAttr for T
// Set functions used by `#[pyproto]`.
pub fn set_str<T>(&mut self)
where
T: for<'p> PyObjectSetAttrProtocol<'p>,
T: for<'p> PyObjectStrProtocol<'p>,
{
fn set_attr() -> Option<ffi::setattrofunc> {
py_func_set!(PyObjectSetAttrProtocol, T, __setattr__)
}
self.tp_str = py_unary_func!(PyObjectStrProtocol, T::__str__);
}
trait DelAttr {
fn del_attr() -> Option<ffi::setattrofunc>;
}
impl<'p, T> DelAttr for T
pub fn set_repr<T>(&mut self)
where
T: PyObjectProtocol<'p>,
T: for<'p> PyObjectReprProtocol<'p>,
{
default fn del_attr() -> Option<ffi::setattrofunc> {
None
}
self.tp_repr = py_unary_func!(PyObjectReprProtocol, T::__repr__);
}
impl<T> DelAttr for T
pub fn set_hash<T>(&mut self)
where
T: for<'p> PyObjectDelAttrProtocol<'p>,
T: for<'p> PyObjectHashProtocol<'p>,
{
fn del_attr() -> Option<ffi::setattrofunc> {
py_func_del!(PyObjectDelAttrProtocol, T, __delattr__)
}
}
trait SetDelAttr {
fn set_del_attr() -> Option<ffi::setattrofunc>;
}
impl<'p, T> SetDelAttr for T
where
T: PyObjectProtocol<'p>,
{
default fn set_del_attr() -> Option<ffi::setattrofunc> {
None
}
}
impl<T> SetDelAttr for T
where
T: for<'p> PyObjectSetAttrProtocol<'p> + for<'p> PyObjectDelAttrProtocol<'p>,
{
fn set_del_attr() -> Option<ffi::setattrofunc> {
py_func_set_del!(
PyObjectSetAttrProtocol,
PyObjectDelAttrProtocol,
T,
__setattr__,
__delattr__
)
}
}
}
trait StrProtocolImpl {
fn tp_str() -> Option<ffi::unaryfunc>;
}
impl<'p, T> StrProtocolImpl for T
where
T: PyObjectProtocol<'p>,
{
default fn tp_str() -> Option<ffi::unaryfunc> {
None
}
}
impl<T> StrProtocolImpl for T
where
T: for<'p> PyObjectStrProtocol<'p>,
{
fn tp_str() -> Option<ffi::unaryfunc> {
py_unary_func!(PyObjectStrProtocol, T::__str__)
}
}
trait ReprProtocolImpl {
fn tp_repr() -> Option<ffi::unaryfunc>;
}
impl<'p, T> ReprProtocolImpl for T
where
T: PyObjectProtocol<'p>,
{
default fn tp_repr() -> Option<ffi::unaryfunc> {
None
}
}
impl<T> ReprProtocolImpl for T
where
T: for<'p> PyObjectReprProtocol<'p>,
{
fn tp_repr() -> Option<ffi::unaryfunc> {
py_unary_func!(PyObjectReprProtocol, T::__repr__)
}
}
trait HashProtocolImpl {
fn tp_hash() -> Option<ffi::hashfunc>;
}
impl<'p, T> HashProtocolImpl for T
where
T: PyObjectProtocol<'p>,
{
default fn tp_hash() -> Option<ffi::hashfunc> {
None
}
}
impl<T> HashProtocolImpl for T
where
T: for<'p> PyObjectHashProtocol<'p>,
{
fn tp_hash() -> Option<ffi::hashfunc> {
py_unary_func!(
self.tp_hash = py_unary_func!(
PyObjectHashProtocol,
T::__hash__,
ffi::Py_hash_t,
HashCallbackOutput
);
}
pub fn set_getattr<T>(&mut self)
where
T: for<'p> PyObjectGetAttrProtocol<'p>,
{
self.tp_getattro = tp_getattro::<T>();
}
pub fn set_richcompare<T>(&mut self)
where
T: for<'p> PyObjectRichcmpProtocol<'p>,
{
self.tp_richcompare = tp_richcompare::<T>();
}
pub fn set_setattr<T>(&mut self)
where
T: for<'p> PyObjectSetAttrProtocol<'p>,
{
self.tp_setattro = py_func_set!(PyObjectSetAttrProtocol, T, __setattr__);
}
pub fn set_delattr<T>(&mut self)
where
T: for<'p> PyObjectDelAttrProtocol<'p>,
{
self.tp_setattro = py_func_del!(PyObjectDelAttrProtocol, T, __delattr__);
}
pub fn set_setdelattr<T>(&mut self)
where
T: for<'p> PyObjectSetAttrProtocol<'p> + for<'p> PyObjectDelAttrProtocol<'p>,
{
self.tp_setattro = py_func_set_del!(
PyObjectSetAttrProtocol,
PyObjectDelAttrProtocol,
T,
__setattr__,
__delattr__
)
}
}
trait BoolProtocolImpl {
fn nb_bool() -> Option<ffi::inquiry>;
}
impl<'p, T> BoolProtocolImpl for T
where
T: PyObjectProtocol<'p>,
{
default fn nb_bool() -> Option<ffi::inquiry> {
None
}
}
impl<T> BoolProtocolImpl for T
where
T: for<'p> PyObjectBoolProtocol<'p>,
{
fn nb_bool() -> Option<ffi::inquiry> {
py_unary_func!(PyObjectBoolProtocol, T::__bool__, c_int)
pub fn set_bool<T>(&mut self)
where
T: for<'p> PyObjectBoolProtocol<'p>,
{
self.nb_bool = py_unary_func!(PyObjectBoolProtocol, T::__bool__, c_int);
}
}
trait RichcmpProtocolImpl {
fn tp_richcompare() -> Option<ffi::richcmpfunc>;
}
impl<'p, T> RichcmpProtocolImpl for T
fn tp_getattro<T>() -> Option<ffi::binaryfunc>
where
T: PyObjectProtocol<'p>,
T: for<'p> PyObjectGetAttrProtocol<'p>,
{
default fn tp_richcompare() -> Option<ffi::richcmpfunc> {
None
unsafe extern "C" fn wrap<T>(
slf: *mut ffi::PyObject,
arg: *mut ffi::PyObject,
) -> *mut ffi::PyObject
where
T: for<'p> PyObjectGetAttrProtocol<'p>,
{
crate::callback_body!(py, {
// Behave like python's __getattr__ (as opposed to __getattribute__) and check
// for existing fields and methods first
let existing = ffi::PyObject_GenericGetAttr(slf, arg);
if existing.is_null() {
// PyObject_HasAttr also tries to get an object and clears the error if it fails
ffi::PyErr_Clear();
} else {
return Ok(existing);
}
let slf = py.from_borrowed_ptr::<PyCell<T>>(slf);
let arg = py.from_borrowed_ptr::<PyAny>(arg);
call_ref!(slf, __getattr__, arg)
})
}
Some(wrap::<T>)
}
impl<T> RichcmpProtocolImpl for T
fn tp_richcompare<T>() -> Option<ffi::richcmpfunc>
where
T: for<'p> PyObjectRichcmpProtocol<'p>,
{
fn tp_richcompare() -> Option<ffi::richcmpfunc> {
unsafe extern "C" fn wrap<T>(
slf: *mut ffi::PyObject,
arg: *mut ffi::PyObject,
op: c_int,
) -> *mut ffi::PyObject
where
T: for<'p> PyObjectRichcmpProtocol<'p>,
{
crate::callback_body!(py, {
let slf = py.from_borrowed_ptr::<crate::PyCell<T>>(slf);
let arg = py.from_borrowed_ptr::<PyAny>(arg);
let op = extract_op(op)?;
let arg = arg.extract()?;
slf.try_borrow()?.__richcmp__(arg, op).into()
})
fn extract_op(op: c_int) -> PyResult<CompareOp> {
match op {
ffi::Py_LT => Ok(CompareOp::Lt),
ffi::Py_LE => Ok(CompareOp::Le),
ffi::Py_EQ => Ok(CompareOp::Eq),
ffi::Py_NE => Ok(CompareOp::Ne),
ffi::Py_GT => Ok(CompareOp::Gt),
ffi::Py_GE => Ok(CompareOp::Ge),
_ => Err(PyErr::new::<exceptions::ValueError, _>(
"tp_richcompare called with invalid comparison operator",
)),
}
Some(wrap::<T>)
}
}
unsafe extern "C" fn wrap<T>(
slf: *mut ffi::PyObject,
arg: *mut ffi::PyObject,
op: c_int,
) -> *mut ffi::PyObject
where
T: for<'p> PyObjectRichcmpProtocol<'p>,
{
crate::callback_body!(py, {
let slf = py.from_borrowed_ptr::<crate::PyCell<T>>(slf);
let arg = py.from_borrowed_ptr::<PyAny>(arg);
fn extract_op(op: c_int) -> PyResult<CompareOp> {
match op {
ffi::Py_LT => Ok(CompareOp::Lt),
ffi::Py_LE => Ok(CompareOp::Le),
ffi::Py_EQ => Ok(CompareOp::Eq),
ffi::Py_NE => Ok(CompareOp::Ne),
ffi::Py_GT => Ok(CompareOp::Gt),
ffi::Py_GE => Ok(CompareOp::Ge),
_ => Err(PyErr::new::<exceptions::ValueError, _>(
"tp_richcompare called with invalid comparison operator",
)),
let op = extract_op(op)?;
let arg = arg.extract()?;
slf.try_borrow()?.__richcmp__(arg, op).into()
})
}
Some(wrap::<T>)
}

View File

@ -5,7 +5,10 @@
//! For more information check [buffer protocol](https://docs.python.org/3/c-api/buffer.html)
//! c-api
use crate::err::PyResult;
use crate::{ffi, PyCell, PyClass, PyRefMut};
use crate::{
ffi::{self, PyBufferProcs},
PyCell, PyClass, PyRefMut,
};
use std::os::raw::c_int;
/// Buffer protocol interface
@ -37,96 +40,55 @@ pub trait PyBufferReleaseBufferProtocol<'p>: PyBufferProtocol<'p> {
type Result: Into<PyResult<()>>;
}
/// Set functions used by `#[pyproto]`.
#[doc(hidden)]
pub trait PyBufferProtocolImpl {
fn tp_as_buffer() -> Option<ffi::PyBufferProcs>;
}
impl<T> PyBufferProtocolImpl for T {
default fn tp_as_buffer() -> Option<ffi::PyBufferProcs> {
None
impl PyBufferProcs {
pub fn set_getbuffer<T>(&mut self)
where
T: for<'p> PyBufferGetBufferProtocol<'p>,
{
self.bf_getbuffer = bf_getbuffer::<T>();
}
pub fn set_releasebuffer<T>(&mut self)
where
T: for<'p> PyBufferReleaseBufferProtocol<'p>,
{
self.bf_releasebuffer = bf_releasebuffer::<T>();
}
}
impl<'p, T> PyBufferProtocolImpl for T
where
T: PyBufferProtocol<'p>,
{
#[inline]
#[allow(clippy::needless_update)] // For python 2 it's not useless
fn tp_as_buffer() -> Option<ffi::PyBufferProcs> {
Some(ffi::PyBufferProcs {
bf_getbuffer: Self::cb_bf_getbuffer(),
bf_releasebuffer: Self::cb_bf_releasebuffer(),
..ffi::PyBufferProcs_INIT
})
}
}
trait PyBufferGetBufferProtocolImpl {
fn cb_bf_getbuffer() -> Option<ffi::getbufferproc>;
}
impl<'p, T> PyBufferGetBufferProtocolImpl for T
where
T: PyBufferProtocol<'p>,
{
default fn cb_bf_getbuffer() -> Option<ffi::getbufferproc> {
None
}
}
impl<T> PyBufferGetBufferProtocolImpl for T
fn bf_getbuffer<T>() -> Option<ffi::getbufferproc>
where
T: for<'p> PyBufferGetBufferProtocol<'p>,
{
#[inline]
fn cb_bf_getbuffer() -> Option<ffi::getbufferproc> {
unsafe extern "C" fn wrap<T>(
slf: *mut ffi::PyObject,
arg1: *mut ffi::Py_buffer,
arg2: c_int,
) -> c_int
where
T: for<'p> PyBufferGetBufferProtocol<'p>,
{
crate::callback_body!(py, {
let slf = py.from_borrowed_ptr::<PyCell<T>>(slf);
T::bf_getbuffer(slf.try_borrow_mut()?, arg1, arg2).into()
})
}
Some(wrap::<T>)
unsafe extern "C" fn wrap<T>(
slf: *mut ffi::PyObject,
arg1: *mut ffi::Py_buffer,
arg2: c_int,
) -> c_int
where
T: for<'p> PyBufferGetBufferProtocol<'p>,
{
crate::callback_body!(py, {
let slf = py.from_borrowed_ptr::<PyCell<T>>(slf);
T::bf_getbuffer(slf.try_borrow_mut()?, arg1, arg2).into()
})
}
Some(wrap::<T>)
}
trait PyBufferReleaseBufferProtocolImpl {
fn cb_bf_releasebuffer() -> Option<ffi::releasebufferproc>;
}
impl<'p, T> PyBufferReleaseBufferProtocolImpl for T
where
T: PyBufferProtocol<'p>,
{
default fn cb_bf_releasebuffer() -> Option<ffi::releasebufferproc> {
None
}
}
impl<T> PyBufferReleaseBufferProtocolImpl for T
fn bf_releasebuffer<T>() -> Option<ffi::releasebufferproc>
where
T: for<'p> PyBufferReleaseBufferProtocol<'p>,
{
#[inline]
fn cb_bf_releasebuffer() -> Option<ffi::releasebufferproc> {
unsafe extern "C" fn wrap<T>(slf: *mut ffi::PyObject, arg1: *mut ffi::Py_buffer)
where
T: for<'p> PyBufferReleaseBufferProtocol<'p>,
{
crate::callback_body!(py, {
let slf = py.from_borrowed_ptr::<crate::PyCell<T>>(slf);
T::bf_releasebuffer(slf.try_borrow_mut()?, arg1).into()
})
}
Some(wrap::<T>)
unsafe extern "C" fn wrap<T>(slf: *mut ffi::PyObject, arg1: *mut ffi::Py_buffer)
where
T: for<'p> PyBufferReleaseBufferProtocol<'p>,
{
crate::callback_body!(py, {
let slf = py.from_borrowed_ptr::<crate::PyCell<T>>(slf);
T::bf_releasebuffer(slf.try_borrow_mut()?, arg1).into()
})
}
Some(wrap::<T>)
}

View File

@ -5,23 +5,26 @@
//! [Python information](
//! https://docs.python.org/3/reference/datamodel.html#implementing-descriptors)
use crate::class::methods::PyMethodDef;
use crate::err::PyResult;
use crate::types::{PyAny, PyType};
use crate::types::PyAny;
use crate::{ffi, FromPyObject, IntoPy, PyClass, PyObject};
use std::os::raw::c_int;
/// Descriptor interface
#[allow(unused_variables)]
pub trait PyDescrProtocol<'p>: PyClass {
fn __get__(&'p self, instance: &'p PyAny, owner: Option<&'p PyType>) -> Self::Result
fn __get__(
slf: Self::Receiver,
instance: Self::Inst,
owner: Option<Self::Owner>,
) -> Self::Result
where
Self: PyDescrGetProtocol<'p>,
{
unimplemented!()
}
fn __set__(&'p self, instance: &'p PyAny, value: &'p PyAny) -> Self::Result
fn __set__(slf: Self::Receiver, instance: Self::Inst, value: Self::Value) -> Self::Result
where
Self: PyDescrSetProtocol<'p>,
{
@ -44,6 +47,7 @@ pub trait PyDescrProtocol<'p>: PyClass {
}
pub trait PyDescrGetProtocol<'p>: PyDescrProtocol<'p> {
type Receiver: crate::derive_utils::TryFromPyCell<'p, Self>;
type Inst: FromPyObject<'p>;
type Owner: FromPyObject<'p>;
type Success: IntoPy<PyObject>;
@ -51,6 +55,7 @@ pub trait PyDescrGetProtocol<'p>: PyDescrProtocol<'p> {
}
pub trait PyDescrSetProtocol<'p>: PyDescrProtocol<'p> {
type Receiver: crate::derive_utils::TryFromPyCell<'p, Self>;
type Inst: FromPyObject<'p>;
type Value: FromPyObject<'p>;
type Result: Into<PyResult<()>>;
@ -66,76 +71,29 @@ pub trait PyDescrSetNameProtocol<'p>: PyDescrProtocol<'p> {
type Result: Into<PyResult<()>>;
}
trait PyDescrGetProtocolImpl {
fn tp_descr_get() -> Option<ffi::descrgetfunc>;
/// All FFI functions for description protocols.
#[derive(Default)]
pub struct PyDescrMethods {
pub tp_descr_get: Option<ffi::descrgetfunc>,
pub tp_descr_set: Option<ffi::descrsetfunc>,
}
impl<'p, T> PyDescrGetProtocolImpl for T
where
T: PyDescrProtocol<'p>,
{
default fn tp_descr_get() -> Option<ffi::descrgetfunc> {
None
}
}
impl<T> PyDescrGetProtocolImpl for T
where
T: for<'p> PyDescrGetProtocol<'p>,
{
fn tp_descr_get() -> Option<ffi::descrgetfunc> {
py_ternary_func!(PyDescrGetProtocol, T::__get__)
}
}
trait PyDescrSetProtocolImpl {
fn tp_descr_set() -> Option<ffi::descrsetfunc>;
}
impl<'p, T> PyDescrSetProtocolImpl for T
where
T: PyDescrProtocol<'p>,
{
default fn tp_descr_set() -> Option<ffi::descrsetfunc> {
None
}
}
impl<T> PyDescrSetProtocolImpl for T
where
T: for<'p> PyDescrSetProtocol<'p>,
{
fn tp_descr_set() -> Option<ffi::descrsetfunc> {
py_ternary_func!(PyDescrSetProtocol, T::__set__, c_int)
}
}
trait PyDescrDelProtocolImpl {
fn __del__() -> Option<PyMethodDef> {
None
}
}
impl<'p, T> PyDescrDelProtocolImpl for T where T: PyDescrProtocol<'p> {}
trait PyDescrSetNameProtocolImpl {
fn __set_name__() -> Option<PyMethodDef> {
None
}
}
impl<'p, T> PyDescrSetNameProtocolImpl for T where T: PyDescrProtocol<'p> {}
#[doc(hidden)]
pub trait PyDescrProtocolImpl {
fn tp_as_descr(_type_object: &mut ffi::PyTypeObject);
}
impl<T> PyDescrProtocolImpl for T {
default fn tp_as_descr(_type_object: &mut ffi::PyTypeObject) {}
}
impl<'p, T> PyDescrProtocolImpl for T
where
T: PyDescrProtocol<'p>,
{
fn tp_as_descr(type_object: &mut ffi::PyTypeObject) {
type_object.tp_descr_get = Self::tp_descr_get();
type_object.tp_descr_set = Self::tp_descr_set();
impl PyDescrMethods {
pub(crate) fn update_typeobj(&self, type_object: &mut ffi::PyTypeObject) {
type_object.tp_descr_get = self.tp_descr_get;
type_object.tp_descr_set = self.tp_descr_set;
}
pub fn set_descr_get<T>(&mut self)
where
T: for<'p> PyDescrGetProtocol<'p>,
{
self.tp_descr_get = py_ternarys_func!(PyDescrGetProtocol, T::__get__);
}
pub fn set_descr_set<T>(&mut self)
where
T: for<'p> PyDescrSetProtocol<'p>,
{
self.tp_descr_set = py_ternarys_func!(PyDescrSetProtocol, T::__set__, c_int);
}
}

View File

@ -18,25 +18,36 @@ pub trait PyGCProtocol<'p>: PyClass {
pub trait PyGCTraverseProtocol<'p>: PyGCProtocol<'p> {}
pub trait PyGCClearProtocol<'p>: PyGCProtocol<'p> {}
/// All FFI functions for gc protocols.
#[derive(Default)]
pub struct PyGCMethods {
pub tp_traverse: Option<ffi::traverseproc>,
pub tp_clear: Option<ffi::inquiry>,
}
#[doc(hidden)]
pub trait PyGCProtocolImpl {
fn update_type_object(_type_object: &mut ffi::PyTypeObject);
}
impl PyGCMethods {
pub(crate) fn update_typeobj(&self, type_object: &mut ffi::PyTypeObject) {
type_object.tp_traverse = self.tp_traverse;
type_object.tp_clear = self.tp_clear;
}
impl<'p, T> PyGCProtocolImpl for T {
default fn update_type_object(_type_object: &mut ffi::PyTypeObject) {}
}
pub fn set_traverse<T>(&mut self)
where
T: for<'p> PyGCTraverseProtocol<'p>,
{
self.tp_traverse = tp_traverse::<T>();
}
impl<'p, T> PyGCProtocolImpl for T
where
T: PyGCProtocol<'p>,
{
fn update_type_object(type_object: &mut ffi::PyTypeObject) {
type_object.tp_traverse = Self::tp_traverse();
type_object.tp_clear = Self::tp_clear();
pub fn set_clear<T>(&mut self)
where
T: for<'p> PyGCClearProtocol<'p>,
{
self.tp_clear = tp_clear::<T>();
}
}
/// Object visitor for GC.
#[derive(Copy, Clone)]
pub struct PyVisit<'p> {
visit: ffi::visitproc,
@ -48,6 +59,7 @@ pub struct PyVisit<'p> {
}
impl<'p> PyVisit<'p> {
/// Visit `obj`.
pub fn call<T>(&self, obj: &T) -> Result<(), PyTraverseError>
where
T: AsPyPointer,
@ -61,88 +73,55 @@ impl<'p> PyVisit<'p> {
}
}
trait PyGCTraverseProtocolImpl {
fn tp_traverse() -> Option<ffi::traverseproc>;
}
impl<'p, T> PyGCTraverseProtocolImpl for T
where
T: PyGCProtocol<'p>,
{
default fn tp_traverse() -> Option<ffi::traverseproc> {
None
}
}
#[doc(hidden)]
impl<T> PyGCTraverseProtocolImpl for T
fn tp_traverse<T>() -> Option<ffi::traverseproc>
where
T: for<'p> PyGCTraverseProtocol<'p>,
{
#[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>,
{
let pool = crate::GILPool::new();
let py = pool.python();
let slf = py.from_borrowed_ptr::<PyCell<T>>(slf);
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>,
{
let pool = crate::GILPool::new();
let py = pool.python();
let slf = py.from_borrowed_ptr::<PyCell<T>>(slf);
let visit = PyVisit {
visit,
arg,
_py: py,
};
let borrow = slf.try_borrow();
if let Ok(borrow) = borrow {
match borrow.__traverse__(visit) {
Ok(()) => 0,
Err(PyTraverseError(code)) => code,
}
} else {
0
let visit = PyVisit {
visit,
arg,
_py: py,
};
let borrow = slf.try_borrow();
if let Ok(borrow) = borrow {
match borrow.__traverse__(visit) {
Ok(()) => 0,
Err(PyTraverseError(code)) => code,
}
} else {
0
}
Some(tp_traverse::<T>)
}
Some(tp_traverse::<T>)
}
trait PyGCClearProtocolImpl {
fn tp_clear() -> Option<ffi::inquiry>;
}
impl<'p, T> PyGCClearProtocolImpl for T
where
T: PyGCProtocol<'p>,
{
default fn tp_clear() -> Option<ffi::inquiry> {
None
}
}
impl<T> PyGCClearProtocolImpl for T
fn tp_clear<T>() -> Option<ffi::inquiry>
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>,
{
let pool = crate::GILPool::new();
let py = pool.python();
let slf = py.from_borrowed_ptr::<PyCell<T>>(slf);
unsafe extern "C" fn tp_clear<T>(slf: *mut ffi::PyObject) -> c_int
where
T: for<'p> PyGCClearProtocol<'p>,
{
let pool = crate::GILPool::new();
let py = pool.python();
let slf = py.from_borrowed_ptr::<PyCell<T>>(slf);
slf.borrow_mut().__clear__();
0
}
Some(tp_clear::<T>)
slf.borrow_mut().__clear__();
0
}
Some(tp_clear::<T>)
}

View File

@ -40,69 +40,29 @@ pub trait PyIterNextProtocol<'p>: PyIterProtocol<'p> {
type Result: Into<PyResult<Option<Self::Success>>>;
}
#[derive(Default)]
pub struct PyIterMethods {
pub tp_iter: Option<ffi::getiterfunc>,
pub tp_iternext: Option<ffi::iternextfunc>,
}
#[doc(hidden)]
pub trait PyIterProtocolImpl {
fn tp_as_iter(_typeob: &mut ffi::PyTypeObject);
}
impl<T> PyIterProtocolImpl for T {
default fn tp_as_iter(_typeob: &mut ffi::PyTypeObject) {}
}
impl<'p, T> PyIterProtocolImpl for T
where
T: PyIterProtocol<'p>,
{
#[inline]
fn tp_as_iter(typeob: &mut ffi::PyTypeObject) {
typeob.tp_iter = Self::tp_iter();
typeob.tp_iternext = Self::tp_iternext();
impl PyIterMethods {
pub(crate) fn update_typeobj(&self, type_object: &mut ffi::PyTypeObject) {
type_object.tp_iter = self.tp_iter;
type_object.tp_iternext = self.tp_iternext;
}
}
trait PyIterIterProtocolImpl {
fn tp_iter() -> Option<ffi::getiterfunc>;
}
impl<'p, T> PyIterIterProtocolImpl for T
where
T: PyIterProtocol<'p>,
{
default fn tp_iter() -> Option<ffi::getiterfunc> {
None
pub fn set_iter<T>(&mut self)
where
T: for<'p> PyIterIterProtocol<'p>,
{
self.tp_iter = py_unarys_func!(PyIterIterProtocol, T::__iter__);
}
}
impl<T> PyIterIterProtocolImpl for T
where
T: for<'p> PyIterIterProtocol<'p>,
{
#[inline]
fn tp_iter() -> Option<ffi::getiterfunc> {
py_unarys_func!(PyIterIterProtocol, T::__iter__)
}
}
trait PyIterNextProtocolImpl {
fn tp_iternext() -> Option<ffi::iternextfunc>;
}
impl<'p, T> PyIterNextProtocolImpl for T
where
T: PyIterProtocol<'p>,
{
default fn tp_iternext() -> Option<ffi::iternextfunc> {
None
}
}
impl<T> PyIterNextProtocolImpl for T
where
T: for<'p> PyIterNextProtocol<'p>,
{
#[inline]
fn tp_iternext() -> Option<ffi::iternextfunc> {
py_unarys_func!(PyIterNextProtocol, T::__next__, IterNextConverter)
pub fn set_iternext<T>(&mut self)
where
T: for<'p> PyIterNextProtocol<'p>,
{
self.tp_iternext = py_unarys_func!(PyIterNextProtocol, T::__next__, IterNextConverter);
}
}

View File

@ -34,8 +34,9 @@ macro_rules! py_unarys_func {
{
$crate::callback_body!(py, {
let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
let borrow = <T::Receiver>::try_from_pycell(slf)
.map_err(|e| e.into())?;
let borrow =
<T::Receiver as $crate::derive_utils::TryFromPyCell<_>>::try_from_pycell(slf)
.map_err(|e| e.into())?;
$class::$f(borrow).into()$(.map($conv))?
})
@ -106,7 +107,7 @@ macro_rules! py_binary_num_func {
#[macro_export]
#[doc(hidden)]
macro_rules! py_binary_reverse_num_func {
macro_rules! py_binary_reversed_num_func {
($trait:ident, $class:ident :: $f:ident) => {{
unsafe extern "C" fn wrap<T>(
lhs: *mut ffi::PyObject,
@ -177,7 +178,7 @@ macro_rules! py_ssizearg_func {
#[macro_export]
#[doc(hidden)]
macro_rules! py_ternary_func {
macro_rules! py_ternarys_func {
($trait:ident, $class:ident :: $f:ident, $return_type:ty) => {{
unsafe extern "C" fn wrap<T>(
slf: *mut $crate::ffi::PyObject,
@ -189,6 +190,9 @@ macro_rules! py_ternary_func {
{
$crate::callback_body!(py, {
let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
let slf =
<T::Receiver as $crate::derive_utils::TryFromPyCell<_>>::try_from_pycell(slf)
.map_err(|e| e.into())?;
let arg1 = py
.from_borrowed_ptr::<$crate::types::PyAny>(arg1)
.extract()?;
@ -196,14 +200,14 @@ macro_rules! py_ternary_func {
.from_borrowed_ptr::<$crate::types::PyAny>(arg2)
.extract()?;
slf.try_borrow()?.$f(arg1, arg2).into()
$class::$f(slf, arg1, arg2).into()
})
}
Some(wrap::<T>)
}};
($trait:ident, $class:ident :: $f:ident) => {
py_ternary_func!($trait, $class::$f, *mut $crate::ffi::PyObject);
py_ternarys_func!($trait, $class::$f, *mut $crate::ffi::PyObject);
};
}
@ -240,7 +244,7 @@ macro_rules! py_ternary_num_func {
#[macro_export]
#[doc(hidden)]
macro_rules! py_ternary_reverse_num_func {
macro_rules! py_ternary_reversed_num_func {
($trait:ident, $class:ident :: $f:ident) => {{
unsafe extern "C" fn wrap<T>(
arg1: *mut $crate::ffi::PyObject,

View File

@ -75,154 +75,41 @@ pub trait PyMappingReversedProtocol<'p>: PyMappingProtocol<'p> {
}
#[doc(hidden)]
pub trait PyMappingProtocolImpl {
fn tp_as_mapping() -> Option<ffi::PyMappingMethods>;
}
impl<T> PyMappingProtocolImpl for T {
default fn tp_as_mapping() -> Option<ffi::PyMappingMethods> {
None
impl ffi::PyMappingMethods {
pub fn set_length<T>(&mut self)
where
T: for<'p> PyMappingLenProtocol<'p>,
{
self.mp_length = py_len_func!(PyMappingLenProtocol, T::__len__);
}
}
impl<'p, T> PyMappingProtocolImpl for T
where
T: PyMappingProtocol<'p>,
{
#[inline]
fn tp_as_mapping() -> Option<ffi::PyMappingMethods> {
let f = if let Some(df) = Self::mp_del_subscript() {
Some(df)
} else {
Self::mp_ass_subscript()
};
Some(ffi::PyMappingMethods {
mp_length: Self::mp_length(),
mp_subscript: Self::mp_subscript(),
mp_ass_subscript: f,
})
pub fn set_getitem<T>(&mut self)
where
T: for<'p> PyMappingGetItemProtocol<'p>,
{
self.mp_subscript = py_binary_func!(PyMappingGetItemProtocol, T::__getitem__);
}
}
trait PyMappingLenProtocolImpl {
fn mp_length() -> Option<ffi::lenfunc>;
}
impl<'p, T> PyMappingLenProtocolImpl for T
where
T: PyMappingProtocol<'p>,
{
default fn mp_length() -> Option<ffi::lenfunc> {
None
pub fn set_setitem<T>(&mut self)
where
T: for<'p> PyMappingSetItemProtocol<'p>,
{
self.mp_ass_subscript = py_func_set!(PyMappingSetItemProtocol, T, __setitem__);
}
}
impl<T> PyMappingLenProtocolImpl for T
where
T: for<'p> PyMappingLenProtocol<'p>,
{
#[inline]
fn mp_length() -> Option<ffi::lenfunc> {
py_len_func!(PyMappingLenProtocol, T::__len__)
pub fn set_delitem<T>(&mut self)
where
T: for<'p> PyMappingDelItemProtocol<'p>,
{
self.mp_ass_subscript = py_func_del!(PyMappingDelItemProtocol, T, __delitem__);
}
}
trait PyMappingGetItemProtocolImpl {
fn mp_subscript() -> Option<ffi::binaryfunc>;
}
impl<'p, T> PyMappingGetItemProtocolImpl for T
where
T: PyMappingProtocol<'p>,
{
default fn mp_subscript() -> Option<ffi::binaryfunc> {
None
}
}
impl<T> PyMappingGetItemProtocolImpl for T
where
T: for<'p> PyMappingGetItemProtocol<'p>,
{
#[inline]
fn mp_subscript() -> Option<ffi::binaryfunc> {
py_binary_func!(PyMappingGetItemProtocol, T::__getitem__)
}
}
trait PyMappingSetItemProtocolImpl {
fn mp_ass_subscript() -> Option<ffi::objobjargproc>;
}
impl<'p, T> PyMappingSetItemProtocolImpl for T
where
T: PyMappingProtocol<'p>,
{
default fn mp_ass_subscript() -> Option<ffi::objobjargproc> {
None
}
}
impl<T> PyMappingSetItemProtocolImpl for T
where
T: for<'p> PyMappingSetItemProtocol<'p>,
{
#[inline]
fn mp_ass_subscript() -> Option<ffi::objobjargproc> {
py_func_set!(PyMappingSetItemProtocol, T, __setitem__)
}
}
/// Returns `None` if PyMappingDelItemProtocol isn't implemented, otherwise dispatches to
/// `DelSetItemDispatch`
trait DeplItemDipatch {
fn mp_del_subscript() -> Option<ffi::objobjargproc>;
}
impl<'p, T> DeplItemDipatch for T
where
T: PyMappingProtocol<'p>,
{
default fn mp_del_subscript() -> Option<ffi::objobjargproc> {
None
}
}
/// Returns `py_func_set_del` if PyMappingSetItemProtocol is implemented, otherwise `py_func_del`
trait DelSetItemDispatch: Sized + for<'p> PyMappingDelItemProtocol<'p> {
fn det_set_dispatch() -> Option<ffi::objobjargproc>;
}
impl<T> DelSetItemDispatch for T
where
T: Sized + for<'p> PyMappingDelItemProtocol<'p>,
{
default fn det_set_dispatch() -> Option<ffi::objobjargproc> {
py_func_del!(PyMappingDelItemProtocol, Self, __delitem__)
}
}
impl<T> DelSetItemDispatch for T
where
T: for<'p> PyMappingSetItemProtocol<'p> + for<'p> PyMappingDelItemProtocol<'p>,
{
fn det_set_dispatch() -> Option<ffi::objobjargproc> {
py_func_set_del!(
pub fn set_setdelitem<T>(&mut self)
where
T: for<'p> PyMappingSetItemProtocol<'p> + for<'p> PyMappingDelItemProtocol<'p>,
{
self.mp_ass_subscript = py_func_set_del!(
PyMappingSetItemProtocol,
PyMappingDelItemProtocol,
T,
__setitem__,
__delitem__
)
}
}
impl<T> DeplItemDipatch for T
where
T: Sized + for<'p> PyMappingDelItemProtocol<'p>,
{
fn mp_del_subscript() -> Option<ffi::objobjargproc> {
<T as DelSetItemDispatch>::det_set_dispatch()
);
}
}

View File

@ -14,6 +14,7 @@ pub mod iter;
pub mod mapping;
pub mod methods;
pub mod number;
pub mod proto_methods;
pub mod pyasync;
pub mod sequence;

File diff suppressed because it is too large Load Diff

154
src/class/proto_methods.rs Normal file
View File

@ -0,0 +1,154 @@
use crate::class::{
basic::PyObjectMethods, descr::PyDescrMethods, gc::PyGCMethods, iter::PyIterMethods,
};
use crate::ffi::{
PyAsyncMethods, PyBufferProcs, PyMappingMethods, PyNumberMethods, PySequenceMethods,
};
use std::{
ptr::{self, NonNull},
sync::atomic::{AtomicPtr, Ordering},
};
/// Defines all method tables we need for object protocols.
// Note(kngwyu): default implementations are for rust-numpy. Please don't remove them.
pub trait PyProtoMethods {
fn async_methods() -> Option<NonNull<PyAsyncMethods>> {
None
}
fn basic_methods() -> Option<NonNull<PyObjectMethods>> {
None
}
fn buffer_methods() -> Option<NonNull<PyBufferProcs>> {
None
}
fn descr_methods() -> Option<NonNull<PyDescrMethods>> {
None
}
fn gc_methods() -> Option<NonNull<PyGCMethods>> {
None
}
fn mapping_methods() -> Option<NonNull<PyMappingMethods>> {
None
}
fn number_methods() -> Option<NonNull<PyNumberMethods>> {
None
}
fn iter_methods() -> Option<NonNull<PyIterMethods>> {
None
}
fn sequence_methods() -> Option<NonNull<PySequenceMethods>> {
None
}
}
/// Indicates that a type has a protocol registry. Implemented by `#[pyclass]`.
#[doc(hidden)]
pub trait HasProtoRegistry: Sized + 'static {
fn registry() -> &'static PyProtoRegistry;
}
impl<T: HasProtoRegistry> PyProtoMethods for T {
fn async_methods() -> Option<NonNull<PyAsyncMethods>> {
NonNull::new(Self::registry().async_methods.load(Ordering::Relaxed))
}
fn basic_methods() -> Option<NonNull<PyObjectMethods>> {
NonNull::new(Self::registry().basic_methods.load(Ordering::Relaxed))
}
fn buffer_methods() -> Option<NonNull<PyBufferProcs>> {
NonNull::new(Self::registry().buffer_methods.load(Ordering::Relaxed))
}
fn descr_methods() -> Option<NonNull<PyDescrMethods>> {
NonNull::new(Self::registry().descr_methods.load(Ordering::Relaxed))
}
fn gc_methods() -> Option<NonNull<PyGCMethods>> {
NonNull::new(Self::registry().gc_methods.load(Ordering::Relaxed))
}
fn mapping_methods() -> Option<NonNull<PyMappingMethods>> {
NonNull::new(Self::registry().mapping_methods.load(Ordering::Relaxed))
}
fn number_methods() -> Option<NonNull<PyNumberMethods>> {
NonNull::new(Self::registry().number_methods.load(Ordering::Relaxed))
}
fn iter_methods() -> Option<NonNull<PyIterMethods>> {
NonNull::new(Self::registry().iter_methods.load(Ordering::Relaxed))
}
fn sequence_methods() -> Option<NonNull<PySequenceMethods>> {
NonNull::new(Self::registry().sequence_methods.load(Ordering::Relaxed))
}
}
/// Stores all method protocols.
/// Used in the proc-macro code as a static variable.
#[doc(hidden)]
pub struct PyProtoRegistry {
/// Async protocols.
async_methods: AtomicPtr<PyAsyncMethods>,
/// Basic protocols.
basic_methods: AtomicPtr<PyObjectMethods>,
/// Buffer protocols.
buffer_methods: AtomicPtr<PyBufferProcs>,
/// Descr pProtocols.
descr_methods: AtomicPtr<PyDescrMethods>,
/// GC protocols.
gc_methods: AtomicPtr<PyGCMethods>,
/// Mapping protocols.
mapping_methods: AtomicPtr<PyMappingMethods>,
/// Number protocols.
number_methods: AtomicPtr<PyNumberMethods>,
/// Iterator protocols.
iter_methods: AtomicPtr<PyIterMethods>,
/// Sequence protocols.
sequence_methods: AtomicPtr<PySequenceMethods>,
}
impl PyProtoRegistry {
pub const fn new() -> Self {
PyProtoRegistry {
async_methods: AtomicPtr::new(ptr::null_mut()),
basic_methods: AtomicPtr::new(ptr::null_mut()),
buffer_methods: AtomicPtr::new(ptr::null_mut()),
descr_methods: AtomicPtr::new(ptr::null_mut()),
gc_methods: AtomicPtr::new(ptr::null_mut()),
mapping_methods: AtomicPtr::new(ptr::null_mut()),
number_methods: AtomicPtr::new(ptr::null_mut()),
iter_methods: AtomicPtr::new(ptr::null_mut()),
sequence_methods: AtomicPtr::new(ptr::null_mut()),
}
}
pub fn set_async_methods(&self, methods: PyAsyncMethods) {
self.async_methods
.store(Box::into_raw(Box::new(methods)), Ordering::Relaxed)
}
pub fn set_basic_methods(&self, methods: PyObjectMethods) {
self.basic_methods
.store(Box::into_raw(Box::new(methods)), Ordering::Relaxed)
}
pub fn set_buffer_methods(&self, methods: PyBufferProcs) {
self.buffer_methods
.store(Box::into_raw(Box::new(methods)), Ordering::Relaxed)
}
pub fn set_descr_methods(&self, methods: PyDescrMethods) {
self.descr_methods
.store(Box::into_raw(Box::new(methods)), Ordering::Relaxed)
}
pub fn set_gc_methods(&self, methods: PyGCMethods) {
self.gc_methods
.store(Box::into_raw(Box::new(methods)), Ordering::Relaxed)
}
pub fn set_mapping_methods(&self, methods: PyMappingMethods) {
self.mapping_methods
.store(Box::into_raw(Box::new(methods)), Ordering::Relaxed)
}
pub fn set_number_methods(&self, methods: PyNumberMethods) {
self.number_methods
.store(Box::into_raw(Box::new(methods)), Ordering::Relaxed)
}
pub fn set_iter_methods(&self, methods: PyIterMethods) {
self.iter_methods
.store(Box::into_raw(Box::new(methods)), Ordering::Relaxed)
}
pub fn set_sequence_methods(&self, methods: PySequenceMethods) {
self.sequence_methods
.store(Box::into_raw(Box::new(methods)), Ordering::Relaxed)
}
}

View File

@ -8,6 +8,7 @@
//! [PEP-0492](https://www.python.org/dev/peps/pep-0492/)
//!
use crate::derive_utils::TryFromPyCell;
use crate::err::PyResult;
use crate::{ffi, PyClass, PyObject};
@ -16,21 +17,21 @@ use crate::{ffi, PyClass, PyObject};
/// Each method in this trait corresponds to Python async/await implementation.
#[allow(unused_variables)]
pub trait PyAsyncProtocol<'p>: PyClass {
fn __await__(&'p self) -> Self::Result
fn __await__(slf: Self::Receiver) -> Self::Result
where
Self: PyAsyncAwaitProtocol<'p>,
{
unimplemented!()
}
fn __aiter__(&'p self) -> Self::Result
fn __aiter__(slf: Self::Receiver) -> Self::Result
where
Self: PyAsyncAiterProtocol<'p>,
{
unimplemented!()
}
fn __anext__(&'p mut self) -> Self::Result
fn __anext__(slf: Self::Receiver) -> Self::Result
where
Self: PyAsyncAnextProtocol<'p>,
{
@ -58,16 +59,19 @@ pub trait PyAsyncProtocol<'p>: PyClass {
}
pub trait PyAsyncAwaitProtocol<'p>: PyAsyncProtocol<'p> {
type Receiver: TryFromPyCell<'p, Self>;
type Success: crate::IntoPy<PyObject>;
type Result: Into<PyResult<Self::Success>>;
}
pub trait PyAsyncAiterProtocol<'p>: PyAsyncProtocol<'p> {
type Receiver: TryFromPyCell<'p, Self>;
type Success: crate::IntoPy<PyObject>;
type Result: Into<PyResult<Self::Success>>;
}
pub trait PyAsyncAnextProtocol<'p>: PyAsyncProtocol<'p> {
type Receiver: TryFromPyCell<'p, Self>;
type Success: crate::IntoPy<PyObject>;
type Result: Into<PyResult<Option<Self::Success>>>;
}
@ -86,91 +90,29 @@ pub trait PyAsyncAexitProtocol<'p>: PyAsyncProtocol<'p> {
}
#[doc(hidden)]
pub trait PyAsyncProtocolImpl {
fn tp_as_async() -> Option<ffi::PyAsyncMethods>;
}
impl<T> PyAsyncProtocolImpl for T {
default fn tp_as_async() -> Option<ffi::PyAsyncMethods> {
None
impl ffi::PyAsyncMethods {
pub fn set_await<T>(&mut self)
where
T: for<'p> PyAsyncAwaitProtocol<'p>,
{
self.am_await = py_unarys_func!(PyAsyncAwaitProtocol, T::__await__);
}
}
impl<'p, T> PyAsyncProtocolImpl for T
where
T: PyAsyncProtocol<'p>,
{
#[inline]
fn tp_as_async() -> Option<ffi::PyAsyncMethods> {
Some(ffi::PyAsyncMethods {
am_await: Self::am_await(),
am_aiter: Self::am_aiter(),
am_anext: Self::am_anext(),
})
pub fn set_aiter<T>(&mut self)
where
T: for<'p> PyAsyncAiterProtocol<'p>,
{
self.am_aiter = py_unarys_func!(PyAsyncAiterProtocol, T::__aiter__);
}
}
trait PyAsyncAwaitProtocolImpl {
fn am_await() -> Option<ffi::unaryfunc>;
}
impl<'p, T> PyAsyncAwaitProtocolImpl for T
where
T: PyAsyncProtocol<'p>,
{
default fn am_await() -> Option<ffi::unaryfunc> {
None
}
}
impl<T> PyAsyncAwaitProtocolImpl for T
where
T: for<'p> PyAsyncAwaitProtocol<'p>,
{
#[inline]
fn am_await() -> Option<ffi::unaryfunc> {
py_unary_func!(PyAsyncAwaitProtocol, T::__await__)
}
}
trait PyAsyncAiterProtocolImpl {
fn am_aiter() -> Option<ffi::unaryfunc>;
}
impl<'p, T> PyAsyncAiterProtocolImpl for T
where
T: PyAsyncProtocol<'p>,
{
default fn am_aiter() -> Option<ffi::unaryfunc> {
None
}
}
impl<T> PyAsyncAiterProtocolImpl for T
where
T: for<'p> PyAsyncAiterProtocol<'p>,
{
#[inline]
fn am_aiter() -> Option<ffi::unaryfunc> {
py_unary_func!(PyAsyncAiterProtocol, T::__aiter__)
}
}
trait PyAsyncAnextProtocolImpl {
fn am_anext() -> Option<ffi::unaryfunc>;
}
impl<'p, T> PyAsyncAnextProtocolImpl for T
where
T: PyAsyncProtocol<'p>,
{
default fn am_anext() -> Option<ffi::unaryfunc> {
None
pub fn set_anext<T>(&mut self)
where
T: for<'p> PyAsyncAnextProtocol<'p>,
{
self.am_anext = anext::am_anext::<T>();
}
}
mod anext {
use super::{PyAsyncAnextProtocol, PyAsyncAnextProtocolImpl};
use super::PyAsyncAnextProtocol;
use crate::callback::IntoPyCallbackOutput;
use crate::err::PyResult;
use crate::IntoPyPointer;
@ -191,19 +133,11 @@ mod anext {
}
}
impl<T> PyAsyncAnextProtocolImpl for T
#[inline]
pub(super) fn am_anext<T>() -> Option<ffi::unaryfunc>
where
T: for<'p> PyAsyncAnextProtocol<'p>,
{
#[inline]
fn am_anext() -> Option<ffi::unaryfunc> {
py_unary_func!(
PyAsyncAnextProtocol,
T::__anext__,
call_mut,
*mut crate::ffi::PyObject,
IterANextOutput
)
}
py_unarys_func!(PyAsyncAnextProtocol, T::__anext__, IterANextOutput)
}
}

View File

@ -127,77 +127,75 @@ pub trait PySequenceInplaceRepeatProtocol<'p>: PySequenceProtocol<'p> + IntoPy<P
}
#[doc(hidden)]
pub trait PySequenceProtocolImpl {
fn tp_as_sequence() -> Option<ffi::PySequenceMethods>;
}
impl<T> PySequenceProtocolImpl for T {
default fn tp_as_sequence() -> Option<ffi::PySequenceMethods> {
None
impl ffi::PySequenceMethods {
pub fn set_len<T>(&mut self)
where
T: for<'p> PySequenceLenProtocol<'p>,
{
self.sq_length = py_len_func!(PySequenceLenProtocol, T::__len__);
}
}
impl<'p, T> PySequenceProtocolImpl for T
where
T: PySequenceProtocol<'p>,
{
fn tp_as_sequence() -> Option<ffi::PySequenceMethods> {
Some(ffi::PySequenceMethods {
sq_length: Self::sq_length(),
sq_concat: Self::sq_concat(),
sq_repeat: Self::sq_repeat(),
sq_item: Self::sq_item(),
was_sq_slice: ::std::ptr::null_mut(),
sq_ass_item: sq_ass_item_impl::sq_ass_item::<Self>(),
was_sq_ass_slice: ::std::ptr::null_mut(),
sq_contains: Self::sq_contains(),
sq_inplace_concat: Self::sq_inplace_concat(),
sq_inplace_repeat: Self::sq_inplace_repeat(),
})
pub fn set_concat<T>(&mut self)
where
T: for<'p> PySequenceConcatProtocol<'p>,
{
self.sq_concat = py_binary_func!(PySequenceConcatProtocol, T::__concat__);
}
}
trait PySequenceLenProtocolImpl {
fn sq_length() -> Option<ffi::lenfunc>;
}
impl<'p, T> PySequenceLenProtocolImpl for T
where
T: PySequenceProtocol<'p>,
{
default fn sq_length() -> Option<ffi::lenfunc> {
None
pub fn set_repeat<T>(&mut self)
where
T: for<'p> PySequenceRepeatProtocol<'p>,
{
self.sq_repeat = py_ssizearg_func!(PySequenceRepeatProtocol, T::__repeat__);
}
}
impl<T> PySequenceLenProtocolImpl for T
where
T: for<'p> PySequenceLenProtocol<'p>,
{
fn sq_length() -> Option<ffi::lenfunc> {
py_len_func!(PySequenceLenProtocol, T::__len__)
pub fn set_getitem<T>(&mut self)
where
T: for<'p> PySequenceGetItemProtocol<'p>,
{
self.sq_item = py_ssizearg_func!(PySequenceGetItemProtocol, T::__getitem__);
}
}
trait PySequenceGetItemProtocolImpl {
fn sq_item() -> Option<ffi::ssizeargfunc>;
}
impl<'p, T> PySequenceGetItemProtocolImpl for T
where
T: PySequenceProtocol<'p>,
{
default fn sq_item() -> Option<ffi::ssizeargfunc> {
None
pub fn set_setitem<T>(&mut self)
where
T: for<'p> PySequenceSetItemProtocol<'p>,
{
self.sq_ass_item = sq_ass_item_impl::set_item::<T>();
}
}
impl<T> PySequenceGetItemProtocolImpl for T
where
T: for<'p> PySequenceGetItemProtocol<'p>,
{
fn sq_item() -> Option<ffi::ssizeargfunc> {
py_ssizearg_func!(PySequenceGetItemProtocol, T::__getitem__)
pub fn set_delitem<T>(&mut self)
where
T: for<'p> PySequenceDelItemProtocol<'p>,
{
self.sq_ass_item = sq_ass_item_impl::del_item::<T>();
}
pub fn set_setdelitem<T>(&mut self)
where
T: for<'p> PySequenceDelItemProtocol<'p> + for<'p> PySequenceSetItemProtocol<'p>,
{
self.sq_ass_item = sq_ass_item_impl::set_del_item::<T>();
}
pub fn set_contains<T>(&mut self)
where
T: for<'p> PySequenceContainsProtocol<'p>,
{
self.sq_contains = py_binary_func!(PySequenceContainsProtocol, T::__contains__, c_int);
}
pub fn set_inplace_concat<T>(&mut self)
where
T: for<'p> PySequenceInplaceConcatProtocol<'p>,
{
self.sq_inplace_concat = py_binary_func!(
PySequenceInplaceConcatProtocol,
T::__inplace_concat__,
*mut ffi::PyObject,
call_mut
)
}
pub fn set_inplace_repeat<T>(&mut self)
where
T: for<'p> PySequenceInplaceRepeatProtocol<'p>,
{
self.sq_inplace_repeat = py_ssizearg_func!(
PySequenceInplaceRepeatProtocol,
T::__inplace_repeat__,
call_mut
)
}
}
@ -207,275 +205,90 @@ where
mod sq_ass_item_impl {
use super::*;
/// ssizeobjargproc PySequenceMethods.sq_ass_item
///
/// This function is used by PySequence_SetItem() and has the same signature. It is also used
/// by PyObject_SetItem() and PyObject_DelItem(), after trying the item assignment and deletion
/// via the mp_ass_subscript slot. This slot may be left to NULL if the object does not support
/// item assignment and deletion.
pub(super) fn sq_ass_item<'p, T>() -> Option<ffi::ssizeobjargproc>
where
T: PySequenceProtocol<'p>,
{
if let Some(del_set_item) = T::del_set_item() {
Some(del_set_item)
} else if let Some(del_item) = T::del_item() {
Some(del_item)
} else if let Some(set_item) = T::set_item() {
Some(set_item)
} else {
None
}
}
trait SetItem {
fn set_item() -> Option<ffi::ssizeobjargproc>;
}
impl<'p, T> SetItem for T
where
T: PySequenceProtocol<'p>,
{
default fn set_item() -> Option<ffi::ssizeobjargproc> {
None
}
}
impl<T> SetItem for T
pub(super) fn set_item<T>() -> Option<ffi::ssizeobjargproc>
where
T: for<'p> PySequenceSetItemProtocol<'p>,
{
fn set_item() -> Option<ffi::ssizeobjargproc> {
unsafe extern "C" fn wrap<T>(
slf: *mut ffi::PyObject,
key: ffi::Py_ssize_t,
value: *mut ffi::PyObject,
) -> c_int
where
T: for<'p> PySequenceSetItemProtocol<'p>,
{
crate::callback_body!(py, {
let slf = py.from_borrowed_ptr::<PyCell<T>>(slf);
unsafe extern "C" fn wrap<T>(
slf: *mut ffi::PyObject,
key: ffi::Py_ssize_t,
value: *mut ffi::PyObject,
) -> c_int
where
T: for<'p> PySequenceSetItemProtocol<'p>,
{
crate::callback_body!(py, {
let slf = py.from_borrowed_ptr::<PyCell<T>>(slf);
if value.is_null() {
return Err(PyErr::new::<exceptions::NotImplementedError, _>(format!(
"Item deletion is not supported by {:?}",
stringify!(T)
)));
}
if value.is_null() {
return Err(PyErr::new::<exceptions::NotImplementedError, _>(format!(
"Item deletion is not supported by {:?}",
stringify!(T)
)));
}
let mut slf = slf.try_borrow_mut()?;
let value = py.from_borrowed_ptr::<PyAny>(value);
let value = value.extract()?;
slf.__setitem__(key.into(), value).into()
})
}
Some(wrap::<T>)
let mut slf = slf.try_borrow_mut()?;
let value = py.from_borrowed_ptr::<PyAny>(value);
let value = value.extract()?;
slf.__setitem__(key.into(), value).into()
})
}
Some(wrap::<T>)
}
trait DelItem {
fn del_item() -> Option<ffi::ssizeobjargproc>;
}
impl<'p, T> DelItem for T
where
T: PySequenceProtocol<'p>,
{
default fn del_item() -> Option<ffi::ssizeobjargproc> {
None
}
}
impl<T> DelItem for T
pub(super) fn del_item<T>() -> Option<ffi::ssizeobjargproc>
where
T: for<'p> PySequenceDelItemProtocol<'p>,
{
fn del_item() -> Option<ffi::ssizeobjargproc> {
unsafe extern "C" fn wrap<T>(
slf: *mut ffi::PyObject,
key: ffi::Py_ssize_t,
value: *mut ffi::PyObject,
) -> c_int
where
T: for<'p> PySequenceDelItemProtocol<'p>,
{
crate::callback_body!(py, {
let slf = py.from_borrowed_ptr::<PyCell<T>>(slf);
unsafe extern "C" fn wrap<T>(
slf: *mut ffi::PyObject,
key: ffi::Py_ssize_t,
value: *mut ffi::PyObject,
) -> c_int
where
T: for<'p> PySequenceDelItemProtocol<'p>,
{
crate::callback_body!(py, {
let slf = py.from_borrowed_ptr::<PyCell<T>>(slf);
if value.is_null() {
slf.borrow_mut().__delitem__(key.into()).into()
} else {
Err(PyErr::new::<exceptions::NotImplementedError, _>(format!(
"Item assignment not supported by {:?}",
stringify!(T)
)))
}
})
}
Some(wrap::<T>)
if value.is_null() {
slf.borrow_mut().__delitem__(key.into()).into()
} else {
Err(PyErr::new::<exceptions::NotImplementedError, _>(format!(
"Item assignment not supported by {:?}",
stringify!(T)
)))
}
})
}
Some(wrap::<T>)
}
trait DelSetItem {
fn del_set_item() -> Option<ffi::ssizeobjargproc>;
}
impl<'p, T> DelSetItem for T
where
T: PySequenceProtocol<'p>,
{
default fn del_set_item() -> Option<ffi::ssizeobjargproc> {
None
}
}
impl<T> DelSetItem for T
pub(super) fn set_del_item<T>() -> Option<ffi::ssizeobjargproc>
where
T: for<'p> PySequenceSetItemProtocol<'p> + for<'p> PySequenceDelItemProtocol<'p>,
{
fn del_set_item() -> Option<ffi::ssizeobjargproc> {
unsafe extern "C" fn wrap<T>(
slf: *mut ffi::PyObject,
key: ffi::Py_ssize_t,
value: *mut ffi::PyObject,
) -> c_int
where
T: for<'p> PySequenceSetItemProtocol<'p> + for<'p> PySequenceDelItemProtocol<'p>,
{
crate::callback_body!(py, {
let slf = py.from_borrowed_ptr::<PyCell<T>>(slf);
unsafe extern "C" fn wrap<T>(
slf: *mut ffi::PyObject,
key: ffi::Py_ssize_t,
value: *mut ffi::PyObject,
) -> c_int
where
T: for<'p> PySequenceSetItemProtocol<'p> + for<'p> PySequenceDelItemProtocol<'p>,
{
crate::callback_body!(py, {
let slf = py.from_borrowed_ptr::<PyCell<T>>(slf);
if value.is_null() {
call_mut!(slf, __delitem__; key.into())
} else {
let value = py.from_borrowed_ptr::<PyAny>(value);
let mut slf_ = slf.try_borrow_mut()?;
let value = value.extract()?;
slf_.__setitem__(key.into(), value).into()
}
})
}
Some(wrap::<T>)
if value.is_null() {
call_mut!(slf, __delitem__; key.into())
} else {
let value = py.from_borrowed_ptr::<PyAny>(value);
let mut slf_ = slf.try_borrow_mut()?;
let value = value.extract()?;
slf_.__setitem__(key.into(), value).into()
}
})
}
}
}
trait PySequenceContainsProtocolImpl {
fn sq_contains() -> Option<ffi::objobjproc>;
}
impl<'p, T> PySequenceContainsProtocolImpl for T
where
T: PySequenceProtocol<'p>,
{
default fn sq_contains() -> Option<ffi::objobjproc> {
None
}
}
impl<T> PySequenceContainsProtocolImpl for T
where
T: for<'p> PySequenceContainsProtocol<'p>,
{
fn sq_contains() -> Option<ffi::objobjproc> {
py_binary_func!(PySequenceContainsProtocol, T::__contains__, c_int)
}
}
trait PySequenceConcatProtocolImpl {
fn sq_concat() -> Option<ffi::binaryfunc>;
}
impl<'p, T> PySequenceConcatProtocolImpl for T
where
T: PySequenceProtocol<'p>,
{
default fn sq_concat() -> Option<ffi::binaryfunc> {
None
}
}
impl<T> PySequenceConcatProtocolImpl for T
where
T: for<'p> PySequenceConcatProtocol<'p>,
{
fn sq_concat() -> Option<ffi::binaryfunc> {
py_binary_func!(PySequenceConcatProtocol, T::__concat__)
}
}
trait PySequenceRepeatProtocolImpl {
fn sq_repeat() -> Option<ffi::ssizeargfunc>;
}
impl<'p, T> PySequenceRepeatProtocolImpl for T
where
T: PySequenceProtocol<'p>,
{
default fn sq_repeat() -> Option<ffi::ssizeargfunc> {
None
}
}
impl<T> PySequenceRepeatProtocolImpl for T
where
T: for<'p> PySequenceRepeatProtocol<'p>,
{
fn sq_repeat() -> Option<ffi::ssizeargfunc> {
py_ssizearg_func!(PySequenceRepeatProtocol, T::__repeat__)
}
}
trait PySequenceInplaceConcatProtocolImpl {
fn sq_inplace_concat() -> Option<ffi::binaryfunc>;
}
impl<'p, T> PySequenceInplaceConcatProtocolImpl for T
where
T: PySequenceProtocol<'p>,
{
default fn sq_inplace_concat() -> Option<ffi::binaryfunc> {
None
}
}
impl<T> PySequenceInplaceConcatProtocolImpl for T
where
T: for<'p> PySequenceInplaceConcatProtocol<'p>,
{
fn sq_inplace_concat() -> Option<ffi::binaryfunc> {
py_binary_func!(
PySequenceInplaceConcatProtocol,
T::__inplace_concat__,
*mut ffi::PyObject,
call_mut
)
}
}
trait PySequenceInplaceRepeatProtocolImpl {
fn sq_inplace_repeat() -> Option<ffi::ssizeargfunc>;
}
impl<'p, T> PySequenceInplaceRepeatProtocolImpl for T
where
T: PySequenceProtocol<'p>,
{
default fn sq_inplace_repeat() -> Option<ffi::ssizeargfunc> {
None
}
}
impl<T> PySequenceInplaceRepeatProtocolImpl for T
where
T: for<'p> PySequenceInplaceRepeatProtocol<'p>,
{
fn sq_inplace_repeat() -> Option<ffi::ssizeargfunc> {
py_ssizearg_func!(
PySequenceInplaceRepeatProtocol,
T::__inplace_repeat__,
call_mut
)
Some(wrap::<T>)
}
}

View File

@ -438,14 +438,16 @@ mod typeobject {
impl Default for PyAsyncMethods {
#[inline]
fn default() -> Self {
unsafe { mem::zeroed() }
PyAsyncMethods_INIT
}
}
pub const PyAsyncMethods_INIT: PyAsyncMethods = PyAsyncMethods {
am_await: None,
am_aiter: None,
am_anext: None,
};
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct PyBufferProcs {
@ -456,9 +458,10 @@ mod typeobject {
impl Default for PyBufferProcs {
#[inline]
fn default() -> Self {
unsafe { mem::zeroed() }
PyBufferProcs_INIT
}
}
pub const PyBufferProcs_INIT: PyBufferProcs = PyBufferProcs {
bf_getbuffer: None,
bf_releasebuffer: None,

View File

@ -47,7 +47,6 @@ pub unsafe trait PyNativeType: Sized {
pub struct Py<T>(NonNull<ffi::PyObject>, PhantomData<T>);
unsafe impl<T> Send for Py<T> {}
unsafe impl<T> Sync for Py<T> {}
impl<T> Py<T>

View File

@ -153,6 +153,7 @@ pub use crate::types::PyAny;
#[cfg(feature = "macros")]
#[doc(hidden)]
pub use {
ctor, // Re-exported for pyproto
indoc, // Re-exported for py_run
inventory, // Re-exported for pymethods
paste, // Re-exported for wrap_function

View File

@ -1,5 +1,6 @@
//! `PyClass` trait
use crate::class::methods::{PyClassAttributeDef, PyMethodDefType, PyMethods};
use crate::class::proto_methods::PyProtoMethods;
use crate::conversion::{IntoPyPointer, ToPyObject};
use crate::pyclass_slots::{PyClassDict, PyClassWeakRef};
use crate::type_object::{type_flags, PyLayout};
@ -77,6 +78,7 @@ pub trait PyClass:
+ Sized
+ PyClassAlloc
+ PyMethods
+ PyProtoMethods
+ Send
{
/// Specify this class has `#[pyclass(dict)]` or not.
@ -141,35 +143,43 @@ where
}
// GC support
<T as class::gc::PyGCProtocolImpl>::update_type_object(type_object);
if let Some(gc) = T::gc_methods() {
unsafe { gc.as_ref() }.update_typeobj(type_object);
}
// descriptor protocol
<T as class::descr::PyDescrProtocolImpl>::tp_as_descr(type_object);
if let Some(descr) = T::descr_methods() {
unsafe { descr.as_ref() }.update_typeobj(type_object);
}
// iterator methods
<T as class::iter::PyIterProtocolImpl>::tp_as_iter(type_object);
if let Some(iter) = T::iter_methods() {
unsafe { iter.as_ref() }.update_typeobj(type_object);
}
// nb_bool is a part of PyObjectProtocol, but should be placed under tp_as_number
let mut nb_bool = None;
// basic methods
<T as class::basic::PyObjectProtocolImpl>::tp_as_object(type_object);
fn to_ptr<T>(value: Option<T>) -> *mut T {
value
.map(|v| Box::into_raw(Box::new(v)))
.unwrap_or_else(ptr::null_mut)
if let Some(basic) = T::basic_methods() {
unsafe { basic.as_ref() }.update_typeobj(type_object);
nb_bool = unsafe { basic.as_ref() }.nb_bool;
}
// number methods
type_object.tp_as_number = to_ptr(<T as class::number::PyNumberProtocolImpl>::tp_as_number());
type_object.tp_as_number = T::number_methods()
.map(|mut p| {
unsafe { p.as_mut() }.nb_bool = nb_bool;
p.as_ptr()
})
.unwrap_or_else(|| nb_bool.map_or_else(ptr::null_mut, ffi::PyNumberMethods::from_nb_bool));
// mapping methods
type_object.tp_as_mapping =
to_ptr(<T as class::mapping::PyMappingProtocolImpl>::tp_as_mapping());
type_object.tp_as_mapping = T::mapping_methods().map_or_else(ptr::null_mut, |p| p.as_ptr());
// sequence methods
type_object.tp_as_sequence =
to_ptr(<T as class::sequence::PySequenceProtocolImpl>::tp_as_sequence());
type_object.tp_as_sequence = T::sequence_methods().map_or_else(ptr::null_mut, |p| p.as_ptr());
// async methods
type_object.tp_as_async = to_ptr(<T as class::pyasync::PyAsyncProtocolImpl>::tp_as_async());
type_object.tp_as_async = T::async_methods().map_or_else(ptr::null_mut, |p| p.as_ptr());
// buffer protocol
type_object.tp_as_buffer = to_ptr(<T as class::buffer::PyBufferProtocolImpl>::tp_as_buffer());
type_object.tp_as_buffer = T::buffer_methods().map_or_else(ptr::null_mut, |p| p.as_ptr());
let (new, call, mut methods, attrs) = py_class_method_defs::<T>();

View File

@ -1,5 +1,6 @@
use pyo3::class::{
PyContextProtocol, PyIterProtocol, PyMappingProtocol, PyObjectProtocol, PySequenceProtocol,
PyAsyncProtocol, PyContextProtocol, PyDescrProtocol, PyIterProtocol, PyMappingProtocol,
PyObjectProtocol, PySequenceProtocol,
};
use pyo3::exceptions::{IndexError, ValueError};
use pyo3::prelude::*;
@ -552,3 +553,131 @@ fn getattr_doesnt_override_member() {
py_assert!(py, inst, "inst.data == 4");
py_assert!(py, inst, "inst.a == 8");
}
/// Wraps a Python future and yield it once.
#[pyclass]
struct OnceFuture {
future: PyObject,
polled: bool,
}
#[pymethods]
impl OnceFuture {
#[new]
fn new(future: PyObject) -> Self {
OnceFuture {
future,
polled: false,
}
}
}
#[pyproto]
impl PyAsyncProtocol for OnceFuture {
fn __await__(slf: PyRef<'p, Self>) -> PyResult<PyRef<'p, Self>> {
Ok(slf)
}
}
#[pyproto]
impl PyIterProtocol for OnceFuture {
fn __iter__(slf: PyRef<'p, Self>) -> PyResult<PyRef<'p, Self>> {
Ok(slf)
}
fn __next__(mut slf: PyRefMut<Self>) -> PyResult<Option<PyObject>> {
if !slf.polled {
slf.polled = true;
Ok(Some(slf.future.clone()))
} else {
Ok(None)
}
}
}
#[test]
fn test_await() {
let gil = Python::acquire_gil();
let py = gil.python();
let once = py.get_type::<OnceFuture>();
let source = pyo3::indoc::indoc!(
r#"
import asyncio
import sys
async def main():
res = await Once(await asyncio.sleep(0.1))
return res
# For an odd error similar to https://bugs.python.org/issue38563
if sys.platform == "win32" and sys.version_info >= (3, 8, 0):
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
# get_event_loop can raise an error: https://github.com/PyO3/pyo3/pull/961#issuecomment-645238579
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
assert loop.run_until_complete(main()) is None
loop.close()
"#
);
let globals = PyModule::import(py, "__main__").unwrap().dict();
globals.set_item("Once", once).unwrap();
py.run(source, Some(globals), None)
.map_err(|e| e.print(py))
.unwrap();
}
/// Increment the count when `__get__` is called.
#[pyclass]
struct DescrCounter {
#[pyo3(get)]
count: usize,
}
#[pymethods]
impl DescrCounter {
#[new]
fn new() -> Self {
DescrCounter { count: 0 }
}
}
#[pyproto]
impl PyDescrProtocol for DescrCounter {
fn __get__(
mut slf: PyRefMut<'p, Self>,
_instance: &PyAny,
_owner: Option<&'p PyType>,
) -> PyResult<PyRefMut<'p, Self>> {
slf.count += 1;
Ok(slf)
}
fn __set__(
_slf: PyRef<'p, Self>,
_instance: &PyAny,
mut new_value: PyRefMut<'p, Self>,
) -> PyResult<()> {
new_value.count = _slf.count;
Ok(())
}
}
#[test]
fn descr_getset() {
let gil = Python::acquire_gil();
let py = gil.python();
let counter = py.get_type::<DescrCounter>();
let source = pyo3::indoc::indoc!(
r#"
class Class:
counter = Counter()
c = Class()
c.counter # count += 1
assert c.counter.count == 2
c.counter = Counter()
assert c.counter.count == 3
"#
);
let globals = PyModule::import(py, "__main__").unwrap().dict();
globals.set_item("Counter", counter).unwrap();
py.run(source, Some(globals), None)
.map_err(|e| e.print(py))
.unwrap();
}