Skip to content

Commit

Permalink
Remove ObjectProtocol; Add methods to PyAny and use Deref
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed May 7, 2020
1 parent 8d28291 commit 38a5edb
Show file tree
Hide file tree
Showing 32 changed files with 582 additions and 640 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ 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)

### 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)
Expand All @@ -31,6 +34,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]
Expand Down
4 changes: 2 additions & 2 deletions guide/src/conversions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down Expand Up @@ -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
Expand Down
10 changes: 5 additions & 5 deletions guide/src/function.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
16 changes: 14 additions & 2 deletions guide/src/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ implemented in Rust.
merged with it in the future.


### `PyAny`
### [`PyAny`]

**Represents:** a Python object of unspecified type, restricted to a GIL
lifetime. Currently, `PyAny` can only ever occur as a reference, usually
Expand All @@ -94,6 +94,9 @@ lifetime. Currently, `PyAny` can only ever occur as a reference, usually
holding the GIL. 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:**

- To `PyObject`: `obj.to_object(py)`
Expand All @@ -108,9 +111,12 @@ lifetime just like `PyAny`.
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:**

- To `PyAny`: `obj.as_ref()`
- To `PyAny`: `obj.as_ref()` or `obj.deref()`
- To `Py<T>`: `Py::from(obj)`


Expand All @@ -124,9 +130,15 @@ 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()`
- To `PyAny`: `.as_ref()` or `.deref()`
- To `PyRef<T>`: `.try_borrow()`
- To `PyRefMut<T>`: `.try_borrow_mut()`


### `PyRef<SomeType>` and `PyRefMut<SomeType>`
Expand Down
1 change: 0 additions & 1 deletion pyo3-derive-backend/src/pymethod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),*
];
Expand Down
3 changes: 0 additions & 3 deletions src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
3 changes: 1 addition & 2 deletions src/class/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
11 changes: 0 additions & 11 deletions src/class/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
1 change: 0 additions & 1 deletion src/class/sequence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
4 changes: 2 additions & 2 deletions src/err.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 0 additions & 1 deletion src/exceptions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down
7 changes: 2 additions & 5 deletions src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -148,30 +147,28 @@ 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();
/// py.eval("[]", None, None).unwrap().to_object(py)
/// };
/// 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();
/// py.eval("[]", None, None).unwrap().to_object(py)
/// };
/// 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;
Expand Down
2 changes: 0 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
Loading

0 comments on commit 38a5edb

Please sign in to comment.