Skip to content

Commit

Permalink
Remove PyObject
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Apr 17, 2020
1 parent c897155 commit 1d6a8ea
Show file tree
Hide file tree
Showing 61 changed files with 738 additions and 1,057 deletions.
4 changes: 2 additions & 2 deletions examples/rustapi_module/src/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ fn make_time<'p>(
minute,
second,
microsecond,
tzinfo.map(|o| o.to_object(py)).as_ref(),
tzinfo.map(|o| o.to_object(py)),
)
}

Expand Down Expand Up @@ -135,7 +135,7 @@ fn make_datetime<'p>(
minute,
second,
microsecond,
tzinfo.map(|o| (o.to_object(py))).as_ref(),
tzinfo.map(|o| (o.to_object(py))),
)
}

Expand Down
4 changes: 2 additions & 2 deletions examples/rustapi_module/src/objstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use pyo3::prelude::*;
#[pyclass]
#[derive(Default)]
pub struct ObjStore {
obj: Vec<PyObject>,
obj: Vec<Py<PyAny>>,
}

#[pymethods]
Expand All @@ -14,7 +14,7 @@ impl ObjStore {
}

fn push(&mut self, py: Python, obj: &PyAny) {
self.obj.push(obj.to_object(py));
self.obj.push(obj.to_object(py).into());
}
}

Expand Down
22 changes: 11 additions & 11 deletions guide/src/class.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ impl pyo3::pyclass::PyClass for MyClass {
type BaseNativeType = PyAny;
}

