Remove ObjectProtocol; Add methods to PyAny and use Deref
This commit is contained in:
parent
8d282913bc
commit
a5ebef4b2e
|
@ -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.)
|
||||
* `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)
|
||||
* 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
|
||||
* `_PyDict_NewPresized`. [#849](https://github.com/PyO3/pyo3/pull/849)
|
||||
* `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
|
||||
* `__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
|
||||
* `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)
|
||||
* `ObjectProtocol`. [#911](https://github.com/PyO3/pyo3/pull/911)
|
||||
|
||||
|
||||
## [0.9.2]
|
||||
|
|
|
@ -34,7 +34,7 @@ same purpose, except that it consumes `self`.
|
|||
## `*args` and `**kwargs` for Python object calls
|
||||
|
||||
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_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
|
||||
[`PyObject`]: https://docs.rs/pyo3/latest/pyo3/struct.PyObject.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
|
||||
|
||||
[`PyRef`]: https://pyo3.rs/master/doc/pyo3/pycell/struct.PyRef.html
|
||||
|
|
|
@ -177,15 +177,15 @@ Currently, there are no conversions between `Fn`s in Rust and callables in Pytho
|
|||
|
||||
### 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
|
||||
|
||||
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
|
||||
[`ObjectProtocol::call`]: https://docs.rs/pyo3/latest/pyo3/trait.ObjectProtocol.html#tymethod.call
|
||||
[`ObjectProtocol::call0`]: https://docs.rs/pyo3/latest/pyo3/trait.ObjectProtocol.html#tymethod.call0
|
||||
[`ObjectProtocol::call1`]: https://docs.rs/pyo3/latest/pyo3/trait.ObjectProtocol.html#tymethod.call1
|
||||
[`PyAny::is_callable`]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html#tymethod.is_callable
|
||||
[`PyAny::call`]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html#tymethod.call
|
||||
[`PyAny::call0`]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html#tymethod.call0
|
||||
[`PyAny::call1`]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html#tymethod.call1
|
||||
[`PyObject`]: https://docs.rs/pyo3/latest/pyo3/struct.PyObject.html
|
||||
[`wrap_pyfunction!`]: https://docs.rs/pyo3/latest/pyo3/macro.wrap_pyfunction.html
|
||||
|
|
|
@ -46,6 +46,79 @@ references is done at runtime using `PyCell`, a scheme very similar to
|
|||
|
||||
## 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`
|
||||
|
||||
**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:**
|
||||
|
||||
- To `&PyAny`: `obj.as_ref(py)`
|
||||
- To `Py<ConcreteType>`: `obj.as_ref(py).extract::<Py<ConcreteType>>`
|
||||
- To `&ConcreteType` (which must be a Python native type): `obj.cast_as(py)`
|
||||
```rust
|
||||
# use pyo3::prelude::*;
|
||||
# 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>`
|
||||
|
||||
|
@ -75,45 +160,25 @@ implemented in Rust.
|
|||
|
||||
**Conversions:**
|
||||
|
||||
- To `PyObject`: `obj.to_object(py)`
|
||||
- To `&SomeType` or `&PyCell<SomeType>`: `obj.as_ref(py)`. For `pyclass` types
|
||||
implemented in Rust, you get a `PyCell` (see below). For Python native types,
|
||||
mutating operations through PyO3's API don't require `&mut` access.
|
||||
```rust
|
||||
# use pyo3::prelude::*;
|
||||
# use pyo3::types::PyList;
|
||||
# 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
|
||||
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>`
|
||||
|
||||
**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
|
||||
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:**
|
||||
|
||||
- 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>`
|
||||
|
||||
|
|
|
@ -414,7 +414,6 @@ pub fn impl_arg_params(spec: &FnSpec<'_>, body: TokenStream) -> TokenStream {
|
|||
let num_normal_params = params.len();
|
||||
// create array of arguments, and then parse
|
||||
quote! {{
|
||||
use pyo3::ObjectProtocol;
|
||||
const PARAMS: &'static [pyo3::derive_utils::ParamDescription] = &[
|
||||
#(#params),*
|
||||
];
|
||||
|
|
|
@ -661,9 +661,6 @@ mod test {
|
|||
use crate::ffi;
|
||||
use crate::Python;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use crate::objectprotocol::ObjectProtocol;
|
||||
|
||||
#[test]
|
||||
fn test_compatible_size() {
|
||||
// for the cast in PyBuffer::shape()
|
||||
|
|
|
@ -11,8 +11,7 @@
|
|||
use crate::callback::HashCallbackOutput;
|
||||
use crate::class::methods::PyMethodDef;
|
||||
use crate::{
|
||||
exceptions, ffi, FromPyObject, IntoPy, ObjectProtocol, PyAny, PyCell, PyClass, PyErr, PyObject,
|
||||
PyResult,
|
||||
exceptions, ffi, FromPyObject, IntoPy, PyAny, PyCell, PyClass, PyErr, PyObject, PyResult,
|
||||
};
|
||||
use std::os::raw::c_int;
|
||||
|
||||
|
|
|
@ -66,7 +66,6 @@ macro_rules! py_binary_func {
|
|||
where
|
||||
T: for<'p> $trait<'p>,
|
||||
{
|
||||
use $crate::ObjectProtocol;
|
||||
$crate::callback_body!(py, {
|
||||
let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
|
||||
let arg = py.from_borrowed_ptr::<$crate::PyAny>(arg);
|
||||
|
@ -94,7 +93,6 @@ macro_rules! py_binary_num_func {
|
|||
where
|
||||
T: for<'p> $trait<'p>,
|
||||
{
|
||||
use $crate::ObjectProtocol;
|
||||
$crate::callback_body!(py, {
|
||||
let lhs = py.from_borrowed_ptr::<$crate::PyAny>(lhs);
|
||||
let rhs = py.from_borrowed_ptr::<$crate::PyAny>(rhs);
|
||||
|
@ -117,7 +115,6 @@ macro_rules! py_binary_reverse_num_func {
|
|||
where
|
||||
T: for<'p> $trait<'p>,
|
||||
{
|
||||
use $crate::ObjectProtocol;
|
||||
$crate::callback_body!(py, {
|
||||
// Swap lhs <-> rhs
|
||||
let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(rhs);
|
||||
|
@ -142,7 +139,6 @@ macro_rules! py_binary_self_func {
|
|||
where
|
||||
T: for<'p> $trait<'p>,
|
||||
{
|
||||
use $crate::ObjectProtocol;
|
||||
$crate::callback_body!(py, {
|
||||
let slf_ = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
|
||||
let arg = py.from_borrowed_ptr::<$crate::PyAny>(arg);
|
||||
|
@ -191,7 +187,6 @@ macro_rules! py_ternary_func {
|
|||
where
|
||||
T: for<'p> $trait<'p>,
|
||||
{
|
||||
use $crate::ObjectProtocol;
|
||||
$crate::callback_body!(py, {
|
||||
let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
|
||||
let arg1 = py
|
||||
|
@ -224,7 +219,6 @@ macro_rules! py_ternary_num_func {
|
|||
where
|
||||
T: for<'p> $trait<'p>,
|
||||
{
|
||||
use $crate::ObjectProtocol;
|
||||
$crate::callback_body!(py, {
|
||||
let arg1 = py
|
||||
.from_borrowed_ptr::<$crate::types::PyAny>(arg1)
|
||||
|
@ -256,7 +250,6 @@ macro_rules! py_ternary_reverse_num_func {
|
|||
where
|
||||
T: for<'p> $trait<'p>,
|
||||
{
|
||||
use $crate::ObjectProtocol;
|
||||
$crate::callback_body!(py, {
|
||||
// Swap lhs <-> rhs
|
||||
let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(arg2);
|
||||
|
@ -284,7 +277,6 @@ macro_rules! py_dummy_ternary_self_func {
|
|||
where
|
||||
T: for<'p> $trait<'p>,
|
||||
{
|
||||
use $crate::ObjectProtocol;
|
||||
$crate::callback_body!(py, {
|
||||
let slf_cell = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
|
||||
let arg1 = py.from_borrowed_ptr::<$crate::PyAny>(arg1);
|
||||
|
@ -307,7 +299,6 @@ macro_rules! py_func_set {
|
|||
where
|
||||
T: for<'p> $trait_name<'p>,
|
||||
{
|
||||
use $crate::ObjectProtocol;
|
||||
$crate::callback_body!(py, {
|
||||
let slf = py.from_borrowed_ptr::<$crate::PyCell<$generic>>(slf);
|
||||
|
||||
|
@ -340,7 +331,6 @@ macro_rules! py_func_del {
|
|||
where
|
||||
U: for<'p> $trait_name<'p>,
|
||||
{
|
||||
use $crate::ObjectProtocol;
|
||||
$crate::callback_body!(py, {
|
||||
if value.is_null() {
|
||||
let slf = py.from_borrowed_ptr::<$crate::PyCell<U>>(slf);
|
||||
|
@ -370,7 +360,6 @@ macro_rules! py_func_set_del {
|
|||
where
|
||||
T: for<'p> $trait1<'p> + for<'p> $trait2<'p>,
|
||||
{
|
||||
use $crate::ObjectProtocol;
|
||||
$crate::callback_body!(py, {
|
||||
let slf = py.from_borrowed_ptr::<$crate::PyCell<$generic>>(slf);
|
||||
let name = py.from_borrowed_ptr::<$crate::PyAny>(name);
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
use crate::conversion::{FromPyObject, IntoPy};
|
||||
use crate::err::{PyErr, PyResult};
|
||||
use crate::objectprotocol::ObjectProtocol;
|
||||
use crate::{exceptions, ffi, PyAny, PyCell, PyClass, PyObject};
|
||||
use std::os::raw::c_int;
|
||||
|
||||
|
|
|
@ -5,8 +5,8 @@ use crate::type_object::PyTypeObject;
|
|||
use crate::types::PyType;
|
||||
use crate::{exceptions, ffi};
|
||||
use crate::{
|
||||
AsPyPointer, FromPy, FromPyPointer, IntoPy, IntoPyPointer, ObjectProtocol, Py, PyAny, PyObject,
|
||||
Python, ToBorrowedObject, ToPyObject,
|
||||
AsPyPointer, FromPy, FromPyPointer, IntoPy, IntoPyPointer, Py, PyAny, PyObject, Python,
|
||||
ToBorrowedObject, ToPyObject,
|
||||
};
|
||||
use libc::c_int;
|
||||
use std::ffi::CString;
|
||||
|
|
|
@ -354,7 +354,6 @@ pub mod socket {
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::exceptions::Exception;
|
||||
use crate::objectprotocol::ObjectProtocol;
|
||||
use crate::types::{IntoPyDict, PyDict};
|
||||
use crate::{PyErr, Python};
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
use crate::err::{PyErr, PyResult};
|
||||
use crate::gil;
|
||||
use crate::object::PyObject;
|
||||
use crate::objectprotocol::ObjectProtocol;
|
||||
use crate::type_object::PyBorrowFlagLayout;
|
||||
use crate::{
|
||||
ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyAny, PyCell, PyClass,
|
||||
|
@ -148,7 +147,6 @@ impl<T> Py<T> {
|
|||
/// `PyObject::as_ref` returns `&PyAny`.
|
||||
/// ```
|
||||
/// # use pyo3::prelude::*;
|
||||
/// use pyo3::ObjectProtocol;
|
||||
/// let obj: PyObject = {
|
||||
/// let gil = Python::acquire_gil();
|
||||
/// let py = gil.python();
|
||||
|
@ -156,14 +154,13 @@ impl<T> Py<T> {
|
|||
/// };
|
||||
/// let gil = Python::acquire_gil();
|
||||
/// 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>`
|
||||
/// for `#[pyclass]`.
|
||||
/// ```
|
||||
/// # use pyo3::prelude::*;
|
||||
/// use pyo3::ObjectProtocol;
|
||||
/// let obj: PyObject = {
|
||||
/// let gil = Python::acquire_gil();
|
||||
/// let py = gil.python();
|
||||
|
@ -171,7 +168,7 @@ impl<T> Py<T> {
|
|||
/// };
|
||||
/// let gil = Python::acquire_gil();
|
||||
/// 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 {
|
||||
type Target;
|
||||
|
|
|
@ -142,7 +142,6 @@ pub use crate::err::{PyDowncastError, PyErr, PyErrArguments, PyErrValue, PyResul
|
|||
pub use crate::gil::{GILGuard, GILPool};
|
||||
pub use crate::instance::{AsPyRef, ManagedPyRef, Py, PyNativeType};
|
||||
pub use crate::object::PyObject;
|
||||
pub use crate::objectprotocol::ObjectProtocol;
|
||||
pub use crate::pycell::{PyCell, PyRef, PyRefMut};
|
||||
pub use crate::pyclass::PyClass;
|
||||
pub use crate::pyclass_init::PyClassInitializer;
|
||||
|
@ -187,7 +186,6 @@ mod instance;
|
|||
mod internal_tricks;
|
||||
pub mod marshal;
|
||||
mod object;
|
||||
mod objectprotocol;
|
||||
pub mod panic;
|
||||
pub mod prelude;
|
||||
pub mod pycell;
|
||||
|
@ -338,6 +336,7 @@ pub mod doc_test {
|
|||
doctest!("../guide/src/exception.md", guide_exception_md);
|
||||
doctest!("../guide/src/function.md", guide_function_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/python_from_rust.md",
|
||||
|
@ -346,5 +345,5 @@ pub mod doc_test {
|
|||
doctest!("../guide/src/parallelism.md", guide_parallelism_md);
|
||||
doctest!("../guide/src/pypy.md", guide_pypy_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);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -14,7 +14,6 @@ pub use crate::err::{PyErr, PyResult};
|
|||
pub use crate::gil::GILGuard;
|
||||
pub use crate::instance::{AsPyRef, Py};
|
||||
pub use crate::object::PyObject;
|
||||
pub use crate::objectprotocol::ObjectProtocol;
|
||||
pub use crate::pycell::{PyCell, PyRef, PyRefMut};
|
||||
pub use crate::pyclass_init::PyClassInitializer;
|
||||
pub use crate::python::Python;
|
||||
|
|
|
@ -3,6 +3,7 @@ use crate::conversion::{AsPyPointer, FromPyPointer, ToPyObject};
|
|||
use crate::pyclass_init::PyClassInitializer;
|
||||
use crate::pyclass_slots::{PyClassDict, PyClassWeakRef};
|
||||
use crate::type_object::{PyBorrowFlagLayout, PyLayout, PySizedLayout, PyTypeInfo};
|
||||
use crate::types::PyAny;
|
||||
use crate::{ffi, FromPy, PyClass, PyErr, PyNativeType, PyObject, PyResult, Python};
|
||||
use std::cell::{Cell, UnsafeCell};
|
||||
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)
|
||||
/// 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
|
||||
///
|
||||
/// 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> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.try_borrow() {
|
||||
|
|
|
@ -204,7 +204,7 @@ impl<'p> Python<'p> {
|
|||
) -> PyResult<()> {
|
||||
let res = self.run_code(code, ffi::Py_file_input, globals, locals);
|
||||
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)]
|
||||
mod test {
|
||||
use crate::objectprotocol::ObjectProtocol;
|
||||
use crate::types::{IntoPyDict, PyAny, PyBool, PyInt, PyList};
|
||||
use crate::Python;
|
||||
|
||||
|
|
452
src/types/any.rs
452
src/types/any.rs
|
@ -1,7 +1,14 @@
|
|||
use crate::conversion::{AsPyPointer, PyTryFrom};
|
||||
use crate::err::PyDowncastError;
|
||||
use crate::ffi;
|
||||
use crate::class::basic::CompareOp;
|
||||
use crate::conversion::{
|
||||
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::cmp::Ordering;
|
||||
|
||||
/// A Python object with GIL lifetime
|
||||
///
|
||||
|
@ -59,10 +66,449 @@ pyobject_native_type_convert!(
|
|||
pyobject_native_type_extract!(PyAny);
|
||||
|
||||
impl PyAny {
|
||||
/// Convert this PyAny to a concrete Python type.
|
||||
pub fn downcast<T>(&self) -> Result<&T, PyDowncastError>
|
||||
where
|
||||
for<'py> T: PyTryFrom<'py>,
|
||||
{
|
||||
<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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,7 +59,6 @@ impl<'source> FromPyObject<'source> for bool {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::objectprotocol::ObjectProtocol;
|
||||
use crate::types::{PyAny, PyBool};
|
||||
use crate::Python;
|
||||
use crate::ToPyObject;
|
||||
|
|
|
@ -96,7 +96,7 @@ impl PyDict {
|
|||
///
|
||||
/// 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>
|
||||
where
|
||||
K: ToBorrowedObject,
|
||||
|
@ -361,8 +361,8 @@ mod test {
|
|||
use crate::instance::AsPyRef;
|
||||
use crate::types::dict::IntoPyDict;
|
||||
use crate::types::{PyDict, PyList, PyTuple};
|
||||
use crate::PyObject;
|
||||
use crate::Python;
|
||||
use crate::{ObjectProtocol, PyObject};
|
||||
use crate::{PyTryFrom, ToPyObject};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
//
|
||||
// based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython
|
||||
use crate::{
|
||||
ffi, AsPyPointer, FromPy, FromPyObject, ObjectProtocol, PyAny, PyErr, PyNativeType, PyObject,
|
||||
PyResult, Python, ToPyObject,
|
||||
ffi, AsPyPointer, FromPy, FromPyObject, PyAny, PyErr, PyNativeType, PyObject, PyResult, Python,
|
||||
ToPyObject,
|
||||
};
|
||||
use std::os::raw::c_double;
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ use crate::{ffi, AsPyPointer, PyAny, PyDowncastError, PyErr, PyNativeType, PyRes
|
|||
/// let gil = Python::acquire_gil();
|
||||
/// let py = gil.python();
|
||||
/// 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();
|
||||
/// assert_eq!(sum, 10);
|
||||
/// # Ok(())
|
||||
|
@ -87,7 +87,6 @@ impl<'p> Drop for PyIterator<'p> {
|
|||
mod tests {
|
||||
use crate::gil::GILPool;
|
||||
use crate::instance::AsPyRef;
|
||||
use crate::objectprotocol::ObjectProtocol;
|
||||
use crate::types::{PyDict, PyList};
|
||||
use crate::GILGuard;
|
||||
use crate::Python;
|
||||
|
|
|
@ -226,7 +226,6 @@ where
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::instance::AsPyRef;
|
||||
use crate::objectprotocol::ObjectProtocol;
|
||||
use crate::types::PyList;
|
||||
use crate::Python;
|
||||
use crate::{IntoPy, PyObject, PyTryFrom, ToPyObject};
|
||||
|
|
|
@ -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 {}
|
||||
|
||||
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)
|
||||
-> Result<(), ::std::fmt::Error>
|
||||
{
|
||||
use $crate::ObjectProtocol;
|
||||
let s = self.repr().map_err(|_| ::std::fmt::Error)?;
|
||||
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)
|
||||
-> Result<(), ::std::fmt::Error>
|
||||
{
|
||||
use $crate::ObjectProtocol;
|
||||
let s = self.str().map_err(|_| ::std::fmt::Error)?;
|
||||
f.write_str(&s.to_string_lossy())
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ use crate::exceptions;
|
|||
use crate::ffi;
|
||||
use crate::instance::PyNativeType;
|
||||
use crate::object::PyObject;
|
||||
use crate::objectprotocol::ObjectProtocol;
|
||||
use crate::pyclass::PyClass;
|
||||
use crate::type_object::PyTypeObject;
|
||||
use crate::types::PyTuple;
|
||||
|
|
|
@ -38,7 +38,7 @@ macro_rules! int_fits_larger_int {
|
|||
|
||||
impl<'source> FromPyObject<'source> for $rust_type {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ use crate::err::{self, PyDowncastError, PyErr, PyResult};
|
|||
use crate::exceptions;
|
||||
use crate::ffi::{self, Py_ssize_t};
|
||||
use crate::instance::PyNativeType;
|
||||
use crate::objectprotocol::ObjectProtocol;
|
||||
use crate::types::{PyAny, PyList, PyTuple};
|
||||
use crate::AsPyPointer;
|
||||
use crate::{FromPyObject, PyTryFrom, ToBorrowedObject};
|
||||
|
@ -385,7 +384,6 @@ impl<'v> PyTryFrom<'v> for PySequence {
|
|||
mod test {
|
||||
use crate::instance::AsPyRef;
|
||||
use crate::object::PyObject;
|
||||
use crate::objectprotocol::ObjectProtocol;
|
||||
use crate::types::PySequence;
|
||||
use crate::AsPyPointer;
|
||||
use crate::Python;
|
||||
|
|
|
@ -303,7 +303,7 @@ impl<'a> std::iter::IntoIterator for &'a PyFrozenSet {
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
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::iter::FromIterator;
|
||||
|
||||
|
|
|
@ -255,4 +255,22 @@ mod test {
|
|||
let py_string = <PyString as PyTryFrom>::try_from(obj.as_ref(py)).unwrap();
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -247,7 +247,7 @@ tuple_conversion!(
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::types::{PyAny, PyTuple};
|
||||
use crate::{AsPyRef, ObjectProtocol, PyTryFrom, Python, ToPyObject};
|
||||
use crate::{AsPyRef, PyTryFrom, Python, ToPyObject};
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use pyo3::prelude::*;
|
||||
use pyo3::{ObjectProtocol, ToPyObject};
|
||||
use pyo3::ToPyObject;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
|
@ -31,7 +31,10 @@ fn test_cloneable_pyclass() {
|
|||
}
|
||||
|
||||
#[pyclass]
|
||||
struct BaseClass {}
|
||||
#[derive(Default)]
|
||||
struct BaseClass {
|
||||
value: i32,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl BaseClass {
|
||||
|
@ -64,7 +67,7 @@ fn test_polymorphic_container_stores_base_class() {
|
|||
let p = PyCell::new(
|
||||
py,
|
||||
PolymorphicContainer {
|
||||
inner: Py::new(py, BaseClass {}).unwrap(),
|
||||
inner: Py::new(py, BaseClass::default()).unwrap(),
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
|
@ -81,7 +84,7 @@ fn test_polymorphic_container_stores_sub_class() {
|
|||
let p = PyCell::new(
|
||||
py,
|
||||
PolymorphicContainer {
|
||||
inner: Py::new(py, BaseClass {}).unwrap(),
|
||||
inner: Py::new(py, BaseClass::default()).unwrap(),
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
|
@ -92,7 +95,7 @@ fn test_polymorphic_container_stores_sub_class() {
|
|||
"inner",
|
||||
PyCell::new(
|
||||
py,
|
||||
PyClassInitializer::from(BaseClass {}).add_subclass(SubClass {}),
|
||||
PyClassInitializer::from(BaseClass::default()).add_subclass(SubClass {}),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
|
@ -109,7 +112,7 @@ fn test_polymorphic_container_does_not_accept_other_types() {
|
|||
let p = PyCell::new(
|
||||
py,
|
||||
PolymorphicContainer {
|
||||
inner: Py::new(py, BaseClass {}).unwrap(),
|
||||
inner: Py::new(py, BaseClass::default()).unwrap(),
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
|
@ -121,3 +124,40 @@ fn test_polymorphic_container_does_not_accept_other_types() {
|
|||
assert!(setattr(py.None()).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"
|
||||
);
|
||||
}
|
||||
|
|
|
@ -240,7 +240,8 @@ fn inheritance_with_new_methods_with_drop() {
|
|||
let obj: &PyCell<SubClassWithDrop> = inst.try_into().unwrap();
|
||||
let mut obj_ref_mut = obj.borrow_mut();
|
||||
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));
|
||||
|
|
Loading…
Reference in a new issue