add PySlice

This commit is contained in:
Nikolay Kim 2017-04-27 19:38:02 -07:00
parent 8ccb749c1e
commit 665921e84e
3 changed files with 151 additions and 0 deletions

View File

@ -37,6 +37,7 @@ pub use self::num::PyInt;
pub use self::num::PyLong as PyInt;
pub use self::num::{PyLong, PyFloat};
pub use self::sequence::PySequence;
pub use self::slice::PySlice;
#[macro_export]
macro_rules! pyobject_newtype(
@ -132,6 +133,7 @@ mod tuple;
mod list;
mod num;
mod sequence;
mod slice;
pub mod exc;
#[cfg(feature="python27-sys")]

99
src/objects/slice.rs Normal file
View File

@ -0,0 +1,99 @@
// Copyright (c) 2017 Nikolay Kim
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
use std::mem;
use libc::c_long;
use super::object::PyObject;
use python::{Python, PythonObject, ToPythonPointer, PyClone, PyDrop};
use err::{self, PyErr, PyResult};
use ffi::{self, Py_ssize_t};
use conversion::{ToPyObject, FromPyObject};
/// Represents a Python `slice` indices
pub struct PySliceIndices {
pub start: isize,
pub stop: isize,
pub step: isize,
pub slicelength: isize,
}
impl PySliceIndices {
pub fn new(start: isize, stop: isize, step: isize) -> PySliceIndices {
PySliceIndices {
start: start,
stop: stop,
step: step,
slicelength: 0,
}
}
}
/// Represents a Python `slice`. Only `c_long` indeces supprted
/// at the moment by PySlice object.
pub struct PySlice(PyObject);
pyobject_newtype!(PySlice, PySlice_Check, PySlice_Type);
impl PySlice {
/// Construct a new slice with the given elements.
pub fn new(py: Python, start: isize, stop: isize, step: isize) -> PySlice {
unsafe {
let ptr = ffi::PySlice_New(ffi::PyLong_FromLong(start as i64),
ffi::PyLong_FromLong(stop as i64),
ffi::PyLong_FromLong(step as i64));
err::result_from_owned_ptr(py, ptr).unwrap().unchecked_cast_into::<PySlice>()
}
}
/// Retrieve the start, stop, and step indices from the slice object slice assuming a sequence of length length, and store the length of the slice in slicelength.
#[inline]
pub fn indices(&self, py: Python, length: c_long) -> PyResult<PySliceIndices> {
// non-negative Py_ssize_t should always fit into Rust usize
unsafe {
let slicelen: isize = 0;
let start: isize = 0;
let stop: isize = 0;
let step: isize = 0;
let r = ffi::PySlice_GetIndicesEx(
self.0.as_ptr(), length as Py_ssize_t,
&start as *const _ as *mut _,
&stop as *const _ as *mut _,
&step as *const _ as *mut _,
&slicelen as *const _ as *mut _);
if r == 0{
Ok(PySliceIndices {
start: start,
stop: stop,
step: step,
slicelength: slicelen,
})
} else {
Err(PyErr::fetch(py))
}
}
}
}
impl ToPyObject for PySliceIndices {
type ObjectType = PySlice;
fn to_py_object(&self, py: Python) -> Self::ObjectType {
PySlice::new(py, self.start, self.stop, self.step)
}
}

50
tests/test_slice.rs Normal file
View File

@ -0,0 +1,50 @@
#![allow(dead_code, unused_variables)]
#[macro_use] extern crate cpython;
use cpython::*;
#[test]
fn test_basics() {
let gil = Python::acquire_gil();
let py = gil.python();
let v = PySlice::new(py, 1, 10, 2);
let indices = v.indices(py, 100).unwrap();
assert_eq!(1, indices.start);
assert_eq!(10, indices.stop);
assert_eq!(2, indices.step);
assert_eq!(5, indices.slicelength);
}
py_class!(pub class Test |py| {
def __getitem__(&self, idx: PyObject) -> PyResult<PyObject> {
if let Ok(slice) = PySlice::downcast_from(py, idx.clone_ref(py)) {
let indices = slice.indices(py, 1000)?;
if indices.start == 100 && indices.stop == 200 && indices.step == 1 {
return Ok("slice".to_py_object(py).into_object())
}
}
else if let Ok(idx) = idx.extract::<isize>(py) {
if idx == 1 {
return Ok("int".to_py_object(py).into_object())
}
}
Err(PyErr::new::<exc::ValueError, _>(py, "error"))
}
});
#[test]
fn test_cls_impl() {
let gil = Python::acquire_gil();
let py = gil.python();
let ob = Test::create_instance(py).unwrap();
let d = PyDict::new(py);
d.set_item(py, "ob", ob).unwrap();
py.run("assert ob[1] == 'int'", None, Some(&d)).unwrap();
py.run("assert ob[100:200:1] == 'slice'", None, Some(&d)).unwrap();
}