Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove ObjectProtocol to make usage more Pythonic #892

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
* When the GIL is held, the refcount is now decreased immediately on drop. (Previously would wait until just before releasing the GIL.)
* 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)
* The trait `ObjectProtocol` has been removed, and all the methods from the trait have been moved to `PyAny`. [#892](https://github.com/PyO3/pyo3/pull/892)

### 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)
* All builtin types (PyList, PyTuple, PyDict) etc. now implement `Deref` for `PyAny`. [#892](https://github.com/PyO3/pyo3/pull/892)
* `pyo3::builtin_methods` module for Python builtin methods such as `repr()`, `str()` etc. [#892](https://github.com/PyO3/pyo3/pull/892)

### Fixed
* `__radd__` and other `__r*__` methods now correctly work with operators. [#839](https://github.com/PyO3/pyo3/pull/839)
Expand All @@ -26,6 +29,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)
* `ObjectProtocol`. [#892](https://github.com/PyO3/pyo3/pull/892)\


## [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/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
[`wrap_pyfunction!`]: https://docs.rs/pyo3/latest/pyo3/macro.wrap_pyfunction.html
10 changes: 8 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` to `PyAny`, so they all have access to the same
convenience 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 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 @@ -445,7 +445,6 @@ fn impl_arg_params_(spec: &FnSpec<'_>, body: TokenStream, into_result: TokenStre
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
65 changes: 65 additions & 0 deletions src/builtin_methods.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use crate::conversion::AsPyPointer;
use crate::err::PyErr;
use crate::instance::PyNativeType;
use crate::types::PyString;
use crate::{ffi, PyAny, PyResult};

/// Computes the "repr" representation of obj.
///
/// This is equivalent to the Python expression `repr(obj)`.
pub fn repr(obj: &PyAny) -> PyResult<&PyString> {
unsafe {
obj.py()
.from_owned_ptr_or_err(ffi::PyObject_Repr(obj.as_ptr()))
}
}

/// Computes the "str" representation of obj.
///
/// This is equivalent to the Python expression `str(obj)`.
pub fn str(obj: &PyAny) -> PyResult<&PyString> {
unsafe {
obj.py()
.from_owned_ptr_or_err(ffi::PyObject_Str(obj.as_ptr()))
}
}

/// Retrieves the hash code of the object.
///
/// This is equivalent to the Python expression `hash(obi)`.
pub fn hash(obj: &PyAny) -> PyResult<isize> {
let v = unsafe { ffi::PyObject_Hash(obj.as_ptr()) };
if v == -1 {
Err(PyErr::fetch(obj.py()))
} else {
Ok(v)
}
}

/// Returns the length of the sequence or mapping.
///
/// This is equivalent to the Python expression `len(obj)`.
pub fn len(obj: &PyAny) -> PyResult<usize> {
let v = unsafe { ffi::PyObject_Size(obj.as_ptr()) };
if v == -1 {
Err(PyErr::fetch(obj.py()))
} else {
Ok(v as usize)
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::types::PyList;
use crate::Python;

#[test]
fn test_list_len() {
let gil = Python::acquire_gil();
let py = gil.python();

let list = PyList::new(py, &[1, 2, 3]);
assert_eq!(len(list).unwrap(), 3);
}
}
4 changes: 2 additions & 2 deletions src/class/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
use crate::callback::HashCallbackOutput;
use crate::class::methods::PyMethodDef;
use crate::{
callback, exceptions, ffi, run_callback, FromPyObject, GILPool, IntoPy, ObjectProtocol, PyAny,
PyCell, PyClass, PyErr, PyObject, PyResult,
callback, exceptions, ffi, run_callback, FromPyObject, GILPool, IntoPy, PyAny, PyCell, PyClass,
PyErr, PyObject, PyResult,
};
use std::os::raw::c_int;

Expand Down
18 changes: 0 additions & 18 deletions src/class/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ macro_rules! py_binary_func {
where
T: for<'p> $trait<'p>,
{
use $crate::ObjectProtocol;
let pool = $crate::GILPool::new();
let py = pool.python();
$crate::run_callback(py, || {
Expand Down Expand Up @@ -100,7 +99,6 @@ macro_rules! py_binary_num_func {
where
T: for<'p> $trait<'p>,
{
use $crate::ObjectProtocol;
let pool = $crate::GILPool::new();
let py = pool.python();
$crate::run_callback(py, || {
Expand All @@ -126,7 +124,6 @@ macro_rules! py_binary_reverse_num_func {
where
T: for<'p> $trait<'p>,
{
use $crate::ObjectProtocol;
let pool = $crate::GILPool::new();
let py = pool.python();
$crate::run_callback(py, || {
Expand Down Expand Up @@ -155,8 +152,6 @@ macro_rules! py_binary_self_func {
where
T: for<'p> $trait<'p>,
{
use $crate::ObjectProtocol;

let pool = $crate::GILPool::new();
let py = pool.python();
$crate::run_callback(py, || {
Expand Down Expand Up @@ -209,8 +204,6 @@ macro_rules! py_ternary_func {
where
T: for<'p> $trait<'p>,
{
use $crate::ObjectProtocol;

let pool = $crate::GILPool::new();
let py = pool.python();
$crate::run_callback(py, || {
Expand Down Expand Up @@ -245,8 +238,6 @@ macro_rules! py_ternary_num_func {
where
T: for<'p> $trait<'p>,
{
use $crate::ObjectProtocol;

let pool = $crate::GILPool::new();
let py = pool.python();
$crate::run_callback(py, || {
Expand Down Expand Up @@ -281,7 +272,6 @@ macro_rules! py_ternary_reverse_num_func {
where
T: for<'p> $trait<'p>,
{
use $crate::ObjectProtocol;
let pool = $crate::GILPool::new();
let py = pool.python();
$crate::run_callback(py, || {
Expand Down Expand Up @@ -312,8 +302,6 @@ macro_rules! py_dummy_ternary_self_func {
where
T: for<'p> $trait<'p>,
{
use $crate::ObjectProtocol;

let pool = $crate::GILPool::new();
let py = pool.python();
$crate::run_callback(py, || {
Expand All @@ -338,8 +326,6 @@ macro_rules! py_func_set {
where
T: for<'p> $trait_name<'p>,
{
use $crate::ObjectProtocol;

let pool = $crate::GILPool::new();
let py = pool.python();
$crate::run_callback(py, || {
Expand Down Expand Up @@ -374,8 +360,6 @@ macro_rules! py_func_del {
where
U: for<'p> $trait_name<'p>,
{
use $crate::ObjectProtocol;

let pool = $crate::GILPool::new();
let py = pool.python();
$crate::run_callback(py, || {
Expand Down Expand Up @@ -407,8 +391,6 @@ macro_rules! py_func_set_del {
where
T: for<'p> $trait1<'p> + for<'p> $trait2<'p>,
{
use $crate::ObjectProtocol;

let pool = $crate::GILPool::new();
let py = pool.python();
$crate::run_callback(py, || {
Expand Down
1 change: 0 additions & 1 deletion src/class/sequence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
use crate::conversion::{FromPyObject, IntoPy};
use crate::err::{PyErr, PyResult};
use crate::gil::GILPool;
use crate::objectprotocol::ObjectProtocol;
use crate::{callback, exceptions, ffi, run_callback, PyAny, PyCell, PyClass, PyObject};
use std::os::raw::c_int;

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!(pyo3::len(obj.as_ref(py)).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!(pyo3::len(obj.as_ref(py)).unwrap(), 0);
/// ```
pub trait AsPyRef: Sized {
type Target;
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@
//! }
//! ```

pub use crate::builtin_methods::*;
pub use crate::callback::run_callback;
pub use crate::class::*;
pub use crate::conversion::{
Expand All @@ -143,7 +144,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 All @@ -169,6 +169,7 @@ pub use libc;
pub use unindent;

pub mod buffer;
pub mod builtin_methods;
#[doc(hidden)]
pub mod callback;
pub mod class;
Expand All @@ -188,7 +189,6 @@ mod instance;
mod internal_tricks;
pub mod marshal;
mod object;
mod objectprotocol;
pub mod prelude;
pub mod pycell;
pub mod pyclass;
Expand Down
Loading