impl pyo3::IntoPy<PyObject> for MyClass {
fn into_py(self, py: pyo3::Python) -> pyo3::PyObject {
impl pyo3::IntoPy<pyo3::Py<pyo3::PyAny>> for MyClass {
fn into_py(self, py: pyo3::Python) -> pyo3::Py<pyo3::PyAny> {
pyo3::IntoPy::into_py(pyo3::Py::new(py, self).unwrap(), py)
}
}
Expand Down Expand Up @@ -498,7 +498,7 @@ impl MyClass {
```

Calls to these methods are protected by the GIL, so both `&self` and `&mut self` can be used.
The return type must be `PyResult<T>` or `T` for some `T` that implements `IntoPy<PyObject>`;
The return type must be `PyResult<T>` or `T` for some `T` that implements `IntoPy<Py<PyAny>>`;
the latter is allowed if the method cannot raise Python exceptions.

A `Python` parameter can be specified as part of method signature, in this case the `py` argument
Expand Down Expand Up @@ -549,13 +549,13 @@ Declares a class method callable from Python.
This may be the type object of a derived class.
* The first parameter implicitly has type `&PyType`.
* For details on `parameter-list`, see the documentation of `Method arguments` section.
* The return type must be `PyResult<T>` or `T` for some `T` that implements `IntoPy<PyObject>`.
* The return type must be `PyResult<T>` or `T` for some `T` that implements `IntoPy<Py<PyAny>>`.

## Static methods

To create a static method for a custom class, the method needs to be annotated with the
`#[staticmethod]` attribute. The return type must be `T` or `PyResult<T>` for some `T` that implements
`IntoPy<PyObject>`.
`IntoPy<Py<PyAny>>`.

```rust
# use pyo3::prelude::*;
Expand Down Expand Up @@ -699,7 +699,7 @@ The [`PyObjectProtocol`] trait provides several basic customizations.

To customize object attribute access, define the following methods:

* `fn __getattr__(&self, name: FromPyObject) -> PyResult<impl IntoPy<PyObject>>`
* `fn __getattr__(&self, name: FromPyObject) -> PyResult<impl IntoPy<Py<PyAny>>>`
* `fn __setattr__(&mut self, name: FromPyObject, value: FromPyObject) -> PyResult<()>`
* `fn __delattr__(&mut self, name: FromPyObject) -> PyResult<()>`

Expand Down Expand Up @@ -764,7 +764,7 @@ use pyo3::gc::{PyGCProtocol, PyVisit};

#[pyclass]
struct ClassWithGCSupport {
obj: Option<PyObject>,
obj: Option<Py<PyAny>>,
}

#[pyproto]
Expand Down Expand Up @@ -805,8 +805,8 @@ struct GCTracked {} // Fails because it does not implement PyGCProtocol
Iterators can be defined using the
[`PyIterProtocol`](https://docs.rs/pyo3/latest/pyo3/class/iter/trait.PyIterProtocol.html) trait.
It includes two methods `__iter__` and `__next__`:
* `fn __iter__(slf: PyRefMut<Self>) -> PyResult<impl IntoPy<PyObject>>`
* `fn __next__(slf: PyRefMut<Self>) -> PyResult<Option<impl IntoPy<PyObject>>>`
* `fn __iter__(slf: PyRefMut<Self>) -> PyResult<impl IntoPy<Py<PyAny>>>`
* `fn __next__(slf: PyRefMut<Self>) -> PyResult<Option<impl IntoPy<Py<PyAny>>>>`

Returning `Ok(None)` from `__next__` indicates that that there are no further items.

Expand All @@ -818,15 +818,15 @@ use pyo3::PyIterProtocol;

#[pyclass]
struct MyIterator {
iter: Box<Iterator<Item = PyObject> + Send>,
iter: Box<Iterator<Item = Py<PyAny>> + Send>,
}

#[pyproto]
impl PyIterProtocol for MyIterator {
fn __iter__(mut slf: PyRefMut<Self>) -> PyResult<Py<MyIterator>> {
Ok(slf.into())
}
fn __next__(mut slf: PyRefMut<Self>) -> PyResult<Option<PyObject>> {
fn __next__(mut slf: PyRefMut<Self>) -> PyResult<Option<Py<PyAny>>> {
Ok(slf.iter.next())
}
}
Expand Down
20 changes: 10 additions & 10 deletions guide/src/conversions.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ and [`PyRefMut`]. They work like the reference wrappers of
## The `ToPyObject` trait

[`ToPyObject`] is a conversion trait that allows various objects to be
converted into [`PyObject`]. `IntoPy<PyObject>` serves the
converted into [`&PyAny`]. `IntoPy<Py<PyAny>>` serves the
same purpose, except that it consumes `self`.


Expand All @@ -48,7 +48,7 @@ use pyo3::types::{PyDict, PyTuple};

struct SomeObject;
impl SomeObject {
fn new(py: Python) -> PyObject {
fn new(py: Python) -> &PyAny {
PyDict::new(py).to_object(py)
}
}
Expand All @@ -64,15 +64,15 @@ fn main() {
let obj = SomeObject::new(py);

// call object without empty arguments
obj.call0(py);
obj.call0();

// call object with PyTuple
let args = PyTuple::new(py, &[arg1, arg2, arg3]);
obj.call1(py, args);
obj.call1(args);

// pass arguments as rust tuple
let args = (arg1, arg2, arg3);
obj.call1(py, args);
obj.call1(args);
}
```

Expand All @@ -89,7 +89,7 @@ use std::collections::HashMap;
struct SomeObject;

impl SomeObject {
fn new(py: Python) -> PyObject {
fn new(py: Python) -> &PyAny {
PyDict::new(py).to_object(py)
}
}
Expand All @@ -107,16 +107,16 @@ fn main() {

// call object with PyDict
let kwargs = [(key1, val1)].into_py_dict(py);
obj.call(py, (), Some(kwargs));
obj.call((), Some(kwargs));

// pass arguments as Vec
let kwargs = vec![(key1, val1), (key2, val2)];
obj.call(py, (), Some(kwargs.into_py_dict(py)));
obj.call((), Some(kwargs.into_py_dict(py)));

// pass arguments as HashMap
let mut kwargs = HashMap::<&str, i32>::new();
kwargs.insert(key1, 1);
obj.call(py, (), Some(kwargs.into_py_dict(py)));
obj.call((), Some(kwargs.into_py_dict(py)));
}
```

Expand All @@ -135,7 +135,7 @@ Eventually, traits such as [`ToPyObject`] will be replaced by this trait and a [
[`FromPy`]: https://docs.rs/pyo3/latest/pyo3/trait.FromPy.html
[`FromPyObject`]: https://docs.rs/pyo3/latest/pyo3/types/trait.FromPyObject.html
[`ToPyObject`]: https://docs.rs/pyo3/latest/pyo3/trait.ToPyObject.html
[`PyObject`]: https://docs.rs/pyo3/latest/pyo3/struct.PyObject.html
[`PyAny`]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html
[`PyTuple`]: https://docs.rs/pyo3/latest/pyo3/types/struct.PyTuple.html
[`ObjectProtocol`]: https://docs.rs/pyo3/latest/pyo3/trait.ObjectProtocol.html
[`IntoPyDict`]: https://docs.rs/pyo3/latest/pyo3/types/trait.IntoPyDict.html
Expand Down
8 changes: 4 additions & 4 deletions guide/src/exception.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ have Rust types as well.
# use pyo3::exceptions;
# use pyo3::prelude::*;
# fn check_for_error() -> bool {false}
fn my_func(arg: PyObject) -> PyResult<()> {
fn my_func(arg: &PyAny) -> PyResult<()> {
if check_for_error() {
Err(exceptions::ValueError::py_err("argument is wrong"))
} else {
Expand Down Expand Up @@ -190,15 +190,15 @@ use pyo3::import_exception;

import_exception!(io, UnsupportedOperation);

fn tell(file: PyObject) -> PyResult<u64> {
fn tell(file: &PyAny) -> PyResult<u64> {
use pyo3::exceptions::*;

let gil = Python::acquire_gil();
let py = gil.python();

match file.call_method0(py, "tell") {
match file.call_method0("tell") {
Err(_) => Err(UnsupportedOperation::py_err("not supported: tell")),
Ok(x) => x.extract::<u64>(py),
Ok(x) => x.extract::<u64>(),
}
}

Expand Down
4 changes: 2 additions & 2 deletions guide/src/function.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,11 @@ You can use [`ObjectProtocol::is_callable`] to check if you have a callable obje

### Calling Rust functions in Python

If you have a static function, you can expose it with `#[pyfunction]` and use [`wrap_pyfunction!`] to get the corresponding [`PyObject`]. For dynamic functions, e.g. lambdas and functions that were passed as arguments, you must put them in some kind of owned container, e.g. a `Box`. (A long-term solution will be a special container similar to wasm-bindgen's `Closure`). You can then use a `#[pyclass]` struct with that container as a field as a way to pass the function over the FFI barrier. You can even make that class callable with `__call__` so it looks like a function in Python code.
If you have a static function, you can expose it with `#[pyfunction]` and use [`wrap_pyfunction!`] to get the corresponding [`PyAny`]. 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
[`PyObject`]: https://docs.rs/pyo3/latest/pyo3/struct.PyObject
[`PyAny`]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny
[`wrap_pyfunction!`]: https://docs.rs/pyo3/latest/pyo3/macro.wrap_pyfunction.html
51 changes: 16 additions & 35 deletions guide/src/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ In PyO3, holding the GIL is modeled by acquiring a token of the type
* It provides some global API for the Python interpreter, such as
[`eval`][eval].
* It can be passed to functions that require a proof of holding the GIL,
such as [`PyObject::clone_ref`][clone_ref].
such as [`Py::clone_ref`][clone_ref].
* Its lifetime can be used to create Rust references that implicitly guarantee
holding the GIL, such as [`&'py PyAny`][PyAny].

Expand All @@ -46,58 +46,39 @@ references is done at runtime using `PyCell`, a scheme very similar to

## Object types

### `PyObject`

**Represents:** a GIL independent reference to a Python object of unspecified
type.
### `PyAny`

**Used:** Whenever you want to carry around references to "some" Python object,
without caring about a GIL lifetime. For example, storing Python object
references in a Rust struct that outlives the Python-Rust FFI boundary,
or returning objects from functions implemented in Rust back to Python.
**Represents:** a Python object of unspecified type, restricted to a GIL
lifetime. Currently, `PyAny` can only ever occur as a reference, usually
`&PyAny`.

Can be cloned using Python reference counts with `.clone_ref()`.
**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 `&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)`

- To `Py<Any>`: `obj.into()`
- To `PyDict`, etc.: `obj.downcast()`
- To rust `Vec<T>` etc.: `obj.extract()`

### `Py<SomeType>`

**Represents:** a GIL independent reference to a Python object of known type.
This can be a Python native type (like `PyTuple`), or a `pyclass` type
implemented in Rust.

**Used:** Like `PyObject`, but with a known inner type.
**Used:** Whenever you want to carry around references to "some" Python object,
without caring about a GIL lifetime. For example, storing Python object
references in a Rust struct that outlives the Python-Rust FFI boundary,
or returning objects from functions implemented in Rust back to Python.

**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.

**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

Expand Down Expand Up @@ -153,7 +134,7 @@ This trait marks structs that mirror native Python types, such as `PyList`.


[eval]: https://docs.rs/pyo3/latest/pyo3/struct.Python.html#method.eval
[clone_ref]: https://docs.rs/pyo3/latest/pyo3/struct.PyObject.html#method.clone_ref
[clone_ref]: https://docs.rs/pyo3/latest/pyo3/struct.Py.html#method.clone_ref
[PyAny]: https://docs.rs/pyo3/latest/pyo3/types/struct.PyAny.html
[PyList_append]: https://docs.rs/pyo3/latest/pyo3/types/struct.PyList.html#method.append
[RefCell]: https://doc.rust-lang.org/std/cell/struct.RefCell.html
5 changes: 2 additions & 3 deletions pyo3-derive-backend/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ pub fn add_fn_to_module(
let wrapper = function_c_wrapper(&func.sig.ident, &spec);

Ok(quote! {
fn #function_wrapper_ident(py: pyo3::Python) -> pyo3::PyObject {
fn #function_wrapper_ident(py: pyo3::Python) -> pyo3::Py<pyo3::PyAny> {
#wrapper

let _def = pyo3::class::PyMethodDef {
Expand All @@ -189,8 +189,7 @@ pub fn add_fn_to_module(
};

let function = unsafe {
pyo3::PyObject::from_owned_ptr_or_panic(
py,
pyo3::Py::from_owned_ptr_or_panic(
pyo3::ffi::PyCFunction_New(
Box::into_raw(Box::new(_def.as_method_def())),
::std::ptr::null_mut()
Expand Down
6 changes: 3 additions & 3 deletions pyo3-derive-backend/src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,11 +365,11 @@ fn impl_class(
quote! { pyo3::PyAny }
};

// If #cls is not extended type, we allow Self->PyObject conversion
// If #cls is not extended type, we allow Self->Py<PyAny> conversion
let into_pyobject = if !attr.has_extends {
quote! {
impl pyo3::IntoPy<PyObject> for #cls {
fn into_py(self, py: pyo3::Python) -> pyo3::PyObject {
impl pyo3::IntoPy<Py<PyAny>> for #cls {
fn into_py(self, py: pyo3::Python) -> pyo3::Py<PyAny> {
pyo3::IntoPy::into_py(pyo3::Py::new(py, self).unwrap(), py)
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/callback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::err::PyResult;
use crate::exceptions::OverflowError;
use crate::ffi::{self, Py_hash_t};
use crate::IntoPyPointer;
use crate::{IntoPy, PyObject, Python};
use crate::{IntoPy, Py, PyAny, Python};
use std::isize;
use std::os::raw::c_int;

Expand Down Expand Up @@ -48,7 +48,7 @@ where

impl<T> IntoPyCallbackOutput<*mut ffi::PyObject> for T
where
T: IntoPy<PyObject>,
T: IntoPy<Py<PyAny>>,
{
fn convert(self, py: Python) -> PyResult<*mut ffi::PyObject> {
Ok(self.into_py(py).into_ptr())
Expand Down
Loading

0 comments on commit 1d6a8ea

Please sign in to comment.