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

Rework exceptions to be native types #1024

Merged
merged 3 commits into from
Jul 18, 2020
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Add `Python::with_gil` for executing a closure with the Python GIL. [#1037](https://github.com/PyO3/pyo3/pull/1037)

### Changed
- Exception types have been renamed from e.g. `RuntimeError` to `PyRuntimeError`, and are now only accessible by `&T` or `Py<T>` similar to other Python-native types. The old names continue to exist but are deprecated. [#1024](https://github.com/PyO3/pyo3/pull/1024)
- Correct FFI definitions `Py_SetProgramName` and `Py_SetPythonHome` to take `*const` argument instead of `*mut`. [#1021](https://github.com/PyO3/pyo3/pull/1021)
- Rename `PyString::to_string` to `to_str`, change return type `Cow<str>` to `&str`. [#1023](https://github.com/PyO3/pyo3/pull/1023)
- Correct FFI definition `_PyLong_AsByteArray` `*mut c_uchar` argument instead of `*const c_uchar`. [#1029](https://github.com/PyO3/pyo3/pull/1029)
Expand Down
4 changes: 2 additions & 2 deletions examples/rustapi_module/src/dict_iter.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use pyo3::exceptions::RuntimeError;
use pyo3::exceptions::PyRuntimeError;
use pyo3::prelude::*;
use pyo3::types::PyDict;

Expand Down Expand Up @@ -33,7 +33,7 @@ impl DictSize {
if seen == self.expected {
Ok(seen)
} else {
Err(PyErr::new::<RuntimeError, _>(format!(
Err(PyErr::new::<PyRuntimeError, _>(format!(
"Expected {} iterations - performed {}",
self.expected, seen
)))
Expand Down
34 changes: 18 additions & 16 deletions guide/src/exception.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ You can use the [`create_exception!`] macro to define a new exception type:
```rust
use pyo3::create_exception;

create_exception!(module, MyError, pyo3::exceptions::Exception);
create_exception!(module, MyError, pyo3::exceptions::PyException);
```

* `module` is the name of the containing module.
Expand All @@ -19,9 +19,9 @@ For example:
use pyo3::prelude::*;
use pyo3::create_exception;
use pyo3::types::IntoPyDict;
use pyo3::exceptions::Exception;
use pyo3::exceptions::PyException;

create_exception!(mymodule, CustomError, Exception);
create_exception!(mymodule, CustomError, PyException);

fn main() {
let gil = Python::acquire_gil();
Expand All @@ -39,12 +39,12 @@ To raise an exception, first you need to obtain an exception type and construct

```rust
use pyo3::{Python, PyErr};
use pyo3::exceptions;
use pyo3::exceptions::PyTypeError;

fn main() {
let gil = Python::acquire_gil();
let py = gil.python();
PyErr::new::<exceptions::TypeError, _>("Error").restore(py);
PyErr::new::<PyTypeError, _>("Error").restore(py);
assert!(PyErr::occurred(py));
drop(PyErr::fetch(py));
}
Expand All @@ -65,12 +65,12 @@ has a corresponding Rust type, exceptions defined by [`create_exception!`] and [
have Rust types as well.

```rust
# use pyo3::exceptions;
# use pyo3::exceptions::PyValueError;
# use pyo3::prelude::*;
# fn check_for_error() -> bool {false}
fn my_func(arg: PyObject) -> PyResult<()> {
if check_for_error() {
Err(exceptions::ValueError::py_err("argument is wrong"))
Err(PyValueError::py_err("argument is wrong"))
} else {
Ok(())
}
Expand Down Expand Up @@ -101,13 +101,13 @@ method to do the actual work.
To check the type of an exception, you can simply do:

```rust
# use pyo3::exceptions;
# use pyo3::exceptions::PyTypeError;
# use pyo3::prelude::*;
# fn main() {
# let gil = Python::acquire_gil();
# let py = gil.python();
# let err = exceptions::TypeError::py_err(());
err.is_instance::<exceptions::TypeError>(py);
# let err = PyTypeError::py_err(());
err.is_instance::<PyTypeError>(py);
# }
```

Expand All @@ -132,7 +132,8 @@ trait can be implemented. In that case, actual exception argument creation is de
until a `Python` object is available.

```rust
# use pyo3::{exceptions, PyErr, PyResult};
# use pyo3::{PyErr, PyResult};
# use pyo3::exceptions::PyOSError;
# use std::error::Error;
# use std::fmt;
#
Expand All @@ -152,7 +153,7 @@ until a `Python` object is available.
# }
impl std::convert::From<CustomIOError> for PyErr {
fn from(err: CustomIOError) -> PyErr {
exceptions::OSError::py_err(err.to_string())
PyOSError::py_err(err.to_string())
}
}

Expand Down Expand Up @@ -181,14 +182,15 @@ The code snippet above will raise a `ValueError` in Python if `String::parse()`
## Using exceptions defined in Python code

It is possible to use an exception defined in Python code as a native Rust type.
The `import_exception!` macro allows importing a specific exception class and defines a zero-sized Rust type
The `import_exception!` macro allows importing a specific exception class and defines a Rust type
for that exception.

```rust
use pyo3::prelude::*;
use pyo3::import_exception;

import_exception!(io, UnsupportedOperation);
mod io {
pyo3::import_exception!(io, UnsupportedOperation);
}

fn tell(file: PyObject) -> PyResult<u64> {
use pyo3::exceptions::*;
Expand All @@ -197,7 +199,7 @@ fn tell(file: PyObject) -> PyResult<u64> {
let py = gil.python();

match file.call_method0(py, "tell") {
Err(_) => Err(UnsupportedOperation::py_err("not supported: tell")),
Err(_) => Err(io::UnsupportedOperation::py_err("not supported: tell")),
Ok(x) => x.extract::<u64>(py),
}
}
Expand Down
5 changes: 2 additions & 3 deletions guide/src/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,13 +198,12 @@ impl Names {
# let gil = Python::acquire_gil();
# let py = gil.python();
# let names = PyCell::new(py, Names::new()).unwrap();
# let borrow_mut_err = py.get_type::<pyo3::pycell::PyBorrowMutError>();
# pyo3::py_run!(py, names borrow_mut_err, r"
# pyo3::py_run!(py, names, r"
# try:
# names.merge(names)
# assert False, 'Unreachable'
# except RuntimeError as e:
# isinstance(e, borrow_mut_err)
# assert str(e) == 'Already borrowed'
# ");
```
`Names` has a `merge` method, which takes `&mut self` and another argument of type `&mut Self`.
Expand Down
14 changes: 8 additions & 6 deletions src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,12 @@ pub unsafe trait Element: Copy {
fn validate(b: &ffi::Py_buffer) -> PyResult<()> {
// shape and stride information must be provided when we use PyBUF_FULL_RO
if b.shape.is_null() {
return Err(exceptions::BufferError::py_err("Shape is Null"));
return Err(exceptions::PyBufferError::py_err("Shape is Null"));
}
if b.strides.is_null() {
return Err(exceptions::BufferError::py_err("PyBuffer: Strides is Null"));
return Err(exceptions::PyBufferError::py_err(
"PyBuffer: Strides is Null",
));
}
Ok(())
}
Expand Down Expand Up @@ -188,7 +190,7 @@ impl<T: Element> PyBuffer<T> {
{
Ok(buf)
} else {
Err(exceptions::BufferError::py_err(
Err(exceptions::PyBufferError::py_err(
"Incompatible type as buffer",
))
}
Expand Down Expand Up @@ -439,7 +441,7 @@ impl<T: Element> PyBuffer<T> {

fn copy_to_slice_impl(&self, py: Python, target: &mut [T], fort: u8) -> PyResult<()> {
if mem::size_of_val(target) != self.len_bytes() {
return Err(exceptions::BufferError::py_err(
return Err(exceptions::PyBufferError::py_err(
"Slice length does not match buffer length.",
));
}
Expand Down Expand Up @@ -526,7 +528,7 @@ impl<T: Element> PyBuffer<T> {
return buffer_readonly_error();
}
if mem::size_of_val(source) != self.len_bytes() {
return Err(exceptions::BufferError::py_err(
return Err(exceptions::PyBufferError::py_err(
"Slice length does not match buffer length.",
));
}
Expand Down Expand Up @@ -562,7 +564,7 @@ impl<T: Element> PyBuffer<T> {

#[inline(always)]
fn buffer_readonly_error() -> PyResult<()> {
Err(exceptions::BufferError::py_err(
Err(exceptions::PyBufferError::py_err(
"Cannot write to read-only buffer.",
))
}
Expand Down
4 changes: 2 additions & 2 deletions src/callback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! Utilities for a Python callable object that invokes a Rust function.

use crate::err::PyResult;
use crate::exceptions::OverflowError;
use crate::exceptions::PyOverflowError;
use crate::ffi::{self, Py_hash_t};
use crate::IntoPyPointer;
use crate::{IntoPy, PyObject, Python};
Expand Down Expand Up @@ -85,7 +85,7 @@ impl IntoPyCallbackOutput<ffi::Py_ssize_t> for usize {
if self <= (isize::MAX as usize) {
Ok(self as isize)
} else {
Err(OverflowError::py_err(()))
Err(PyOverflowError::py_err(()))
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/class/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ where
ffi::Py_NE => Ok(CompareOp::Ne),
ffi::Py_GT => Ok(CompareOp::Gt),
ffi::Py_GE => Ok(CompareOp::Ge),
_ => Err(PyErr::new::<exceptions::ValueError, _>(
_ => Err(PyErr::new::<exceptions::PyValueError, _>(
"tp_richcompare called with invalid comparison operator",
)),
}
Expand Down
2 changes: 1 addition & 1 deletion src/class/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ impl IntoPyCallbackOutput<*mut ffi::PyObject> for PyIterNextOutput {
fn convert(self, _py: Python) -> PyResult<*mut ffi::PyObject> {
match self {
IterNextOutput::Yield(o) => Ok(o.into_ptr()),
IterNextOutput::Return(opt) => Err(crate::exceptions::StopIteration::py_err((opt,))),
IterNextOutput::Return(opt) => Err(crate::exceptions::PyStopIteration::py_err((opt,))),
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/class/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ macro_rules! py_func_set {
let slf = py.from_borrowed_ptr::<$crate::PyCell<$generic>>(slf);

if value.is_null() {
Err($crate::PyErr::new::<exceptions::NotImplementedError, _>(
Err($crate::PyErr::new::<exceptions::PyNotImplementedError, _>(
format!(
"Subscript deletion not supported by {:?}",
stringify!($generic)
Expand Down Expand Up @@ -338,7 +338,7 @@ macro_rules! py_func_del {
.extract()?;
slf.try_borrow_mut()?.$fn_del(name).convert(py)
} else {
Err(PyErr::new::<exceptions::NotImplementedError, _>(
Err(PyErr::new::<exceptions::PyNotImplementedError, _>(
"Subscript assignment not supported",
))
}
Expand Down
2 changes: 1 addition & 1 deletion src/class/pyasync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ impl IntoPyCallbackOutput<*mut ffi::PyObject> for PyIterANextOutput {
match self {
IterANextOutput::Yield(o) => Ok(o.into_ptr()),
IterANextOutput::Return(opt) => {
Err(crate::exceptions::StopAsyncIteration::py_err((opt,)))
Err(crate::exceptions::PyStopAsyncIteration::py_err((opt,)))
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/class/sequence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ mod sq_ass_item_impl {
let slf = py.from_borrowed_ptr::<PyCell<T>>(slf);

if value.is_null() {
return Err(PyErr::new::<exceptions::NotImplementedError, _>(format!(
return Err(PyErr::new::<exceptions::PyNotImplementedError, _>(format!(
"Item deletion is not supported by {:?}",
stringify!(T)
)));
Expand Down Expand Up @@ -256,7 +256,7 @@ mod sq_ass_item_impl {
if value.is_null() {
crate::callback::convert(py, slf.borrow_mut().__delitem__(key.into()))
} else {
Err(PyErr::new::<exceptions::NotImplementedError, _>(format!(
Err(PyErr::new::<exceptions::PyNotImplementedError, _>(format!(
"Item assignment not supported by {:?}",
stringify!(T)
)))
Expand Down
4 changes: 2 additions & 2 deletions src/derive_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//! Functionality for the code generated by the derive backend

use crate::err::{PyErr, PyResult};
use crate::exceptions::TypeError;
use crate::exceptions::PyTypeError;
use crate::instance::PyNativeType;
use crate::pyclass::{PyClass, PyClassThreadChecker};
use crate::types::{PyAny, PyDict, PyModule, PyTuple};
Expand Down Expand Up @@ -43,7 +43,7 @@ pub fn parse_fn_args<'p>(
let nargs = args.len();
let mut used_args = 0;
macro_rules! raise_error {
($s: expr $(,$arg:expr)*) => (return Err(TypeError::py_err(format!(
($s: expr $(,$arg:expr)*) => (return Err(PyTypeError::py_err(format!(
concat!("{} ", $s), fname.unwrap_or("function") $(,$arg)*
))))
}
Expand Down
Loading