various cleanups

This commit is contained in:
Nikolay Kim 2017-07-26 20:29:07 -07:00
parent 7631f0a6c1
commit 8227ce81e2
6 changed files with 90 additions and 115 deletions

View file

@ -5,9 +5,9 @@
//! Python argument parsing
use ffi;
use err::PyResult;
use python::{Python, PyDowncastFrom};
use objects::{PyObjectRef, PyTuple, PyDict, PyString, exc};
use err::{self, PyResult};
#[derive(Debug)]
/// Description of a python parameter; used for `parse_args()`.
@ -33,12 +33,10 @@ pub fn parse_args<'p>(fname: Option<&str>, params: &[ParamDescription],
accept_args: bool, accept_kwargs: bool,
output: &mut[Option<&'p PyObjectRef>]) -> PyResult<()>
{
let nargs = args.len();
let nkeywords = kwargs.map_or(0, |d| d.len());
if !accept_args && (nargs + nkeywords > params.len()) {
return Err(err::PyErr::new::<exc::TypeError, _>(
return Err(exc::TypeError::new(
format!("{}{} takes at most {} argument{} ({} given)",
fname.unwrap_or("function"),
if fname.is_some() { "()" } else { "" },
@ -55,14 +53,14 @@ pub fn parse_args<'p>(fname: Option<&str>, params: &[ParamDescription],
*out = Some(kwarg);
used_keywords += 1;
if i < nargs {
return Err(err::PyErr::new::<exc::TypeError, _>(
return Err(exc::TypeError::new(
format!("Argument given by name ('{}') and position ({})", p.name, i+1)));
}
},
None => {
if p.kw_only {
if !p.is_optional {
return Err(err::PyErr::new::<exc::TypeError, _>(
return Err(exc::TypeError::new(
format!("Required argument ('{}') is keyword only argument", p.name)));
}
*out = None;
@ -72,7 +70,7 @@ pub fn parse_args<'p>(fname: Option<&str>, params: &[ParamDescription],
} else {
*out = None;
if !p.is_optional {
return Err(err::PyErr::new::<exc::TypeError, _>(
return Err(exc::TypeError::new(
format!("Required argument ('{}') (pos {}) not found", p.name, i+1)));
}
}
@ -85,7 +83,7 @@ pub fn parse_args<'p>(fname: Option<&str>, params: &[ParamDescription],
let item = PyTuple::downcast_from(item)?;
let key = PyString::downcast_from(item.get_item(0))?.to_string()?;
if !params.iter().any(|p| p.name == key) {
return Err(err::PyErr::new::<exc::TypeError, _>(
return Err(exc::TypeError::new(
format!("'{}' is an invalid keyword argument for this function", key)));
}
}

View file

@ -394,7 +394,7 @@ impl PyBuffer {
fn copy_to_slice_impl<T: Element+Copy>(&self, py: Python, target: &mut [T], fort: u8) -> PyResult<()> {
if mem::size_of_val(target) != self.len_bytes() {
return slice_length_error();
return Err(exc::BufferError::new("Slice length does not match buffer length."));
}
if !T::is_compatible_format(self.format()) || mem::size_of::<T>() != self.item_size() {
return incompatible_format_error();
@ -480,7 +480,7 @@ impl PyBuffer {
return buffer_readonly_error();
}
if mem::size_of_val(source) != self.len_bytes() {
return slice_length_error();
return Err(exc::BufferError::new("Slice length does not match buffer length."));
}
if !T::is_compatible_format(self.format()) || mem::size_of::<T>() != self.item_size() {
return incompatible_format_error();
@ -504,16 +504,12 @@ impl PyBuffer {
}
}
fn slice_length_error() -> PyResult<()> {
Err(err::PyErr::new::<exc::BufferError, _>("Slice length does not match buffer length."))
}
fn incompatible_format_error() -> PyResult<()> {
Err(err::PyErr::new::<exc::BufferError, _>("Slice type is incompatible with buffer format."))
Err(exc::BufferError::new("Slice type is incompatible with buffer format."))
}
fn buffer_readonly_error() -> PyResult<()> {
Err(err::PyErr::new::<exc::BufferError, _>("Cannot write to read-only buffer."))
Err(exc::BufferError::new("Cannot write to read-only buffer."))
}
impl Drop for PyBuffer {

View file

@ -3,7 +3,7 @@
//! Utilities for a Python callable object that invokes a Rust function.
use std::os::raw::c_int;
use std::{any, ptr, isize};
use std::{ptr, isize};
use err::PyResult;
use ffi::{self, Py_hash_t};
@ -150,10 +150,3 @@ pub unsafe fn cb_convert<C, T>(_c: C, py: Python, value: PyResult<T>) -> C::R
}
}
}
pub fn handle_panic(_py: Python, _panic: &any::Any) {
unsafe {
ffi::PyErr_SetString(ffi::PyExc_SystemError, "Rust panic\0".as_ptr() as *const i8);
}
}

View file

@ -58,22 +58,21 @@ macro_rules! py_exception {
pub fn new<T: $crate::ToPyObject + 'static>(args: T) -> $crate::PyErr {
$crate::PyErr::new::<$name, T>(args)
}
#[inline(always)]
#[inline]
fn type_object() -> *mut $crate::ffi::PyTypeObject {
#[allow(non_upper_case_globals)]
static mut type_object: *mut $crate::ffi::PyTypeObject =
static mut TYPE_OBJECT: *mut $crate::ffi::PyTypeObject =
0 as *mut $crate::ffi::PyTypeObject;
unsafe {
if type_object.is_null() {
if TYPE_OBJECT.is_null() {
let gil = $crate::Python::acquire_gil();
let py = gil.python();
type_object = $crate::PyErr::new_type(
TYPE_OBJECT = $crate::PyErr::new_type(
py, concat!(stringify!($module), ".", stringify!($name)),
Some(py.get_type::<$base>()), None);
}
type_object
TYPE_OBJECT
}
}
}
@ -188,76 +187,6 @@ impl PyErr {
}
}
/// Gets whether an error is present in the Python interpreter's global state.
#[inline]
pub fn occurred(_: Python) -> bool {
unsafe { !ffi::PyErr_Occurred().is_null() }
}
/// Creates a new exception type with the given name, which must be of the form
/// `<module>.<ExceptionName>`, as required by `PyErr_NewException`.
///
/// `base` can be an existing exception type to subclass, or a tuple of classes
/// `dict` specifies an optional dictionary of class variables and methods
pub fn new_type<'p>(_: Python<'p>, name: &str, base: Option<&PyType>, dict: Option<PyObject>)
-> *mut ffi::PyTypeObject
{
let base: *mut ffi::PyObject = match base {
None => std::ptr::null_mut(),
Some(obj) => obj.as_ptr()
};
let dict: *mut ffi::PyObject = match dict {
None => std::ptr::null_mut(),
Some(obj) => obj.as_ptr(),
};
unsafe {
let null_terminated_name = CString::new(name)
.expect("Failed to initialize nul terminated exception name");
ffi::PyErr_NewException(
null_terminated_name.as_ptr() as *mut c_char, base, dict) as *mut ffi::PyTypeObject
}
}
/// Retrieves the current error from the Python interpreter's global state.
/// The error is cleared from the Python interpreter.
/// If no error is set, returns a `SystemError`.
pub fn fetch(_: Python) -> PyErr {
unsafe {
let mut ptype : *mut ffi::PyObject = std::ptr::null_mut();
let mut pvalue : *mut ffi::PyObject = std::ptr::null_mut();
let mut ptraceback : *mut ffi::PyObject = std::ptr::null_mut();
ffi::PyErr_Fetch(&mut ptype, &mut pvalue, &mut ptraceback);
PyErr::new_from_ffi_tuple(ptype, pvalue, ptraceback)
}
}
unsafe fn new_from_ffi_tuple(ptype: *mut ffi::PyObject,
pvalue: *mut ffi::PyObject,
ptraceback: *mut ffi::PyObject) -> PyErr {
// Note: must not panic to ensure all owned pointers get acquired correctly,
// and because we mustn't panic in normalize().
let pvalue = if let Some(obj) =
PyObject::from_owned_ptr_or_opt(Python::assume_gil_acquired(), pvalue)
{
PyErrValue::Value(obj)
} else {
PyErrValue::None
};
PyErr {
ptype: if ptype.is_null() {
<exc::SystemError as PyTypeObject>::type_object()
} else {
Py::from_owned_ptr(ptype)
},
pvalue: pvalue,
ptraceback: PyObject::from_owned_ptr_or_opt(Python::assume_gil_acquired(), ptraceback),
}
}
/// Creates a new PyErr.
///
/// `obj` must be an Python exception instance, the PyErr will use that instance.
@ -289,6 +218,76 @@ impl PyErr {
}
}
/// Gets whether an error is present in the Python interpreter's global state.
#[inline]
pub fn occurred(_: Python) -> bool {
unsafe { !ffi::PyErr_Occurred().is_null() }
}
/// Retrieves the current error from the Python interpreter's global state.
/// The error is cleared from the Python interpreter.
/// If no error is set, returns a `SystemError`.
pub fn fetch(_: Python) -> PyErr {
unsafe {
let mut ptype : *mut ffi::PyObject = std::ptr::null_mut();
let mut pvalue : *mut ffi::PyObject = std::ptr::null_mut();
let mut ptraceback : *mut ffi::PyObject = std::ptr::null_mut();
ffi::PyErr_Fetch(&mut ptype, &mut pvalue, &mut ptraceback);
PyErr::new_from_ffi_tuple(ptype, pvalue, ptraceback)
}
}
/// Creates a new exception type with the given name, which must be of the form
/// `<module>.<ExceptionName>`, as required by `PyErr_NewException`.
///
/// `base` can be an existing exception type to subclass, or a tuple of classes
/// `dict` specifies an optional dictionary of class variables and methods
pub fn new_type<'p>(_: Python<'p>, name: &str, base: Option<&PyType>, dict: Option<PyObject>)
-> *mut ffi::PyTypeObject
{
let base: *mut ffi::PyObject = match base {
None => std::ptr::null_mut(),
Some(obj) => obj.as_ptr()
};
let dict: *mut ffi::PyObject = match dict {
None => std::ptr::null_mut(),
Some(obj) => obj.as_ptr(),
};
unsafe {
let null_terminated_name = CString::new(name)
.expect("Failed to initialize nul terminated exception name");
ffi::PyErr_NewException(
null_terminated_name.as_ptr() as *mut c_char, base, dict) as *mut ffi::PyTypeObject
}
}
unsafe fn new_from_ffi_tuple(ptype: *mut ffi::PyObject,
pvalue: *mut ffi::PyObject,
ptraceback: *mut ffi::PyObject) -> PyErr {
// Note: must not panic to ensure all owned pointers get acquired correctly,
// and because we mustn't panic in normalize().
let pvalue = if let Some(obj) =
PyObject::from_owned_ptr_or_opt(Python::assume_gil_acquired(), pvalue)
{
PyErrValue::Value(obj)
} else {
PyErrValue::None
};
PyErr {
ptype: if ptype.is_null() {
<exc::SystemError as PyTypeObject>::type_object()
} else {
Py::from_owned_ptr(ptype)
},
pvalue: pvalue,
ptraceback: PyObject::from_owned_ptr_or_opt(Python::assume_gil_acquired(), ptraceback),
}
}
/// Print a standard traceback to sys.stderr.
pub fn print(self, py: Python) {
self.restore(py);
@ -322,7 +321,8 @@ impl PyErr {
}
}
/// Normalizes the error. This ensures that the exception value is an instance of the exception type.
/// Normalizes the error. This ensures that the exception value is an instance
/// of the exception type.
pub fn normalize(&mut self, py: Python) {
// The normalization helper function involves temporarily moving out of the &mut self,
// which requires some unsafe trickery:
@ -352,11 +352,6 @@ impl PyErr {
}
}
/// Retrieves the exception type.
pub fn get_type(&self, py: Python) -> Py<PyType> {
self.ptype.clone_ref(py)
}
/// Retrieves the exception instance for this error.
/// This method takes `mut self` because the error might need
/// to be normalized in order to create the exception instance.
@ -410,13 +405,6 @@ impl PyErr {
ptraceback: t,
}
}
pub fn release(self, py: Python) {
#[allow(unused_variables)]
let PyErr { ptype, pvalue, ptraceback } = self;
py.release(ptype);
py.release(ptraceback);
}
}
impl std::fmt::Debug for PyErr {

View file

@ -15,7 +15,7 @@ use python::{Python, ToPyPointer, IntoPyPointer};
#[derive(Debug)]
pub struct PyObject(*mut ffi::PyObject);
// `PyObject` is thread-safe, because any python related operations require a Python<'p> token.
// `PyObject` is thread-safe, any python related operations require a Python<'p> token.
unsafe impl Send for PyObject {}
unsafe impl Sync for PyObject {}

View file

@ -124,7 +124,7 @@ pub trait ObjectProtocol {
/// Casts the PyObject to a concrete Python object type.
fn cast_as<'a, D>(&'a self) -> Option<&'a D>
where D: PyDowncastFrom,
&'a PyObjectRef: std::convert::From<&'a Self>;
&'a PyObjectRef: std::convert::From<&'a Self>;
/// Extracts some type from the Python object.
/// This is a wrapper function around `FromPyObject::extract()`.