pyo3 icon indicating copy to clipboard operation
pyo3 copied to clipboard

CastError is not Send

Open aseyboldt opened this issue 2 months ago • 2 comments

Bug Description

I'm not sure if this actually is a bug or expected behavior, but with the update from pyo3 0.26 to 0.27 some of by code broke, because the error returned by Py<PyAny>::extract is no longer Send (I think because of the change in https://github.com/PyO3/pyo3/pull/5468).

Steps to Reproduce

Code like this used to work

use anyhow::Context;

let point: Py<PyAny> = ...
let point: PyReadonlyArray1<f64> = point
    .extract(py)
    .context("some info")?;

This now gives a compile error.

Backtrace

error[E0277]: `NonNull<PyObject>` cannot be sent between threads safely
   --> src/pyfunc.rs:531:18
    |
531 |                 .context("Initializition array returned incorrect argument")?;
    |                  ^^^^^^^ `NonNull<PyObject>` cannot be sent between threads safely
    |
    = help: within `CastError<'_, '_>`, the trait `Send` is not implemented for `NonNull<PyObject>`
    = help: the trait `anyhow::Context<T, E>` is implemented for `Result<T, E>`
note: required because it appears within the type `pyo3::Borrowed<'_, '_, pyo3::PyAny>`
   --> /home/adr/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pyo3-0.27.1/src/instance.rs:931:12
    |
931 | pub struct Borrowed<'a, 'py, T>(NonNull<ffi::PyObject>, PhantomData<&'a Py<T>>, Python<'p...
    |            ^^^^^^^^
note: required because it appears within the type `CastError<'_, '_>`
   --> /home/adr/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pyo3-0.27.1/src/err/cast_error.rs:11:12
    |
 11 | pub struct CastError<'a, 'py> {
    |            ^^^^^^^^^
    = note: required for `Result<PyReadonlyArray<'_, f64, ...>, ...>` to implement `anyhow::Context<PyReadonlyArray<'_, f64, Dim<[usize; 1]>>, CastError<'_, '_>>`
    = note: the full name for the type has been written to '/home/adr/git/nuts-py/target/debug/deps/_lib-d99f07d11670af81.long-type-7367848190499037911.txt'
    = note: consider using `--verbose` to print the full type name to the console
help: consider removing this method call, as the receiver has type `pyo3::Py<pyo3::PyAny>` and `pyo3::Py<pyo3::PyAny>: Send` trivially holds
    |
529 -             let init_point: PyReadonlyArray1<f64> = init_point
530 -                 .extract(py)
529 +             let init_point: PyReadonlyArray1<f64> = init_point
    |

Your operating system and version

archlinux

Your Python version (python --version)

3.13

Your Rust version (rustc --version)

1.90.0

Your PyO3 version

0.27.1

How did you install python? Did you use a virtualenv?

pixi

Additional Info

No response

aseyboldt avatar Nov 12 '25 12:11 aseyboldt

Thanks for the report.

We have been reworking error handling to help avoid creating Python exceptions when in pure-rust code, which benefits performance. CastError is one such error.

Making CastError send would incur Python reference count overhead where it is currently not needed. I think for now I'd suggest changing code to something like:

use anyhow::{anyhow, Context};

let point: Py<PyAny> = ...
let point: PyReadonlyArray1<f64> = point
    .extract(py)
    .map_err(|e| anyhow!("some info: {e}"))?;

which will format the cast error to a string without needing to create a full Python exception.

davidhewitt avatar Nov 18 '25 12:11 davidhewitt

I guess there's also the question if the error should have a reference to the objects that it tried to cast, which I think introduces the question of Send in the first place?

aseyboldt avatar Nov 21 '25 17:11 aseyboldt