Skip to content
Merged
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
7 changes: 0 additions & 7 deletions guide/src/class.md
Original file line number Diff line number Diff line change
Expand Up @@ -1410,13 +1410,6 @@ impl<'a, 'py> pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'py, false>
}
}

#[allow(deprecated)]
impl pyo3::IntoPy<PyObject> for MyClass {
fn into_py(self, py: pyo3::Python<'_>) -> pyo3::PyObject {
pyo3::IntoPy::into_py(pyo3::Py::new(py, self).unwrap(), py)
}
}

impl pyo3::impl_::pyclass::PyClassImpl for MyClass {
const IS_BASETYPE: bool = false;
const IS_SUBCLASS: bool = false;
Expand Down
48 changes: 0 additions & 48 deletions guide/src/conversions/traits.md
Original file line number Diff line number Diff line change
Expand Up @@ -738,55 +738,7 @@ let vec_of_pyobjs: Vec<Py<PyAny>> = Python::with_gil(|py| {

In the example above we used `BoundObject::into_any` and `BoundObject::unbind` to manipulate the python types and smart pointers into the result type we wanted to produce from the function.

### `IntoPy<T>`

<div class="warning">

⚠️ Warning: API update in progress 🛠️

PyO3 0.23 has introduced `IntoPyObject` as the new trait for to-python conversions. While `#[pymethods]` and `#[pyfunction]` contain a compatibility layer to allow `IntoPy<PyObject>` as a return type, all Python API have been migrated to use `IntoPyObject`. To migrate implement `IntoPyObject` for your type.
</div>


This trait defines the to-python conversion for a Rust type. It is usually implemented as
`IntoPy<PyObject>`, which is the trait needed for returning a value from `#[pyfunction]` and
`#[pymethods]`.

All types in PyO3 implement this trait, as does a `#[pyclass]` which doesn't use `extends`.

Occasionally you may choose to implement this for custom types which are mapped to Python types
_without_ having a unique python type.

```rust,no_run
use pyo3::prelude::*;
# #[allow(dead_code)]
struct MyPyObjectWrapper(PyObject);

#[allow(deprecated)]
impl IntoPy<PyObject> for MyPyObjectWrapper {
fn into_py(self, py: Python<'_>) -> PyObject {
self.0
}
}
```

### The `ToPyObject` trait

<div class="warning">

⚠️ Warning: API update in progress 🛠️

PyO3 0.23 has introduced `IntoPyObject` as the new trait for to-python conversions. To migrate
implement `IntoPyObject` on a reference of your type (`impl<'py> IntoPyObject<'py> for &Type { ... }`).
</div>

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

[`IntoPy`]: {{#PYO3_DOCS_URL}}/pyo3/conversion/trait.IntoPy.html
[`FromPyObject`]: {{#PYO3_DOCS_URL}}/pyo3/conversion/trait.FromPyObject.html
[`ToPyObject`]: {{#PYO3_DOCS_URL}}/pyo3/conversion/trait.ToPyObject.html
[`IntoPyObject`]: {{#PYO3_DOCS_URL}}/pyo3/conversion/trait.IntoPyObject.html
[`IntoPyObjectExt`]: {{#PYO3_DOCS_URL}}/pyo3/conversion/trait.IntoPyObjectExt.html
[`PyObject`]: {{#PYO3_DOCS_URL}}/pyo3/type.PyObject.html
Expand Down
6 changes: 3 additions & 3 deletions guide/src/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -1237,7 +1237,7 @@ Python::with_gil(|py| {

After, some type annotations may be necessary:

```rust
```rust,ignore
# #![allow(deprecated)]
# use pyo3::prelude::*;
#
Expand Down Expand Up @@ -1712,7 +1712,7 @@ impl FromPy<MyPyObjectWrapper> for PyObject {
```

After
```rust,no_run
```rust,ignore
# use pyo3::prelude::*;
# #[allow(dead_code)]
struct MyPyObjectWrapper(PyObject);
Expand All @@ -1736,7 +1736,7 @@ let obj = PyObject::from_py(1.234, py);
```

After:
```rust,no_run
```rust,ignore
# #![allow(deprecated)]
# use pyo3::prelude::*;
# Python::with_gil(|py| {
Expand Down
1 change: 1 addition & 0 deletions newsfragments/5010.removed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Remove deprecated `IntoPy` and `ToPyObject` traits
19 changes: 0 additions & 19 deletions pyo3-benches/benches/bench_intopyobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,30 +46,13 @@ fn byte_slice_into_pyobject_large(b: &mut Bencher<'_>) {
bench_bytes_into_pyobject(b, &data);
}

#[allow(deprecated)]
fn byte_slice_into_py(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
let data = (0..u8::MAX).collect::<Vec<u8>>();
let bytes = data.as_slice();
b.iter_with_large_drop(|| black_box(bytes).into_py(py));
});
}

fn vec_into_pyobject(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
let bytes = (0..u8::MAX).collect::<Vec<u8>>();
b.iter_with_large_drop(|| black_box(&bytes).clone().into_pyobject(py));
});
}

#[allow(deprecated)]
fn vec_into_py(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
let bytes = (0..u8::MAX).collect::<Vec<u8>>();
b.iter_with_large_drop(|| black_box(&bytes).clone().into_py(py));
});
}

fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("bytes_new_small", bytes_new_small);
c.bench_function("bytes_new_medium", bytes_new_medium);
Expand All @@ -86,9 +69,7 @@ fn criterion_benchmark(c: &mut Criterion) {
"byte_slice_into_pyobject_large",
byte_slice_into_pyobject_large,
);
c.bench_function("byte_slice_into_py", byte_slice_into_py);
c.bench_function("vec_into_pyobject", vec_into_pyobject);
c.bench_function("vec_into_py", vec_into_py);
}

criterion_group!(benches, criterion_benchmark);
Expand Down
8 changes: 0 additions & 8 deletions pyo3-benches/benches/bench_tuple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,6 @@ fn tuple_to_list(b: &mut Bencher<'_>) {
});
}

#[allow(deprecated)]
fn tuple_into_py(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
b.iter(|| -> PyObject { (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12).into_py(py) });
});
}

fn tuple_into_pyobject(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
b.iter(|| {
Expand Down Expand Up @@ -175,7 +168,6 @@ fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("sequence_from_tuple", sequence_from_tuple);
c.bench_function("tuple_new_list", tuple_new_list);
c.bench_function("tuple_to_list", tuple_to_list);
c.bench_function("tuple_into_py", tuple_into_py);
c.bench_function("tuple_into_pyobject", tuple_into_pyobject);
}

Expand Down
37 changes: 0 additions & 37 deletions pyo3-macros-backend/src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1029,35 +1029,6 @@ fn impl_complex_enum(
)
.doc(doc);

// Need to customize the into_py impl so that it returns the variant PyClass
let enum_into_py_impl = {
let match_arms: Vec<TokenStream> = variants
.iter()
.map(|variant| {
let variant_ident = variant.get_ident();
let variant_cls = gen_complex_enum_variant_class_ident(cls, variant.get_ident());
quote! {
#cls::#variant_ident { .. } => {
let pyclass_init = <#pyo3_path::PyClassInitializer<Self> as ::std::convert::From<Self>>::from(self).add_subclass(#variant_cls);
let variant_value = #pyo3_path::Py::new(py, pyclass_init).unwrap();
#pyo3_path::IntoPy::into_py(variant_value, py)
}
}
})
.collect();

quote! {
#[allow(deprecated)]
impl #pyo3_path::IntoPy<#pyo3_path::PyObject> for #cls {
fn into_py(self, py: #pyo3_path::Python) -> #pyo3_path::PyObject {
match self {
#(#match_arms)*
}
}
}
}
};

let enum_into_pyobject_impl = {
let match_arms = variants
.iter()
Expand Down Expand Up @@ -1093,7 +1064,6 @@ fn impl_complex_enum(
let pyclass_impls: TokenStream = [
impl_builder.impl_pyclass(ctx),
impl_builder.impl_extractext(ctx),
enum_into_py_impl,
enum_into_pyobject_impl,
impl_builder.impl_pyclassimpl(ctx)?,
impl_builder.impl_add_to_module(ctx),
Expand Down Expand Up @@ -2142,13 +2112,6 @@ impl<'a> PyClassImplsBuilder<'a> {
// If #cls is not extended type, we allow Self->PyObject conversion
if attr.options.extends.is_none() {
quote! {
#[allow(deprecated)]
impl #pyo3_path::IntoPy<#pyo3_path::PyObject> for #cls {
fn into_py(self, py: #pyo3_path::Python<'_>) -> #pyo3_path::PyObject {
#pyo3_path::IntoPy::into_py(#pyo3_path::Py::new(py, self).unwrap(), py)
}
}

impl<'py> #pyo3_path::conversion::IntoPyObject<'py> for #cls {
type Target = Self;
type Output = #pyo3_path::Bound<'py, <Self as #pyo3_path::conversion::IntoPyObject<'py>>::Target>;
Expand Down
2 changes: 0 additions & 2 deletions pyo3-macros-backend/src/pymethod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -845,8 +845,6 @@ pub fn impl_py_getter_def(
#ty,
Offset,
{ #pyo3_path::impl_::pyclass::IsPyT::<#ty>::VALUE },
{ #pyo3_path::impl_::pyclass::IsToPyObject::<#ty>::VALUE },
{ #pyo3_path::impl_::pyclass::IsIntoPy::<#ty>::VALUE },
{ #pyo3_path::impl_::pyclass::IsIntoPyObjectRef::<#ty>::VALUE },
{ #pyo3_path::impl_::pyclass::IsIntoPyObject::<#ty>::VALUE },
> = unsafe { #pyo3_path::impl_::pyclass::PyClassGetterGenerator::new() };
Expand Down
130 changes: 1 addition & 129 deletions src/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::pyclass::boolean_struct::False;
use crate::types::any::PyAnyMethods;
use crate::types::PyTuple;
use crate::{
ffi, Borrowed, Bound, BoundObject, Py, PyAny, PyClass, PyErr, PyObject, PyRef, PyRefMut, Python,
ffi, Borrowed, Bound, BoundObject, Py, PyAny, PyClass, PyErr, PyRef, PyRefMut, Python,
};
use std::convert::Infallible;

Expand Down Expand Up @@ -66,116 +66,6 @@ pub unsafe trait AsPyPointer {
fn as_ptr(&self) -> *mut ffi::PyObject;
}

/// Conversion trait that allows various objects to be converted into `PyObject`.
#[deprecated(
since = "0.23.0",
note = "`ToPyObject` is going to be replaced by `IntoPyObject`. See the migration guide (https://pyo3.rs/v0.23.0/migration) for more information."
)]
pub trait ToPyObject {
/// Converts self into a Python object.
fn to_object(&self, py: Python<'_>) -> PyObject;
}

/// Defines a conversion from a Rust type to a Python object.
///
/// It functions similarly to std's [`Into`] trait, but requires a [GIL token](Python)
/// as an argument. Many functions and traits internal to PyO3 require this trait as a bound,
/// so a lack of this trait can manifest itself in different error messages.
///
/// # Examples
/// ## With `#[pyclass]`
/// The easiest way to implement `IntoPy` is by exposing a struct as a native Python object
/// by annotating it with [`#[pyclass]`](crate::prelude::pyclass).
///
/// ```rust,no_run
/// use pyo3::prelude::*;
///
/// # #[allow(dead_code)]
/// #[pyclass]
/// struct Number {
/// #[pyo3(get, set)]
/// value: i32,
/// }
/// ```
/// Python code will see this as an instance of the `Number` class with a `value` attribute.
///
/// ## Conversion to a Python object
///
/// However, it may not be desirable to expose the existence of `Number` to Python code.
/// `IntoPy` allows us to define a conversion to an appropriate Python object.
/// ```rust,no_run
/// #![allow(deprecated)]
/// use pyo3::prelude::*;
///
/// # #[allow(dead_code)]
/// struct Number {
/// value: i32,
/// }
///
/// impl IntoPy<PyObject> for Number {
/// fn into_py(self, py: Python<'_>) -> PyObject {
/// // delegates to i32's IntoPy implementation.
/// self.value.into_py(py)
/// }
/// }
/// ```
/// Python code will see this as an `int` object.
///
/// ## Dynamic conversion into Python objects.
/// It is also possible to return a different Python object depending on some condition.
/// This is useful for types like enums that can carry different types.
///
/// ```rust
/// #![allow(deprecated)]
/// use pyo3::prelude::*;
///
/// enum Value {
/// Integer(i32),
/// String(String),
/// None,
/// }
///
/// impl IntoPy<PyObject> for Value {
/// fn into_py(self, py: Python<'_>) -> PyObject {
/// match self {
/// Self::Integer(val) => val.into_py(py),
/// Self::String(val) => val.into_py(py),
/// Self::None => py.None(),
/// }
/// }
/// }
/// # fn main() {
/// # Python::with_gil(|py| {
/// # let v = Value::Integer(73).into_py(py);
/// # let v = v.extract::<i32>(py).unwrap();
/// #
/// # let v = Value::String("foo".into()).into_py(py);
/// # let v = v.extract::<String>(py).unwrap();
/// #
/// # let v = Value::None.into_py(py);
/// # let v = v.extract::<Option<Vec<i32>>>(py).unwrap();
/// # });
/// # }
/// ```
/// Python code will see this as any of the `int`, `string` or `None` objects.
#[cfg_attr(
diagnostic_namespace,
diagnostic::on_unimplemented(
message = "`{Self}` cannot be converted to a Python object",
note = "`IntoPy` is automatically implemented by the `#[pyclass]` macro",
note = "if you do not wish to have a corresponding Python type, implement it manually",
note = "if you do not own `{Self}` you can perform a manual conversion to one of the types in `pyo3::types::*`"
)
)]
#[deprecated(
since = "0.23.0",
note = "`IntoPy` is going to be replaced by `IntoPyObject`. See the migration guide (https://pyo3.rs/v0.23.0/migration) for more information."
)]
pub trait IntoPy<T>: Sized {
/// Performs the conversion.
fn into_py(self, py: Python<'_>) -> T;
}

/// Defines a conversion from a Rust type to a Python object, which may fail.
///
/// This trait has `#[derive(IntoPyObject)]` to automatically implement it for simple types and
Expand Down Expand Up @@ -541,16 +431,6 @@ where
}
}

/// Identity conversion: allows using existing `PyObject` instances where
/// `T: ToPyObject` is expected.
#[allow(deprecated)]
impl<T: ?Sized + ToPyObject> ToPyObject for &'_ T {
#[inline]
fn to_object(&self, py: Python<'_>) -> PyObject {
<T as ToPyObject>::to_object(*self, py)
}
}

impl<T> FromPyObject<'_> for T
where
T: PyClass + Clone,
Expand Down Expand Up @@ -579,14 +459,6 @@ where
}
}

/// Converts `()` to an empty Python tuple.
#[allow(deprecated)]
impl IntoPy<Py<PyTuple>> for () {
fn into_py(self, py: Python<'_>) -> Py<PyTuple> {
PyTuple::empty(py).unbind()
}
}

impl<'py> IntoPyObject<'py> for () {
type Target = PyTuple;
type Output = Bound<'py, Self::Target>;
Expand Down
Loading
Loading