Merge pull request #1152 from PyO3/abi3

Complete abi3 support
This commit is contained in:
Yuji Kanagawa 2020-10-27 23:30:36 +09:00 committed by GitHub
commit 3b3ba4e3ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
63 changed files with 1748 additions and 1423 deletions

View File

@ -94,6 +94,10 @@ jobs:
- if: matrix.python-version != 'pypy3'
name: Test
run: cargo test --features "num-bigint num-complex" --target ${{ matrix.platform.rust-target }}
# Run tests again, but in abi3 mode
- if: matrix.python-version != 'pypy3'
name: Test (abi3)
run: cargo test --no-default-features --features "abi3,macros" --target ${{ matrix.platform.rust-target }}
- name: Test proc-macro code
run: cargo test --manifest-path=pyo3-derive-backend/Cargo.toml --target ${{ matrix.platform.rust-target }}

View File

@ -12,11 +12,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Drop support for Python 3.5 (as it is now end-of-life). [#1250](https://github.com/PyO3/pyo3/pull/1250)
### Added
- Add support for building for CPython limited API. This required a few minor changes to runtime behaviour of of pyo3 `#[pyclass]` types. See the migration guide for full details. [#1152](https://github.com/PyO3/pyo3/pull/1152)
- Add argument names to `TypeError` messages generated by pymethod wrappers. [#1212](https://github.com/PyO3/pyo3/pull/1212)
- Add `PyEval_SetProfile` and `PyEval_SetTrace` to FFI. [#1255](https://github.com/PyO3/pyo3/pull/1255)
- Add context.h functions (`PyContext_New`, etc) to FFI. [#1259](https://github.com/PyO3/pyo3/pull/1259)
### Changed
- Change return type `PyType::name()` from `Cow<str>` to `PyResult<&str>`. [#1152](https://github.com/PyO3/pyo3/pull/1152)
- `#[pyclass(subclass)]` is now required for subclassing from Rust (was previously just required for subclassing from Python). [#1152](https://github.com/PyO3/pyo3/pull/1152)
- Change `PyIterator` to be consistent with other native types: it is now used as `&PyIterator` instead of `PyIterator<'a>`. [#1176](https://github.com/PyO3/pyo3/pull/1176)
- Change formatting of `PyDowncastError` messages to be closer to Python's builtin error messages. [#1212](https://github.com/PyO3/pyo3/pull/1212)

View File

@ -35,6 +35,10 @@ rustversion = "1.0"
[features]
default = ["macros"]
macros = ["ctor", "indoc", "inventory", "paste", "pyo3cls", "unindent"]
# Use the Python limited API. See https://www.python.org/dev/peps/pep-0384/ for
# more.
abi3 = []
# Optimizes PyObject to Vec conversion and so on.
nightly = []
@ -43,11 +47,6 @@ nightly = []
# so that the module can also be used with statically linked python interpreters.
extension-module = []
# The stable cpython abi as defined in PEP 384. Currently broken with
# many compilation errors. Pull Requests working towards fixing that
# are welcome.
# abi3 = []
[workspace]
members = [
"pyo3cls",

View File

@ -557,6 +557,11 @@ fn run_python_script(interpreter: &Path, script: &str) -> Result<String> {
fn get_library_link_name(version: &PythonVersion, ld_version: &str) -> String {
if cfg!(target_os = "windows") {
// Mirrors the behavior in CPython's `PC/pyconfig.h`.
if env::var_os("CARGO_FEATURE_ABI3").is_some() {
return "python3".to_string();
}
let minor_or_empty_string = match version.minor {
Some(minor) => format!("{}", minor),
None => String::new(),

View File

@ -1,6 +1,7 @@
import datetime as pdt
import platform
import struct
import re
import sys
import pytest
@ -310,4 +311,5 @@ def test_tz_class_introspection():
tzi = rdt.TzClass()
assert tzi.__class__ == rdt.TzClass
assert repr(tzi).startswith("<TzClass object at")
# PyPy generates <importlib.bootstrap.TzClass ...> for some reason.
assert re.match(r"^<[\w\.]*TzClass object at", repr(tzi))

View File

@ -36,7 +36,27 @@ On Linux/macOS you might have to change `LD_LIBRARY_PATH` to include libpython,
## Distribution
There are two ways to distribute your module as a Python package: The old, [setuptools-rust](https://github.com/PyO3/setuptools-rust), and the new, [maturin](https://github.com/pyo3/maturin). setuptools-rust needs several configuration files (`setup.py`, `MANIFEST.in`, `build-wheels.sh`, etc.). maturin doesn't need any configuration files, however it does not support some functionality of setuptools such as package data ([pyo3/maturin#258](https://github.com/PyO3/maturin/issues/258)) and requires a rigid project structure, while setuptools-rust allows (and sometimes requires) configuration with python code.
There are two ways to distribute your module as a Python package: The old, [setuptools-rust], and the new, [maturin]. setuptools-rust needs several configuration files (`setup.py`, `MANIFEST.in`, `build-wheels.sh`, etc.). maturin doesn't need any configuration files, however it does not support some functionality of setuptools such as package data ([pyo3/maturin#258](https://github.com/PyO3/maturin/issues/258)) and requires a rigid project structure, while setuptools-rust allows (and sometimes requires) configuration with python code.
## `Py_LIMITED_API`/`abi3`
By default, Python extension modules can only be used with the same Python version they were compiled against -- if you build an extension module with Python 3.5, you can't import it using Python 3.8. [PEP 384](https://www.python.org/dev/peps/pep-0384/) introduced the idea of the limited Python API, which would have a stable ABI enabling extension modules built with it to be used against multiple Python versions. This is also known as `abi3`.
Note that [maturin] >= 0.9.0 or [setuptools-rust] >= 0.12.0 is going to support `abi3` wheels.
See the [corresponding](https://github.com/PyO3/maturin/pull/353) [PRs](https://github.com/PyO3/setuptools-rust/pull/82) for more.
There are three steps involved in making use of `abi3` when building Python packages as wheels:
1. Enable the `abi3` feature in `pyo3`. This ensures `pyo3` only calls Python C-API functions which are part of the stable API, and on Windows also ensures that the project links against the correct shared object (no special behavior is required on other platforms):
```toml
[dependencies]
pyo3 = { version = "...", features = ["abi3"]}
```
2. Ensure that the built shared objects are correctly marked as `abi3`. This is accomplished by telling your build system that you're using the limited API.
3. Ensure that the `.whl` is correctly marked as `abi3`. For projects using `setuptools`, this is accomplished by passing `--py-limited-api=cp3x` (where `x` is the minimum Python version supported by the wheel, e.g. `--py-limited-api=cp35` for Python 3.5) to `setup.py bdist_wheel`.
## Cross Compiling
@ -83,3 +103,8 @@ cargo build --target x86_64-pc-windows-gnu
## Bazel
For an example of how to build python extensions using Bazel, see https://github.com/TheButlah/rules_pyo3
[maturin]: https://github.com/PyO3/maturin
[setuptools-rust]: https://github.com/PyO3/setuptools-rust

View File

@ -205,7 +205,7 @@ or by `self_.into_super()` as `PyRef<Self::BaseClass>`.
```rust
# use pyo3::prelude::*;
#[pyclass]
#[pyclass(subclass)]
struct BaseClass {
val1: usize,
}
@ -222,7 +222,7 @@ impl BaseClass {
}
}
#[pyclass(extends=BaseClass)]
#[pyclass(extends=BaseClass, subclass)]
struct SubClass {
val2: usize,
}
@ -266,12 +266,14 @@ impl SubSubClass {
```
You can also inherit native types such as `PyDict`, if they implement
[`PySizedLayout`](https://docs.rs/pyo3/latest/pyo3/type_object/trait.PySizedLayout.html).
[`PySizedLayout`](https://docs.rs/pyo3/latest/pyo3/type_object/trait.PySizedLayout.html). However, this is not supported when building for the Python limited API (aka the `abi3` feature of PyO3).
However, because of some technical problems, we don't currently provide safe upcasting methods for types
that inherit native types. Even in such cases, you can unsafely get a base class by raw pointer conversion.
```rust
# #[cfg(Py_LIMITED_API)] fn main() {}
# #[cfg(not(Py_LIMITED_API))] fn main() {
# use pyo3::prelude::*;
use pyo3::types::PyDict;
use pyo3::{AsPyPointer, PyNativeType};
@ -300,6 +302,7 @@ impl DictWithCounter {
# let py = gil.python();
# let cnt = pyo3::PyCell::new(py, DictWithCounter::new()).unwrap();
# pyo3::py_run!(py, cnt, "cnt.set('abc', 10); assert cnt['abc'] == 10")
# }
```
If `SubClass` does not provide a baseclass initialization, the compilation fails.
@ -769,13 +772,23 @@ impl pyo3::class::methods::HasMethodsInventory for MyClass {
}
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
pub struct Pyo3ProtoInventoryForMyClass {
def: pyo3::class::proto_methods::PyProtoMethodDef,
}
impl pyo3::class::proto_methods::PyProtoInventory for Pyo3ProtoInventoryForMyClass {
fn new(def: pyo3::class::proto_methods::PyProtoMethodDef) -> Self {
Self { def }
}
fn get(&'static self) -> &'static pyo3::class::proto_methods::PyProtoMethodDef {
&self.def
}
}
impl pyo3::class::proto_methods::HasProtoInventory for MyClass {
type ProtoMethods = Pyo3ProtoInventoryForMyClass;
}
pyo3::inventory::collect!(Pyo3ProtoInventoryForMyClass);
impl pyo3::pyclass::PyClassSend for MyClass {
type ThreadChecker = pyo3::pyclass::ThreadCheckerStub<MyClass>;

View File

@ -3,6 +3,18 @@
This guide can help you upgrade code through breaking changes from one PyO3 version to the next.
For a detailed list of all changes, see the [CHANGELOG](changelog.md).
## from 0.12.* to 0.13
### Runtime changes to support the CPython limited API
In PyO3 `0.13` support was added for compiling against the CPython limited API. This had a number of implications for _all_ PyO3 users, described here.
The largest of these is that all types created from PyO3 are what CPython calls "heap" types. The specific implications of this are:
- If you wish to subclass one of these types _from Rust_ you must mark it `#[pyclass(subclass)]`, as you would if you wished to allow subclassing it from Python code.
- Type objects are now mutable - Python code can set attributes on them.
- `__module__` on types without `#[pyclass(module="mymodule")]` no longer returns `builtins`, it now raises `AttributeError`.
## from 0.11.* to 0.12
### `PyErr` has been reworked

View File

@ -408,8 +408,8 @@ impl Model for UserModel {
.call_method("get_results", (), None)
.unwrap();
if py_result.get_type().name() != "list" {
panic!("Expected a list for the get_results() method signature, got {}", py_result.get_type().name());
if py_result.get_type().name().unwrap() != "list" {
panic!("Expected a list for the get_results() method signature, got {}", py_result.get_type().name().unwrap());
}
py_result.extract()
})
@ -536,8 +536,8 @@ impl Model for UserModel {
.call_method("get_results", (), None)
.unwrap();
if py_result.get_type().name() != "list" {
panic!("Expected a list for the get_results() method signature, got {}", py_result.get_type().name());
if py_result.get_type().name().unwrap() != "list" {
panic!("Expected a list for the get_results() method signature, got {}", py_result.get_type().name().unwrap());
}
py_result.extract()
})

View File

@ -6,16 +6,14 @@ use std::collections::HashSet;
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,
/// Extension trait that has `get_*` methods
pub extension_trait: &'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.
slot_setters: &'static [SlotSetter],
slot_getters: &'static [SlotGetter],
}
impl Proto {
@ -32,13 +30,13 @@ 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(
pub(crate) fn slot_getters(
&self,
mut implemented_protocols: HashSet<String>,
) -> impl Iterator<Item = &'static str> {
self.slot_setters.iter().filter_map(move |setter| {
self.slot_getters.iter().filter_map(move |getter| {
// If any required method is not implemented, we skip this setter.
if setter
if getter
.proto_names
.iter()
.any(|name| !implemented_protocols.contains(*name))
@ -47,10 +45,10 @@ impl Proto {
}
// 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 {
for name in getter.proto_names {
implemented_protocols.remove(*name);
}
Some(setter.set_function)
Some(getter.get_function)
})
}
}
@ -82,27 +80,26 @@ impl PyMethod {
}
/// Represents a setter used to register a method to the method table.
struct SlotSetter {
struct SlotGetter {
/// 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,
pub get_function: &'static str,
}
impl SlotSetter {
const fn new(names: &'static [&'static str], set_function: &'static str) -> Self {
SlotSetter {
impl SlotGetter {
const fn new(names: &'static [&'static str], get_function: &'static str) -> Self {
SlotGetter {
proto_names: names,
set_function,
get_function,
}
}
}
pub const OBJECT: Proto = Proto {
name: "Object",
slot_table: "pyo3::class::basic::PyObjectMethods",
set_slot_table: "set_basic_methods",
extension_trait: "pyo3::class::basic::PyBasicSlots",
methods: &[
MethodProto::Binary {
name: "__getattr__",
@ -156,23 +153,22 @@ pub const OBJECT: Proto = Proto {
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::new(&["__setattr__", "__delattr__"], "set_setdelattr"),
SlotSetter::new(&["__setattr__"], "set_setattr"),
SlotSetter::new(&["__delattr__"], "set_delattr"),
SlotSetter::new(&["__bool__"], "set_bool"),
slot_getters: &[
SlotGetter::new(&["__str__"], "get_str"),
SlotGetter::new(&["__repr__"], "get_repr"),
SlotGetter::new(&["__hash__"], "get_hash"),
SlotGetter::new(&["__getattr__"], "get_getattr"),
SlotGetter::new(&["__richcmp__"], "get_richcmp"),
SlotGetter::new(&["__setattr__", "__delattr__"], "get_setdelattr"),
SlotGetter::new(&["__setattr__"], "get_setattr"),
SlotGetter::new(&["__delattr__"], "get_delattr"),
SlotGetter::new(&["__bool__"], "get_bool"),
],
};
pub const ASYNC: Proto = Proto {
name: "Async",
slot_table: "pyo3::ffi::PyAsyncMethods",
set_slot_table: "set_async_methods",
extension_trait: "pyo3::class::pyasync::PyAsyncSlots",
methods: &[
MethodProto::UnaryS {
name: "__await__",
@ -211,17 +207,16 @@ 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"),
slot_getters: &[
SlotGetter::new(&["__await__"], "get_await"),
SlotGetter::new(&["__aiter__"], "get_aiter"),
SlotGetter::new(&["__anext__"], "get_anext"),
],
};
pub const BUFFER: Proto = Proto {
name: "Buffer",
slot_table: "pyo3::ffi::PyBufferProcs",
set_slot_table: "set_buffer_methods",
extension_trait: "pyo3::class::buffer::PyBufferSlots",
methods: &[
MethodProto::Unary {
name: "bf_getbuffer",
@ -233,16 +228,15 @@ pub const BUFFER: Proto = Proto {
},
],
py_methods: &[],
slot_setters: &[
SlotSetter::new(&["bf_getbuffer"], "set_getbuffer"),
SlotSetter::new(&["bf_releasebuffer"], "set_releasebuffer"),
slot_getters: &[
SlotGetter::new(&["bf_getbuffer"], "get_getbuffer"),
SlotGetter::new(&["bf_releasebuffer"], "get_releasebuffer"),
],
};
pub const CONTEXT: Proto = Proto {
name: "Context",
slot_table: "",
set_slot_table: "",
extension_trait: "",
methods: &[
MethodProto::Unary {
name: "__enter__",
@ -266,13 +260,12 @@ pub const CONTEXT: Proto = Proto {
"pyo3::class::context::PyContextExitProtocolImpl",
),
],
slot_setters: &[],
slot_getters: &[],
};
pub const GC: Proto = Proto {
name: "GC",
slot_table: "pyo3::class::gc::PyGCMethods",
set_slot_table: "set_gc_methods",
extension_trait: "pyo3::class::gc::PyGCSlots",
methods: &[
MethodProto::Free {
name: "__traverse__",
@ -284,16 +277,15 @@ pub const GC: Proto = Proto {
},
],
py_methods: &[],
slot_setters: &[
SlotSetter::new(&["__traverse__"], "set_traverse"),
SlotSetter::new(&["__clear__"], "set_clear"),
slot_getters: &[
SlotGetter::new(&["__traverse__"], "get_traverse"),
SlotGetter::new(&["__clear__"], "get_clear"),
],
};
pub const DESCR: Proto = Proto {
name: "Descriptor",
slot_table: "pyo3::class::descr::PyDescrMethods",
set_slot_table: "set_descr_methods",
extension_trait: "pyo3::class::descr::PyDescrSlots",
methods: &[
MethodProto::TernaryS {
name: "__get__",
@ -327,16 +319,15 @@ pub const DESCR: Proto = Proto {
"pyo3::class::context::PyDescrNameProtocolImpl",
),
],
slot_setters: &[
SlotSetter::new(&["__get__"], "set_descr_get"),
SlotSetter::new(&["__set__"], "set_descr_set"),
slot_getters: &[
SlotGetter::new(&["__get__"], "get_descr_get"),
SlotGetter::new(&["__set__"], "get_descr_set"),
],
};
pub const ITER: Proto = Proto {
name: "Iter",
slot_table: "pyo3::class::iter::PyIterMethods",
set_slot_table: "set_iter_methods",
extension_trait: "pyo3::class::iter::PyIterSlots",
py_methods: &[],
methods: &[
MethodProto::UnaryS {
@ -350,16 +341,15 @@ pub const ITER: Proto = Proto {
proto: "pyo3::class::iter::PyIterNextProtocol",
},
],
slot_setters: &[
SlotSetter::new(&["__iter__"], "set_iter"),
SlotSetter::new(&["__next__"], "set_iternext"),
slot_getters: &[
SlotGetter::new(&["__iter__"], "get_iter"),
SlotGetter::new(&["__next__"], "get_iternext"),
],
};
pub const MAPPING: Proto = Proto {
name: "Mapping",
slot_table: "pyo3::ffi::PyMappingMethods",
set_slot_table: "set_mapping_methods",
extension_trait: "pyo3::class::mapping::PyMappingSlots",
methods: &[
MethodProto::Unary {
name: "__len__",
@ -390,19 +380,18 @@ pub const MAPPING: Proto = Proto {
"__reversed__",
"pyo3::class::mapping::PyMappingReversedProtocolImpl",
)],
slot_setters: &[
SlotSetter::new(&["__len__"], "set_length"),
SlotSetter::new(&["__getitem__"], "set_getitem"),
SlotSetter::new(&["__setitem__", "__delitem__"], "set_setdelitem"),
SlotSetter::new(&["__setitem__"], "set_setitem"),
SlotSetter::new(&["__delitem__"], "set_delitem"),
slot_getters: &[
SlotGetter::new(&["__len__"], "get_len"),
SlotGetter::new(&["__getitem__"], "get_getitem"),
SlotGetter::new(&["__setitem__", "__delitem__"], "get_setdelitem"),
SlotGetter::new(&["__setitem__"], "get_setitem"),
SlotGetter::new(&["__delitem__"], "get_delitem"),
],
};
pub const SEQ: Proto = Proto {
name: "Sequence",
slot_table: "pyo3::ffi::PySequenceMethods",
set_slot_table: "set_sequence_methods",
extension_trait: "pyo3::class::sequence::PySequenceSlots",
methods: &[
MethodProto::Unary {
name: "__len__",
@ -451,24 +440,23 @@ 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::new(&["__setitem__", "__delitem__"], "set_setdelitem"),
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"),
slot_getters: &[
SlotGetter::new(&["__len__"], "get_len"),
SlotGetter::new(&["__concat__"], "get_concat"),
SlotGetter::new(&["__repeat__"], "get_repeat"),
SlotGetter::new(&["__getitem__"], "get_getitem"),
SlotGetter::new(&["__setitem__", "__delitem__"], "get_setdelitem"),
SlotGetter::new(&["__setitem__"], "get_setitem"),
SlotGetter::new(&["__delitem__"], "get_delitem"),
SlotGetter::new(&["__contains__"], "get_contains"),
SlotGetter::new(&["__inplace_concat__"], "get_inplace_concat"),
SlotGetter::new(&["__inplace_repeat__"], "get_inplace_repeat"),
],
};
pub const NUM: Proto = Proto {
name: "Number",
slot_table: "pyo3::ffi::PyNumberMethods",
set_slot_table: "set_number_methods",
extension_trait: "pyo3::class::number::PyNumberSlots",
methods: &[
MethodProto::BinaryS {
name: "__add__",
@ -771,66 +759,66 @@ pub const NUM: Proto = Proto {
"pyo3::class::number::PyNumberRoundProtocolImpl",
),
],
slot_setters: &[
SlotSetter::new(&["__add__", "__radd__"], "set_add_radd"),
SlotSetter::new(&["__add__"], "set_add"),
SlotSetter::new(&["__radd__"], "set_radd"),
SlotSetter::new(&["__sub__", "__rsub__"], "set_sub_rsub"),
SlotSetter::new(&["__sub__"], "set_sub"),
SlotSetter::new(&["__rsub__"], "set_rsub"),
SlotSetter::new(&["__mul__", "__rmul__"], "set_mul_rmul"),
SlotSetter::new(&["__mul__"], "set_mul"),
SlotSetter::new(&["__rmul__"], "set_rmul"),
SlotSetter::new(&["__mod__"], "set_mod"),
SlotSetter::new(&["__divmod__", "__rdivmod__"], "set_divmod_rdivmod"),
SlotSetter::new(&["__divmod__"], "set_divmod"),
SlotSetter::new(&["__rdivmod__"], "set_rdivmod"),
SlotSetter::new(&["__pow__", "__rpow__"], "set_pow_rpow"),
SlotSetter::new(&["__pow__"], "set_pow"),
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(&["__lshift__", "__rlshift__"], "set_lshift_rlshift"),
SlotSetter::new(&["__lshift__"], "set_lshift"),
SlotSetter::new(&["__rlshift__"], "set_rlshift"),
SlotSetter::new(&["__rshift__", "__rrshift__"], "set_rshift_rrshift"),
SlotSetter::new(&["__rshift__"], "set_rshift"),
SlotSetter::new(&["__rrshift__"], "set_rrshift"),
SlotSetter::new(&["__and__", "__rand__"], "set_and_rand"),
SlotSetter::new(&["__and__"], "set_and"),
SlotSetter::new(&["__rand__"], "set_rand"),
SlotSetter::new(&["__xor__", "__rxor__"], "set_xor_rxor"),
SlotSetter::new(&["__xor__"], "set_xor"),
SlotSetter::new(&["__rxor__"], "set_rxor"),
SlotSetter::new(&["__or__", "__ror__"], "set_or_ror"),
SlotSetter::new(&["__or__"], "set_or"),
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::new(&["__floordiv__", "__rfloordiv__"], "set_floordiv_rfloordiv"),
SlotSetter::new(&["__floordiv__"], "set_floordiv"),
SlotSetter::new(&["__rfloordiv__"], "set_rfloordiv"),
SlotSetter::new(&["__truediv__", "__rtruediv__"], "set_truediv_rtruediv"),
SlotSetter::new(&["__truediv__"], "set_truediv"),
SlotSetter::new(&["__rtruediv__"], "set_rtruediv"),
SlotSetter::new(&["__ifloordiv__"], "set_ifloordiv"),
SlotSetter::new(&["__itruediv__"], "set_itruediv"),
SlotSetter::new(&["__index__"], "set_index"),
SlotSetter::new(&["__matmul__", "__rmatmul__"], "set_matmul_rmatmul"),
SlotSetter::new(&["__matmul__"], "set_matmul"),
SlotSetter::new(&["__rmatmul__"], "set_rmatmul"),
SlotSetter::new(&["__imatmul__"], "set_imatmul"),
slot_getters: &[
SlotGetter::new(&["__add__", "__radd__"], "get_add_radd"),
SlotGetter::new(&["__add__"], "get_add"),
SlotGetter::new(&["__radd__"], "get_radd"),
SlotGetter::new(&["__sub__", "__rsub__"], "get_sub_rsub"),
SlotGetter::new(&["__sub__"], "get_sub"),
SlotGetter::new(&["__rsub__"], "get_rsub"),
SlotGetter::new(&["__mul__", "__rmul__"], "get_mul_rmul"),
SlotGetter::new(&["__mul__"], "get_mul"),
SlotGetter::new(&["__rmul__"], "get_rmul"),
SlotGetter::new(&["__mod__"], "get_mod"),
SlotGetter::new(&["__divmod__", "__rdivmod__"], "get_divmod_rdivmod"),
SlotGetter::new(&["__divmod__"], "get_divmod"),
SlotGetter::new(&["__rdivmod__"], "get_rdivmod"),
SlotGetter::new(&["__pow__", "__rpow__"], "get_pow_rpow"),
SlotGetter::new(&["__pow__"], "get_pow"),
SlotGetter::new(&["__rpow__"], "get_rpow"),
SlotGetter::new(&["__neg__"], "get_neg"),
SlotGetter::new(&["__pos__"], "get_pos"),
SlotGetter::new(&["__abs__"], "get_abs"),
SlotGetter::new(&["__invert__"], "get_invert"),
SlotGetter::new(&["__lshift__", "__rlshift__"], "get_lshift_rlshift"),
SlotGetter::new(&["__lshift__"], "get_lshift"),
SlotGetter::new(&["__rlshift__"], "get_rlshift"),
SlotGetter::new(&["__rshift__", "__rrshift__"], "get_rshift_rrshift"),
SlotGetter::new(&["__rshift__"], "get_rshift"),
SlotGetter::new(&["__rrshift__"], "get_rrshift"),
SlotGetter::new(&["__and__", "__rand__"], "get_and_rand"),
SlotGetter::new(&["__and__"], "get_and"),
SlotGetter::new(&["__rand__"], "get_rand"),
SlotGetter::new(&["__xor__", "__rxor__"], "get_xor_rxor"),
SlotGetter::new(&["__xor__"], "get_xor"),
SlotGetter::new(&["__rxor__"], "get_rxor"),
SlotGetter::new(&["__or__", "__ror__"], "get_or_ror"),
SlotGetter::new(&["__or__"], "get_or"),
SlotGetter::new(&["__ror__"], "get_ror"),
SlotGetter::new(&["__int__"], "get_int"),
SlotGetter::new(&["__float__"], "get_float"),
SlotGetter::new(&["__iadd__"], "get_iadd"),
SlotGetter::new(&["__isub__"], "get_isub"),
SlotGetter::new(&["__imul__"], "get_imul"),
SlotGetter::new(&["__imod__"], "get_imod"),
SlotGetter::new(&["__ipow__"], "get_ipow"),
SlotGetter::new(&["__ilshift__"], "get_ilshift"),
SlotGetter::new(&["__irshift__"], "get_irshift"),
SlotGetter::new(&["__iand__"], "get_iand"),
SlotGetter::new(&["__ixor__"], "get_ixor"),
SlotGetter::new(&["__ior__"], "get_ior"),
SlotGetter::new(&["__floordiv__", "__rfloordiv__"], "get_floordiv_rfloordiv"),
SlotGetter::new(&["__floordiv__"], "get_floordiv"),
SlotGetter::new(&["__rfloordiv__"], "get_rfloordiv"),
SlotGetter::new(&["__truediv__", "__rtruediv__"], "get_truediv_rtruediv"),
SlotGetter::new(&["__truediv__"], "get_truediv"),
SlotGetter::new(&["__rtruediv__"], "get_rtruediv"),
SlotGetter::new(&["__ifloordiv__"], "get_ifloordiv"),
SlotGetter::new(&["__itruediv__"], "get_itruediv"),
SlotGetter::new(&["__index__"], "get_index"),
SlotGetter::new(&["__matmul__", "__rmatmul__"], "get_matmul_rmatmul"),
SlotGetter::new(&["__matmul__"], "get_matmul"),
SlotGetter::new(&["__rmatmul__"], "get_rmatmul"),
SlotGetter::new(&["__imatmul__"], "get_imatmul"),
],
};

View File

@ -72,7 +72,7 @@ impl<'a> Enum<'a> {
};
quote!(
#(#var_extracts)*
let type_name = obj.get_type().name();
let type_name = obj.get_type().name()?;
let err_msg = format!("'{}' object cannot be converted to '{}'", type_name, #error_names);
Err(::pyo3::exceptions::PyTypeError::new_err(err_msg))
)
@ -246,7 +246,7 @@ impl<'a> Container<'a> {
let self_ty = &self.path;
let mut fields: Punctuated<TokenStream, syn::Token![,]> = Punctuated::new();
for i in 0..len {
fields.push(quote!(slice[#i].extract()?));
fields.push(quote!(s.get_item(#i).extract()?));
}
let msg = if self.is_enum_variant {
quote!(format!(
@ -262,7 +262,6 @@ impl<'a> Container<'a> {
if s.len() != #len {
return Err(::pyo3::exceptions::PyValueError::new_err(#msg))
}
let slice = s.as_slice();
Ok(#self_ty(#fields))
)
}

View File

@ -234,16 +234,31 @@ 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 {
/// Implement `HasProtoInventory` for the class for lazy protocol initialization.
fn impl_proto_inventory(cls: &syn::Ident) -> TokenStream {
// Try to build a unique type for better error messages
let name = format!("Pyo3ProtoInventoryFor{}", cls);
let inventory_cls = syn::Ident::new(&name, Span::call_site());
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
#[doc(hidden)]
pub struct #inventory_cls {
def: pyo3::class::proto_methods::PyProtoMethodDef,
}
impl pyo3::class::proto_methods::PyProtoInventory for #inventory_cls {
fn new(def: pyo3::class::proto_methods::PyProtoMethodDef) -> Self {
Self { def }
}
fn get(&'static self) -> &'static pyo3::class::proto_methods::PyProtoMethodDef {
&self.def
}
}
impl pyo3::class::proto_methods::HasProtoInventory for #cls {
type ProtoMethods = #inventory_cls;
}
pyo3::inventory::collect!(#inventory_cls);
}
}
@ -351,7 +366,7 @@ fn impl_class(
};
let impl_inventory = impl_methods_inventory(&cls);
let impl_proto_registry = impl_proto_registry(&cls);
let impl_proto_inventory = impl_proto_inventory(&cls);
let base = &attr.base;
let flags = &attr.flags;
@ -440,7 +455,7 @@ fn impl_class(
#impl_inventory
#impl_proto_registry
#impl_proto_inventory
#extra

View File

@ -106,16 +106,16 @@ fn impl_proto_impl(
}
}
}
let inventory_submission = inventory_submission(py_methods, ty);
let slot_initialization = slot_initialization(method_names, ty, proto)?;
let normal_methods = submit_normal_methods(py_methods, ty);
let protocol_methods = submit_protocol_methods(method_names, ty, proto)?;
Ok(quote! {
#trait_impls
#inventory_submission
#slot_initialization
#normal_methods
#protocol_methods
})
}
fn inventory_submission(py_methods: Vec<TokenStream>, ty: &syn::Type) -> TokenStream {
fn submit_normal_methods(py_methods: Vec<TokenStream>, ty: &syn::Type) -> TokenStream {
if py_methods.is_empty() {
return quote! {};
}
@ -129,42 +129,49 @@ fn inventory_submission(py_methods: Vec<TokenStream>, ty: &syn::Type) -> TokenSt
}
}
fn slot_initialization(
fn submit_protocol_methods(
method_names: HashSet<String>,
ty: &syn::Type,
proto: &defs::Proto,
) -> syn::Result<TokenStream> {
// Collect initializers
let mut initializers: Vec<TokenStream> = vec![];
for setter in proto.setters(method_names) {
// Add slot methods to PyProtoRegistry
let set = syn::Ident::new(setter, Span::call_site());
initializers.push(quote! { table.#set::<#ty>(); });
}
if initializers.is_empty() {
if proto.extension_trait == "" {
return Ok(quote! {});
}
let ext_trait: syn::Path = syn::parse_str(proto.extension_trait)?;
let mut tokens = vec![];
if proto.name == "Buffer" {
// For buffer, we construct `PyProtoMethods` from PyBufferProcs
tokens.push(quote! {
let mut proto_methods = pyo3::ffi::PyBufferProcs::default();
});
for getter in proto.slot_getters(method_names) {
let get = syn::Ident::new(getter, Span::call_site());
let field = syn::Ident::new(&format!("bf_{}", &getter[4..]), Span::call_site());
tokens.push(quote! { proto_methods.#field = Some(<#ty as #ext_trait>::#get()); });
}
} else {
// For other protocols, we construct `PyProtoMethods` from Vec<ffi::PyType_Slot>
tokens.push(quote! { let mut proto_methods = vec![]; });
for getter in proto.slot_getters(method_names) {
let get = syn::Ident::new(getter, Span::call_site());
tokens.push(quote! {
let slot = <#ty as #ext_trait>::#get();
proto_methods.push(pyo3::ffi::PyType_Slot { slot: slot.0, pfunc: slot.1 as _ });
});
}
};
if tokens.len() <= 1 {
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! {
#[allow(non_snake_case)]
#[pyo3::ctor::ctor]
fn #init() {
let mut table = #table::default();
#(#initializers)*
<#ty as pyo3::class::proto_methods::HasProtoRegistry>::registry().#set(table);
pyo3::inventory::submit! {
#![crate = pyo3] {
type Inventory =
<#ty as pyo3::class::proto_methods::HasProtoInventory>::ProtoMethods;
<Inventory as pyo3::class::proto_methods::PyProtoInventory>::new(
{ #(#tokens)* proto_methods.into() }
)
}
}
})
}
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

@ -8,6 +8,7 @@
//! Parts of the documentation are copied from the respective methods from the
//! [typeobj docs](https://docs.python.org/3/c-api/typeobj.html)
use super::proto_methods::TypedSlot;
use crate::callback::{HashCallbackOutput, IntoPyCallbackOutput};
use crate::{exceptions, ffi, FromPyObject, PyAny, PyCell, PyClass, PyObject, PyResult};
use std::os::raw::c_int;
@ -133,153 +134,150 @@ pub trait PyObjectRichcmpProtocol<'p>: PyObjectProtocol<'p> {
type Result: IntoPyCallbackOutput<PyObject>;
}
/// 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>,
}
/// Extension trait for proc-macro backend.
#[doc(hidden)]
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;
}
// Set functions used by `#[pyproto]`.
pub fn set_str<T>(&mut self)
pub trait PyBasicSlots {
fn get_str() -> TypedSlot<ffi::reprfunc>
where
T: for<'p> PyObjectStrProtocol<'p>,
Self: for<'p> PyObjectStrProtocol<'p>,
{
self.tp_str = py_unary_func!(PyObjectStrProtocol, T::__str__);
}
pub fn set_repr<T>(&mut self)
where
T: for<'p> PyObjectReprProtocol<'p>,
{
self.tp_repr = py_unary_func!(PyObjectReprProtocol, T::__repr__);
}
pub fn set_hash<T>(&mut self)
where
T: for<'p> PyObjectHashProtocol<'p>,
{
self.tp_hash = py_unary_func!(PyObjectHashProtocol, T::__hash__, ffi::Py_hash_t);
}
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__
TypedSlot(
ffi::Py_tp_str,
py_unary_func!(PyObjectStrProtocol, Self::__str__),
)
}
pub fn set_bool<T>(&mut self)
fn get_repr() -> TypedSlot<ffi::reprfunc>
where
T: for<'p> PyObjectBoolProtocol<'p>,
Self: for<'p> PyObjectReprProtocol<'p>,
{
self.nb_bool = py_unary_func!(PyObjectBoolProtocol, T::__bool__, c_int);
TypedSlot(
ffi::Py_tp_repr,
py_unary_func!(PyObjectReprProtocol, Self::__repr__),
)
}
}
fn tp_getattro<T>() -> Option<ffi::binaryfunc>
where
T: for<'p> PyObjectGetAttrProtocol<'p>,
{
unsafe extern "C" fn wrap<T>(
slf: *mut ffi::PyObject,
arg: *mut ffi::PyObject,
) -> *mut ffi::PyObject
fn get_hash() -> TypedSlot<ffi::hashfunc>
where
T: for<'p> PyObjectGetAttrProtocol<'p>,
Self: for<'p> PyObjectHashProtocol<'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).convert(py)
})
TypedSlot(
ffi::Py_tp_hash,
py_unary_func!(PyObjectHashProtocol, Self::__hash__, ffi::Py_hash_t),
)
}
Some(wrap::<T>)
}
fn tp_richcompare<T>() -> Option<ffi::richcmpfunc>
where
T: for<'p> PyObjectRichcmpProtocol<'p>,
{
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(exceptions::PyValueError::new_err(
"tp_richcompare called with invalid comparison operator",
)),
fn get_getattr() -> TypedSlot<ffi::getattrofunc>
where
Self: for<'p> PyObjectGetAttrProtocol<'p>,
{
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).convert(py)
})
}
TypedSlot(ffi::Py_tp_getattro, wrap::<Self>)
}
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 = extract_or_return_not_implemented!(py, arg);
let op = extract_op(op)?;
slf.try_borrow()?.__richcmp__(arg, op).convert(py)
})
fn get_richcmp() -> TypedSlot<ffi::richcmpfunc>
where
Self: for<'p> PyObjectRichcmpProtocol<'p>,
{
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(exceptions::PyValueError::new_err(
"tp_richcompare called with invalid comparison operator",
)),
}
}
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 = extract_or_return_not_implemented!(py, arg);
let op = extract_op(op)?;
slf.try_borrow()?.__richcmp__(arg, op).convert(py)
})
}
TypedSlot(ffi::Py_tp_richcompare, wrap::<Self>)
}
fn get_setattr() -> TypedSlot<ffi::setattrofunc>
where
Self: for<'p> PyObjectSetAttrProtocol<'p>,
{
TypedSlot(
ffi::Py_tp_setattro,
py_func_set!(PyObjectSetAttrProtocol, Self::__setattr__),
)
}
fn get_delattr() -> TypedSlot<ffi::setattrofunc>
where
Self: for<'p> PyObjectDelAttrProtocol<'p>,
{
TypedSlot(
ffi::Py_tp_setattro,
py_func_del!(PyObjectDelAttrProtocol, Self::__delattr__),
)
}
fn get_setdelattr() -> TypedSlot<ffi::setattrofunc>
where
Self: for<'p> PyObjectSetAttrProtocol<'p> + for<'p> PyObjectDelAttrProtocol<'p>,
{
TypedSlot(
ffi::Py_tp_setattro,
py_func_set_del!(
PyObjectSetAttrProtocol,
PyObjectDelAttrProtocol,
Self,
__setattr__,
__delattr__
),
)
}
fn get_bool() -> TypedSlot<ffi::inquiry>
where
Self: for<'p> PyObjectBoolProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_bool,
py_unary_func!(PyObjectBoolProtocol, Self::__bool__, c_int),
)
}
Some(wrap::<T>)
}
impl<'p, T> PyBasicSlots for T where T: PyObjectProtocol<'p> {}

View File

@ -5,10 +5,7 @@
//! For more information check [buffer protocol](https://docs.python.org/3/c-api/buffer.html)
//! c-api
use crate::callback::IntoPyCallbackOutput;
use crate::{
ffi::{self, PyBufferProcs},
PyCell, PyClass, PyRefMut,
};
use crate::{ffi, PyCell, PyClass, PyRefMut};
use std::os::raw::c_int;
/// Buffer protocol interface
@ -40,55 +37,46 @@ pub trait PyBufferReleaseBufferProtocol<'p>: PyBufferProtocol<'p> {
type Result: IntoPyCallbackOutput<()>;
}
/// Set functions used by `#[pyproto]`.
/// Extension trait for proc-macro backend.
#[doc(hidden)]
impl PyBufferProcs {
pub fn set_getbuffer<T>(&mut self)
pub trait PyBufferSlots {
fn get_getbuffer() -> ffi::getbufferproc
where
T: for<'p> PyBufferGetBufferProtocol<'p>,
Self: for<'p> PyBufferGetBufferProtocol<'p>,
{
self.bf_getbuffer = bf_getbuffer::<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).convert(py)
})
}
wrap::<Self>
}
pub fn set_releasebuffer<T>(&mut self)
fn get_releasebuffer() -> ffi::releasebufferproc
where
T: for<'p> PyBufferReleaseBufferProtocol<'p>,
Self: for<'p> PyBufferReleaseBufferProtocol<'p>,
{
self.bf_releasebuffer = bf_releasebuffer::<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).convert(py)
})
}
wrap::<Self>
}
}
fn bf_getbuffer<T>() -> Option<ffi::getbufferproc>
where
T: for<'p> PyBufferGetBufferProtocol<'p>,
{
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).convert(py)
})
}
Some(wrap::<T>)
}
fn bf_releasebuffer<T>() -> Option<ffi::releasebufferproc>
where
T: for<'p> PyBufferReleaseBufferProtocol<'p>,
{
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).convert(py)
})
}
Some(wrap::<T>)
}
impl<'p, T> PyBufferSlots for T where T: PyBufferProtocol<'p> {}

View File

@ -5,6 +5,7 @@
//! [Python information](
//! https://docs.python.org/3/reference/datamodel.html#implementing-descriptors)
use super::proto_methods::TypedSlot;
use crate::callback::IntoPyCallbackOutput;
use crate::types::PyAny;
use crate::{ffi, FromPyObject, PyClass, PyObject};
@ -70,29 +71,28 @@ pub trait PyDescrSetNameProtocol<'p>: PyDescrProtocol<'p> {
type Result: IntoPyCallbackOutput<()>;
}
/// 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>,
/// Extension trait for our proc-macro backend.
#[doc(hidden)]
pub trait PyDescrSlots {
fn get_descr_get() -> TypedSlot<ffi::descrgetfunc>
where
Self: for<'p> PyDescrGetProtocol<'p>,
{
TypedSlot(
ffi::Py_tp_descr_get,
py_ternarys_func!(PyDescrGetProtocol, Self::__get__),
)
}
fn get_descr_set() -> TypedSlot<ffi::descrsetfunc>
where
Self: for<'p> PyDescrSetProtocol<'p>,
{
TypedSlot(
ffi::Py_tp_descr_set,
py_ternarys_func!(PyDescrSetProtocol, Self::__set__, c_int),
)
}
}
#[doc(hidden)]
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);
}
}
impl<'p, T> PyDescrSlots for T where T: PyDescrProtocol<'p> {}

View File

@ -3,6 +3,7 @@
//! Python GC support
//!
use super::proto_methods::TypedSlot;
use crate::{ffi, AsPyPointer, PyCell, PyClass, Python};
use std::os::raw::{c_int, c_void};
@ -18,37 +19,67 @@ 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>,
}
/// Extension trait for proc-macro backend.
#[doc(hidden)]
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;
pub trait PyGCSlots {
fn get_traverse() -> TypedSlot<ffi::traverseproc>
where
Self: for<'p> PyGCTraverseProtocol<'p>,
{
unsafe extern "C" fn wrap<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
}
}
TypedSlot(ffi::Py_tp_traverse, wrap::<Self>)
}
pub fn set_traverse<T>(&mut self)
fn get_clear() -> TypedSlot<ffi::inquiry>
where
T: for<'p> PyGCTraverseProtocol<'p>,
Self: for<'p> PyGCClearProtocol<'p>,
{
self.tp_traverse = tp_traverse::<T>();
}
unsafe extern "C" fn wrap<T>(slf: *mut ffi::PyObject) -> c_int
where
T: for<'p> PyGCClearProtocol<'p>,
{
let pool = crate::GILPool::new();
let slf = pool.python().from_borrowed_ptr::<PyCell<T>>(slf);
pub fn set_clear<T>(&mut self)
where
T: for<'p> PyGCClearProtocol<'p>,
{
self.tp_clear = tp_clear::<T>();
slf.borrow_mut().__clear__();
0
}
TypedSlot(ffi::Py_tp_clear, wrap::<Self>)
}
}
impl<'p, T> PyGCSlots for T where T: PyGCProtocol<'p> {}
/// Object visitor for GC.
#[derive(Copy, Clone)]
#[derive(Clone)]
pub struct PyVisit<'p> {
visit: ffi::visitproc,
arg: *mut c_void,
@ -72,56 +103,3 @@ impl<'p> PyVisit<'p> {
}
}
}
fn tp_traverse<T>() -> Option<ffi::traverseproc>
where
T: for<'p> PyGCTraverseProtocol<'p>,
{
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
}
}
Some(tp_traverse::<T>)
}
fn tp_clear<T>() -> Option<ffi::inquiry>
where
T: for<'p> PyGCClearProtocol<'p>,
{
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>)
}

View File

@ -2,6 +2,7 @@
//! Python Iterator Interface.
//! Trait and support implementation for implementing iterators
use super::proto_methods::TypedSlot;
use crate::callback::IntoPyCallbackOutput;
use crate::derive_utils::TryFromPyCell;
use crate::err::PyResult;
@ -71,31 +72,30 @@ pub trait PyIterNextProtocol<'p>: PyIterProtocol<'p> {
type Result: IntoPyCallbackOutput<PyIterNextOutput>;
}
#[derive(Default)]
pub struct PyIterMethods {
pub tp_iter: Option<ffi::getiterfunc>,
pub tp_iternext: Option<ffi::iternextfunc>,
/// Extension trait for proc-macro backend.
#[doc(hidden)]
pub trait PyIterSlots {
fn get_iter() -> TypedSlot<ffi::getiterfunc>
where
Self: for<'p> PyIterIterProtocol<'p>,
{
TypedSlot(
ffi::Py_tp_iter,
py_unarys_func!(PyIterIterProtocol, Self::__iter__),
)
}
fn get_iternext() -> TypedSlot<ffi::iternextfunc>
where
Self: for<'p> PyIterNextProtocol<'p>,
{
TypedSlot(
ffi::Py_tp_iternext,
py_unarys_func!(PyIterNextProtocol, Self::__next__),
)
}
}
#[doc(hidden)]
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;
}
pub fn set_iter<T>(&mut self)
where
T: for<'p> PyIterIterProtocol<'p>,
{
self.tp_iter = py_unarys_func!(PyIterIterProtocol, T::__iter__);
}
pub fn set_iternext<T>(&mut self)
where
T: for<'p> PyIterNextProtocol<'p>,
{
self.tp_iternext = py_unarys_func!(PyIterNextProtocol, T::__next__);
}
}
impl<'p, T> PyIterSlots for T where T: PyIterProtocol<'p> {}
/// Output of `__next__` which can either `yield` the next value in the iteration, or
/// `return` a value to raise `StopIteration` in Python.

View File

@ -11,7 +11,7 @@ macro_rules! py_unary_func {
$call!(slf, $f).convert(py)
})
}
Some(wrap::<$class>)
wrap::<$class>
}};
// Use call_ref! by default
($trait:ident, $class:ident :: $f:ident, $ret_type:ty) => {
@ -34,10 +34,10 @@ macro_rules! py_unarys_func {
<T::Receiver as $crate::derive_utils::TryFromPyCell<_>>::try_from_pycell(slf)
.map_err(|e| e.into())?;
$class::$f(borrow).convert(py)
T::$f(borrow).convert(py)
})
}
Some(wrap::<$class>)
wrap::<$class>
}};
}
@ -60,7 +60,7 @@ macro_rules! py_binary_func {
$call!(slf, $f, arg).convert(py)
})
}
Some(wrap::<$class>)
wrap::<$class>
}};
($trait:ident, $class:ident :: $f:ident, $return:ty) => {
py_binary_func!($trait, $class::$f, $return, call_ref)
@ -82,10 +82,10 @@ macro_rules! py_binary_num_func {
$crate::callback_body!(py, {
let lhs = py.from_borrowed_ptr::<$crate::PyAny>(lhs);
let rhs = extract_or_return_not_implemented!(py, rhs);
$class::$f(lhs.extract()?, rhs).convert(py)
T::$f(lhs.extract()?, rhs).convert(py)
})
}
Some(wrap::<$class>)
wrap::<$class>
}};
}
@ -102,10 +102,10 @@ macro_rules! py_binary_reversed_num_func {
// Swap lhs <-> rhs
let slf: &$crate::PyCell<T> = extract_or_return_not_implemented!(py, rhs);
let arg = extract_or_return_not_implemented!(py, lhs);
$class::$f(&*slf.try_borrow()?, arg).convert(py)
T::$f(&*slf.try_borrow()?, arg).convert(py)
})
}
Some(wrap::<$class>)
wrap::<$class>
}};
}
@ -123,17 +123,17 @@ macro_rules! py_binary_fallback_num_func {
let rhs = py.from_borrowed_ptr::<$crate::PyAny>(rhs);
// First, try the left hand method (e.g., __add__)
match (lhs.extract(), rhs.extract()) {
(Ok(l), Ok(r)) => $class::$lop(l, r).convert(py),
(Ok(l), Ok(r)) => T::$lop(l, r).convert(py),
_ => {
// 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)
T::$rop(&*slf.try_borrow()?, arg).convert(py)
}
}
})
}
Some(wrap::<$class>)
wrap::<$class>
}};
}
@ -155,7 +155,7 @@ macro_rules! py_binary_self_func {
Ok::<_, $crate::err::PyErr>(slf)
})
}
Some(wrap::<$class>)
wrap::<$class>
}};
}
@ -177,7 +177,7 @@ macro_rules! py_ssizearg_func {
$call!(slf, $f; arg.into()).convert(py)
})
}
Some(wrap::<$class>)
wrap::<$class>
}};
}
@ -203,11 +203,11 @@ macro_rules! py_ternarys_func {
.from_borrowed_ptr::<$crate::types::PyAny>(arg2)
.extract()?;
$class::$f(slf, arg1, arg2).convert(py)
T::$f(slf, arg1, arg2).convert(py)
})
}
Some(wrap::<T>)
wrap::<$class>
}};
($trait:ident, $class:ident :: $f:ident) => {
py_ternarys_func!($trait, $class::$f, *mut $crate::ffi::PyObject);
@ -215,8 +215,8 @@ macro_rules! py_ternarys_func {
}
macro_rules! py_func_set {
($trait_name:ident, $generic:ident, $fn_set:ident) => {{
unsafe extern "C" fn wrap<$generic>(
($trait_name:ident, $class:ident :: $fn_set:ident) => {{
unsafe extern "C" fn wrap<T>(
slf: *mut $crate::ffi::PyObject,
name: *mut $crate::ffi::PyObject,
value: *mut $crate::ffi::PyObject,
@ -225,12 +225,12 @@ macro_rules! py_func_set {
T: for<'p> $trait_name<'p>,
{
$crate::callback_body!(py, {
let slf = py.from_borrowed_ptr::<$crate::PyCell<$generic>>(slf);
let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
if value.is_null() {
Err($crate::exceptions::PyNotImplementedError::new_err(format!(
"Subscript deletion not supported by {:?}",
stringify!($generic)
stringify!($class)
)))
} else {
let name = py.from_borrowed_ptr::<$crate::PyAny>(name);
@ -240,23 +240,23 @@ macro_rules! py_func_set {
})
}
Some(wrap::<$generic>)
wrap::<$class>
}};
}
macro_rules! py_func_del {
($trait_name:ident, $generic:ident, $fn_del:ident) => {{
unsafe extern "C" fn wrap<U>(
($trait_name:ident, $class:ident :: $fn_del:ident) => {{
unsafe extern "C" fn wrap<T>(
slf: *mut $crate::ffi::PyObject,
name: *mut $crate::ffi::PyObject,
value: *mut $crate::ffi::PyObject,
) -> libc::c_int
where
U: for<'p> $trait_name<'p>,
T: for<'p> $trait_name<'p>,
{
$crate::callback_body!(py, {
if value.is_null() {
let slf = py.from_borrowed_ptr::<$crate::PyCell<U>>(slf);
let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
let name = py
.from_borrowed_ptr::<$crate::types::PyAny>(name)
.extract()?;
@ -269,13 +269,13 @@ macro_rules! py_func_del {
})
}
Some(wrap::<$generic>)
wrap::<$class>
}};
}
macro_rules! py_func_set_del {
($trait1:ident, $trait2:ident, $generic:ident, $fn_set:ident, $fn_del:ident) => {{
unsafe extern "C" fn wrap<$generic>(
($trait1:ident, $trait2:ident, $class:ident, $fn_set:ident, $fn_del:ident) => {{
unsafe extern "C" fn wrap<T>(
slf: *mut $crate::ffi::PyObject,
name: *mut $crate::ffi::PyObject,
value: *mut $crate::ffi::PyObject,
@ -284,7 +284,7 @@ macro_rules! py_func_set_del {
T: for<'p> $trait1<'p> + for<'p> $trait2<'p>,
{
$crate::callback_body!(py, {
let slf = py.from_borrowed_ptr::<$crate::PyCell<$generic>>(slf);
let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
let name = py.from_borrowed_ptr::<$crate::PyAny>(name);
if value.is_null() {
@ -295,7 +295,7 @@ macro_rules! py_func_set_del {
}
})
}
Some(wrap::<$generic>)
wrap::<$class>
}};
}

View File

@ -3,6 +3,7 @@
//! Python Mapping Interface
//! Trait and support implementation for implementing mapping support
use super::proto_methods::TypedSlot;
use crate::callback::IntoPyCallbackOutput;
use crate::{exceptions, ffi, FromPyObject, PyClass, PyObject};
@ -72,42 +73,64 @@ pub trait PyMappingReversedProtocol<'p>: PyMappingProtocol<'p> {
type Result: IntoPyCallbackOutput<PyObject>;
}
/// Extension trait for proc-macro backend.
#[doc(hidden)]
impl ffi::PyMappingMethods {
pub fn set_length<T>(&mut self)
pub trait PyMappingSlots {
fn get_len() -> TypedSlot<ffi::lenfunc>
where
T: for<'p> PyMappingLenProtocol<'p>,
Self: for<'p> PyMappingLenProtocol<'p>,
{
self.mp_length = py_len_func!(PyMappingLenProtocol, T::__len__);
TypedSlot(
ffi::Py_mp_length,
py_len_func!(PyMappingLenProtocol, Self::__len__),
)
}
pub fn set_getitem<T>(&mut self)
fn get_getitem() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyMappingGetItemProtocol<'p>,
Self: for<'p> PyMappingGetItemProtocol<'p>,
{
self.mp_subscript = py_binary_func!(PyMappingGetItemProtocol, T::__getitem__);
TypedSlot(
ffi::Py_mp_subscript,
py_binary_func!(PyMappingGetItemProtocol, Self::__getitem__),
)
}
pub fn set_setitem<T>(&mut self)
fn get_setitem() -> TypedSlot<ffi::objobjargproc>
where
T: for<'p> PyMappingSetItemProtocol<'p>,
Self: for<'p> PyMappingSetItemProtocol<'p>,
{
self.mp_ass_subscript = py_func_set!(PyMappingSetItemProtocol, T, __setitem__);
TypedSlot(
ffi::Py_mp_ass_subscript,
py_func_set!(PyMappingSetItemProtocol, Self::__setitem__),
)
}
pub fn set_delitem<T>(&mut self)
fn get_delitem() -> TypedSlot<ffi::objobjargproc>
where
T: for<'p> PyMappingDelItemProtocol<'p>,
Self: for<'p> PyMappingDelItemProtocol<'p>,
{
self.mp_ass_subscript = py_func_del!(PyMappingDelItemProtocol, T, __delitem__);
TypedSlot(
ffi::Py_mp_ass_subscript,
py_func_del!(PyMappingDelItemProtocol, Self::__delitem__),
)
}
pub fn set_setdelitem<T>(&mut self)
fn get_setdelitem() -> TypedSlot<ffi::objobjargproc>
where
T: for<'p> PyMappingSetItemProtocol<'p> + for<'p> PyMappingDelItemProtocol<'p>,
Self: for<'p> PyMappingSetItemProtocol<'p> + for<'p> PyMappingDelItemProtocol<'p>,
{
self.mp_ass_subscript = py_func_set_del!(
PyMappingSetItemProtocol,
PyMappingDelItemProtocol,
T,
__setitem__,
__delitem__
);
TypedSlot(
ffi::Py_mp_ass_subscript,
py_func_set_del!(
PyMappingSetItemProtocol,
PyMappingDelItemProtocol,
Self,
__setitem__,
__delitem__
),
)
}
}
impl<'p, T> PyMappingSlots for T where T: PyMappingProtocol<'p> {}

View File

@ -2,7 +2,7 @@
//! Python Number Interface
//! Trait and support implementation for implementing number protocol
use super::proto_methods::TypedSlot;
use crate::callback::IntoPyCallbackOutput;
use crate::err::PyErr;
use crate::{ffi, FromPyObject, PyClass, PyObject};
@ -579,116 +579,164 @@ pub trait PyNumberIndexProtocol<'p>: PyNumberProtocol<'p> {
type Result: IntoPyCallbackOutput<PyObject>;
}
/// Extension trait for proc-macro backend.
#[doc(hidden)]
impl ffi::PyNumberMethods {
pub(crate) fn from_nb_bool(nb_bool: ffi::inquiry) -> *mut Self {
let mut nm = ffi::PyNumberMethods_INIT;
nm.nb_bool = Some(nb_bool);
Box::into_raw(Box::new(nm))
}
pub fn set_add_radd<T>(&mut self)
pub trait PyNumberSlots {
fn get_add_radd() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberAddProtocol<'p> + for<'p> PyNumberRAddProtocol<'p>,
Self: for<'p> PyNumberAddProtocol<'p> + for<'p> PyNumberRAddProtocol<'p>,
{
self.nb_add = py_binary_fallback_num_func!(
T,
PyNumberAddProtocol::__add__,
PyNumberRAddProtocol::__radd__
);
TypedSlot(
ffi::Py_nb_add,
py_binary_fallback_num_func!(
Self,
PyNumberAddProtocol::__add__,
PyNumberRAddProtocol::__radd__
),
)
}
pub fn set_add<T>(&mut self)
fn get_add() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberAddProtocol<'p>,
Self: for<'p> PyNumberAddProtocol<'p>,
{
self.nb_add = py_binary_num_func!(PyNumberAddProtocol, T::__add__);
TypedSlot(
ffi::Py_nb_add,
py_binary_num_func!(PyNumberAddProtocol, Self::__add__),
)
}
pub fn set_radd<T>(&mut self)
fn get_radd() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberRAddProtocol<'p>,
Self: for<'p> PyNumberRAddProtocol<'p>,
{
self.nb_add = py_binary_reversed_num_func!(PyNumberRAddProtocol, T::__radd__);
TypedSlot(
ffi::Py_nb_add,
py_binary_reversed_num_func!(PyNumberRAddProtocol, Self::__radd__),
)
}
pub fn set_sub_rsub<T>(&mut self)
fn get_sub_rsub() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberSubProtocol<'p> + for<'p> PyNumberRSubProtocol<'p>,
Self: for<'p> PyNumberSubProtocol<'p> + for<'p> PyNumberRSubProtocol<'p>,
{
self.nb_subtract = py_binary_fallback_num_func!(
T,
PyNumberSubProtocol::__sub__,
PyNumberRSubProtocol::__rsub__
);
TypedSlot(
ffi::Py_nb_subtract,
py_binary_fallback_num_func!(
Self,
PyNumberSubProtocol::__sub__,
PyNumberRSubProtocol::__rsub__
),
)
}
pub fn set_sub<T>(&mut self)
fn get_sub() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberSubProtocol<'p>,
Self: for<'p> PyNumberSubProtocol<'p>,
{
self.nb_subtract = py_binary_num_func!(PyNumberSubProtocol, T::__sub__);
TypedSlot(
ffi::Py_nb_subtract,
py_binary_num_func!(PyNumberSubProtocol, Self::__sub__),
)
}
pub fn set_rsub<T>(&mut self)
fn get_rsub() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberRSubProtocol<'p>,
Self: for<'p> PyNumberRSubProtocol<'p>,
{
self.nb_subtract = py_binary_reversed_num_func!(PyNumberRSubProtocol, T::__rsub__);
TypedSlot(
ffi::Py_nb_subtract,
py_binary_reversed_num_func!(PyNumberRSubProtocol, Self::__rsub__),
)
}
pub fn set_mul_rmul<T>(&mut self)
fn get_mul_rmul() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberMulProtocol<'p> + for<'p> PyNumberRMulProtocol<'p>,
Self: for<'p> PyNumberMulProtocol<'p> + for<'p> PyNumberRMulProtocol<'p>,
{
self.nb_multiply = py_binary_fallback_num_func!(
T,
PyNumberMulProtocol::__mul__,
PyNumberRMulProtocol::__rmul__
);
TypedSlot(
ffi::Py_nb_multiply,
py_binary_fallback_num_func!(
Self,
PyNumberMulProtocol::__mul__,
PyNumberRMulProtocol::__rmul__
),
)
}
pub fn set_mul<T>(&mut self)
fn get_mul() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberMulProtocol<'p>,
Self: for<'p> PyNumberMulProtocol<'p>,
{
self.nb_multiply = py_binary_num_func!(PyNumberMulProtocol, T::__mul__);
TypedSlot(
ffi::Py_nb_multiply,
py_binary_num_func!(PyNumberMulProtocol, Self::__mul__),
)
}
pub fn set_rmul<T>(&mut self)
fn get_rmul() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberRMulProtocol<'p>,
Self: for<'p> PyNumberRMulProtocol<'p>,
{
self.nb_multiply = py_binary_reversed_num_func!(PyNumberRMulProtocol, T::__rmul__);
TypedSlot(
ffi::Py_nb_multiply,
py_binary_reversed_num_func!(PyNumberRMulProtocol, Self::__rmul__),
)
}
pub fn set_mod<T>(&mut self)
fn get_mod() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberModProtocol<'p>,
Self: for<'p> PyNumberModProtocol<'p>,
{
self.nb_remainder = py_binary_num_func!(PyNumberModProtocol, T::__mod__);
TypedSlot(
ffi::Py_nb_remainder,
py_binary_num_func!(PyNumberModProtocol, Self::__mod__),
)
}
pub fn set_divmod_rdivmod<T>(&mut self)
fn get_divmod_rdivmod() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberDivmodProtocol<'p> + for<'p> PyNumberRDivmodProtocol<'p>,
Self: for<'p> PyNumberDivmodProtocol<'p> + for<'p> PyNumberRDivmodProtocol<'p>,
{
self.nb_divmod = py_binary_fallback_num_func!(
T,
PyNumberDivmodProtocol::__divmod__,
PyNumberRDivmodProtocol::__rdivmod__
);
TypedSlot(
ffi::Py_nb_divmod,
py_binary_fallback_num_func!(
Self,
PyNumberDivmodProtocol::__divmod__,
PyNumberRDivmodProtocol::__rdivmod__
),
)
}
pub fn set_divmod<T>(&mut self)
fn get_divmod() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberDivmodProtocol<'p>,
Self: for<'p> PyNumberDivmodProtocol<'p>,
{
self.nb_divmod = py_binary_num_func!(PyNumberDivmodProtocol, T::__divmod__);
TypedSlot(
ffi::Py_nb_divmod,
py_binary_num_func!(PyNumberDivmodProtocol, Self::__divmod__),
)
}
pub fn set_rdivmod<T>(&mut self)
fn get_rdivmod() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberRDivmodProtocol<'p>,
Self: for<'p> PyNumberRDivmodProtocol<'p>,
{
self.nb_divmod = py_binary_reversed_num_func!(PyNumberRDivmodProtocol, T::__rdivmod__);
TypedSlot(
ffi::Py_nb_divmod,
py_binary_reversed_num_func!(PyNumberRDivmodProtocol, Self::__rdivmod__),
)
}
pub fn set_pow_rpow<T>(&mut self)
fn get_pow_rpow() -> TypedSlot<ffi::ternaryfunc>
where
T: for<'p> PyNumberPowProtocol<'p> + for<'p> PyNumberRPowProtocol<'p>,
Self: for<'p> PyNumberPowProtocol<'p> + for<'p> PyNumberRPowProtocol<'p>,
{
unsafe extern "C" fn wrap_pow_and_rpow<T>(
lhs: *mut crate::ffi::PyObject,
rhs: *mut crate::ffi::PyObject,
modulo: *mut crate::ffi::PyObject,
) -> *mut crate::ffi::PyObject
lhs: *mut ffi::PyObject,
rhs: *mut ffi::PyObject,
modulo: *mut ffi::PyObject,
) -> *mut ffi::PyObject
where
T: for<'p> PyNumberPowProtocol<'p> + for<'p> PyNumberRPowProtocol<'p>,
{
@ -709,17 +757,19 @@ impl ffi::PyNumberMethods {
}
})
}
self.nb_power = Some(wrap_pow_and_rpow::<T>);
TypedSlot(ffi::Py_nb_power, wrap_pow_and_rpow::<Self>)
}
pub fn set_pow<T>(&mut self)
fn get_pow() -> TypedSlot<ffi::ternaryfunc>
where
T: for<'p> PyNumberPowProtocol<'p>,
Self: for<'p> PyNumberPowProtocol<'p>,
{
unsafe extern "C" fn wrap_pow<T>(
lhs: *mut crate::ffi::PyObject,
rhs: *mut crate::ffi::PyObject,
modulo: *mut crate::ffi::PyObject,
) -> *mut crate::ffi::PyObject
lhs: *mut ffi::PyObject,
rhs: *mut ffi::PyObject,
modulo: *mut ffi::PyObject,
) -> *mut ffi::PyObject
where
T: for<'p> PyNumberPowProtocol<'p>,
{
@ -730,17 +780,19 @@ impl ffi::PyNumberMethods {
T::__pow__(lhs, rhs, modulo).convert(py)
})
}
self.nb_power = Some(wrap_pow::<T>);
TypedSlot(ffi::Py_nb_power, wrap_pow::<Self>)
}
pub fn set_rpow<T>(&mut self)
fn get_rpow() -> TypedSlot<ffi::ternaryfunc>
where
T: for<'p> PyNumberRPowProtocol<'p>,
Self: for<'p> PyNumberRPowProtocol<'p>,
{
unsafe extern "C" fn wrap_rpow<T>(
arg: *mut crate::ffi::PyObject,
slf: *mut crate::ffi::PyObject,
modulo: *mut crate::ffi::PyObject,
) -> *mut crate::ffi::PyObject
arg: *mut ffi::PyObject,
slf: *mut ffi::PyObject,
modulo: *mut ffi::PyObject,
) -> *mut ffi::PyObject
where
T: for<'p> PyNumberRPowProtocol<'p>,
{
@ -751,189 +803,291 @@ impl ffi::PyNumberMethods {
slf.try_borrow()?.__rpow__(arg, modulo).convert(py)
})
}
self.nb_power = Some(wrap_rpow::<T>);
TypedSlot(ffi::Py_nb_power, wrap_rpow::<Self>)
}
pub fn set_neg<T>(&mut self)
fn get_neg() -> TypedSlot<ffi::unaryfunc>
where
T: for<'p> PyNumberNegProtocol<'p>,
Self: for<'p> PyNumberNegProtocol<'p>,
{
self.nb_negative = py_unary_func!(PyNumberNegProtocol, T::__neg__);
TypedSlot(
ffi::Py_nb_negative,
py_unary_func!(PyNumberNegProtocol, Self::__neg__),
)
}
pub fn set_pos<T>(&mut self)
fn get_pos() -> TypedSlot<ffi::unaryfunc>
where
T: for<'p> PyNumberPosProtocol<'p>,
Self: for<'p> PyNumberPosProtocol<'p>,
{
self.nb_positive = py_unary_func!(PyNumberPosProtocol, T::__pos__);
TypedSlot(
ffi::Py_nb_positive,
py_unary_func!(PyNumberPosProtocol, Self::__pos__),
)
}
pub fn set_abs<T>(&mut self)
fn get_abs() -> TypedSlot<ffi::unaryfunc>
where
T: for<'p> PyNumberAbsProtocol<'p>,
Self: for<'p> PyNumberAbsProtocol<'p>,
{
self.nb_absolute = py_unary_func!(PyNumberAbsProtocol, T::__abs__);
TypedSlot(
ffi::Py_nb_absolute,
py_unary_func!(PyNumberAbsProtocol, Self::__abs__),
)
}
pub fn set_invert<T>(&mut self)
fn get_invert() -> TypedSlot<ffi::unaryfunc>
where
T: for<'p> PyNumberInvertProtocol<'p>,
Self: for<'p> PyNumberInvertProtocol<'p>,
{
self.nb_invert = py_unary_func!(PyNumberInvertProtocol, T::__invert__);
TypedSlot(
ffi::Py_nb_invert,
py_unary_func!(PyNumberInvertProtocol, Self::__invert__),
)
}
pub fn set_lshift_rlshift<T>(&mut self)
fn get_lshift_rlshift() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberLShiftProtocol<'p> + for<'p> PyNumberRLShiftProtocol<'p>,
Self: for<'p> PyNumberLShiftProtocol<'p> + for<'p> PyNumberRLShiftProtocol<'p>,
{
self.nb_lshift = py_binary_fallback_num_func!(
T,
PyNumberLShiftProtocol::__lshift__,
PyNumberRLShiftProtocol::__rlshift__
);
TypedSlot(
ffi::Py_nb_lshift,
py_binary_fallback_num_func!(
Self,
PyNumberLShiftProtocol::__lshift__,
PyNumberRLShiftProtocol::__rlshift__
),
)
}
pub fn set_lshift<T>(&mut self)
fn get_lshift() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberLShiftProtocol<'p>,
Self: for<'p> PyNumberLShiftProtocol<'p>,
{
self.nb_lshift = py_binary_num_func!(PyNumberLShiftProtocol, T::__lshift__);
TypedSlot(
ffi::Py_nb_lshift,
py_binary_num_func!(PyNumberLShiftProtocol, Self::__lshift__),
)
}
pub fn set_rlshift<T>(&mut self)
fn get_rlshift() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberRLShiftProtocol<'p>,
Self: for<'p> PyNumberRLShiftProtocol<'p>,
{
self.nb_lshift = py_binary_reversed_num_func!(PyNumberRLShiftProtocol, T::__rlshift__);
TypedSlot(
ffi::Py_nb_lshift,
py_binary_reversed_num_func!(PyNumberRLShiftProtocol, Self::__rlshift__),
)
}
pub fn set_rshift_rrshift<T>(&mut self)
fn get_rshift_rrshift() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberRShiftProtocol<'p> + for<'p> PyNumberRRShiftProtocol<'p>,
Self: for<'p> PyNumberRShiftProtocol<'p> + for<'p> PyNumberRRShiftProtocol<'p>,
{
self.nb_rshift = py_binary_fallback_num_func!(
T,
PyNumberRShiftProtocol::__rshift__,
PyNumberRRShiftProtocol::__rrshift__
);
TypedSlot(
ffi::Py_nb_rshift,
py_binary_fallback_num_func!(
Self,
PyNumberRShiftProtocol::__rshift__,
PyNumberRRShiftProtocol::__rrshift__
),
)
}
pub fn set_rshift<T>(&mut self)
fn get_rshift() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberRShiftProtocol<'p>,
Self: for<'p> PyNumberRShiftProtocol<'p>,
{
self.nb_rshift = py_binary_num_func!(PyNumberRShiftProtocol, T::__rshift__);
TypedSlot(
ffi::Py_nb_rshift,
py_binary_num_func!(PyNumberRShiftProtocol, Self::__rshift__),
)
}
pub fn set_rrshift<T>(&mut self)
fn get_rrshift() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberRRShiftProtocol<'p>,
Self: for<'p> PyNumberRRShiftProtocol<'p>,
{
self.nb_rshift = py_binary_reversed_num_func!(PyNumberRRShiftProtocol, T::__rrshift__);
TypedSlot(
ffi::Py_nb_rshift,
py_binary_reversed_num_func!(PyNumberRRShiftProtocol, Self::__rrshift__),
)
}
pub fn set_and_rand<T>(&mut self)
fn get_and_rand() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberAndProtocol<'p> + for<'p> PyNumberRAndProtocol<'p>,
Self: for<'p> PyNumberAndProtocol<'p> + for<'p> PyNumberRAndProtocol<'p>,
{
self.nb_and = py_binary_fallback_num_func!(
T,
PyNumberAndProtocol::__and__,
PyNumberRAndProtocol::__rand__
);
TypedSlot(
ffi::Py_nb_and,
py_binary_fallback_num_func!(
Self,
PyNumberAndProtocol::__and__,
PyNumberRAndProtocol::__rand__
),
)
}
pub fn set_and<T>(&mut self)
fn get_and() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberAndProtocol<'p>,
Self: for<'p> PyNumberAndProtocol<'p>,
{
self.nb_and = py_binary_num_func!(PyNumberAndProtocol, T::__and__);
TypedSlot(
ffi::Py_nb_and,
py_binary_num_func!(PyNumberAndProtocol, Self::__and__),
)
}
pub fn set_rand<T>(&mut self)
fn get_rand() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberRAndProtocol<'p>,
Self: for<'p> PyNumberRAndProtocol<'p>,
{
self.nb_and = py_binary_reversed_num_func!(PyNumberRAndProtocol, T::__rand__);
TypedSlot(
ffi::Py_nb_and,
py_binary_reversed_num_func!(PyNumberRAndProtocol, Self::__rand__),
)
}
pub fn set_xor_rxor<T>(&mut self)
fn get_xor_rxor() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberXorProtocol<'p> + for<'p> PyNumberRXorProtocol<'p>,
Self: for<'p> PyNumberXorProtocol<'p> + for<'p> PyNumberRXorProtocol<'p>,
{
self.nb_xor = py_binary_fallback_num_func!(
T,
PyNumberXorProtocol::__xor__,
PyNumberRXorProtocol::__rxor__
);
TypedSlot(
ffi::Py_nb_xor,
py_binary_fallback_num_func!(
Self,
PyNumberXorProtocol::__xor__,
PyNumberRXorProtocol::__rxor__
),
)
}
pub fn set_xor<T>(&mut self)
fn get_xor() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberXorProtocol<'p>,
Self: for<'p> PyNumberXorProtocol<'p>,
{
self.nb_xor = py_binary_num_func!(PyNumberXorProtocol, T::__xor__);
TypedSlot(
ffi::Py_nb_xor,
py_binary_num_func!(PyNumberXorProtocol, Self::__xor__),
)
}
pub fn set_rxor<T>(&mut self)
fn get_rxor() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberRXorProtocol<'p>,
Self: for<'p> PyNumberRXorProtocol<'p>,
{
self.nb_xor = py_binary_reversed_num_func!(PyNumberRXorProtocol, T::__rxor__);
TypedSlot(
ffi::Py_nb_xor,
py_binary_reversed_num_func!(PyNumberRXorProtocol, Self::__rxor__),
)
}
pub fn set_or_ror<T>(&mut self)
fn get_or_ror() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberOrProtocol<'p> + for<'p> PyNumberROrProtocol<'p>,
Self: for<'p> PyNumberOrProtocol<'p> + for<'p> PyNumberROrProtocol<'p>,
{
self.nb_or = py_binary_fallback_num_func!(
T,
PyNumberOrProtocol::__or__,
PyNumberROrProtocol::__ror__
);
TypedSlot(
ffi::Py_nb_or,
py_binary_fallback_num_func!(
Self,
PyNumberOrProtocol::__or__,
PyNumberROrProtocol::__ror__
),
)
}
pub fn set_or<T>(&mut self)
fn get_or() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberOrProtocol<'p>,
Self: for<'p> PyNumberOrProtocol<'p>,
{
self.nb_or = py_binary_num_func!(PyNumberOrProtocol, T::__or__);
TypedSlot(
ffi::Py_nb_or,
py_binary_num_func!(PyNumberOrProtocol, Self::__or__),
)
}
pub fn set_ror<T>(&mut self)
fn get_ror() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberROrProtocol<'p>,
Self: for<'p> PyNumberROrProtocol<'p>,
{
self.nb_or = py_binary_reversed_num_func!(PyNumberROrProtocol, T::__ror__);
TypedSlot(
ffi::Py_nb_or,
py_binary_reversed_num_func!(PyNumberROrProtocol, Self::__ror__),
)
}
pub fn set_int<T>(&mut self)
fn get_int() -> TypedSlot<ffi::unaryfunc>
where
T: for<'p> PyNumberIntProtocol<'p>,
Self: for<'p> PyNumberIntProtocol<'p>,
{
self.nb_int = py_unary_func!(PyNumberIntProtocol, T::__int__);
TypedSlot(
ffi::Py_nb_int,
py_unary_func!(PyNumberIntProtocol, Self::__int__),
)
}
pub fn set_float<T>(&mut self)
fn get_float() -> TypedSlot<ffi::unaryfunc>
where
T: for<'p> PyNumberFloatProtocol<'p>,
Self: for<'p> PyNumberFloatProtocol<'p>,
{
self.nb_float = py_unary_func!(PyNumberFloatProtocol, T::__float__);
TypedSlot(
ffi::Py_nb_float,
py_unary_func!(PyNumberFloatProtocol, Self::__float__),
)
}
pub fn set_iadd<T>(&mut self)
fn get_iadd() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberIAddProtocol<'p>,
Self: for<'p> PyNumberIAddProtocol<'p>,
{
self.nb_inplace_add = py_binary_self_func!(PyNumberIAddProtocol, T::__iadd__);
TypedSlot(
ffi::Py_nb_inplace_add,
py_binary_self_func!(PyNumberIAddProtocol, Self::__iadd__),
)
}
pub fn set_isub<T>(&mut self)
fn get_isub() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberISubProtocol<'p>,
Self: for<'p> PyNumberISubProtocol<'p>,
{
self.nb_inplace_subtract = py_binary_self_func!(PyNumberISubProtocol, T::__isub__);
TypedSlot(
ffi::Py_nb_inplace_subtract,
py_binary_self_func!(PyNumberISubProtocol, Self::__isub__),
)
}
pub fn set_imul<T>(&mut self)
fn get_imul() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberIMulProtocol<'p>,
Self: for<'p> PyNumberIMulProtocol<'p>,
{
self.nb_inplace_multiply = py_binary_self_func!(PyNumberIMulProtocol, T::__imul__);
TypedSlot(
ffi::Py_nb_inplace_multiply,
py_binary_self_func!(PyNumberIMulProtocol, Self::__imul__),
)
}
pub fn set_imod<T>(&mut self)
fn get_imod() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberIModProtocol<'p>,
Self: for<'p> PyNumberIModProtocol<'p>,
{
self.nb_inplace_remainder = py_binary_self_func!(PyNumberIModProtocol, T::__imod__);
TypedSlot(
ffi::Py_nb_inplace_remainder,
py_binary_self_func!(PyNumberIModProtocol, Self::__imod__),
)
}
pub fn set_ipow<T>(&mut self)
fn get_ipow() -> TypedSlot<ffi::ternaryfunc>
where
T: for<'p> PyNumberIPowProtocol<'p>,
Self: for<'p> PyNumberIPowProtocol<'p>,
{
// NOTE: Somehow __ipow__ causes SIGSEGV in Python < 3.8 when we extract,
// so we ignore it. It's the same as what CPython does.
unsafe extern "C" fn wrap_ipow<T>(
slf: *mut crate::ffi::PyObject,
other: *mut crate::ffi::PyObject,
_modulo: *mut crate::ffi::PyObject,
) -> *mut crate::ffi::PyObject
slf: *mut ffi::PyObject,
other: *mut ffi::PyObject,
_modulo: *mut ffi::PyObject,
) -> *mut ffi::PyObject
where
T: for<'p> PyNumberIPowProtocol<'p>,
{
@ -945,132 +1099,201 @@ impl ffi::PyNumberMethods {
Ok::<_, PyErr>(slf)
})
}
self.nb_inplace_power = Some(wrap_ipow::<T>);
TypedSlot(ffi::Py_nb_inplace_power, wrap_ipow::<Self>)
}
pub fn set_ilshift<T>(&mut self)
fn get_ilshift() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberILShiftProtocol<'p>,
Self: for<'p> PyNumberILShiftProtocol<'p>,
{
self.nb_inplace_lshift = py_binary_self_func!(PyNumberILShiftProtocol, T::__ilshift__);
TypedSlot(
ffi::Py_nb_inplace_lshift,
py_binary_self_func!(PyNumberILShiftProtocol, Self::__ilshift__),
)
}
pub fn set_irshift<T>(&mut self)
fn get_irshift() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberIRShiftProtocol<'p>,
Self: for<'p> PyNumberIRShiftProtocol<'p>,
{
self.nb_inplace_rshift = py_binary_self_func!(PyNumberIRShiftProtocol, T::__irshift__);
TypedSlot(
ffi::Py_nb_inplace_rshift,
py_binary_self_func!(PyNumberIRShiftProtocol, Self::__irshift__),
)
}
pub fn set_iand<T>(&mut self)
fn get_iand() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberIAndProtocol<'p>,
Self: for<'p> PyNumberIAndProtocol<'p>,
{
self.nb_inplace_and = py_binary_self_func!(PyNumberIAndProtocol, T::__iand__);
TypedSlot(
ffi::Py_nb_inplace_and,
py_binary_self_func!(PyNumberIAndProtocol, Self::__iand__),
)
}
pub fn set_ixor<T>(&mut self)
fn get_ixor() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberIXorProtocol<'p>,
Self: for<'p> PyNumberIXorProtocol<'p>,
{
self.nb_inplace_xor = py_binary_self_func!(PyNumberIXorProtocol, T::__ixor__);
TypedSlot(
ffi::Py_nb_inplace_xor,
py_binary_self_func!(PyNumberIXorProtocol, Self::__ixor__),
)
}
pub fn set_ior<T>(&mut self)
fn get_ior() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberIOrProtocol<'p>,
Self: for<'p> PyNumberIOrProtocol<'p>,
{
self.nb_inplace_or = py_binary_self_func!(PyNumberIOrProtocol, T::__ior__);
TypedSlot(
ffi::Py_nb_inplace_or,
py_binary_self_func!(PyNumberIOrProtocol, Self::__ior__),
)
}
pub fn set_floordiv_rfloordiv<T>(&mut self)
fn get_floordiv_rfloordiv() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberFloordivProtocol<'p> + for<'p> PyNumberRFloordivProtocol<'p>,
Self: for<'p> PyNumberFloordivProtocol<'p> + for<'p> PyNumberRFloordivProtocol<'p>,
{
self.nb_floor_divide = py_binary_fallback_num_func!(
T,
PyNumberFloordivProtocol::__floordiv__,
PyNumberRFloordivProtocol::__rfloordiv__
);
TypedSlot(
ffi::Py_nb_floor_divide,
py_binary_fallback_num_func!(
Self,
PyNumberFloordivProtocol::__floordiv__,
PyNumberRFloordivProtocol::__rfloordiv__
),
)
}
pub fn set_floordiv<T>(&mut self)
fn get_floordiv() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberFloordivProtocol<'p>,
Self: for<'p> PyNumberFloordivProtocol<'p>,
{
self.nb_floor_divide = py_binary_num_func!(PyNumberFloordivProtocol, T::__floordiv__);
TypedSlot(
ffi::Py_nb_floor_divide,
py_binary_num_func!(PyNumberFloordivProtocol, Self::__floordiv__),
)
}
pub fn set_rfloordiv<T>(&mut self)
fn get_rfloordiv() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberRFloordivProtocol<'p>,
Self: for<'p> PyNumberRFloordivProtocol<'p>,
{
self.nb_floor_divide =
py_binary_reversed_num_func!(PyNumberRFloordivProtocol, T::__rfloordiv__);
TypedSlot(
ffi::Py_nb_floor_divide,
py_binary_reversed_num_func!(PyNumberRFloordivProtocol, Self::__rfloordiv__),
)
}
pub fn set_truediv_rtruediv<T>(&mut self)
fn get_truediv_rtruediv() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberTruedivProtocol<'p> + for<'p> PyNumberRTruedivProtocol<'p>,
Self: for<'p> PyNumberTruedivProtocol<'p> + for<'p> PyNumberRTruedivProtocol<'p>,
{
self.nb_true_divide = py_binary_fallback_num_func!(
T,
PyNumberTruedivProtocol::__truediv__,
PyNumberRTruedivProtocol::__rtruediv__
);
TypedSlot(
ffi::Py_nb_true_divide,
py_binary_fallback_num_func!(
Self,
PyNumberTruedivProtocol::__truediv__,
PyNumberRTruedivProtocol::__rtruediv__
),
)
}
pub fn set_truediv<T>(&mut self)
fn get_truediv() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberTruedivProtocol<'p>,
Self: for<'p> PyNumberTruedivProtocol<'p>,
{
self.nb_true_divide = py_binary_num_func!(PyNumberTruedivProtocol, T::__truediv__);
TypedSlot(
ffi::Py_nb_true_divide,
py_binary_num_func!(PyNumberTruedivProtocol, Self::__truediv__),
)
}
pub fn set_rtruediv<T>(&mut self)
fn get_rtruediv() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberRTruedivProtocol<'p>,
Self: for<'p> PyNumberRTruedivProtocol<'p>,
{
self.nb_true_divide =
py_binary_reversed_num_func!(PyNumberRTruedivProtocol, T::__rtruediv__);
TypedSlot(
ffi::Py_nb_true_divide,
py_binary_reversed_num_func!(PyNumberRTruedivProtocol, Self::__rtruediv__),
)
}
pub fn set_ifloordiv<T>(&mut self)
fn get_ifloordiv() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberIFloordivProtocol<'p>,
Self: for<'p> PyNumberIFloordivProtocol<'p>,
{
self.nb_inplace_floor_divide =
py_binary_self_func!(PyNumberIFloordivProtocol, T::__ifloordiv__);
TypedSlot(
ffi::Py_nb_inplace_floor_divide,
py_binary_self_func!(PyNumberIFloordivProtocol, Self::__ifloordiv__),
)
}
pub fn set_itruediv<T>(&mut self)
fn get_itruediv() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberITruedivProtocol<'p>,
Self: for<'p> PyNumberITruedivProtocol<'p>,
{
self.nb_inplace_true_divide =
py_binary_self_func!(PyNumberITruedivProtocol, T::__itruediv__);
TypedSlot(
ffi::Py_nb_inplace_true_divide,
py_binary_self_func!(PyNumberITruedivProtocol, Self::__itruediv__),
)
}
pub fn set_index<T>(&mut self)
fn get_index() -> TypedSlot<ffi::unaryfunc>
where
T: for<'p> PyNumberIndexProtocol<'p>,
Self: for<'p> PyNumberIndexProtocol<'p>,
{
self.nb_index = py_unary_func!(PyNumberIndexProtocol, T::__index__);
TypedSlot(
ffi::Py_nb_index,
py_unary_func!(PyNumberIndexProtocol, Self::__index__),
)
}
pub fn set_matmul_rmatmul<T>(&mut self)
fn get_matmul_rmatmul() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberMatmulProtocol<'p> + for<'p> PyNumberRMatmulProtocol<'p>,
Self: for<'p> PyNumberMatmulProtocol<'p> + for<'p> PyNumberRMatmulProtocol<'p>,
{
self.nb_matrix_multiply = py_binary_fallback_num_func!(
T,
PyNumberMatmulProtocol::__matmul__,
PyNumberRMatmulProtocol::__rmatmul__
);
TypedSlot(
ffi::Py_nb_matrix_multiply,
py_binary_fallback_num_func!(
Self,
PyNumberMatmulProtocol::__matmul__,
PyNumberRMatmulProtocol::__rmatmul__
),
)
}
pub fn set_matmul<T>(&mut self)
fn get_matmul() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberMatmulProtocol<'p>,
Self: for<'p> PyNumberMatmulProtocol<'p>,
{
self.nb_matrix_multiply = py_binary_num_func!(PyNumberMatmulProtocol, T::__matmul__);
TypedSlot(
ffi::Py_nb_matrix_multiply,
py_binary_num_func!(PyNumberMatmulProtocol, Self::__matmul__),
)
}
pub fn set_rmatmul<T>(&mut self)
fn get_rmatmul() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberRMatmulProtocol<'p>,
Self: for<'p> PyNumberRMatmulProtocol<'p>,
{
self.nb_matrix_multiply =
py_binary_reversed_num_func!(PyNumberRMatmulProtocol, T::__rmatmul__);
TypedSlot(
ffi::Py_nb_matrix_multiply,
py_binary_reversed_num_func!(PyNumberRMatmulProtocol, Self::__rmatmul__),
)
}
pub fn set_imatmul<T>(&mut self)
fn get_imatmul() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PyNumberIMatmulProtocol<'p>,
Self: for<'p> PyNumberIMatmulProtocol<'p>,
{
self.nb_inplace_matrix_multiply =
py_binary_self_func!(PyNumberIMatmulProtocol, T::__imatmul__);
TypedSlot(
ffi::Py_nb_inplace_matrix_multiply,
py_binary_self_func!(PyNumberIMatmulProtocol, Self::__imatmul__),
)
}
}
impl<'p, T> PyNumberSlots for T where T: PyNumberProtocol<'p> {}

View File

@ -1,154 +1,78 @@
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},
};
use crate::ffi;
#[cfg(not(Py_LIMITED_API))]
use crate::ffi::PyBufferProcs;
/// ABI3 doesn't have buffer APIs, so here we define the empty one.
#[cfg(Py_LIMITED_API)]
#[doc(hidden)]
#[derive(Clone)]
pub struct PyBufferProcs;
/// 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 get_type_slots() -> Vec<ffi::PyType_Slot> {
vec![]
}
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>> {
fn get_buffer() -> Option<PyBufferProcs> {
None
}
}
/// Indicates that a type has a protocol registry. Implemented by `#[pyclass]`.
/// Typed version of `ffi::PyType_Slot`
#[doc(hidden)]
pub trait HasProtoRegistry: Sized + 'static {
fn registry() -> &'static PyProtoRegistry;
}
pub struct TypedSlot<T: Sized>(pub std::os::raw::c_int, pub T);
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>,
pub enum PyProtoMethodDef {
Slots(Vec<ffi::PyType_Slot>),
Buffer(PyBufferProcs),
}
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)
impl From<Vec<ffi::PyType_Slot>> for PyProtoMethodDef {
fn from(slots: Vec<ffi::PyType_Slot>) -> Self {
PyProtoMethodDef::Slots(slots)
}
}
impl From<PyBufferProcs> for PyProtoMethodDef {
fn from(buffer_procs: PyBufferProcs) -> Self {
PyProtoMethodDef::Buffer(buffer_procs)
}
}
#[doc(hidden)]
#[cfg(feature = "macros")]
pub trait PyProtoInventory: inventory::Collect {
fn new(methods: PyProtoMethodDef) -> Self;
fn get(&'static self) -> &'static PyProtoMethodDef;
}
#[doc(hidden)]
#[cfg(feature = "macros")]
pub trait HasProtoInventory {
type ProtoMethods: PyProtoInventory;
}
#[cfg(feature = "macros")]
impl<T: HasProtoInventory> PyProtoMethods for T {
fn get_type_slots() -> Vec<ffi::PyType_Slot> {
inventory::iter::<T::ProtoMethods>
.into_iter()
.filter_map(|def| match def.get() {
PyProtoMethodDef::Slots(slots) => Some(slots),
PyProtoMethodDef::Buffer(_) => None,
})
.flatten()
.cloned()
.collect()
}
fn get_buffer() -> Option<PyBufferProcs> {
inventory::iter::<T::ProtoMethods>
.into_iter()
.find_map(|def| match def.get() {
PyProtoMethodDef::Slots(_) => None,
PyProtoMethodDef::Buffer(buf) => Some(buf.clone()),
})
}
}

View File

@ -8,6 +8,7 @@
//! [PEP-0492](https://www.python.org/dev/peps/pep-0492/)
//!
use super::proto_methods::TypedSlot;
use crate::callback::IntoPyCallbackOutput;
use crate::derive_utils::TryFromPyCell;
use crate::err::PyResult;
@ -85,28 +86,43 @@ pub trait PyAsyncAexitProtocol<'p>: PyAsyncProtocol<'p> {
type Result: IntoPyCallbackOutput<PyObject>;
}
/// Extension trait for proc-macro backend.
#[doc(hidden)]
impl ffi::PyAsyncMethods {
pub fn set_await<T>(&mut self)
pub trait PyAsyncSlots {
fn get_await() -> TypedSlot<ffi::unaryfunc>
where
T: for<'p> PyAsyncAwaitProtocol<'p>,
Self: for<'p> PyAsyncAwaitProtocol<'p>,
{
self.am_await = py_unarys_func!(PyAsyncAwaitProtocol, T::__await__);
TypedSlot(
ffi::Py_am_await,
py_unarys_func!(PyAsyncAwaitProtocol, Self::__await__),
)
}
pub fn set_aiter<T>(&mut self)
fn get_aiter() -> TypedSlot<ffi::unaryfunc>
where
T: for<'p> PyAsyncAiterProtocol<'p>,
Self: for<'p> PyAsyncAiterProtocol<'p>,
{
self.am_aiter = py_unarys_func!(PyAsyncAiterProtocol, T::__aiter__);
TypedSlot(
ffi::Py_am_aiter,
py_unarys_func!(PyAsyncAiterProtocol, Self::__aiter__),
)
}
pub fn set_anext<T>(&mut self)
fn get_anext() -> TypedSlot<ffi::unaryfunc>
where
T: for<'p> PyAsyncAnextProtocol<'p>,
Self: for<'p> PyAsyncAnextProtocol<'p>,
{
self.am_anext = am_anext::<T>();
TypedSlot(
ffi::Py_am_anext,
py_unarys_func!(PyAsyncAnextProtocol, Self::__anext__),
)
}
}
impl<'p, T> PyAsyncSlots for T where T: PyAsyncProtocol<'p> {}
/// Output of `__anext__`.
pub enum IterANextOutput<T, U> {
Yield(T),
Return(U),
@ -149,11 +165,3 @@ where
}
}
}
#[inline]
fn am_anext<T>() -> Option<ffi::unaryfunc>
where
T: for<'p> PyAsyncAnextProtocol<'p>,
{
py_unarys_func!(PyAsyncAnextProtocol, T::__anext__)
}

View File

@ -3,6 +3,7 @@
//! Python Sequence Interface
//! Trait and support implementation for implementing sequence
use super::proto_methods::TypedSlot;
use crate::callback::IntoPyCallbackOutput;
use crate::conversion::{FromPyObject, IntoPy};
use crate::err::PyErr;
@ -128,88 +129,52 @@ pub trait PySequenceInplaceRepeatProtocol<'p>:
type Result: IntoPyCallbackOutput<Self>;
}
/// Extension trait for proc-macro backend.
#[doc(hidden)]
impl ffi::PySequenceMethods {
pub fn set_len<T>(&mut self)
pub trait PySequenceSlots {
fn get_len() -> TypedSlot<ffi::lenfunc>
where
T: for<'p> PySequenceLenProtocol<'p>,
Self: for<'p> PySequenceLenProtocol<'p>,
{
self.sq_length = py_len_func!(PySequenceLenProtocol, T::__len__);
}
pub fn set_concat<T>(&mut self)
where
T: for<'p> PySequenceConcatProtocol<'p>,
{
self.sq_concat = py_binary_func!(PySequenceConcatProtocol, T::__concat__);
}
pub fn set_repeat<T>(&mut self)
where
T: for<'p> PySequenceRepeatProtocol<'p>,
{
self.sq_repeat = py_ssizearg_func!(PySequenceRepeatProtocol, T::__repeat__);
}
pub fn set_getitem<T>(&mut self)
where
T: for<'p> PySequenceGetItemProtocol<'p>,
{
self.sq_item = py_ssizearg_func!(PySequenceGetItemProtocol, T::__getitem__);
}
pub fn set_setitem<T>(&mut self)
where
T: for<'p> PySequenceSetItemProtocol<'p>,
{
self.sq_ass_item = sq_ass_item_impl::set_item::<T>();
}
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
TypedSlot(
ffi::Py_sq_length,
py_len_func!(PySequenceLenProtocol, Self::__len__),
)
}
pub fn set_inplace_repeat<T>(&mut self)
fn get_concat() -> TypedSlot<ffi::binaryfunc>
where
T: for<'p> PySequenceInplaceRepeatProtocol<'p>,
Self: for<'p> PySequenceConcatProtocol<'p>,
{
self.sq_inplace_repeat = py_ssizearg_func!(
PySequenceInplaceRepeatProtocol,
T::__inplace_repeat__,
call_mut
TypedSlot(
ffi::Py_sq_concat,
py_binary_func!(PySequenceConcatProtocol, Self::__concat__),
)
}
}
/// It can be possible to delete and set items (PySequenceSetItemProtocol and
/// PySequenceDelItemProtocol implemented), only to delete (PySequenceDelItemProtocol implemented)
/// or no deleting or setting is possible
mod sq_ass_item_impl {
use super::*;
pub(super) fn set_item<T>() -> Option<ffi::ssizeobjargproc>
fn get_repeat() -> TypedSlot<ffi::ssizeargfunc>
where
T: for<'p> PySequenceSetItemProtocol<'p>,
Self: for<'p> PySequenceRepeatProtocol<'p>,
{
TypedSlot(
ffi::Py_sq_repeat,
py_ssizearg_func!(PySequenceRepeatProtocol, Self::__repeat__),
)
}
fn get_getitem() -> TypedSlot<ffi::ssizeargfunc>
where
Self: for<'p> PySequenceGetItemProtocol<'p>,
{
TypedSlot(
ffi::Py_sq_item,
py_ssizearg_func!(PySequenceGetItemProtocol, Self::__getitem__),
)
}
fn get_setitem() -> TypedSlot<ffi::ssizeobjargproc>
where
Self: for<'p> PySequenceSetItemProtocol<'p>,
{
unsafe extern "C" fn wrap<T>(
slf: *mut ffi::PyObject,
@ -235,12 +200,13 @@ mod sq_ass_item_impl {
crate::callback::convert(py, slf.__setitem__(key.into(), value))
})
}
Some(wrap::<T>)
TypedSlot(ffi::Py_sq_ass_item, wrap::<Self>)
}
pub(super) fn del_item<T>() -> Option<ffi::ssizeobjargproc>
fn get_delitem() -> TypedSlot<ffi::ssizeobjargproc>
where
T: for<'p> PySequenceDelItemProtocol<'p>,
Self: for<'p> PySequenceDelItemProtocol<'p>,
{
unsafe extern "C" fn wrap<T>(
slf: *mut ffi::PyObject,
@ -263,12 +229,13 @@ mod sq_ass_item_impl {
}
})
}
Some(wrap::<T>)
TypedSlot(ffi::Py_sq_ass_item, wrap::<Self>)
}
pub(super) fn set_del_item<T>() -> Option<ffi::ssizeobjargproc>
fn get_setdelitem() -> TypedSlot<ffi::ssizeobjargproc>
where
T: for<'p> PySequenceSetItemProtocol<'p> + for<'p> PySequenceDelItemProtocol<'p>,
Self: for<'p> PySequenceDelItemProtocol<'p> + for<'p> PySequenceSetItemProtocol<'p>,
{
unsafe extern "C" fn wrap<T>(
slf: *mut ffi::PyObject,
@ -291,6 +258,48 @@ mod sq_ass_item_impl {
}
})
}
Some(wrap::<T>)
TypedSlot(ffi::Py_sq_ass_item, wrap::<Self>)
}
fn get_contains() -> TypedSlot<ffi::objobjproc>
where
Self: for<'p> PySequenceContainsProtocol<'p>,
{
TypedSlot(
ffi::Py_sq_contains,
py_binary_func!(PySequenceContainsProtocol, Self::__contains__, c_int),
)
}
fn get_inplace_concat() -> TypedSlot<ffi::binaryfunc>
where
Self: for<'p> PySequenceInplaceConcatProtocol<'p>,
{
TypedSlot(
ffi::Py_sq_inplace_concat,
py_binary_func!(
PySequenceInplaceConcatProtocol,
Self::__inplace_concat__,
*mut ffi::PyObject,
call_mut
),
)
}
fn get_inplace_repeat() -> TypedSlot<ffi::ssizeargfunc>
where
Self: for<'p> PySequenceInplaceRepeatProtocol<'p>,
{
TypedSlot(
ffi::Py_sq_inplace_repeat,
py_ssizearg_func!(
PySequenceInplaceRepeatProtocol,
Self::__inplace_repeat__,
call_mut
),
)
}
}
impl<'p, T> PySequenceSlots for T where T: PySequenceProtocol<'p> {}

View File

@ -491,7 +491,7 @@ impl<'a> std::fmt::Display for PyDowncastError<'a> {
write!(
f,
"'{}' object cannot be converted to '{}'",
self.from.get_type().name(),
self.from.get_type().name().map_err(|_| std::fmt::Error)?,
self.to
)
}

View File

@ -29,8 +29,8 @@ macro_rules! impl_exception_boilerplate {
impl std::fmt::Debug for $name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let type_name = self.get_type().name();
f.debug_struct(&*type_name)
let type_name = self.get_type().name().map_err(|_| std::fmt::Error)?;
f.debug_struct(type_name)
// TODO: print out actual fields!
.finish()
}
@ -38,7 +38,7 @@ macro_rules! impl_exception_boilerplate {
impl std::fmt::Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let type_name = self.get_type().name();
let type_name = self.get_type().name().map_err(|_| std::fmt::Error)?;
write!(f, "{}", type_name)?;
if let Ok(s) = self.str() {
write!(f, ": {}", &s.to_string_lossy())

View File

@ -1,3 +1,4 @@
#[cfg(not(Py_LIMITED_API))]
use crate::ffi::code::FreeFunc;
use crate::ffi::object::PyObject;
use crate::ffi::pystate::{PyThreadState, Py_tracefunc};
@ -61,6 +62,7 @@ extern "C" {
arg1: *mut crate::ffi::PyFrameObject,
exc: c_int,
) -> *mut PyObject;
#[cfg(not(Py_LIMITED_API))]
pub fn _PyEval_RequestCodeExtraIndex(func: FreeFunc) -> c_int;
pub fn PyEval_EvalFrameEx(f: *mut crate::ffi::PyFrameObject, exc: c_int) -> *mut PyObject;
#[cfg_attr(PyPy, link_name = "PyPyEval_SaveThread")]

View File

@ -1,13 +1,12 @@
use crate::ffi::methodobject::PyMethodDef;
use crate::ffi::object::{PyObject, PyTypeObject};
#[cfg(not(PyPy))]
#[cfg(all(not(PyPy), not(Py_LIMITED_API)))]
use crate::ffi::object::{PyObject_GenericGetDict, PyObject_GenericSetDict};
use crate::ffi::structmember::PyMemberDef;
use std::os::raw::{c_char, c_int, c_void};
use std::ptr;
pub type getter = unsafe extern "C" fn(slf: *mut PyObject, closure: *mut c_void) -> *mut PyObject;
pub type setter =
unsafe extern "C" fn(slf: *mut PyObject, value: *mut PyObject, closure: *mut c_void) -> c_int;
@ -29,11 +28,12 @@ pub const PyGetSetDef_INIT: PyGetSetDef = PyGetSetDef {
closure: ptr::null_mut(),
};
#[cfg(PyPy)]
#[cfg(any(PyPy, Py_LIMITED_API))]
pub const PyGetSetDef_DICT: PyGetSetDef = PyGetSetDef_INIT;
// PyPy doesn't export neither PyObject_GenericGetDict/PyObject_GenericSetDict
#[cfg(not(PyPy))]
// Py_LIMITED_API exposes PyObject_GenericSetDict but not Get.
#[cfg(all(not(PyPy), not(Py_LIMITED_API)))]
pub const PyGetSetDef_DICT: PyGetSetDef = PyGetSetDef {
name: "__dict__\0".as_ptr() as *mut c_char,
get: Some(PyObject_GenericGetDict),

View File

@ -4,10 +4,12 @@ use crate::ffi::object::{PyObject, PyTypeObject, Py_TYPE};
#[cfg_attr(windows, link(name = "pythonXY"))]
extern "C" {
#[cfg(not(Py_LIMITED_API))]
#[cfg_attr(PyPy, link_name = "PyPyFunction_Type")]
pub static mut PyFunction_Type: PyTypeObject;
}
#[cfg(not(Py_LIMITED_API))]
#[inline]
pub unsafe fn PyFunction_Check(op: *mut PyObject) -> c_int {
(Py_TYPE(op) == &mut PyFunction_Type) as c_int

View File

@ -1,6 +1,7 @@
use super::PyObject;
use std::os::raw::{c_char, c_int};
#[cfg(not(Py_LIMITED_API))]
extern "C" {
#[cfg_attr(PyPy, link_name = "PyPyMarshal_WriteObjectToString")]
pub fn PyMarshal_WriteObjectToString(object: *mut PyObject, version: c_int) -> *mut PyObject;

View File

@ -314,69 +314,14 @@ mod typeobject {
pub nb_inplace_matrix_multiply: Option<object::binaryfunc>,
}
impl Default for PyNumberMethods {
#[inline]
fn default() -> Self {
PyNumberMethods_INIT
}
}
macro_rules! as_expr {
($e:expr) => {
$e
};
}
macro_rules! py_number_methods_init {
($($tail:tt)*) => {
as_expr! {
PyNumberMethods {
nb_add: None,
nb_subtract: None,
nb_multiply: None,
nb_remainder: None,
nb_divmod: None,
nb_power: None,
nb_negative: None,
nb_positive: None,
nb_absolute: None,
nb_bool: None,
nb_invert: None,
nb_lshift: None,
nb_rshift: None,
nb_and: None,
nb_xor: None,
nb_or: None,
nb_int: None,
nb_reserved: ::std::ptr::null_mut(),
nb_float: None,
nb_inplace_add: None,
nb_inplace_subtract: None,
nb_inplace_multiply: None,
nb_inplace_remainder: None,
nb_inplace_power: None,
nb_inplace_lshift: None,
nb_inplace_rshift: None,
nb_inplace_and: None,
nb_inplace_xor: None,
nb_inplace_or: None,
nb_floor_divide: None,
nb_true_divide: None,
nb_inplace_floor_divide: None,
nb_inplace_true_divide: None,
nb_index: None,
$($tail)*
}
}
}
}
pub const PyNumberMethods_INIT: PyNumberMethods = py_number_methods_init! {
nb_matrix_multiply: None,
nb_inplace_matrix_multiply: None,
};
#[repr(C)]
#[derive(Copy, Clone)]
#[derive(Clone)]
pub struct PySequenceMethods {
pub sq_length: Option<object::lenfunc>,
pub sq_concat: Option<object::binaryfunc>,
@ -390,83 +335,29 @@ mod typeobject {
pub sq_inplace_repeat: Option<object::ssizeargfunc>,
}
impl Default for PySequenceMethods {
#[inline]
fn default() -> Self {
unsafe { mem::zeroed() }
}
}
pub const PySequenceMethods_INIT: PySequenceMethods = PySequenceMethods {
sq_length: None,
sq_concat: None,
sq_repeat: None,
sq_item: None,
was_sq_slice: ptr::null_mut(),
sq_ass_item: None,
was_sq_ass_slice: ptr::null_mut(),
sq_contains: None,
sq_inplace_concat: None,
sq_inplace_repeat: None,
};
#[repr(C)]
#[derive(Copy, Clone)]
#[derive(Clone, Default)]
pub struct PyMappingMethods {
pub mp_length: Option<object::lenfunc>,
pub mp_subscript: Option<object::binaryfunc>,
pub mp_ass_subscript: Option<object::objobjargproc>,
}
impl Default for PyMappingMethods {
#[inline]
fn default() -> Self {
unsafe { mem::zeroed() }
}
}
pub const PyMappingMethods_INIT: PyMappingMethods = PyMappingMethods {
mp_length: None,
mp_subscript: None,
mp_ass_subscript: None,
};
#[repr(C)]
#[derive(Copy, Clone)]
#[derive(Clone, Default)]
pub struct PyAsyncMethods {
pub am_await: Option<object::unaryfunc>,
pub am_aiter: Option<object::unaryfunc>,
pub am_anext: Option<object::unaryfunc>,
}
impl Default for PyAsyncMethods {
#[inline]
fn default() -> Self {
PyAsyncMethods_INIT
}
}
pub const PyAsyncMethods_INIT: PyAsyncMethods = PyAsyncMethods {
am_await: None,
am_aiter: None,
am_anext: None,
};
#[repr(C)]
#[derive(Copy, Clone, Debug)]
#[derive(Clone, Default)]
pub struct PyBufferProcs {
pub bf_getbuffer: Option<object::getbufferproc>,
pub bf_releasebuffer: Option<object::releasebufferproc>,
}
impl Default for PyBufferProcs {
#[inline]
fn default() -> Self {
PyBufferProcs_INIT
}
}
pub const PyBufferProcs_INIT: PyBufferProcs = PyBufferProcs {
bf_getbuffer: None,
bf_releasebuffer: None,
};
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct PyTypeObject {
@ -650,7 +541,7 @@ mod typeobject {
pub const PyTypeObject_INIT: PyTypeObject = type_object_init!();
#[repr(C)]
#[derive(Copy, Clone)]
#[derive(Clone)]
pub struct PyHeapTypeObject {
pub ht_type: PyTypeObject,
pub as_async: PyAsyncMethods,
@ -825,6 +716,7 @@ extern "C" {
arg2: *mut PyObject,
arg3: *mut PyObject,
) -> c_int;
#[cfg(not(Py_LIMITED_API))]
pub fn PyObject_GenericGetDict(arg1: *mut PyObject, arg2: *mut c_void) -> *mut PyObject;
pub fn PyObject_GenericSetDict(
arg1: *mut PyObject,

View File

@ -143,6 +143,11 @@ pub unsafe fn PyIter_Check(o: *mut PyObject) -> c_int {
}) as c_int
}
#[cfg(all(Py_LIMITED_API, Py_3_8))]
extern "C" {
pub fn PyIter_Check(obj: *mut PyObject) -> c_int;
}
extern "C" {
#[cfg_attr(PyPy, link_name = "PyPyIter_Next")]
pub fn PyIter_Next(arg1: *mut PyObject) -> *mut PyObject;

View File

@ -2,7 +2,7 @@
//! Free allocation list
use crate::pyclass::{tp_free_fallback, PyClassAlloc};
use crate::pyclass::{get_type_free, tp_free_fallback, PyClassAlloc};
use crate::type_object::{PyLayout, PyTypeInfo};
use crate::{ffi, AsPyPointer, FromPyPointer, PyAny, Python};
use std::mem;
@ -85,15 +85,14 @@ where
unsafe fn dealloc(py: Python, self_: *mut Self::Layout) {
(*self_).py_drop(py);
let obj = PyAny::from_borrowed_ptr_or_panic(py, self_ as _);
if Self::is_exact_instance(obj) && ffi::PyObject_CallFinalizerFromDealloc(obj.as_ptr()) < 0
{
// tp_finalize resurrected.
return;
}
if let Some(obj) = <Self as PyClassWithFreeList>::get_free_list(py).insert(obj.as_ptr()) {
match (*ffi::Py_TYPE(obj)).tp_free {
Some(free) => free(obj as *mut c_void),
match get_type_free(ffi::Py_TYPE(obj)) {
Some(free) => {
let ty = ffi::Py_TYPE(obj);
free(obj as *mut c_void);
ffi::Py_DECREF(ty as *mut ffi::PyObject);
}
None => tp_free_fallback(obj),
}
}

View File

@ -184,6 +184,7 @@ mod gil;
mod instance;
#[macro_use]
mod internal_tricks;
#[cfg(not(Py_LIMITED_API))]
pub mod marshal;
pub mod once_cell;
pub mod panic;

View File

@ -1,3 +1,7 @@
#![cfg(not(Py_LIMITED_API))]
//! Support for the Python `marshal` format. Not supported in limited API
//! builds.
use crate::ffi;
use crate::types::{PyAny, PyBytes};
use crate::{AsPyPointer, FromPyPointer, PyResult, Python};

View File

@ -8,9 +8,9 @@ use crate::type_object::{PyBorrowFlagLayout, PyLayout, PySizedLayout, PyTypeInfo
use crate::types::PyAny;
use crate::{ffi, IntoPy, PyErr, PyNativeType, PyObject, PyResult, Python};
use std::cell::{Cell, UnsafeCell};
use std::fmt;
use std::mem::ManuallyDrop;
use std::ops::{Deref, DerefMut};
use std::{fmt, mem};
/// Base layout of PyCell.
/// This is necessary for sharing BorrowFlag between parents and children.
@ -170,20 +170,26 @@ pub struct PyCell<T: PyClass> {
impl<T: PyClass> PyCell<T> {
/// Get the offset of the dictionary from the start of the struct in bytes.
#[cfg(not(Py_LIMITED_API))]
pub(crate) fn dict_offset() -> Option<usize> {
if T::Dict::IS_DUMMY {
None
} else {
Some(mem::size_of::<Self>() - mem::size_of::<T::Dict>() - mem::size_of::<T::WeakRef>())
Some(
std::mem::size_of::<Self>()
- std::mem::size_of::<T::Dict>()
- std::mem::size_of::<T::WeakRef>(),
)
}
}
/// Get the offset of the weakref list from the start of the struct in bytes.
#[cfg(not(Py_LIMITED_API))]
pub(crate) fn weakref_offset() -> Option<usize> {
if T::WeakRef::IS_DUMMY {
None
} else {
Some(mem::size_of::<Self>() - mem::size_of::<T::WeakRef>())
Some(std::mem::size_of::<Self>() - std::mem::size_of::<T::WeakRef>())
}
}
}
@ -455,7 +461,7 @@ impl<T: PyClass + fmt::Debug> fmt::Debug for PyCell<T> {
/// - You want to get super class.
/// ```
/// # use pyo3::prelude::*;
/// #[pyclass]
/// #[pyclass(subclass)]
/// struct Parent {
/// basename: &'static str,
/// }
@ -516,11 +522,11 @@ where
/// # Examples
/// ```
/// # use pyo3::prelude::*;
/// #[pyclass]
/// #[pyclass(subclass)]
/// struct Base1 {
/// name1: &'static str,
/// }
/// #[pyclass(extends=Base1)]
/// #[pyclass(extends=Base1, subclass)]
/// struct Base2 {
/// name2: &'static str,
/// }

View File

@ -7,11 +7,34 @@ use crate::pyclass_slots::{PyClassDict, PyClassWeakRef};
use crate::type_object::{type_flags, PyLayout};
use crate::types::PyAny;
use crate::{ffi, PyCell, PyErr, PyNativeType, PyResult, PyTypeInfo, Python};
use std::convert::TryInto;
use std::ffi::CString;
use std::marker::PhantomData;
use std::os::raw::c_void;
#[cfg(not(PyPy))]
use std::mem;
use std::os::raw::{c_char, c_int, c_uint, c_void};
use std::{ptr, thread};
#[cfg(PyPy)]
unsafe fn get_type_alloc(tp: *mut ffi::PyTypeObject) -> Option<ffi::allocfunc> {
(*tp).tp_alloc
}
#[cfg(not(PyPy))]
unsafe fn get_type_alloc(tp: *mut ffi::PyTypeObject) -> Option<ffi::allocfunc> {
mem::transmute(ffi::PyType_GetSlot(tp, ffi::Py_tp_alloc))
}
#[cfg(PyPy)]
pub(crate) unsafe fn get_type_free(tp: *mut ffi::PyTypeObject) -> Option<ffi::freefunc> {
(*tp).tp_free
}
#[cfg(not(PyPy))]
pub(crate) unsafe fn get_type_free(tp: *mut ffi::PyTypeObject) -> Option<ffi::freefunc> {
mem::transmute(ffi::PyType_GetSlot(tp, ffi::Py_tp_free))
}
#[inline]
pub(crate) unsafe fn default_new<T: PyTypeInfo>(
py: Python,
@ -19,12 +42,21 @@ pub(crate) unsafe fn default_new<T: PyTypeInfo>(
) -> *mut ffi::PyObject {
// if the class derives native types(e.g., PyDict), call special new
if T::FLAGS & type_flags::EXTENDED != 0 && T::BaseLayout::IS_NATIVE_TYPE {
let base_tp = T::BaseType::type_object_raw(py);
if let Some(base_new) = (*base_tp).tp_new {
return base_new(subtype, ptr::null_mut(), ptr::null_mut());
#[cfg(not(Py_LIMITED_API))]
{
let base_tp = T::BaseType::type_object_raw(py);
if let Some(base_new) = (*base_tp).tp_new {
return base_new(subtype, ptr::null_mut(), ptr::null_mut());
}
}
#[cfg(Py_LIMITED_API)]
{
// Silence unused parameter warning.
let _ = py;
unreachable!("Subclassing native types isn't support in limited API mode");
}
}
let alloc = (*subtype).tp_alloc.unwrap_or(ffi::PyType_GenericAlloc);
let alloc = get_type_alloc(subtype).unwrap_or(ffi::PyType_GenericAlloc);
alloc(subtype, 0) as _
}
@ -45,14 +77,13 @@ pub trait PyClassAlloc: PyTypeInfo + Sized {
unsafe fn dealloc(py: Python, self_: *mut Self::Layout) {
(*self_).py_drop(py);
let obj = PyAny::from_borrowed_ptr_or_panic(py, self_ as _);
if Self::is_exact_instance(obj) && ffi::PyObject_CallFinalizerFromDealloc(obj.as_ptr()) < 0
{
// tp_finalize resurrected.
return;
}
match (*ffi::Py_TYPE(obj.as_ptr())).tp_free {
Some(free) => free(obj.as_ptr() as *mut c_void),
match get_type_free(ffi::Py_TYPE(obj.as_ptr())) {
Some(free) => {
let ty = ffi::Py_TYPE(obj.as_ptr());
free(obj.as_ptr() as *mut c_void);
ffi::Py_DECREF(ty as *mut ffi::PyObject);
}
None => tp_free_fallback(obj.as_ptr()),
}
}
@ -107,134 +138,151 @@ pub trait PyClass:
type BaseNativeType: PyTypeInfo + PyNativeType;
}
#[cfg(not(Py_LIMITED_API))]
pub(crate) fn initialize_type_object<T>(
py: Python,
module_name: Option<&str>,
type_object: &mut ffi::PyTypeObject,
) -> PyResult<()>
where
T: PyClass,
{
type_object.tp_doc = match T::DESCRIPTION {
// PyPy will segfault if passed only a nul terminator as `tp_doc`, ptr::null() is OK though.
"\0" => ptr::null(),
s if s.as_bytes().ends_with(b"\0") => s.as_ptr() as _,
// If the description is not null-terminated, create CString and leak it
s => CString::new(s)?.into_raw(),
};
/// For collecting slot items.
#[derive(Default)]
pub(crate) struct TypeSlots(Vec<ffi::PyType_Slot>);
type_object.tp_base = T::BaseType::type_object_raw(py);
type_object.tp_name = match module_name {
Some(module_name) => CString::new(format!("{}.{}", module_name, T::NAME))?.into_raw(),
None => CString::new(T::NAME)?.into_raw(),
};
// dealloc
type_object.tp_dealloc = tp_dealloc::<T>();
// type size
type_object.tp_basicsize = std::mem::size_of::<T::Layout>() as ffi::Py_ssize_t;
// __dict__ support
if let Some(dict_offset) = PyCell::<T>::dict_offset() {
type_object.tp_dictoffset = dict_offset as ffi::Py_ssize_t;
impl TypeSlots {
fn push(&mut self, slot: c_int, pfunc: *mut c_void) {
self.0.push(ffi::PyType_Slot { slot, pfunc });
}
// weakref support
if let Some(weakref_offset) = PyCell::<T>::weakref_offset() {
type_object.tp_weaklistoffset = weakref_offset as ffi::Py_ssize_t;
}
// GC support
if let Some(gc) = T::gc_methods() {
unsafe { gc.as_ref() }.update_typeobj(type_object);
}
// descriptor protocol
if let Some(descr) = T::descr_methods() {
unsafe { descr.as_ref() }.update_typeobj(type_object);
}
// iterator methods
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
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 = 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 = T::mapping_methods().map_or_else(ptr::null_mut, |p| p.as_ptr());
// sequence methods
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 = T::async_methods().map_or_else(ptr::null_mut, |p| p.as_ptr());
// buffer protocol
type_object.tp_as_buffer = T::buffer_methods().map_or_else(ptr::null_mut, |p| p.as_ptr());
let (new, call, mut methods) = py_class_method_defs::<T>();
// normal methods
if !methods.is_empty() {
methods.push(ffi::PyMethodDef_INIT);
type_object.tp_methods = Box::into_raw(methods.into_boxed_slice()) as _;
}
// __new__ method
type_object.tp_new = new;
// __call__ method
type_object.tp_call = call;
// properties
let mut props = py_class_properties::<T>();
if !T::Dict::IS_DUMMY {
props.push(ffi::PyGetSetDef_DICT);
}
if !props.is_empty() {
props.push(ffi::PyGetSetDef_INIT);
type_object.tp_getset = Box::into_raw(props.into_boxed_slice()) as _;
}
// set type flags
py_class_flags::<T>(type_object);
// register type object
unsafe {
if ffi::PyType_Ready(type_object) == 0 {
Ok(())
} else {
Err(PyErr::fetch(py))
pub(crate) fn maybe_push(&mut self, slot: c_int, value: Option<*mut c_void>) {
if let Some(pfunc) = value {
self.push(slot, pfunc);
}
}
}
fn py_class_flags<T: PyTypeInfo>(type_object: &mut ffi::PyTypeObject) {
if type_object.tp_traverse != None
|| type_object.tp_clear != None
|| T::FLAGS & type_flags::GC != 0
{
type_object.tp_flags = ffi::Py_TPFLAGS_DEFAULT | ffi::Py_TPFLAGS_HAVE_GC;
fn tp_doc<T: PyClass>() -> PyResult<Option<*mut c_void>> {
Ok(match T::DESCRIPTION {
"\0" => None,
s if s.as_bytes().ends_with(b"\0") => Some(s.as_ptr() as _),
// If the description is not null-terminated, create CString and leak it
s => Some(CString::new(s)?.into_raw() as _),
})
}
fn get_type_name<T: PyTypeInfo>(module_name: Option<&str>) -> PyResult<*mut c_char> {
Ok(match module_name {
Some(module_name) => CString::new(format!("{}.{}", module_name, T::NAME))?.into_raw(),
None => CString::new(T::NAME)?.into_raw(),
})
}
fn into_raw<T>(vec: Vec<T>) -> *mut c_void {
Box::into_raw(vec.into_boxed_slice()) as _
}
pub(crate) fn create_type_object<T>(
py: Python,
module_name: Option<&str>,
) -> PyResult<*mut ffi::PyTypeObject>
where
T: PyClass,
{
let mut slots = TypeSlots::default();
slots.push(ffi::Py_tp_base, T::BaseType::type_object_raw(py) as _);
slots.maybe_push(ffi::Py_tp_doc, tp_doc::<T>()?);
slots.maybe_push(ffi::Py_tp_dealloc, tp_dealloc::<T>().map(|v| v as _));
let (new, call, methods) = py_class_method_defs::<T>();
slots.maybe_push(ffi::Py_tp_new, new.map(|v| v as _));
slots.maybe_push(ffi::Py_tp_call, call.map(|v| v as _));
// normal methods
if !methods.is_empty() {
slots.push(ffi::Py_tp_methods, into_raw(methods));
}
// properties
let props = py_class_properties::<T>();
if !props.is_empty() {
slots.push(ffi::Py_tp_getset, into_raw(props));
}
// protocol methods
let mut has_gc_methods = false;
for slot in T::get_type_slots() {
has_gc_methods |= slot.slot == ffi::Py_tp_clear;
has_gc_methods |= slot.slot == ffi::Py_tp_traverse;
slots.0.push(slot);
}
slots.push(0, ptr::null_mut());
let mut spec = ffi::PyType_Spec {
name: get_type_name::<T>(module_name)?,
basicsize: std::mem::size_of::<T::Layout>() as c_int,
itemsize: 0,
flags: py_class_flags::<T>(has_gc_methods),
slots: slots.0.as_mut_slice().as_mut_ptr(),
};
let type_object = unsafe { ffi::PyType_FromSpec(&mut spec) };
if type_object.is_null() {
Err(PyErr::fetch(py))
} else {
type_object.tp_flags = ffi::Py_TPFLAGS_DEFAULT;
tp_init_additional::<T>(type_object as _);
Ok(type_object as _)
}
}
#[cfg(not(Py_LIMITED_API))]
fn tp_init_additional<T: PyClass>(type_object: *mut ffi::PyTypeObject) {
// Just patch the type objects for the things there's no
// PyType_FromSpec API for... there's no reason this should work,
// except for that it does and we have tests.
// Running this causes PyPy to segfault.
#[cfg(all(not(PyPy), not(Py_3_10)))]
{
if T::DESCRIPTION != "\0" {
unsafe {
// Until CPython 3.10, tp_doc was treated specially for
// heap-types, and it removed the text_signature value from it.
// We go in after the fact and replace tp_doc with something
// that _does_ include the text_signature value!
ffi::PyObject_Free((*type_object).tp_doc as _);
let data = ffi::PyObject_Malloc(T::DESCRIPTION.len());
data.copy_from(T::DESCRIPTION.as_ptr() as _, T::DESCRIPTION.len());
(*type_object).tp_doc = data as _;
}
}
}
if let Some(buffer) = T::get_buffer() {
unsafe {
(*(*type_object).tp_as_buffer).bf_getbuffer = buffer.bf_getbuffer;
(*(*type_object).tp_as_buffer).bf_releasebuffer = buffer.bf_releasebuffer;
}
}
// __dict__ support
if let Some(dict_offset) = PyCell::<T>::dict_offset() {
unsafe {
(*type_object).tp_dictoffset = dict_offset as ffi::Py_ssize_t;
}
}
// weakref support
if let Some(weakref_offset) = PyCell::<T>::weakref_offset() {
unsafe {
(*type_object).tp_weaklistoffset = weakref_offset as ffi::Py_ssize_t;
}
}
}
#[cfg(Py_LIMITED_API)]
fn tp_init_additional<T: PyClass>(_type_object: *mut ffi::PyTypeObject) {}
fn py_class_flags<T: PyClass + PyTypeInfo>(has_gc_methods: bool) -> c_uint {
let mut flags = if has_gc_methods || T::FLAGS & type_flags::GC != 0 {
ffi::Py_TPFLAGS_DEFAULT | ffi::Py_TPFLAGS_HAVE_GC
} else {
ffi::Py_TPFLAGS_DEFAULT
};
if T::FLAGS & type_flags::BASETYPE != 0 {
type_object.tp_flags |= ffi::Py_TPFLAGS_BASETYPE;
flags |= ffi::Py_TPFLAGS_BASETYPE;
}
flags.try_into().unwrap()
}
pub(crate) fn py_class_attributes<T: PyMethods>() -> impl Iterator<Item = PyClassAttributeDef> {
@ -244,6 +292,21 @@ pub(crate) fn py_class_attributes<T: PyMethods>() -> impl Iterator<Item = PyClas
})
}
fn fallback_new() -> Option<ffi::newfunc> {
unsafe extern "C" fn fallback_new(
_subtype: *mut ffi::PyTypeObject,
_args: *mut ffi::PyObject,
_kwds: *mut ffi::PyObject,
) -> *mut ffi::PyObject {
crate::callback_body!(py, {
Err::<(), _>(crate::exceptions::PyTypeError::new_err(
"No constructor defined",
))
})
}
Some(fallback_new)
}
fn py_class_method_defs<T: PyMethods>() -> (
Option<ffi::newfunc>,
Option<ffi::PyCFunctionWithKeywords>,
@ -251,7 +314,7 @@ fn py_class_method_defs<T: PyMethods>() -> (
) {
let mut defs = Vec::new();
let mut call = None;
let mut new = None;
let mut new = fallback_new();
for def in T::py_methods() {
match *def {
@ -272,10 +335,14 @@ fn py_class_method_defs<T: PyMethods>() -> (
}
}
if !defs.is_empty() {
defs.push(ffi::PyMethodDef_INIT);
}
(new, call, defs)
}
fn py_class_properties<T: PyMethods>() -> Vec<ffi::PyGetSetDef> {
fn py_class_properties<T: PyClass>() -> Vec<ffi::PyGetSetDef> {
let mut defs = std::collections::HashMap::new();
for def in T::py_methods() {
@ -298,7 +365,14 @@ fn py_class_properties<T: PyMethods>() -> Vec<ffi::PyGetSetDef> {
}
}
defs.values().cloned().collect()
let mut props: Vec<_> = defs.values().cloned().collect();
if !T::Dict::IS_DUMMY {
props.push(ffi::PyGetSetDef_DICT);
}
if !props.is_empty() {
props.push(ffi::PyGetSetDef_INIT);
}
props
}
/// This trait is implemented for `#[pyclass]` and handles following two situations:

View File

@ -30,12 +30,12 @@ impl<T: PyTypeInfo> PyObjectInit<T> for PyNativeTypeInitializer<T> {
/// ```
/// # use pyo3::prelude::*;
/// # use pyo3::py_run;
/// #[pyclass]
/// #[pyclass(subclass)]
/// struct BaseClass {
/// #[pyo3(get)]
/// basename: &'static str,
/// }
/// #[pyclass(extends=BaseClass)]
/// #[pyclass(extends=BaseClass, subclass)]
/// struct SubClass {
/// #[pyo3(get)]
/// subname: &'static str,

View File

@ -3,7 +3,7 @@
use crate::conversion::IntoPyPointer;
use crate::once_cell::GILOnceCell;
use crate::pyclass::{initialize_type_object, py_class_attributes, PyClass};
use crate::pyclass::{create_type_object, py_class_attributes, PyClass};
use crate::pyclass_init::PyObjectInit;
use crate::types::{PyAny, PyType};
use crate::{ffi, AsPyPointer, PyErr, PyNativeType, PyObject, PyResult, Python};
@ -157,12 +157,10 @@ impl LazyStaticType {
pub fn get_or_init<T: PyClass>(&self, py: Python) -> *mut ffi::PyTypeObject {
let type_object = *self.value.get_or_init(py, || {
let mut type_object = Box::new(ffi::PyTypeObject_INIT);
initialize_type_object::<T>(py, T::MODULE, type_object.as_mut()).unwrap_or_else(|e| {
create_type_object::<T>(py, T::MODULE).unwrap_or_else(|e| {
e.print(py);
panic!("An error occurred while initializing class {}", T::NAME)
});
Box::into_raw(type_object)
})
});
// We might want to fill the `tp_dict` with python instances of `T`
@ -204,10 +202,7 @@ impl LazyStaticType {
// Now we hold the GIL and we can assume it won't be released until we
// return from the function.
let result = self.tp_dict_filled.get_or_init(py, move || {
let tp_dict = unsafe { (*type_object).tp_dict };
let result = initialize_tp_dict(py, tp_dict, items);
// See discussion on #982 for why we need this.
unsafe { ffi::PyType_Modified(type_object) };
let result = initialize_tp_dict(py, type_object as *mut ffi::PyObject, items);
// Initialization successfully complete, can clear the thread list.
// (No further calls to get_or_init() will try to init, on any thread.)
@ -226,13 +221,13 @@ impl LazyStaticType {
fn initialize_tp_dict(
py: Python,
tp_dict: *mut ffi::PyObject,
type_object: *mut ffi::PyObject,
items: Vec<(&'static std::ffi::CStr, PyObject)>,
) -> PyResult<()> {
// We hold the GIL: the dictionary update can be considered atomic from
// the POV of other threads.
for (key, val) in items {
let ret = unsafe { ffi::PyDict_SetItemString(tp_dict, key.as_ptr(), val.into_ptr()) };
let ret = unsafe { ffi::PyObject_SetAttrString(type_object, key.as_ptr(), val.into_ptr()) };
if ret < 0 {
return Err(PyErr::fetch(py));
}

View File

@ -1,7 +1,7 @@
#[cfg(not(PyPy))]
#[cfg(all(not(PyPy), not(Py_LIMITED_API)))]
use crate::instance::PyNativeType;
use crate::{ffi, AsPyPointer, PyAny, Python};
#[cfg(not(PyPy))]
#[cfg(all(not(PyPy), not(Py_LIMITED_API)))]
use std::ops::*;
use std::os::raw::c_double;

View File

@ -83,7 +83,9 @@ impl<'source> FromPyObject<'source> for f32 {
mod test {
#[cfg(not(Py_LIMITED_API))]
use crate::ffi::PyFloat_AS_DOUBLE;
use crate::{AsPyPointer, Python, ToPyObject};
#[cfg(not(Py_LIMITED_API))]
use crate::AsPyPointer;
use crate::{Python, ToPyObject};
macro_rules! num_to_py_object_and_back (
($func_name:ident, $t1:ty, $t2:ty) => (

View File

@ -96,4 +96,5 @@ impl PyCFunction {
#[repr(transparent)]
pub struct PyFunction(PyAny);
#[cfg(not(Py_LIMITED_API))]
pyobject_native_var_type!(PyFunction, ffi::PyFunction_Type, ffi::PyFunction_Check);

View File

@ -2,9 +2,9 @@
//
// based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython
use crate::{
ffi, AsPyPointer, PyAny, PyDowncastError, PyErr, PyNativeType, PyResult, PyTryFrom, Python,
};
use crate::{ffi, AsPyPointer, PyAny, PyErr, PyNativeType, PyResult, Python};
#[cfg(any(not(Py_LIMITED_API), Py_3_8))]
use crate::{PyDowncastError, PyTryFrom};
/// A Python iterator object.
///
@ -28,6 +28,7 @@ use crate::{
#[repr(transparent)]
pub struct PyIterator(PyAny);
pyobject_native_type_named!(PyIterator);
#[cfg(any(not(Py_LIMITED_API), Py_3_8))]
pyobject_native_type_extract!(PyIterator);
impl PyIterator {
@ -67,6 +68,8 @@ impl<'p> Iterator for &'p PyIterator {
}
}
// PyIter_Check does not exist in the limited API until 3.8
#[cfg(any(not(Py_LIMITED_API), Py_3_8))]
impl<'v> PyTryFrom<'v> for PyIterator {
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v PyIterator, PyDowncastError<'v>> {
let value = value.into();
@ -96,7 +99,9 @@ mod tests {
use crate::exceptions::PyTypeError;
use crate::gil::GILPool;
use crate::types::{PyDict, PyList};
use crate::{Py, PyAny, PyTryFrom, Python, ToPyObject};
#[cfg(any(not(Py_LIMITED_API), Py_3_8))]
use crate::{Py, PyAny, PyTryFrom};
use crate::{Python, ToPyObject};
use indoc::indoc;
#[test]
@ -200,6 +205,7 @@ mod tests {
}
#[test]
#[cfg(any(not(Py_LIMITED_API), Py_3_8))]
fn iterator_try_from() {
let gil_guard = Python::acquire_gil();
let py = gil_guard.python();

View File

@ -106,6 +106,8 @@ macro_rules! pyobject_native_type_core {
#[macro_export]
macro_rules! pyobject_native_type_sized {
($name: ty, $layout: path $(,$type_param: ident)*) => {
// To prevent inheriting native types with ABI3
#[cfg(not(Py_LIMITED_API))]
impl $crate::type_object::PySizedLayout<$name> for $layout {}
impl<'a, $($type_param,)*> $crate::derive_utils::PyBaseTypeUtils for $name {
type Dict = $crate::pyclass_slots::PyClassDummySlot;

View File

@ -2,6 +2,8 @@
//
use crate::err::{self, PyErr, PyResult};
#[cfg(Py_LIMITED_API)]
use crate::types::PyIterator;
use crate::{
ffi, AsPyPointer, FromPyObject, IntoPy, PyAny, PyNativeType, PyObject, Python,
ToBorrowedObject, ToPyObject,
@ -110,21 +112,48 @@ impl PySet {
/// Returns an iterator of values in this set.
///
/// Note that it can be unsafe to use when the set might be changed by other code.
#[cfg(not(Py_LIMITED_API))]
pub fn iter(&self) -> PySetIterator {
PySetIterator::new(self)
}
}
#[cfg(Py_LIMITED_API)]
pub struct PySetIterator<'p> {
it: &'p PyIterator,
}
#[cfg(Py_LIMITED_API)]
impl PySetIterator<'_> {
fn new(set: &PyAny) -> PySetIterator {
PySetIterator {
set: self.as_ref(),
pos: 0,
it: PyIterator::from_object(set.py(), set).unwrap(),
}
}
}
#[cfg(Py_LIMITED_API)]
impl<'py> Iterator for PySetIterator<'py> {
type Item = &'py super::PyAny;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.it.next().map(|p| p.unwrap())
}
}
#[cfg(not(Py_LIMITED_API))]
pub struct PySetIterator<'py> {
set: &'py super::PyAny,
pos: isize,
}
#[cfg(not(Py_LIMITED_API))]
impl PySetIterator<'_> {
fn new(set: &PyAny) -> PySetIterator {
PySetIterator { set, pos: 0 }
}
}
#[cfg(not(Py_LIMITED_API))]
impl<'py> Iterator for PySetIterator<'py> {
type Item = &'py super::PyAny;
@ -145,7 +174,6 @@ impl<'py> Iterator for PySetIterator<'py> {
}
}
#[cfg(not(Py_LIMITED_API))]
impl<'a> std::iter::IntoIterator for &'a PySet {
type Item = &'a PyAny;
type IntoIter = PySetIterator<'a>;
@ -281,22 +309,17 @@ impl PyFrozenSet {
/// Returns an iterator of values in this frozen set.
///
/// Note that it can be unsafe to use when the set might be changed by other code.
#[cfg(not(Py_LIMITED_API))]
pub fn iter(&self) -> PySetIterator {
self.into_iter()
PySetIterator::new(self.as_ref())
}
}
#[cfg(not(Py_LIMITED_API))]
impl<'a> std::iter::IntoIterator for &'a PyFrozenSet {
type Item = &'a PyAny;
type IntoIter = PySetIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
PySetIterator {
set: self.as_ref(),
pos: 0,
}
self.iter()
}
}

View File

@ -44,6 +44,7 @@ impl PyString {
/// (containing unpaired surrogates).
#[inline]
pub fn to_str(&self) -> PyResult<&str> {
#[cfg(not(Py_LIMITED_API))]
unsafe {
let mut size: ffi::Py_ssize_t = 0;
let data = ffi::PyUnicode_AsUTF8AndSize(self.as_ptr(), &mut size) as *const u8;
@ -54,6 +55,16 @@ impl PyString {
Ok(std::str::from_utf8_unchecked(slice))
}
}
#[cfg(Py_LIMITED_API)]
unsafe {
let data = ffi::PyUnicode_AsUTF8String(self.as_ptr());
if data.is_null() {
Err(PyErr::fetch(self.py()))
} else {
let bytes = self.py().from_owned_ptr::<PyBytes>(data);
Ok(std::str::from_utf8_unchecked(bytes.as_bytes()))
}
}
}
/// Converts the `PyString` into a Rust string.

View File

@ -5,7 +5,6 @@ use crate::{
exceptions, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, Py, PyAny, PyErr, PyNativeType,
PyObject, PyResult, PyTryFrom, Python, ToPyObject,
};
use std::slice;
/// Represents a Python `tuple` object.
///
@ -87,7 +86,7 @@ impl PyTuple {
// and because tuples are immutable.
unsafe {
let ptr = self.as_ptr() as *mut ffi::PyTupleObject;
let slice = slice::from_raw_parts((*ptr).ob_item.as_ptr(), self.len());
let slice = std::slice::from_raw_parts((*ptr).ob_item.as_ptr(), self.len());
&*(slice as *const [*mut ffi::PyObject] as *const [&PyAny])
}
}

View File

@ -6,8 +6,6 @@ use crate::err::{PyErr, PyResult};
use crate::instance::PyNativeType;
use crate::type_object::PyTypeObject;
use crate::{ffi, AsPyPointer, PyAny, Python};
use std::borrow::Cow;
use std::ffi::CStr;
/// Represents a reference to a Python `type object`.
#[repr(transparent)]
@ -39,8 +37,8 @@ impl PyType {
}
/// Gets the name of the `PyType`.
pub fn name(&self) -> Cow<str> {
unsafe { CStr::from_ptr((*self.as_type_ptr()).tp_name).to_string_lossy() }
pub fn name(&self) -> PyResult<&str> {
self.getattr("__qualname__")?.extract()
}
/// Checks whether `self` is subclass of type `T`.

View File

@ -1,3 +1,5 @@
#![cfg(not(Py_LIMITED_API))]
use pyo3::buffer::PyBuffer;
use pyo3::class::PyBufferProtocol;
use pyo3::exceptions::PyBufferError;

View File

@ -51,7 +51,10 @@ fn class_attributes() {
py_assert!(py, foo_obj, "foo_obj.MY_CONST == 'foobar'");
}
// Ignored because heap types are not immutable:
// https://github.com/python/cpython/blob/master/Objects/typeobject.c#L3399-L3409
#[test]
#[ignore]
fn class_attributes_are_immutable() {
let gil = Python::acquire_gil();
let py = gil.python();

View File

@ -119,7 +119,11 @@ fn test_raw_idents() {
#[pyclass]
struct EmptyClassInModule {}
// Ignored because heap types do not show up as being in builtins, instead they
// raise AttributeError:
// https://github.com/python/cpython/blob/master/Objects/typeobject.c#L544-L573
#[test]
#[ignore]
fn empty_class_in_module() {
let gil = Python::acquire_gil();
let py = gil.python();
@ -165,7 +169,7 @@ fn class_with_object_field() {
py_assert!(py, ty, "ty(None).value == None");
}
#[pyclass(unsendable)]
#[pyclass(unsendable, subclass)]
struct UnsendableBase {
value: std::rc::Rc<usize>,
}

View File

@ -30,7 +30,7 @@ fn test_cloneable_pyclass() {
assert_eq!(&c, &*mrc);
}
#[pyclass]
#[pyclass(subclass)]
#[derive(Default)]
struct BaseClass {
value: i32,

View File

@ -26,6 +26,8 @@ fn test_compile_errors() {
t.compile_fail("tests/ui/invalid_pymethod_receiver.rs");
t.compile_fail("tests/ui/invalid_result_conversion.rs");
t.compile_fail("tests/ui/missing_clone.rs");
#[cfg(Py_LIMITED_API)]
t.compile_fail("tests/ui/abi3_nativetype_inheritance.rs");
}
#[rustversion::before(1.46)]
fn tests_rust_1_46(_t: &trybuild::TestCases) {}

View File

@ -453,10 +453,11 @@ fn test_cls_impl() {
.unwrap();
}
#[pyclass(dict)]
#[pyclass(dict, subclass)]
struct DunderDictSupport {}
#[test]
#[cfg_attr(Py_LIMITED_API, ignore)]
fn dunder_dict_support() {
let gil = Python::acquire_gil();
let py = gil.python();
@ -472,6 +473,7 @@ fn dunder_dict_support() {
}
#[test]
#[cfg_attr(Py_LIMITED_API, ignore)]
fn access_dunder_dict() {
let gil = Python::acquire_gil();
let py = gil.python();
@ -493,6 +495,7 @@ struct InheritDict {
}
#[test]
#[cfg_attr(Py_LIMITED_API, ignore)]
fn inherited_dict() {
let gil = Python::acquire_gil();
let py = gil.python();
@ -511,6 +514,7 @@ fn inherited_dict() {
struct WeakRefDunderDictSupport {}
#[test]
#[cfg_attr(Py_LIMITED_API, ignore)]
fn weakref_dunder_dict_support() {
let gil = Python::acquire_gil();
let py = gil.python();

View File

@ -146,10 +146,11 @@ fn gc_integration2() {
py_run!(py, inst, "import gc; assert inst in gc.get_objects()");
}
#[pyclass(weakref)]
#[pyclass(weakref, subclass)]
struct WeakRefSupport {}
#[test]
#[cfg_attr(Py_LIMITED_API, ignore)]
fn weakref_support() {
let gil = Python::acquire_gil();
let py = gil.python();
@ -168,6 +169,7 @@ struct InheritWeakRef {
}
#[test]
#[cfg_attr(Py_LIMITED_API, ignore)]
fn inherited_weakref() {
let gil = Python::acquire_gil();
let py = gil.python();
@ -179,7 +181,7 @@ fn inherited_weakref() {
);
}
#[pyclass]
#[pyclass(subclass)]
struct BaseClassWithDrop {
data: Option<Arc<AtomicBool>>,
}
@ -269,6 +271,16 @@ impl PyGCProtocol for TraversableClass {
}
}
#[cfg(PyPy)]
unsafe fn get_type_traverse(tp: *mut pyo3::ffi::PyTypeObject) -> Option<pyo3::ffi::traverseproc> {
(*tp).tp_traverse
}
#[cfg(not(PyPy))]
unsafe fn get_type_traverse(tp: *mut pyo3::ffi::PyTypeObject) -> Option<pyo3::ffi::traverseproc> {
std::mem::transmute(pyo3::ffi::PyType_GetSlot(tp, pyo3::ffi::Py_tp_traverse))
}
#[test]
fn gc_during_borrow() {
let gil = Python::acquire_gil();
@ -285,7 +297,7 @@ fn gc_during_borrow() {
// get the traverse function
let ty = TraversableClass::type_object(py).as_type_ptr();
let traverse = (*ty).tp_traverse.unwrap();
let traverse = get_type_traverse(ty).unwrap();
// create an object and check that traversing it works normally
// when it's not borrowed

View File

@ -3,10 +3,9 @@ use pyo3::py_run;
use pyo3::types::IntoPyDict;
use pyo3::types::{PyDict, PySet};
mod common;
#[pyclass]
#[pyclass(subclass)]
struct BaseClass {
#[pyo3(get)]
val1: usize,
@ -106,7 +105,7 @@ fn mutation_fails() {
)
}
#[pyclass]
#[pyclass(subclass)]
struct BaseClassWithResult {
_val: usize,
}
@ -153,56 +152,63 @@ except Exception as e:
);
}
#[pyclass(extends=PySet)]
#[derive(Debug)]
struct SetWithName {
#[pyo3(get(name))]
_name: &'static str,
}
// Subclassing builtin types is not allowed in the LIMITED API.
#[cfg(not(Py_LIMITED_API))]
mod inheriting_native_type {
use super::*;
use pyo3::types::{PyDict, PySet};
#[pymethods]
impl SetWithName {
#[new]
fn new() -> Self {
SetWithName { _name: "Hello :)" }
#[pyclass(extends=PySet)]
#[derive(Debug)]
struct SetWithName {
#[pyo3(get(name))]
_name: &'static str,
}
#[pymethods]
impl SetWithName {
#[new]
fn new() -> Self {
SetWithName { _name: "Hello :)" }
}
}
#[test]
fn inherit_set() {
let gil = Python::acquire_gil();
let py = gil.python();
let set_sub = pyo3::PyCell::new(py, SetWithName::new()).unwrap();
py_run!(
py,
set_sub,
r#"set_sub.add(10); assert list(set_sub) == [10]; assert set_sub._name == "Hello :)""#
);
}
#[pyclass(extends=PyDict)]
#[derive(Debug)]
struct DictWithName {
#[pyo3(get(name))]
_name: &'static str,
}
#[pymethods]
impl DictWithName {
#[new]
fn new() -> Self {
DictWithName { _name: "Hello :)" }
}
}
#[test]
fn inherit_dict() {
let gil = Python::acquire_gil();
let py = gil.python();
let dict_sub = pyo3::PyCell::new(py, DictWithName::new()).unwrap();
py_run!(
py,
dict_sub,
r#"dict_sub[0] = 1; assert dict_sub[0] == 1; assert dict_sub._name == "Hello :)""#
);
}
}
#[test]
fn inherit_set() {
let gil = Python::acquire_gil();
let py = gil.python();
let set_sub = pyo3::PyCell::new(py, SetWithName::new()).unwrap();
py_run!(
py,
set_sub,
r#"set_sub.add(10); assert list(set_sub) == [10]; assert set_sub._name == "Hello :)""#
);
}
#[pyclass(extends=PyDict)]
#[derive(Debug)]
struct DictWithName {
#[pyo3(get(name))]
_name: &'static str,
}
#[pymethods]
impl DictWithName {
#[new]
fn new() -> Self {
DictWithName { _name: "Hello :)" }
}
}
#[test]
fn inherit_dict() {
let gil = Python::acquire_gil();
let py = gil.python();
let dict_sub = pyo3::PyCell::new(py, DictWithName::new()).unwrap();
py_run!(
py,
dict_sub,
r#"dict_sub[0] = 1; assert dict_sub[0] == 1; assert dict_sub._name == "Hello :)""#
);
}

View File

@ -76,7 +76,7 @@ impl ClassMethod {
#[classmethod]
/// Test class method.
fn method(cls: &PyType) -> PyResult<String> {
Ok(format!("{}.method()!", cls.name()))
Ok(format!("{}.method()!", cls.name()?))
}
}
@ -104,7 +104,7 @@ struct ClassMethodWithArgs {}
impl ClassMethodWithArgs {
#[classmethod]
fn method(cls: &PyType, input: &PyString) -> PyResult<String> {
Ok(format!("{}.method({})", cls.name(), input))
Ok(format!("{}.method({})", cls.name()?, input))
}
}

View File

@ -1,6 +1,9 @@
#[cfg(not(Py_LIMITED_API))]
use pyo3::buffer::PyBuffer;
use pyo3::prelude::*;
use pyo3::types::{PyCFunction, PyFunction};
use pyo3::types::PyCFunction;
#[cfg(not(Py_LIMITED_API))]
use pyo3::types::PyFunction;
use pyo3::{raw_pycfunction, wrap_pyfunction};
mod common;
@ -23,6 +26,7 @@ fn test_optional_bool() {
py_assert!(py, f, "f(None) == 'None'");
}
#[cfg(not(Py_LIMITED_API))]
#[pyfunction]
fn buffer_inplace_add(py: Python, x: PyBuffer<i32>, y: PyBuffer<i32>) {
let x = x.as_mut_slice(py).unwrap();
@ -33,6 +37,7 @@ fn buffer_inplace_add(py: Python, x: PyBuffer<i32>, y: PyBuffer<i32>) {
}
}
#[cfg(not(Py_LIMITED_API))]
#[test]
fn test_buffer_add() {
let gil = Python::acquire_gil();
@ -64,6 +69,7 @@ assert a, array.array("i", [2, 4, 6, 8])
);
}
#[cfg(not(Py_LIMITED_API))]
#[pyfunction]
fn function_with_pyfunction_arg(fun: &PyFunction) -> PyResult<&PyAny> {
fun.call((), None)
@ -78,21 +84,31 @@ fn function_with_pycfunction_arg(fun: &PyCFunction) -> PyResult<&PyAny> {
fn test_functions_with_function_args() {
let gil = Python::acquire_gil();
let py = gil.python();
let py_func_arg = wrap_pyfunction!(function_with_pyfunction_arg)(py).unwrap();
let py_cfunc_arg = wrap_pyfunction!(function_with_pycfunction_arg)(py).unwrap();
let bool_to_string = wrap_pyfunction!(optional_bool)(py).unwrap();
pyo3::py_run!(
py,
py_func_arg
py_cfunc_arg
bool_to_string,
r#"
def foo(): return "bar"
assert py_func_arg(foo) == "bar"
assert py_cfunc_arg(bool_to_string) == "Some(true)"
"#
)
);
#[cfg(not(Py_LIMITED_API))]
{
let py_func_arg = wrap_pyfunction!(function_with_pyfunction_arg)(py).unwrap();
pyo3::py_run!(
py,
py_func_arg,
r#"
def foo(): return "bar"
assert py_func_arg(foo) == "bar"
"#
);
}
}
#[test]

View File

@ -32,6 +32,7 @@ fn class_with_docs() {
}
#[test]
#[cfg_attr(all(Py_LIMITED_API, not(Py_3_10)), ignore)]
fn class_with_docs_and_signature() {
/// docs line1
#[pyclass]
@ -67,6 +68,7 @@ fn class_with_docs_and_signature() {
}
#[test]
#[cfg_attr(all(Py_LIMITED_API, not(Py_3_10)), ignore)]
fn class_with_signature() {
#[pyclass]
#[text_signature = "(a, b=None, *, c=42)"]
@ -86,7 +88,11 @@ fn class_with_signature() {
let py = gil.python();
let typeobj = py.get_type::<MyClass>();
py_assert!(py, typeobj, "typeobj.__doc__ is None");
py_assert!(
py,
typeobj,
"typeobj.__doc__ is None or typeobj.__doc__ == ''"
);
py_assert!(
py,
typeobj,

View File

@ -13,6 +13,7 @@ impl UnsendableDictClass {
}
#[test]
#[cfg_attr(Py_LIMITED_API, ignore)]
fn test_unsendable_dict() {
let gil = Python::acquire_gil();
let py = gil.python();
@ -32,6 +33,7 @@ impl UnsendableDictClassWithWeakRef {
}
#[test]
#[cfg_attr(Py_LIMITED_API, ignore)]
fn test_unsendable_dict_with_weakref() {
let gil = Python::acquire_gil();
let py = gil.python();

View File

@ -149,6 +149,7 @@ fn add_module(py: Python, module: &PyModule) -> PyResult<()> {
}
#[test]
#[cfg_attr(Py_LIMITED_API, ignore)]
fn test_pickle() {
let gil = Python::acquire_gil();
let py = gil.python();

View File

@ -0,0 +1,8 @@
//! With abi3, we cannot inherit native types.
use pyo3::prelude::*;
use pyo3::types::PyDict;
#[pyclass(extends=PyDict)]
struct TestClass {}
fn main() {}

View File

@ -0,0 +1,13 @@
error[E0277]: the trait bound `pyo3::ffi::PyDictObject: pyo3::type_object::PySizedLayout<pyo3::types::PyDict>` is not satisfied
--> $DIR/abi3_nativetype_inheritance.rs:5:1
|
5 | #[pyclass(extends=PyDict)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `pyo3::type_object::PySizedLayout<pyo3::types::PyDict>` is not implemented for `pyo3::ffi::PyDictObject`
|
::: $WORKSPACE/src/type_object.rs:96:22
|
96 | type BaseLayout: PySizedLayout<Self::BaseType>;
| ----------------------------- required by this bound in `pyo3::PyTypeInfo`
|
= note: required because of the requirements on the impl of `pyo3::type_object::PySizedLayout<pyo3::types::PyDict>` for `pyo3::pycell::PyCellBase<pyo3::types::PyDict>`
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)