Remove ObjectProtocol; Add methods to PyAny and use Deref

This commit is contained in:
David Hewitt 2020-05-03 13:12:51 +01:00
parent 8d282913bc
commit a5ebef4b2e
32 changed files with 703 additions and 677 deletions

View file

@ -15,11 +15,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
* When the GIL is not held, the refcount is now decreased when the GIL is next acquired. (Previously would wait until next time the GIL was released.) * When the GIL is not held, the refcount is now decreased when the GIL is next acquired. (Previously would wait until next time the GIL was released.)
* `FromPyObject` for `Py<T>` now works for a wider range of `T`, in particular for `T: PyClass`. [#880](https://github.com/PyO3/pyo3/pull/880) * `FromPyObject` for `Py<T>` now works for a wider range of `T`, in particular for `T: PyClass`. [#880](https://github.com/PyO3/pyo3/pull/880)
* Some functions such as `PyList::get_item` which return borrowed objects at the C ffi layer now return owned objects at the PyO3 layer, for safety reasons. [#890](https://github.com/PyO3/pyo3/pull/890) * Some functions such as `PyList::get_item` which return borrowed objects at the C ffi layer now return owned objects at the PyO3 layer, for safety reasons. [#890](https://github.com/PyO3/pyo3/pull/890)
* The trait `ObjectProtocol` has been removed, and all the methods from the trait have been moved to `PyAny`. [#911](https://github.com/PyO3/pyo3/pull/911)
* The exception to this is `ObjectProtocol::None`, which has simply been removed. Use `Python::None` instead.
### Added ### Added
* `_PyDict_NewPresized`. [#849](https://github.com/PyO3/pyo3/pull/849) * `_PyDict_NewPresized`. [#849](https://github.com/PyO3/pyo3/pull/849)
* `IntoPy<PyObject>` for `HashSet` and `BTreeSet`. [#864](https://github.com/PyO3/pyo3/pull/864) * `IntoPy<PyObject>` for `HashSet` and `BTreeSet`. [#864](https://github.com/PyO3/pyo3/pull/864)
* `ObjectProtocol::dir`. [#886](https://github.com/PyO3/pyo3/pull/886) * `PyAny::dir`. [#886](https://github.com/PyO3/pyo3/pull/886)
* All builtin types (`PyList`, `PyTuple`, `PyDict`) etc. now implement `Deref<Target = PyAny>`. [#911](https://github.com/PyO3/pyo3/pull/911)
* `PyCell<T>` now implements `Deref<Target = PyAny>`. [#911](https://github.com/PyO3/pyo3/pull/911)
### Fixed ### Fixed
* `__radd__` and other `__r*__` methods now correctly work with operators. [#839](https://github.com/PyO3/pyo3/pull/839) * `__radd__` and other `__r*__` methods now correctly work with operators. [#839](https://github.com/PyO3/pyo3/pull/839)
@ -31,6 +35,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Removed ### Removed
* `PyMethodsProtocol` is now renamed to `PyMethodsImpl` and hidden. [#889](https://github.com/PyO3/pyo3/pull/889) * `PyMethodsProtocol` is now renamed to `PyMethodsImpl` and hidden. [#889](https://github.com/PyO3/pyo3/pull/889)
* `num-traits` is no longer a dependency. [#895](https://github.com/PyO3/pyo3/pull/895) * `num-traits` is no longer a dependency. [#895](https://github.com/PyO3/pyo3/pull/895)
* `ObjectProtocol`. [#911](https://github.com/PyO3/pyo3/pull/911)
## [0.9.2] ## [0.9.2]

View file

@ -34,7 +34,7 @@ same purpose, except that it consumes `self`.
## `*args` and `**kwargs` for Python object calls ## `*args` and `**kwargs` for Python object calls
There are several ways how to pass positional and keyword arguments to a Python object call. There are several ways how to pass positional and keyword arguments to a Python object call.
The [`ObjectProtocol`] trait provides two methods: [`PyAny`] provides two methods:
* `call` - call any callable Python object. * `call` - call any callable Python object.
* `call_method` - call a specific method on the object, shorthand for `get_attr` then `call`. * `call_method` - call a specific method on the object, shorthand for `get_attr` then `call`.
@ -137,7 +137,7 @@ Eventually, traits such as [`ToPyObject`] will be replaced by this trait and a [
[`ToPyObject`]: https://docs.rs/pyo3/latest/pyo3/conversion/trait.ToPyObject.html [`ToPyObject`]: https://docs.rs/pyo3/latest/pyo3/conversion/trait.ToPyObject.html
[`PyObject`]: https://docs.rs/pyo3/latest/pyo3/struct.PyObject.html [`PyObject`]: https://docs.rs/pyo3/latest/pyo3/struct.PyObject.html
[`PyTuple`]: https://docs.rs/pyo3/latest/pyo3/types/struct.PyTuple.html [`PyTuple`]: https://docs.rs/pyo3/latest/pyo3/types/struct.PyTuple.html
[`ObjectProtocol`]: https://docs.rs/pyo3/latest/pyo3/trait.ObjectProtocol.html [`PyAny`]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html
[`IntoPyDict`]: https://docs.rs/pyo3/latest/pyo3/types/trait.IntoPyDict.html [`IntoPyDict`]: https://docs.rs/pyo3/latest/pyo3/types/trait.IntoPyDict.html
[`PyRef`]: https://pyo3.rs/master/doc/pyo3/pycell/struct.PyRef.html [`PyRef`]: https://pyo3.rs/master/doc/pyo3/pycell/struct.PyRef.html

View file

@ -177,15 +177,15 @@ Currently, there are no conversions between `Fn`s in Rust and callables in Pytho
### Calling Python functions in Rust ### Calling Python functions in Rust
You can use [`ObjectProtocol::is_callable`] to check if you have a callable object. `is_callable` will return `true` for functions (including lambdas), methods and objects with a `__call__` method. You can call the object with [`ObjectProtocol::call`] with the args as first parameter and the kwargs (or `None`) as second parameter. There are also [`ObjectProtocol::call0`] with no args and [`ObjectProtocol::call1`] with only positional args. You can use [`PyAny::is_callable`] to check if you have a callable object. `is_callable` will return `true` for functions (including lambdas), methods and objects with a `__call__` method. You can call the object with [`PyAny::call`] with the args as first parameter and the kwargs (or `None`) as second parameter. There are also [`PyAny::call0`] with no args and [`PyAny::call1`] with only positional args.
### Calling Rust functions in Python ### Calling Rust functions in Python
If you have a static function, you can expose it with `#[pyfunction]` and use [`wrap_pyfunction!`] to get the corresponding [`PyObject`]. For dynamic functions, e.g. lambdas and functions that were passed as arguments, you must put them in some kind of owned container, e.g. a `Box`. (A long-term solution will be a special container similar to wasm-bindgen's `Closure`). You can then use a `#[pyclass]` struct with that container as a field as a way to pass the function over the FFI barrier. You can even make that class callable with `__call__` so it looks like a function in Python code. If you have a static function, you can expose it with `#[pyfunction]` and use [`wrap_pyfunction!`] to get the corresponding [`PyObject`]. For dynamic functions, e.g. lambdas and functions that were passed as arguments, you must put them in some kind of owned container, e.g. a `Box`. (A long-term solution will be a special container similar to wasm-bindgen's `Closure`). You can then use a `#[pyclass]` struct with that container as a field as a way to pass the function over the FFI barrier. You can even make that class callable with `__call__` so it looks like a function in Python code.
[`ObjectProtocol::is_callable`]: https://docs.rs/pyo3/latest/pyo3/trait.ObjectProtocol.html#tymethod.is_callable [`PyAny::is_callable`]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html#tymethod.is_callable
[`ObjectProtocol::call`]: https://docs.rs/pyo3/latest/pyo3/trait.ObjectProtocol.html#tymethod.call [`PyAny::call`]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html#tymethod.call
[`ObjectProtocol::call0`]: https://docs.rs/pyo3/latest/pyo3/trait.ObjectProtocol.html#tymethod.call0 [`PyAny::call0`]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html#tymethod.call0
[`ObjectProtocol::call1`]: https://docs.rs/pyo3/latest/pyo3/trait.ObjectProtocol.html#tymethod.call1 [`PyAny::call1`]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html#tymethod.call1
[`PyObject`]: https://docs.rs/pyo3/latest/pyo3/struct.PyObject.html [`PyObject`]: https://docs.rs/pyo3/latest/pyo3/struct.PyObject.html
[`wrap_pyfunction!`]: https://docs.rs/pyo3/latest/pyo3/macro.wrap_pyfunction.html [`wrap_pyfunction!`]: https://docs.rs/pyo3/latest/pyo3/macro.wrap_pyfunction.html

View file

@ -46,6 +46,79 @@ references is done at runtime using `PyCell`, a scheme very similar to
## Object types ## Object types
### [`PyAny`]
**Represents:** a Python object of unspecified type, restricted to a GIL
lifetime. Currently, `PyAny` can only ever occur as a reference, `&PyAny`.
**Used:** Whenever you want to refer to some Python object and will have the
GIL for the whole duration you need to access that object. For example,
intermediate values and arguments to `pyfunction`s or `pymethod`s implemented
in Rust where any type is allowed.
Many general methods for interacting with Python objects are on the `PyAny` struct,
such as `getattr`, `setattr`, and `.call`.
**Conversions:**
```rust
# use pyo3::prelude::*;
# use pyo3::types::PyList;
# let gil = Python::acquire_gil();
# let py = gil.python();
let obj: &PyAny = PyList::empty(py);
// Convert to &ConcreteType using PyAny::downcast
let _: &PyList = obj.downcast().unwrap();
// Convert to PyObject using .into() or .to_object(py)
let _: PyObject = obj.into();
// Convert to Py<PyAny> using .into() or Py::from
let _: Py<PyAny> = obj.into();
// Convert to Py<ConcreteType> using PyAny::extract
let _: Py<PyList> = obj.extract().unwrap();
```
### `PyTuple`, `PyDict`, and many more
**Represents:** a native Python object of known type, restricted to a GIL
lifetime just like `PyAny`.
**Used:** Whenever you want to operate with native Python types while holding
the GIL. Like `PyAny`, this is the most convenient form to use for function
arguments and intermediate values.
These types all implement `Deref<Target = PyAny>`, so they all expose the same
methods which can be found on `PyAny`.
**Conversions:**
```rust
# use pyo3::prelude::*;
# use pyo3::types::PyList;
# let gil = Python::acquire_gil();
# let py = gil.python();
let list = PyList::empty(py);
// Can use methods from PyAny on all Python types due to Deref implementation
let _ = list.repr();
// Rust will convert &PyList etc. to &PyAny automatically due to Deref implementation
let _: &PyAny = list;
// For more explicit &PyAny conversion, use .as_ref()
let _: &PyAny = list.as_ref();
// To convert to PyObject use .into() or .to_object(py)
let _: PyObject = list.into();
// To convert to Py<T> use .into() or Py::from()
let _: Py<PyList> = list.into();
```
### `PyObject` ### `PyObject`
**Represents:** a GIL independent reference to a Python object of unspecified **Represents:** a GIL independent reference to a Python object of unspecified
@ -60,10 +133,22 @@ Can be cloned using Python reference counts with `.clone_ref()`.
**Conversions:** **Conversions:**
- To `&PyAny`: `obj.as_ref(py)` ```rust
- To `Py<ConcreteType>`: `obj.as_ref(py).extract::<Py<ConcreteType>>` # use pyo3::prelude::*;
- To `&ConcreteType` (which must be a Python native type): `obj.cast_as(py)` # use pyo3::types::PyList;
# let gil = Python::acquire_gil();
# let py = gil.python();
let obj: PyObject = PyList::empty(py).into();
// Convert to &PyAny using AsPyRef::as_ref
let _: &PyAny = obj.as_ref(py);
// Convert to &ConcreteType using PyObject::cast_as
let _: &PyList = obj.cast_as(py).unwrap();
// Convert to Py<ConcreteType> using PyObject::extract
let _: Py<PyList> = obj.extract(py).unwrap();
```
### `Py<SomeType>` ### `Py<SomeType>`
@ -75,45 +160,25 @@ implemented in Rust.
**Conversions:** **Conversions:**
- To `PyObject`: `obj.to_object(py)` ```rust
- To `&SomeType` or `&PyCell<SomeType>`: `obj.as_ref(py)`. For `pyclass` types # use pyo3::prelude::*;
implemented in Rust, you get a `PyCell` (see below). For Python native types, # use pyo3::types::PyList;
mutating operations through PyO3's API don't require `&mut` access. # let gil = Python::acquire_gil();
# let py = gil.python();
let list: Py<PyList> = PyList::empty(py).into();
// Access the native type using AsPyRef::as_ref(py)
// (For #[pyclass] types, as_ref() will return &PyCell<T>)
let _: &PyList = list.as_ref(py);
// Convert to PyObject with .into()
let _: PyObject = list.into();
```
**Note:** `PyObject` is semantically equivalent to `Py<PyAny>` and might be **Note:** `PyObject` is semantically equivalent to `Py<PyAny>` and might be
merged with it in the future. merged with it in the future.
### `PyAny`
**Represents:** a Python object of unspecified type, restricted to a GIL
lifetime. Currently, `PyAny` can only ever occur as a reference, usually
`&PyAny`.
**Used:** Whenever you want to refer to some Python object only as long as
holding the GIL. For example, intermediate values and arguments to
`pyfunction`s or `pymethod`s implemented in Rust where any type is allowed.
**Conversions:**
- To `PyObject`: `obj.to_object(py)`
### `PyTuple`, `PyDict`, and many more
**Represents:** a native Python object of known type, restricted to a GIL
lifetime just like `PyAny`.
**Used:** Whenever you want to operate with native Python types while holding
the GIL. Like `PyAny`, this is the most convenient form to use for function
arguments and intermediate values.
**Conversions:**
- To `PyAny`: `obj.as_ref()`
- To `Py<T>`: `Py::from(obj)`
### `PyCell<SomeType>` ### `PyCell<SomeType>`
**Represents:** a reference to a Rust object (instance of `PyClass`) which is **Represents:** a reference to a Rust object (instance of `PyClass`) which is
@ -124,10 +189,39 @@ wrapped in a Python object. The cell part is an analog to stdlib's
taking `&SomeType` or `&mut SomeType`) while maintaining the aliasing rules of taking `&SomeType` or `&mut SomeType`) while maintaining the aliasing rules of
Rust references. Rust references.
Like pyo3's Python native types, `PyCell<T>` implements `Deref<Target = PyAny>`,
so it also exposes all of the methods on `PyAny`.
**Conversions:** **Conversions:**
- From `PyAny`: `.downcast()` ```rust
# use pyo3::prelude::*;
# use pyo3::types::PyList;
# #[pyclass] struct MyClass { }
# let gil = Python::acquire_gil();
# let py = gil.python();
let cell: &PyCell<MyClass> = PyCell::new(py, MyClass { }).unwrap();
// Obtain PyRef<T> with .try_borrow()
let pr: PyRef<MyClass> = cell.try_borrow().unwrap();
# drop(pr);
// Obtain PyRefMut<T> with .try_borrow_mut()
let prm: PyRefMut<MyClass> = cell.try_borrow_mut().unwrap();
# drop(prm);
// Can use methods from PyAny on PyCell<T> due to Deref implementation
let _ = cell.repr();
// Rust will convert &PyCell<T> to &PyAny automatically due to Deref implementation
let _: &PyAny = cell;
// For more explicit &PyAny conversion, use .as_ref()
let any: &PyAny = cell.as_ref();
// To obtain a PyCell<T> from PyAny, use PyAny::downcast
let _: &PyCell<MyClass> = any.downcast().unwrap();
```
### `PyRef<SomeType>` and `PyRefMut<SomeType>` ### `PyRef<SomeType>` and `PyRefMut<SomeType>`

View file

@ -414,7 +414,6 @@ pub fn impl_arg_params(spec: &FnSpec<'_>, body: TokenStream) -> TokenStream {
let num_normal_params = params.len(); let num_normal_params = params.len();
// create array of arguments, and then parse // create array of arguments, and then parse
quote! {{ quote! {{
use pyo3::ObjectProtocol;
const PARAMS: &'static [pyo3::derive_utils::ParamDescription] = &[ const PARAMS: &'static [pyo3::derive_utils::ParamDescription] = &[
#(#params),* #(#params),*
]; ];

View file

@ -661,9 +661,6 @@ mod test {
use crate::ffi; use crate::ffi;
use crate::Python; use crate::Python;
#[allow(unused_imports)]
use crate::objectprotocol::ObjectProtocol;
#[test] #[test]
fn test_compatible_size() { fn test_compatible_size() {
// for the cast in PyBuffer::shape() // for the cast in PyBuffer::shape()

View file

@ -11,8 +11,7 @@
use crate::callback::HashCallbackOutput; use crate::callback::HashCallbackOutput;
use crate::class::methods::PyMethodDef; use crate::class::methods::PyMethodDef;
use crate::{ use crate::{
exceptions, ffi, FromPyObject, IntoPy, ObjectProtocol, PyAny, PyCell, PyClass, PyErr, PyObject, exceptions, ffi, FromPyObject, IntoPy, PyAny, PyCell, PyClass, PyErr, PyObject, PyResult,
PyResult,
}; };
use std::os::raw::c_int; use std::os::raw::c_int;

View file

@ -66,7 +66,6 @@ macro_rules! py_binary_func {
where where
T: for<'p> $trait<'p>, T: for<'p> $trait<'p>,
{ {
use $crate::ObjectProtocol;
$crate::callback_body!(py, { $crate::callback_body!(py, {
let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf); let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
let arg = py.from_borrowed_ptr::<$crate::PyAny>(arg); let arg = py.from_borrowed_ptr::<$crate::PyAny>(arg);
@ -94,7 +93,6 @@ macro_rules! py_binary_num_func {
where where
T: for<'p> $trait<'p>, T: for<'p> $trait<'p>,
{ {
use $crate::ObjectProtocol;
$crate::callback_body!(py, { $crate::callback_body!(py, {
let lhs = py.from_borrowed_ptr::<$crate::PyAny>(lhs); let lhs = py.from_borrowed_ptr::<$crate::PyAny>(lhs);
let rhs = py.from_borrowed_ptr::<$crate::PyAny>(rhs); let rhs = py.from_borrowed_ptr::<$crate::PyAny>(rhs);
@ -117,7 +115,6 @@ macro_rules! py_binary_reverse_num_func {
where where
T: for<'p> $trait<'p>, T: for<'p> $trait<'p>,
{ {
use $crate::ObjectProtocol;
$crate::callback_body!(py, { $crate::callback_body!(py, {
// Swap lhs <-> rhs // Swap lhs <-> rhs
let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(rhs); let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(rhs);
@ -142,7 +139,6 @@ macro_rules! py_binary_self_func {
where where
T: for<'p> $trait<'p>, T: for<'p> $trait<'p>,
{ {
use $crate::ObjectProtocol;
$crate::callback_body!(py, { $crate::callback_body!(py, {
let slf_ = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf); let slf_ = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
let arg = py.from_borrowed_ptr::<$crate::PyAny>(arg); let arg = py.from_borrowed_ptr::<$crate::PyAny>(arg);
@ -191,7 +187,6 @@ macro_rules! py_ternary_func {
where where
T: for<'p> $trait<'p>, T: for<'p> $trait<'p>,
{ {
use $crate::ObjectProtocol;
$crate::callback_body!(py, { $crate::callback_body!(py, {
let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf); let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
let arg1 = py let arg1 = py
@ -224,7 +219,6 @@ macro_rules! py_ternary_num_func {
where where
T: for<'p> $trait<'p>, T: for<'p> $trait<'p>,
{ {
use $crate::ObjectProtocol;
$crate::callback_body!(py, { $crate::callback_body!(py, {
let arg1 = py let arg1 = py
.from_borrowed_ptr::<$crate::types::PyAny>(arg1) .from_borrowed_ptr::<$crate::types::PyAny>(arg1)
@ -256,7 +250,6 @@ macro_rules! py_ternary_reverse_num_func {
where where
T: for<'p> $trait<'p>, T: for<'p> $trait<'p>,
{ {
use $crate::ObjectProtocol;
$crate::callback_body!(py, { $crate::callback_body!(py, {
// Swap lhs <-> rhs // Swap lhs <-> rhs
let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(arg2); let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(arg2);
@ -284,7 +277,6 @@ macro_rules! py_dummy_ternary_self_func {
where where
T: for<'p> $trait<'p>, T: for<'p> $trait<'p>,
{ {
use $crate::ObjectProtocol;
$crate::callback_body!(py, { $crate::callback_body!(py, {
let slf_cell = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf); let slf_cell = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
let arg1 = py.from_borrowed_ptr::<$crate::PyAny>(arg1); let arg1 = py.from_borrowed_ptr::<$crate::PyAny>(arg1);
@ -307,7 +299,6 @@ macro_rules! py_func_set {
where where
T: for<'p> $trait_name<'p>, T: for<'p> $trait_name<'p>,
{ {
use $crate::ObjectProtocol;
$crate::callback_body!(py, { $crate::callback_body!(py, {
let slf = py.from_borrowed_ptr::<$crate::PyCell<$generic>>(slf); let slf = py.from_borrowed_ptr::<$crate::PyCell<$generic>>(slf);
@ -340,7 +331,6 @@ macro_rules! py_func_del {
where where
U: for<'p> $trait_name<'p>, U: for<'p> $trait_name<'p>,
{ {
use $crate::ObjectProtocol;
$crate::callback_body!(py, { $crate::callback_body!(py, {
if value.is_null() { if value.is_null() {
let slf = py.from_borrowed_ptr::<$crate::PyCell<U>>(slf); let slf = py.from_borrowed_ptr::<$crate::PyCell<U>>(slf);
@ -370,7 +360,6 @@ macro_rules! py_func_set_del {
where where
T: for<'p> $trait1<'p> + for<'p> $trait2<'p>, T: for<'p> $trait1<'p> + for<'p> $trait2<'p>,
{ {
use $crate::ObjectProtocol;
$crate::callback_body!(py, { $crate::callback_body!(py, {
let slf = py.from_borrowed_ptr::<$crate::PyCell<$generic>>(slf); let slf = py.from_borrowed_ptr::<$crate::PyCell<$generic>>(slf);
let name = py.from_borrowed_ptr::<$crate::PyAny>(name); let name = py.from_borrowed_ptr::<$crate::PyAny>(name);

View file

@ -5,7 +5,6 @@
use crate::conversion::{FromPyObject, IntoPy}; use crate::conversion::{FromPyObject, IntoPy};
use crate::err::{PyErr, PyResult}; use crate::err::{PyErr, PyResult};
use crate::objectprotocol::ObjectProtocol;
use crate::{exceptions, ffi, PyAny, PyCell, PyClass, PyObject}; use crate::{exceptions, ffi, PyAny, PyCell, PyClass, PyObject};
use std::os::raw::c_int; use std::os::raw::c_int;

View file

@ -5,8 +5,8 @@ use crate::type_object::PyTypeObject;
use crate::types::PyType; use crate::types::PyType;
use crate::{exceptions, ffi}; use crate::{exceptions, ffi};
use crate::{ use crate::{
AsPyPointer, FromPy, FromPyPointer, IntoPy, IntoPyPointer, ObjectProtocol, Py, PyAny, PyObject, AsPyPointer, FromPy, FromPyPointer, IntoPy, IntoPyPointer, Py, PyAny, PyObject, Python,
Python, ToBorrowedObject, ToPyObject, ToBorrowedObject, ToPyObject,
}; };
use libc::c_int; use libc::c_int;
use std::ffi::CString; use std::ffi::CString;

View file

@ -354,7 +354,6 @@ pub mod socket {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::exceptions::Exception; use crate::exceptions::Exception;
use crate::objectprotocol::ObjectProtocol;
use crate::types::{IntoPyDict, PyDict}; use crate::types::{IntoPyDict, PyDict};
use crate::{PyErr, Python}; use crate::{PyErr, Python};

View file

@ -2,7 +2,6 @@
use crate::err::{PyErr, PyResult}; use crate::err::{PyErr, PyResult};
use crate::gil; use crate::gil;
use crate::object::PyObject; use crate::object::PyObject;
use crate::objectprotocol::ObjectProtocol;
use crate::type_object::PyBorrowFlagLayout; use crate::type_object::PyBorrowFlagLayout;
use crate::{ use crate::{
ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyAny, PyCell, PyClass, ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyAny, PyCell, PyClass,
@ -148,7 +147,6 @@ impl<T> Py<T> {
/// `PyObject::as_ref` returns `&PyAny`. /// `PyObject::as_ref` returns `&PyAny`.
/// ``` /// ```
/// # use pyo3::prelude::*; /// # use pyo3::prelude::*;
/// use pyo3::ObjectProtocol;
/// let obj: PyObject = { /// let obj: PyObject = {
/// let gil = Python::acquire_gil(); /// let gil = Python::acquire_gil();
/// let py = gil.python(); /// let py = gil.python();
@ -156,14 +154,13 @@ impl<T> Py<T> {
/// }; /// };
/// let gil = Python::acquire_gil(); /// let gil = Python::acquire_gil();
/// let py = gil.python(); /// let py = gil.python();
/// assert_eq!(obj.as_ref(py).len().unwrap(), 0); // PyAny implements ObjectProtocol /// assert_eq!(obj.as_ref(py).len().unwrap(), 0);
/// ``` /// ```
/// ///
/// `Py<T>::as_ref` returns `&PyDict`, `&PyList` or so for native types, and `&PyCell<T>` /// `Py<T>::as_ref` returns `&PyDict`, `&PyList` or so for native types, and `&PyCell<T>`
/// for `#[pyclass]`. /// for `#[pyclass]`.
/// ``` /// ```
/// # use pyo3::prelude::*; /// # use pyo3::prelude::*;
/// use pyo3::ObjectProtocol;
/// let obj: PyObject = { /// let obj: PyObject = {
/// let gil = Python::acquire_gil(); /// let gil = Python::acquire_gil();
/// let py = gil.python(); /// let py = gil.python();
@ -171,7 +168,7 @@ impl<T> Py<T> {
/// }; /// };
/// let gil = Python::acquire_gil(); /// let gil = Python::acquire_gil();
/// let py = gil.python(); /// let py = gil.python();
/// assert_eq!(obj.as_ref(py).len().unwrap(), 0); // PyAny implements ObjectProtocol /// assert_eq!(obj.as_ref(py).len().unwrap(), 0);
/// ``` /// ```
pub trait AsPyRef: Sized { pub trait AsPyRef: Sized {
type Target; type Target;

View file

@ -142,7 +142,6 @@ pub use crate::err::{PyDowncastError, PyErr, PyErrArguments, PyErrValue, PyResul
pub use crate::gil::{GILGuard, GILPool}; pub use crate::gil::{GILGuard, GILPool};
pub use crate::instance::{AsPyRef, ManagedPyRef, Py, PyNativeType}; pub use crate::instance::{AsPyRef, ManagedPyRef, Py, PyNativeType};
pub use crate::object::PyObject; pub use crate::object::PyObject;
pub use crate::objectprotocol::ObjectProtocol;
pub use crate::pycell::{PyCell, PyRef, PyRefMut}; pub use crate::pycell::{PyCell, PyRef, PyRefMut};
pub use crate::pyclass::PyClass; pub use crate::pyclass::PyClass;
pub use crate::pyclass_init::PyClassInitializer; pub use crate::pyclass_init::PyClassInitializer;
@ -187,7 +186,6 @@ mod instance;
mod internal_tricks; mod internal_tricks;
pub mod marshal; pub mod marshal;
mod object; mod object;
mod objectprotocol;
pub mod panic; pub mod panic;
pub mod prelude; pub mod prelude;
pub mod pycell; pub mod pycell;
@ -338,6 +336,7 @@ pub mod doc_test {
doctest!("../guide/src/exception.md", guide_exception_md); doctest!("../guide/src/exception.md", guide_exception_md);
doctest!("../guide/src/function.md", guide_function_md); doctest!("../guide/src/function.md", guide_function_md);
doctest!("../guide/src/get_started.md", guide_get_started_md); doctest!("../guide/src/get_started.md", guide_get_started_md);
doctest!("../guide/src/migration.md", guide_migration_md);
doctest!("../guide/src/module.md", guide_module_md); doctest!("../guide/src/module.md", guide_module_md);
doctest!( doctest!(
"../guide/src/python_from_rust.md", "../guide/src/python_from_rust.md",
@ -346,5 +345,5 @@ pub mod doc_test {
doctest!("../guide/src/parallelism.md", guide_parallelism_md); doctest!("../guide/src/parallelism.md", guide_parallelism_md);
doctest!("../guide/src/pypy.md", guide_pypy_md); doctest!("../guide/src/pypy.md", guide_pypy_md);
doctest!("../guide/src/rust_cpython.md", guide_rust_cpython_md); doctest!("../guide/src/rust_cpython.md", guide_rust_cpython_md);
doctest!("../guide/src/migration.md", guide_migration_md); doctest!("../guide/src/types.md", guide_types_md);
} }

View file

@ -1,573 +0,0 @@
// Copyright (c) 2017-present PyO3 Project and Contributors
use crate::class::basic::CompareOp;
use crate::err::{self, PyDowncastError, PyErr, PyResult};
use crate::exceptions::TypeError;
use crate::types::{PyAny, PyDict, PyIterator, PyList, PyString, PyTuple, PyType};
use crate::{
ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, Py, PyNativeType, PyObject, PyTryFrom,
Python, ToBorrowedObject, ToPyObject,
};
use std::cmp::Ordering;
use std::os::raw::c_int;
/// Python object model helper methods
pub trait ObjectProtocol {
/// Determines whether this object has the given attribute.
///
/// This is equivalent to the Python expression `hasattr(self, attr_name)`.
fn hasattr<N>(&self, attr_name: N) -> PyResult<bool>
where
N: ToPyObject;
/// Retrieves an attribute value.
///
/// This is equivalent to the Python expression `self.attr_name`.
fn getattr<N>(&self, attr_name: N) -> PyResult<&PyAny>
where
N: ToPyObject;
/// Sets an attribute value.
///
/// This is equivalent to the Python expression `self.attr_name = value`.
fn setattr<N, V>(&self, attr_name: N, value: V) -> PyResult<()>
where
N: ToBorrowedObject,
V: ToBorrowedObject;
/// Deletes an attribute.
///
/// This is equivalent to the Python expression `del self.attr_name`.
fn delattr<N>(&self, attr_name: N) -> PyResult<()>
where
N: ToPyObject;
/// Compares two Python objects.
///
/// This is equivalent to:
/// ```python
/// if self == other:
/// return Equal
/// elif a < b:
/// return Less
/// elif a > b:
/// return Greater
/// else:
/// raise TypeError("ObjectProtocol::compare(): All comparisons returned false")
/// ```
fn compare<O>(&self, other: O) -> PyResult<Ordering>
where
O: ToPyObject;
/// Compares two Python objects.
///
/// Depending on the value of `compare_op`, this is equivalent to one of the
/// following Python expressions:
/// * CompareOp::Eq: `self == other`
/// * CompareOp::Ne: `self != other`
/// * CompareOp::Lt: `self < other`
/// * CompareOp::Le: `self <= other`
/// * CompareOp::Gt: `self > other`
/// * CompareOp::Ge: `self >= other`
fn rich_compare<O>(&self, other: O, compare_op: CompareOp) -> PyResult<PyObject>
where
O: ToPyObject;
/// Computes the "repr" representation of self.
///
/// This is equivalent to the Python expression `repr(self)`.
fn repr(&self) -> PyResult<&PyString>;
/// Computes the "str" representation of self.
///
/// This is equivalent to the Python expression `str(self)`.
fn str(&self) -> PyResult<&PyString>;
/// Determines whether this object is callable.
fn is_callable(&self) -> bool;
/// Calls the object.
///
/// This is equivalent to the Python expression `self(*args, **kwargs)`.
fn call(&self, args: impl IntoPy<Py<PyTuple>>, kwargs: Option<&PyDict>) -> PyResult<&PyAny>;
/// Calls the object with only positional arguments.
///
/// This is equivalent to the Python expression `self(*args)`.
fn call1(&self, args: impl IntoPy<Py<PyTuple>>) -> PyResult<&PyAny>;
/// Calls the object without arguments.
///
/// This is equivalent to the Python expression `self()`.
fn call0(&self) -> PyResult<&PyAny>;
/// Calls a method on the object.
///
/// This is equivalent to the Python expression `self.name(*args, **kwargs)`.
///
/// # Example
/// ```rust
/// # use pyo3::prelude::*;
/// use pyo3::types::IntoPyDict;
///
/// let gil = Python::acquire_gil();
/// let py = gil.python();
/// let list = vec![3, 6, 5, 4, 7].to_object(py);
/// let dict = vec![("reverse", true)].into_py_dict(py);
/// list.call_method(py, "sort", (), Some(dict)).unwrap();
/// assert_eq!(list.extract::<Vec<i32>>(py).unwrap(), vec![7, 6, 5, 4, 3]);
/// ```
fn call_method(
&self,
name: &str,
args: impl IntoPy<Py<PyTuple>>,
kwargs: Option<&PyDict>,
) -> PyResult<&PyAny>;
/// Calls a method on the object with only positional arguments.
///
/// This is equivalent to the Python expression `self.name(*args)`.
fn call_method1(&self, name: &str, args: impl IntoPy<Py<PyTuple>>) -> PyResult<&PyAny>;
/// Calls a method on the object without arguments.
///
/// This is equivalent to the Python expression `self.name()`.
fn call_method0(&self, name: &str) -> PyResult<&PyAny>;
/// Retrieves the hash code of the object.
///
/// This is equivalent to the Python expression `hash(self)`.
fn hash(&self) -> PyResult<isize>;
/// Returns whether the object is considered to be true.
///
/// This is equivalent to the Python expression `bool(self)`.
fn is_true(&self) -> PyResult<bool>;
/// Returns whether the object is considered to be None.
///
/// This is equivalent to the Python expression `self is None`.
fn is_none(&self) -> bool;
/// Returns the length of the sequence or mapping.
///
/// This is equivalent to the Python expression `len(self)`.
fn len(&self) -> PyResult<usize>;
/// Returns true if the sequence or mapping has a length of 0.
///
/// This is equivalent to the Python expression `len(self) == 0`.
fn is_empty(&self) -> PyResult<bool>;
/// Gets an item from the collections.
///
/// This is equivalent to the Python expression `self[key]`.
fn get_item<K>(&self, key: K) -> PyResult<&PyAny>
where
K: ToBorrowedObject;
/// Sets a collection item value.
///
/// This is equivalent to the Python expression `self[key] = value`.
fn set_item<K, V>(&self, key: K, value: V) -> PyResult<()>
where
K: ToBorrowedObject,
V: ToBorrowedObject;
/// Deletes an item from the collection.
///
/// This is equivalent to the Python expression `del self[key]`.
fn del_item<K>(&self, key: K) -> PyResult<()>
where
K: ToBorrowedObject;
/// Takes an object and returns an iterator for it.
///
/// This is typically a new iterator but if the argument is an iterator,
/// this returns itself.
fn iter(&self) -> PyResult<PyIterator>;
/// Returns the Python type object for this object's type.
fn get_type(&self) -> &PyType;
/// Returns the Python type pointer for this object.
fn get_type_ptr(&self) -> *mut ffi::PyTypeObject;
/// Casts the PyObject to a concrete Python object type.
///
/// This can cast only to native Python types, not types implemented in Rust.
fn cast_as<'a, D>(&'a self) -> Result<&'a D, PyDowncastError>
where
D: PyTryFrom<'a>,
&'a PyAny: std::convert::From<&'a Self>;
/// Extracts some type from the Python object.
///
/// This is a wrapper function around `FromPyObject::extract()`.
fn extract<'a, D>(&'a self) -> PyResult<D>
where
D: FromPyObject<'a>,
&'a PyAny: std::convert::From<&'a Self>;
/// Returns the reference count for the Python object.
fn get_refcnt(&self) -> isize;
/// Returns the list of attributes of this object.
fn dir(&self) -> &PyList;
/// Gets the Python builtin value `None`.
#[allow(non_snake_case)] // the Python keyword starts with uppercase
fn None(&self) -> PyObject;
}
impl<T> ObjectProtocol for T
where
T: PyNativeType + AsPyPointer,
{
fn hasattr<N>(&self, attr_name: N) -> PyResult<bool>
where
N: ToPyObject,
{
attr_name.with_borrowed_ptr(self.py(), |attr_name| unsafe {
Ok(ffi::PyObject_HasAttr(self.as_ptr(), attr_name) != 0)
})
}
fn getattr<N>(&self, attr_name: N) -> PyResult<&PyAny>
where
N: ToPyObject,
{
attr_name.with_borrowed_ptr(self.py(), |attr_name| unsafe {
self.py()
.from_owned_ptr_or_err(ffi::PyObject_GetAttr(self.as_ptr(), attr_name))
})
}
fn setattr<N, V>(&self, attr_name: N, value: V) -> PyResult<()>
where
N: ToBorrowedObject,
V: ToBorrowedObject,
{
attr_name.with_borrowed_ptr(self.py(), move |attr_name| {
value.with_borrowed_ptr(self.py(), |value| unsafe {
err::error_on_minusone(
self.py(),
ffi::PyObject_SetAttr(self.as_ptr(), attr_name, value),
)
})
})
}
fn delattr<N>(&self, attr_name: N) -> PyResult<()>
where
N: ToPyObject,
{
attr_name.with_borrowed_ptr(self.py(), |attr_name| unsafe {
err::error_on_minusone(self.py(), ffi::PyObject_DelAttr(self.as_ptr(), attr_name))
})
}
fn compare<O>(&self, other: O) -> PyResult<Ordering>
where
O: ToPyObject,
{
unsafe fn do_compare(
py: Python,
a: *mut ffi::PyObject,
b: *mut ffi::PyObject,
) -> PyResult<Ordering> {
let result = ffi::PyObject_RichCompareBool(a, b, ffi::Py_EQ);
if result == 1 {
return Ok(Ordering::Equal);
} else if result < 0 {
return Err(PyErr::fetch(py));
}
let result = ffi::PyObject_RichCompareBool(a, b, ffi::Py_LT);
if result == 1 {
return Ok(Ordering::Less);
} else if result < 0 {
return Err(PyErr::fetch(py));
}
let result = ffi::PyObject_RichCompareBool(a, b, ffi::Py_GT);
if result == 1 {
return Ok(Ordering::Greater);
} else if result < 0 {
return Err(PyErr::fetch(py));
}
Err(TypeError::py_err(
"ObjectProtocol::compare(): All comparisons returned false",
))
}
other.with_borrowed_ptr(self.py(), |other| unsafe {
do_compare(self.py(), self.as_ptr(), other)
})
}
fn rich_compare<O>(&self, other: O, compare_op: CompareOp) -> PyResult<PyObject>
where
O: ToPyObject,
{
unsafe {
other.with_borrowed_ptr(self.py(), |other| {
PyObject::from_owned_ptr_or_err(
self.py(),
ffi::PyObject_RichCompare(self.as_ptr(), other, compare_op as c_int),
)
})
}
}
fn repr(&self) -> PyResult<&PyString> {
unsafe {
self.py()
.from_owned_ptr_or_err(ffi::PyObject_Repr(self.as_ptr()))
}
}
fn str(&self) -> PyResult<&PyString> {
unsafe {
self.py()
.from_owned_ptr_or_err(ffi::PyObject_Str(self.as_ptr()))
}
}
fn is_callable(&self) -> bool {
unsafe { ffi::PyCallable_Check(self.as_ptr()) != 0 }
}
fn call(&self, args: impl IntoPy<Py<PyTuple>>, kwargs: Option<&PyDict>) -> PyResult<&PyAny> {
let args = args.into_py(self.py()).into_ptr();
let kwargs = kwargs.into_ptr();
let result = unsafe {
let return_value = ffi::PyObject_Call(self.as_ptr(), args, kwargs);
self.py().from_owned_ptr_or_err(return_value)
};
unsafe {
ffi::Py_XDECREF(args);
ffi::Py_XDECREF(kwargs);
}
result
}
fn call0(&self) -> PyResult<&PyAny> {
self.call((), None)
}
fn call1(&self, args: impl IntoPy<Py<PyTuple>>) -> PyResult<&PyAny> {
self.call(args, None)
}
fn call_method(
&self,
name: &str,
args: impl IntoPy<Py<PyTuple>>,
kwargs: Option<&PyDict>,
) -> PyResult<&PyAny> {
name.with_borrowed_ptr(self.py(), |name| unsafe {
let py = self.py();
let ptr = ffi::PyObject_GetAttr(self.as_ptr(), name);
if ptr.is_null() {
return Err(PyErr::fetch(py));
}
let args = args.into_py(py).into_ptr();
let kwargs = kwargs.into_ptr();
let result_ptr = ffi::PyObject_Call(ptr, args, kwargs);
let result = py.from_owned_ptr_or_err(result_ptr);
ffi::Py_DECREF(ptr);
ffi::Py_XDECREF(args);
ffi::Py_XDECREF(kwargs);
result
})
}
fn call_method0(&self, name: &str) -> PyResult<&PyAny> {
self.call_method(name, (), None)
}
fn call_method1(&self, name: &str, args: impl IntoPy<Py<PyTuple>>) -> PyResult<&PyAny> {
self.call_method(name, args, None)
}
fn hash(&self) -> PyResult<isize> {
let v = unsafe { ffi::PyObject_Hash(self.as_ptr()) };
if v == -1 {
Err(PyErr::fetch(self.py()))
} else {
Ok(v)
}
}
fn is_true(&self) -> PyResult<bool> {
let v = unsafe { ffi::PyObject_IsTrue(self.as_ptr()) };
if v == -1 {
Err(PyErr::fetch(self.py()))
} else {
Ok(v != 0)
}
}
fn is_none(&self) -> bool {
unsafe { ffi::Py_None() == self.as_ptr() }
}
fn len(&self) -> PyResult<usize> {
let v = unsafe { ffi::PyObject_Size(self.as_ptr()) };
if v == -1 {
Err(PyErr::fetch(self.py()))
} else {
Ok(v as usize)
}
}
fn is_empty(&self) -> PyResult<bool> {
self.len().map(|l| l == 0)
}
fn get_item<K>(&self, key: K) -> PyResult<&PyAny>
where
K: ToBorrowedObject,
{
key.with_borrowed_ptr(self.py(), |key| unsafe {
self.py()
.from_owned_ptr_or_err(ffi::PyObject_GetItem(self.as_ptr(), key))
})
}
fn set_item<K, V>(&self, key: K, value: V) -> PyResult<()>
where
K: ToBorrowedObject,
V: ToBorrowedObject,
{
key.with_borrowed_ptr(self.py(), move |key| {
value.with_borrowed_ptr(self.py(), |value| unsafe {
err::error_on_minusone(self.py(), ffi::PyObject_SetItem(self.as_ptr(), key, value))
})
})
}
fn del_item<K>(&self, key: K) -> PyResult<()>
where
K: ToBorrowedObject,
{
key.with_borrowed_ptr(self.py(), |key| unsafe {
err::error_on_minusone(self.py(), ffi::PyObject_DelItem(self.as_ptr(), key))
})
}
fn iter(&self) -> PyResult<PyIterator> {
Ok(PyIterator::from_object(self.py(), self)?)
}
fn get_type(&self) -> &PyType {
unsafe { PyType::from_type_ptr(self.py(), (*self.as_ptr()).ob_type) }
}
#[inline]
fn get_type_ptr(&self) -> *mut ffi::PyTypeObject {
unsafe { (*self.as_ptr()).ob_type }
}
fn cast_as<'a, D>(&'a self) -> Result<&'a D, PyDowncastError>
where
D: PyTryFrom<'a>,
&'a PyAny: std::convert::From<&'a Self>,
{
D::try_from(self)
}
fn extract<'a, D>(&'a self) -> PyResult<D>
where
D: FromPyObject<'a>,
&'a PyAny: std::convert::From<&'a T>,
{
FromPyObject::extract(self.into())
}
fn get_refcnt(&self) -> isize {
unsafe { ffi::Py_REFCNT(self.as_ptr()) }
}
fn dir(&self) -> &PyList {
unsafe { self.py().from_owned_ptr(ffi::PyObject_Dir(self.as_ptr())) }
}
#[allow(non_snake_case)] // the Python keyword starts with uppercase
fn None(&self) -> PyObject {
unsafe { PyObject::from_borrowed_ptr(self.py(), ffi::Py_None()) }
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::instance::AsPyRef;
use crate::types::{IntoPyDict, PyString};
use crate::Python;
use crate::{PyTryFrom, ToPyObject};
#[test]
fn test_debug_string() {
let gil = Python::acquire_gil();
let py = gil.python();
let v = "Hello\n".to_object(py);
let s = <PyString as PyTryFrom>::try_from(v.as_ref(py)).unwrap();
assert_eq!(format!("{:?}", s), "'Hello\\n'");
}
#[test]
fn test_display_string() {
let gil = Python::acquire_gil();
let py = gil.python();
let v = "Hello\n".to_object(py);
let s = <PyString as PyTryFrom>::try_from(v.as_ref(py)).unwrap();
assert_eq!(format!("{}", s), "Hello\n");
}
#[test]
fn test_call_for_non_existing_method() {
let gil = Python::acquire_gil();
let py = gil.python();
let a = py.eval("42", None, None).unwrap();
a.call_method0("__str__").unwrap(); // ok
assert!(a.call_method("nonexistent_method", (1,), None).is_err());
assert!(a.call_method0("nonexistent_method").is_err());
assert!(a.call_method1("nonexistent_method", (1,)).is_err());
}
#[test]
fn test_call_with_kwargs() {
let gil = Python::acquire_gil();
let py = gil.python();
let list = vec![3, 6, 5, 4, 7].to_object(py);
let dict = vec![("reverse", true)].into_py_dict(py);
list.call_method(py, "sort", (), Some(dict)).unwrap();
assert_eq!(list.extract::<Vec<i32>>(py).unwrap(), vec![7, 6, 5, 4, 3]);
}
#[test]
fn test_type() {
let gil = Python::acquire_gil();
let py = gil.python();
let obj = py.eval("42", None, None).unwrap();
assert_eq!(unsafe { obj.get_type().as_type_ptr() }, obj.get_type_ptr())
}
#[test]
fn test_dir() {
let gil = Python::acquire_gil();
let py = gil.python();
let obj = py.eval("42", None, None).unwrap();
let dir = py
.eval("dir(42)", None, None)
.unwrap()
.extract::<&PyList>()
.unwrap();
let a = obj
.dir()
.into_iter()
.map(|x| x.extract::<String>().unwrap());
let b = dir.into_iter().map(|x| x.extract::<String>().unwrap());
assert!(a.eq(b));
}
}

View file

@ -14,7 +14,6 @@ pub use crate::err::{PyErr, PyResult};
pub use crate::gil::GILGuard; pub use crate::gil::GILGuard;
pub use crate::instance::{AsPyRef, Py}; pub use crate::instance::{AsPyRef, Py};
pub use crate::object::PyObject; pub use crate::object::PyObject;
pub use crate::objectprotocol::ObjectProtocol;
pub use crate::pycell::{PyCell, PyRef, PyRefMut}; pub use crate::pycell::{PyCell, PyRef, PyRefMut};
pub use crate::pyclass_init::PyClassInitializer; pub use crate::pyclass_init::PyClassInitializer;
pub use crate::python::Python; pub use crate::python::Python;

View file

@ -3,6 +3,7 @@ use crate::conversion::{AsPyPointer, FromPyPointer, ToPyObject};
use crate::pyclass_init::PyClassInitializer; use crate::pyclass_init::PyClassInitializer;
use crate::pyclass_slots::{PyClassDict, PyClassWeakRef}; use crate::pyclass_slots::{PyClassDict, PyClassWeakRef};
use crate::type_object::{PyBorrowFlagLayout, PyLayout, PySizedLayout, PyTypeInfo}; use crate::type_object::{PyBorrowFlagLayout, PyLayout, PySizedLayout, PyTypeInfo};
use crate::types::PyAny;
use crate::{ffi, FromPy, PyClass, PyErr, PyNativeType, PyObject, PyResult, Python}; use crate::{ffi, FromPy, PyClass, PyErr, PyNativeType, PyObject, PyResult, Python};
use std::cell::{Cell, UnsafeCell}; use std::cell::{Cell, UnsafeCell};
use std::fmt; use std::fmt;
@ -101,6 +102,9 @@ impl<T: PyClass> PyCellInner<T> {
/// [Interior Mutability Pattern](https://doc.rust-lang.org/book/ch15-05-interior-mutability.html) /// [Interior Mutability Pattern](https://doc.rust-lang.org/book/ch15-05-interior-mutability.html)
/// like [std::cell::RefCell](https://doc.rust-lang.org/std/cell/struct.RefCell.html). /// like [std::cell::RefCell](https://doc.rust-lang.org/std/cell/struct.RefCell.html).
/// ///
/// `PyCell` implements `Deref<Target = PyAny>`, so you can also call methods from `PyAny`
/// when you have a `PyCell<T>`.
///
/// # Examples /// # Examples
/// ///
/// In most cases, `PyCell` is hidden behind `#[pymethods]`. /// In most cases, `PyCell` is hidden behind `#[pymethods]`.
@ -374,6 +378,20 @@ impl<T: PyClass> ToPyObject for &PyCell<T> {
} }
} }
impl<T: PyClass> AsRef<PyAny> for PyCell<T> {
fn as_ref(&self) -> &PyAny {
unsafe { self.py().from_borrowed_ptr(self.as_ptr()) }
}
}
impl<T: PyClass> Deref for PyCell<T> {
type Target = PyAny;
fn deref(&self) -> &PyAny {
unsafe { self.py().from_borrowed_ptr(self.as_ptr()) }
}
}
impl<T: PyClass + fmt::Debug> fmt::Debug for PyCell<T> { impl<T: PyClass + fmt::Debug> fmt::Debug for PyCell<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.try_borrow() { match self.try_borrow() {

View file

@ -204,7 +204,7 @@ impl<'p> Python<'p> {
) -> PyResult<()> { ) -> PyResult<()> {
let res = self.run_code(code, ffi::Py_file_input, globals, locals); let res = self.run_code(code, ffi::Py_file_input, globals, locals);
res.map(|obj| { res.map(|obj| {
debug_assert!(crate::ObjectProtocol::is_none(obj)); debug_assert!(obj.is_none());
}) })
} }
@ -412,7 +412,6 @@ impl<'p> Python<'p> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::objectprotocol::ObjectProtocol;
use crate::types::{IntoPyDict, PyAny, PyBool, PyInt, PyList}; use crate::types::{IntoPyDict, PyAny, PyBool, PyInt, PyList};
use crate::Python; use crate::Python;

View file

@ -1,7 +1,14 @@
use crate::conversion::{AsPyPointer, PyTryFrom}; use crate::class::basic::CompareOp;
use crate::err::PyDowncastError; use crate::conversion::{
use crate::ffi; AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyTryFrom, ToBorrowedObject, ToPyObject,
};
use crate::err::{PyDowncastError, PyErr, PyResult};
use crate::exceptions::TypeError;
use crate::types::{PyDict, PyIterator, PyList, PyString, PyTuple, PyType};
use crate::{err, ffi, Py, PyNativeType, Python};
use libc::c_int;
use std::cell::UnsafeCell; use std::cell::UnsafeCell;
use std::cmp::Ordering;
/// A Python object with GIL lifetime /// A Python object with GIL lifetime
/// ///
@ -59,10 +66,449 @@ pyobject_native_type_convert!(
pyobject_native_type_extract!(PyAny); pyobject_native_type_extract!(PyAny);
impl PyAny { impl PyAny {
/// Convert this PyAny to a concrete Python type.
pub fn downcast<T>(&self) -> Result<&T, PyDowncastError> pub fn downcast<T>(&self) -> Result<&T, PyDowncastError>
where where
for<'py> T: PyTryFrom<'py>, for<'py> T: PyTryFrom<'py>,
{ {
<T as PyTryFrom>::try_from(self) <T as PyTryFrom>::try_from(self)
} }
/// Determines whether this object has the given attribute.
///
/// This is equivalent to the Python expression `hasattr(self, attr_name)`.
pub fn hasattr<N>(&self, attr_name: N) -> PyResult<bool>
where
N: ToPyObject,
{
attr_name.with_borrowed_ptr(self.py(), |attr_name| unsafe {
Ok(ffi::PyObject_HasAttr(self.as_ptr(), attr_name) != 0)
})
}
/// Retrieves an attribute value.
///
/// This is equivalent to the Python expression `self.attr_name`.
pub fn getattr<N>(&self, attr_name: N) -> PyResult<&PyAny>
where
N: ToPyObject,
{
attr_name.with_borrowed_ptr(self.py(), |attr_name| unsafe {
self.py()
.from_owned_ptr_or_err(ffi::PyObject_GetAttr(self.as_ptr(), attr_name))
})
}
/// Sets an attribute value.
///
/// This is equivalent to the Python expression `self.attr_name = value`.
pub fn setattr<N, V>(&self, attr_name: N, value: V) -> PyResult<()>
where
N: ToBorrowedObject,
V: ToBorrowedObject,
{
attr_name.with_borrowed_ptr(self.py(), move |attr_name| {
value.with_borrowed_ptr(self.py(), |value| unsafe {
err::error_on_minusone(
self.py(),
ffi::PyObject_SetAttr(self.as_ptr(), attr_name, value),
)
})
})
}
/// Deletes an attribute.
///
/// This is equivalent to the Python expression `del self.attr_name`.
pub fn delattr<N>(&self, attr_name: N) -> PyResult<()>
where
N: ToPyObject,
{
attr_name.with_borrowed_ptr(self.py(), |attr_name| unsafe {
err::error_on_minusone(self.py(), ffi::PyObject_DelAttr(self.as_ptr(), attr_name))
})
}
/// Compares two Python objects.
///
/// This is equivalent to:
/// ```python
/// if self == other:
/// return Equal
/// elif a < b:
/// return Less
/// elif a > b:
/// return Greater
/// else:
/// raise TypeError("PyAny::compare(): All comparisons returned false")
/// ```
pub fn compare<O>(&self, other: O) -> PyResult<Ordering>
where
O: ToPyObject,
{
unsafe fn do_compare(
py: Python,
a: *mut ffi::PyObject,
b: *mut ffi::PyObject,
) -> PyResult<Ordering> {
let result = ffi::PyObject_RichCompareBool(a, b, ffi::Py_EQ);
if result == 1 {
return Ok(Ordering::Equal);
} else if result < 0 {
return Err(PyErr::fetch(py));
}
let result = ffi::PyObject_RichCompareBool(a, b, ffi::Py_LT);
if result == 1 {
return Ok(Ordering::Less);
} else if result < 0 {
return Err(PyErr::fetch(py));
}
let result = ffi::PyObject_RichCompareBool(a, b, ffi::Py_GT);
if result == 1 {
return Ok(Ordering::Greater);
} else if result < 0 {
return Err(PyErr::fetch(py));
}
Err(TypeError::py_err(
"PyAny::compare(): All comparisons returned false",
))
}
other.with_borrowed_ptr(self.py(), |other| unsafe {
do_compare(self.py(), self.as_ptr(), other)
})
}
/// Compares two Python objects.
///
/// Depending on the value of `compare_op`, this is equivalent to one of the
/// following Python expressions:
/// * CompareOp::Eq: `self == other`
/// * CompareOp::Ne: `self != other`
/// * CompareOp::Lt: `self < other`
/// * CompareOp::Le: `self <= other`
/// * CompareOp::Gt: `self > other`
/// * CompareOp::Ge: `self >= other`
pub fn rich_compare<O>(&self, other: O, compare_op: CompareOp) -> PyResult<&PyAny>
where
O: ToPyObject,
{
unsafe {
other.with_borrowed_ptr(self.py(), |other| {
self.py().from_owned_ptr_or_err(ffi::PyObject_RichCompare(
self.as_ptr(),
other,
compare_op as c_int,
))
})
}
}
/// Determines whether this object is callable.
pub fn is_callable(&self) -> bool {
unsafe { ffi::PyCallable_Check(self.as_ptr()) != 0 }
}
/// Calls the object.
///
/// This is equivalent to the Python expression `self(*args, **kwargs)`.
pub fn call(
&self,
args: impl IntoPy<Py<PyTuple>>,
kwargs: Option<&PyDict>,
) -> PyResult<&PyAny> {
let args = args.into_py(self.py()).into_ptr();
let kwargs = kwargs.into_ptr();
let result = unsafe {
let return_value = ffi::PyObject_Call(self.as_ptr(), args, kwargs);
self.py().from_owned_ptr_or_err(return_value)
};
unsafe {
ffi::Py_XDECREF(args);
ffi::Py_XDECREF(kwargs);
}
result
}
/// Calls the object with only positional arguments.
///
/// This is equivalent to the Python expression `self(*args)`.
pub fn call0(&self) -> PyResult<&PyAny> {
self.call((), None)
}
/// Calls the object without arguments.
///
/// This is equivalent to the Python expression `self()`.
pub fn call1(&self, args: impl IntoPy<Py<PyTuple>>) -> PyResult<&PyAny> {
self.call(args, None)
}
/// Calls a method on the object.
///
/// This is equivalent to the Python expression `self.name(*args, **kwargs)`.
///
/// # Example
/// ```rust
/// # use pyo3::prelude::*;
/// use pyo3::types::IntoPyDict;
///
/// let gil = Python::acquire_gil();
/// let py = gil.python();
/// let list = vec![3, 6, 5, 4, 7].to_object(py);
/// let dict = vec![("reverse", true)].into_py_dict(py);
/// list.call_method(py, "sort", (), Some(dict)).unwrap();
/// assert_eq!(list.extract::<Vec<i32>>(py).unwrap(), vec![7, 6, 5, 4, 3]);
/// ```
pub fn call_method(
&self,
name: &str,
args: impl IntoPy<Py<PyTuple>>,
kwargs: Option<&PyDict>,
) -> PyResult<&PyAny> {
name.with_borrowed_ptr(self.py(), |name| unsafe {
let py = self.py();
let ptr = ffi::PyObject_GetAttr(self.as_ptr(), name);
if ptr.is_null() {
return Err(PyErr::fetch(py));
}
let args = args.into_py(py).into_ptr();
let kwargs = kwargs.into_ptr();
let result_ptr = ffi::PyObject_Call(ptr, args, kwargs);
let result = py.from_owned_ptr_or_err(result_ptr);
ffi::Py_DECREF(ptr);
ffi::Py_XDECREF(args);
ffi::Py_XDECREF(kwargs);
result
})
}
/// Calls a method on the object without arguments.
///
/// This is equivalent to the Python expression `self.name()`.
pub fn call_method0(&self, name: &str) -> PyResult<&PyAny> {
self.call_method(name, (), None)
}
/// Calls a method on the object with only positional arguments.
///
/// This is equivalent to the Python expression `self.name(*args)`.
pub fn call_method1(&self, name: &str, args: impl IntoPy<Py<PyTuple>>) -> PyResult<&PyAny> {
self.call_method(name, args, None)
}
/// Returns whether the object is considered to be true.
///
/// This is equivalent to the Python expression `bool(self)`.
pub fn is_true(&self) -> PyResult<bool> {
let v = unsafe { ffi::PyObject_IsTrue(self.as_ptr()) };
if v == -1 {
Err(PyErr::fetch(self.py()))
} else {
Ok(v != 0)
}
}
/// Returns whether the object is considered to be None.
///
/// This is equivalent to the Python expression `self is None`.
pub fn is_none(&self) -> bool {
unsafe { ffi::Py_None() == self.as_ptr() }
}
/// Returns true if the sequence or mapping has a length of 0.
///
/// This is equivalent to the Python expression `len(self) == 0`.
pub fn is_empty(&self) -> PyResult<bool> {
self.len().map(|l| l == 0)
}
/// Gets an item from the collection.
///
/// This is equivalent to the Python expression `self[key]`.
pub fn get_item<K>(&self, key: K) -> PyResult<&PyAny>
where
K: ToBorrowedObject,
{
key.with_borrowed_ptr(self.py(), |key| unsafe {
self.py()
.from_owned_ptr_or_err(ffi::PyObject_GetItem(self.as_ptr(), key))
})
}
/// Sets a collection item value.
///
/// This is equivalent to the Python expression `self[key] = value`.
pub fn set_item<K, V>(&self, key: K, value: V) -> PyResult<()>
where
K: ToBorrowedObject,
V: ToBorrowedObject,
{
key.with_borrowed_ptr(self.py(), move |key| {
value.with_borrowed_ptr(self.py(), |value| unsafe {
err::error_on_minusone(self.py(), ffi::PyObject_SetItem(self.as_ptr(), key, value))
})
})
}
/// Deletes an item from the collection.
///
/// This is equivalent to the Python expression `del self[key]`.
pub fn del_item<K>(&self, key: K) -> PyResult<()>
where
K: ToBorrowedObject,
{
key.with_borrowed_ptr(self.py(), |key| unsafe {
err::error_on_minusone(self.py(), ffi::PyObject_DelItem(self.as_ptr(), key))
})
}
/// Takes an object and returns an iterator for it.
///
/// This is typically a new iterator but if the argument is an iterator,
/// this returns itself.
pub fn iter(&self) -> PyResult<PyIterator> {
Ok(PyIterator::from_object(self.py(), self)?)
}
/// Returns the Python type object for this object's type.
pub fn get_type(&self) -> &PyType {
unsafe { PyType::from_type_ptr(self.py(), (*self.as_ptr()).ob_type) }
}
/// Returns the Python type pointer for this object.
#[inline]
pub fn get_type_ptr(&self) -> *mut ffi::PyTypeObject {
unsafe { (*self.as_ptr()).ob_type }
}
/// Casts the PyObject to a concrete Python object type.
///
/// This can cast only to native Python types, not types implemented in Rust.
pub fn cast_as<'a, D>(&'a self) -> Result<&'a D, PyDowncastError>
where
D: PyTryFrom<'a>,
{
D::try_from(self)
}
/// Extracts some type from the Python object.
///
/// This is a wrapper function around `FromPyObject::extract()`.
pub fn extract<'a, D>(&'a self) -> PyResult<D>
where
D: FromPyObject<'a>,
{
FromPyObject::extract(self)
}
/// Returns the reference count for the Python object.
pub fn get_refcnt(&self) -> isize {
unsafe { ffi::Py_REFCNT(self.as_ptr()) }
}
/// Computes the "repr" representation of self.
///
/// This is equivalent to the Python expression `repr(self)`.
pub fn repr(&self) -> PyResult<&PyString> {
unsafe {
self.py()
.from_owned_ptr_or_err(ffi::PyObject_Repr(self.as_ptr()))
}
}
/// Computes the "str" representation of self.
///
/// This is equivalent to the Python expression `str(self)`.
pub fn str(&self) -> PyResult<&PyString> {
unsafe {
self.py()
.from_owned_ptr_or_err(ffi::PyObject_Str(self.as_ptr()))
}
}
/// Retrieves the hash code of self.
///
/// This is equivalent to the Python expression `hash(obi)`.
pub fn hash(&self) -> PyResult<isize> {
let v = unsafe { ffi::PyObject_Hash(self.as_ptr()) };
if v == -1 {
Err(PyErr::fetch(self.py()))
} else {
Ok(v)
}
}
/// Returns the length of the sequence or mapping.
///
/// This is equivalent to the Python expression `len(self)`.
pub fn len(&self) -> PyResult<usize> {
let v = unsafe { ffi::PyObject_Size(self.as_ptr()) };
if v == -1 {
Err(PyErr::fetch(self.py()))
} else {
Ok(v as usize)
}
}
/// Returns the list of attributes of this object.
///
/// This is equivalent to the Python expression `dir(self)`.
pub fn dir(&self) -> &PyList {
unsafe { self.py().from_owned_ptr(ffi::PyObject_Dir(self.as_ptr())) }
}
}
#[cfg(test)]
mod test {
use crate::types::{IntoPyDict, PyList};
use crate::Python;
use crate::ToPyObject;
#[test]
fn test_call_for_non_existing_method() {
let gil = Python::acquire_gil();
let py = gil.python();
let a = py.eval("42", None, None).unwrap();
a.call_method0("__str__").unwrap(); // ok
assert!(a.call_method("nonexistent_method", (1,), None).is_err());
assert!(a.call_method0("nonexistent_method").is_err());
assert!(a.call_method1("nonexistent_method", (1,)).is_err());
}
#[test]
fn test_call_with_kwargs() {
let gil = Python::acquire_gil();
let py = gil.python();
let list = vec![3, 6, 5, 4, 7].to_object(py);
let dict = vec![("reverse", true)].into_py_dict(py);
list.call_method(py, "sort", (), Some(dict)).unwrap();
assert_eq!(list.extract::<Vec<i32>>(py).unwrap(), vec![7, 6, 5, 4, 3]);
}
#[test]
fn test_type() {
let gil = Python::acquire_gil();
let py = gil.python();
let obj = py.eval("42", None, None).unwrap();
assert_eq!(unsafe { obj.get_type().as_type_ptr() }, obj.get_type_ptr())
}
#[test]
fn test_dir() {
let gil = Python::acquire_gil();
let py = gil.python();
let obj = py.eval("42", None, None).unwrap();
let dir = py
.eval("dir(42)", None, None)
.unwrap()
.downcast::<PyList>()
.unwrap();
let a = obj
.dir()
.into_iter()
.map(|x| x.extract::<String>().unwrap());
let b = dir.into_iter().map(|x| x.extract::<String>().unwrap());
assert!(a.eq(b));
}
} }

View file

@ -59,7 +59,6 @@ impl<'source> FromPyObject<'source> for bool {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::objectprotocol::ObjectProtocol;
use crate::types::{PyAny, PyBool}; use crate::types::{PyAny, PyBool};
use crate::Python; use crate::Python;
use crate::ToPyObject; use crate::ToPyObject;

View file

@ -96,7 +96,7 @@ impl PyDict {
/// ///
/// Returns `None` if the item is not present, or if an error occurs. /// Returns `None` if the item is not present, or if an error occurs.
/// ///
/// To get a `KeyError` for non-existing keys, use `ObjectProtocol::get_item`. /// To get a `KeyError` for non-existing keys, use `PyAny::get_item`.
pub fn get_item<K>(&self, key: K) -> Option<&PyAny> pub fn get_item<K>(&self, key: K) -> Option<&PyAny>
where where
K: ToBorrowedObject, K: ToBorrowedObject,
@ -361,8 +361,8 @@ mod test {
use crate::instance::AsPyRef; use crate::instance::AsPyRef;
use crate::types::dict::IntoPyDict; use crate::types::dict::IntoPyDict;
use crate::types::{PyDict, PyList, PyTuple}; use crate::types::{PyDict, PyList, PyTuple};
use crate::PyObject;
use crate::Python; use crate::Python;
use crate::{ObjectProtocol, PyObject};
use crate::{PyTryFrom, ToPyObject}; use crate::{PyTryFrom, ToPyObject};
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, HashMap};

View file

@ -2,8 +2,8 @@
// //
// based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython // based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython
use crate::{ use crate::{
ffi, AsPyPointer, FromPy, FromPyObject, ObjectProtocol, PyAny, PyErr, PyNativeType, PyObject, ffi, AsPyPointer, FromPy, FromPyObject, PyAny, PyErr, PyNativeType, PyObject, PyResult, Python,
PyResult, Python, ToPyObject, ToPyObject,
}; };
use std::os::raw::c_double; use std::os::raw::c_double;

View file

@ -19,7 +19,7 @@ use crate::{ffi, AsPyPointer, PyAny, PyDowncastError, PyErr, PyNativeType, PyRes
/// let gil = Python::acquire_gil(); /// let gil = Python::acquire_gil();
/// let py = gil.python(); /// let py = gil.python();
/// let list = py.eval("iter([1, 2, 3, 4])", None, None)?; /// let list = py.eval("iter([1, 2, 3, 4])", None, None)?;
/// let numbers: PyResult<Vec<usize>> = list.iter()?.map(|i| i.and_then(ObjectProtocol::extract::<usize>)).collect(); /// let numbers: PyResult<Vec<usize>> = list.iter()?.map(|i| i.and_then(PyAny::extract::<usize>)).collect();
/// let sum: usize = numbers?.iter().sum(); /// let sum: usize = numbers?.iter().sum();
/// assert_eq!(sum, 10); /// assert_eq!(sum, 10);
/// # Ok(()) /// # Ok(())
@ -87,7 +87,6 @@ impl<'p> Drop for PyIterator<'p> {
mod tests { mod tests {
use crate::gil::GILPool; use crate::gil::GILPool;
use crate::instance::AsPyRef; use crate::instance::AsPyRef;
use crate::objectprotocol::ObjectProtocol;
use crate::types::{PyDict, PyList}; use crate::types::{PyDict, PyList};
use crate::GILGuard; use crate::GILGuard;
use crate::Python; use crate::Python;

View file

@ -226,7 +226,6 @@ where
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::instance::AsPyRef; use crate::instance::AsPyRef;
use crate::objectprotocol::ObjectProtocol;
use crate::types::PyList; use crate::types::PyList;
use crate::Python; use crate::Python;
use crate::{IntoPy, PyObject, PyTryFrom, ToPyObject}; use crate::{IntoPy, PyObject, PyTryFrom, ToPyObject};

View file

@ -35,6 +35,15 @@ macro_rules! pyobject_native_type_named (
} }
} }
impl<$($type_param,)*> ::std::ops::Deref for $name {
type Target = $crate::PyAny;
#[inline]
fn deref(&self) -> &$crate::PyAny {
unsafe { &*(self.as_ptr() as *const $crate::PyAny) }
}
}
unsafe impl<$($type_param,)*> $crate::PyNativeType for $name {} unsafe impl<$($type_param,)*> $crate::PyNativeType for $name {}
impl<$($type_param,)*> $crate::AsPyPointer for $name { impl<$($type_param,)*> $crate::AsPyPointer for $name {
@ -158,7 +167,6 @@ macro_rules! pyobject_native_type_convert(
fn fmt(&self, f: &mut ::std::fmt::Formatter) fn fmt(&self, f: &mut ::std::fmt::Formatter)
-> Result<(), ::std::fmt::Error> -> Result<(), ::std::fmt::Error>
{ {
use $crate::ObjectProtocol;
let s = self.repr().map_err(|_| ::std::fmt::Error)?; let s = self.repr().map_err(|_| ::std::fmt::Error)?;
f.write_str(&s.to_string_lossy()) f.write_str(&s.to_string_lossy())
} }
@ -168,7 +176,6 @@ macro_rules! pyobject_native_type_convert(
fn fmt(&self, f: &mut ::std::fmt::Formatter) fn fmt(&self, f: &mut ::std::fmt::Formatter)
-> Result<(), ::std::fmt::Error> -> Result<(), ::std::fmt::Error>
{ {
use $crate::ObjectProtocol;
let s = self.str().map_err(|_| ::std::fmt::Error)?; let s = self.str().map_err(|_| ::std::fmt::Error)?;
f.write_str(&s.to_string_lossy()) f.write_str(&s.to_string_lossy())
} }

View file

@ -7,7 +7,6 @@ use crate::exceptions;
use crate::ffi; use crate::ffi;
use crate::instance::PyNativeType; use crate::instance::PyNativeType;
use crate::object::PyObject; use crate::object::PyObject;
use crate::objectprotocol::ObjectProtocol;
use crate::pyclass::PyClass; use crate::pyclass::PyClass;
use crate::type_object::PyTypeObject; use crate::type_object::PyTypeObject;
use crate::types::PyTuple; use crate::types::PyTuple;

View file

@ -38,7 +38,7 @@ macro_rules! int_fits_larger_int {
impl<'source> FromPyObject<'source> for $rust_type { impl<'source> FromPyObject<'source> for $rust_type {
fn extract(obj: &'source PyAny) -> PyResult<Self> { fn extract(obj: &'source PyAny) -> PyResult<Self> {
let val = $crate::objectprotocol::ObjectProtocol::extract::<$larger_type>(obj)?; let val: $larger_type = obj.extract()?;
<$rust_type>::try_from(val).map_err(|_| exceptions::OverflowError.into()) <$rust_type>::try_from(val).map_err(|_| exceptions::OverflowError.into())
} }
} }

View file

@ -5,7 +5,6 @@ use crate::err::{self, PyDowncastError, PyErr, PyResult};
use crate::exceptions; use crate::exceptions;
use crate::ffi::{self, Py_ssize_t}; use crate::ffi::{self, Py_ssize_t};
use crate::instance::PyNativeType; use crate::instance::PyNativeType;
use crate::objectprotocol::ObjectProtocol;
use crate::types::{PyAny, PyList, PyTuple}; use crate::types::{PyAny, PyList, PyTuple};
use crate::AsPyPointer; use crate::AsPyPointer;
use crate::{FromPyObject, PyTryFrom, ToBorrowedObject}; use crate::{FromPyObject, PyTryFrom, ToBorrowedObject};
@ -385,7 +384,6 @@ impl<'v> PyTryFrom<'v> for PySequence {
mod test { mod test {
use crate::instance::AsPyRef; use crate::instance::AsPyRef;
use crate::object::PyObject; use crate::object::PyObject;
use crate::objectprotocol::ObjectProtocol;
use crate::types::PySequence; use crate::types::PySequence;
use crate::AsPyPointer; use crate::AsPyPointer;
use crate::Python; use crate::Python;

View file

@ -303,7 +303,7 @@ impl<'a> std::iter::IntoIterator for &'a PyFrozenSet {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::{PyFrozenSet, PySet}; use super::{PyFrozenSet, PySet};
use crate::{AsPyRef, IntoPy, ObjectProtocol, PyObject, PyTryFrom, Python, ToPyObject}; use crate::{AsPyRef, IntoPy, PyObject, PyTryFrom, Python, ToPyObject};
use std::collections::{BTreeSet, HashSet}; use std::collections::{BTreeSet, HashSet};
use std::iter::FromIterator; use std::iter::FromIterator;

View file

@ -255,4 +255,22 @@ mod test {
let py_string = <PyString as PyTryFrom>::try_from(obj.as_ref(py)).unwrap(); let py_string = <PyString as PyTryFrom>::try_from(obj.as_ref(py)).unwrap();
assert_eq!(py_string.to_string_lossy(), "🐈 Hello <20><><EFBFBD>World"); assert_eq!(py_string.to_string_lossy(), "🐈 Hello <20><><EFBFBD>World");
} }
#[test]
fn test_debug_string() {
let gil = Python::acquire_gil();
let py = gil.python();
let v = "Hello\n".to_object(py);
let s = <PyString as PyTryFrom>::try_from(v.as_ref(py)).unwrap();
assert_eq!(format!("{:?}", s), "'Hello\\n'");
}
#[test]
fn test_display_string() {
let gil = Python::acquire_gil();
let py = gil.python();
let v = "Hello\n".to_object(py);
let s = <PyString as PyTryFrom>::try_from(v.as_ref(py)).unwrap();
assert_eq!(format!("{}", s), "Hello\n");
}
} }

View file

@ -247,7 +247,7 @@ tuple_conversion!(
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::types::{PyAny, PyTuple}; use crate::types::{PyAny, PyTuple};
use crate::{AsPyRef, ObjectProtocol, PyTryFrom, Python, ToPyObject}; use crate::{AsPyRef, PyTryFrom, Python, ToPyObject};
use std::collections::HashSet; use std::collections::HashSet;
#[test] #[test]

View file

@ -1,5 +1,5 @@
use pyo3::prelude::*; use pyo3::prelude::*;
use pyo3::{ObjectProtocol, ToPyObject}; use pyo3::ToPyObject;
#[macro_use] #[macro_use]
mod common; mod common;
@ -31,7 +31,10 @@ fn test_cloneable_pyclass() {
} }
#[pyclass] #[pyclass]
struct BaseClass {} #[derive(Default)]
struct BaseClass {
value: i32,
}
#[pymethods] #[pymethods]
impl BaseClass { impl BaseClass {
@ -64,7 +67,7 @@ fn test_polymorphic_container_stores_base_class() {
let p = PyCell::new( let p = PyCell::new(
py, py,
PolymorphicContainer { PolymorphicContainer {
inner: Py::new(py, BaseClass {}).unwrap(), inner: Py::new(py, BaseClass::default()).unwrap(),
}, },
) )
.unwrap() .unwrap()
@ -81,7 +84,7 @@ fn test_polymorphic_container_stores_sub_class() {
let p = PyCell::new( let p = PyCell::new(
py, py,
PolymorphicContainer { PolymorphicContainer {
inner: Py::new(py, BaseClass {}).unwrap(), inner: Py::new(py, BaseClass::default()).unwrap(),
}, },
) )
.unwrap() .unwrap()
@ -92,7 +95,7 @@ fn test_polymorphic_container_stores_sub_class() {
"inner", "inner",
PyCell::new( PyCell::new(
py, py,
PyClassInitializer::from(BaseClass {}).add_subclass(SubClass {}), PyClassInitializer::from(BaseClass::default()).add_subclass(SubClass {}),
) )
.unwrap(), .unwrap(),
) )
@ -109,7 +112,7 @@ fn test_polymorphic_container_does_not_accept_other_types() {
let p = PyCell::new( let p = PyCell::new(
py, py,
PolymorphicContainer { PolymorphicContainer {
inner: Py::new(py, BaseClass {}).unwrap(), inner: Py::new(py, BaseClass::default()).unwrap(),
}, },
) )
.unwrap() .unwrap()
@ -121,3 +124,40 @@ fn test_polymorphic_container_does_not_accept_other_types() {
assert!(setattr(py.None()).is_err()); assert!(setattr(py.None()).is_err());
assert!(setattr((1i32, 2i32).into_py(py)).is_err()); assert!(setattr((1i32, 2i32).into_py(py)).is_err());
} }
#[test]
fn test_pyref_as_base() {
let gil = Python::acquire_gil();
let py = gil.python();
let cell = PyCell::new(py, (SubClass {}, BaseClass { value: 120 })).unwrap();
// First try PyRefMut
let sub: PyRefMut<SubClass> = cell.borrow_mut();
let mut base: PyRefMut<BaseClass> = sub.into_super();
assert_eq!(120, base.value);
base.value = 999;
assert_eq!(999, base.value);
drop(base);
// Repeat for PyRef
let sub: PyRef<SubClass> = cell.borrow();
let base: PyRef<BaseClass> = sub.into_super();
assert_eq!(999, base.value);
}
#[test]
fn test_pycell_deref() {
let gil = Python::acquire_gil();
let py = gil.python();
let cell = PyCell::new(py, (SubClass {}, BaseClass { value: 120 })).unwrap();
// Should be able to deref as PyAny
assert_eq!(
cell.call_method0("foo")
.and_then(PyAny::extract::<&str>)
.unwrap(),
"SubClass"
);
}

View file

@ -240,7 +240,8 @@ fn inheritance_with_new_methods_with_drop() {
let obj: &PyCell<SubClassWithDrop> = inst.try_into().unwrap(); let obj: &PyCell<SubClassWithDrop> = inst.try_into().unwrap();
let mut obj_ref_mut = obj.borrow_mut(); let mut obj_ref_mut = obj.borrow_mut();
obj_ref_mut.data = Some(Arc::clone(&drop_called1)); obj_ref_mut.data = Some(Arc::clone(&drop_called1));
obj_ref_mut.as_mut().data = Some(Arc::clone(&drop_called2)); let base: &mut BaseClassWithDrop = obj_ref_mut.as_mut();
base.data = Some(Arc::clone(&drop_called2));
} }
assert!(drop_called1.load(Ordering::Relaxed)); assert!(drop_called1.load(Ordering::Relaxed));