Skip to content

Commit 2a4d409

Browse files
committed
BUG: bounds check in Cython get_value_at method, close #495
1 parent edd778c commit 2a4d409

File tree

5 files changed

+29
-35
lines changed

5 files changed

+29
-35
lines changed

pandas/core/series.py

+2
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,8 @@ def __getitem__(self, key):
260260
except KeyError, e1:
261261
try:
262262
return _gin.get_value_at(self, key)
263+
except IndexError:
264+
raise
263265
except Exception, _:
264266
pass
265267
raise e1

pandas/src/engines.pyx

+6-28
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,11 @@ cnp.import_array()
66

77
cimport util
88

9-
cpdef inline object get_value_at(ndarray arr, object loc):
10-
cdef:
11-
Py_ssize_t i
12-
void* data_ptr
13-
if util.is_float_object(loc):
14-
casted = int(loc)
15-
if casted == loc:
16-
loc = casted
17-
i = <Py_ssize_t> loc
18-
if i < 0:
19-
i += cnp.PyArray_SIZE(arr)
20-
data_ptr = cnp.PyArray_GETPTR1(arr, i)
21-
return cnp.PyArray_GETITEM(arr, data_ptr)
22-
23-
cpdef inline set_value_at(ndarray arr, object loc, object value):
24-
cdef:
25-
Py_ssize_t i
26-
if util.is_float_object(loc):
27-
casted = int(loc)
28-
if casted == loc:
29-
loc = casted
30-
i = <Py_ssize_t> loc
31-
if i < 0:
32-
i += cnp.PyArray_SIZE(arr)
33-
34-
util.assign_value_1d(arr, i, value)
9+
def get_value_at(ndarray arr, object loc):
10+
return util.get_value_at(arr, loc)
3511

12+
def set_value_at(ndarray arr, object loc, object val):
13+
return util.set_value_at(arr, loc, val)
3614

3715
cdef class IndexEngine:
3816

@@ -45,7 +23,7 @@ cdef class IndexEngine:
4523
void* data_ptr
4624

4725
loc = self.get_loc(key)
48-
return get_value_at(arr, loc)
26+
return util.get_value_at(arr, loc)
4927

5028
cpdef set_value(self, ndarray arr, object key, object value):
5129
'''
@@ -56,7 +34,7 @@ cdef class IndexEngine:
5634
void* data_ptr
5735

5836
loc = self.get_loc(key)
59-
set_value_at(arr, loc, value)
37+
util.set_value_at(arr, loc, value)
6038

6139
cdef class DictIndexEngine(IndexEngine):
6240
'''

pandas/src/util.pxd

+15-6
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,38 @@ cdef extern from "numpy_helper.h":
88
inline int is_string_object(object)
99
inline int assign_value_1d (ndarray, Py_ssize_t, object) except -1
1010

11-
cpdef inline object get_value_at(ndarray arr, object loc):
11+
12+
cdef inline object get_value_at(ndarray arr, object loc):
1213
cdef:
13-
Py_ssize_t i
14+
Py_ssize_t i, sz
1415
void* data_ptr
1516
if is_float_object(loc):
1617
casted = int(loc)
1718
if casted == loc:
1819
loc = casted
1920
i = <Py_ssize_t> loc
21+
sz = cnp.PyArray_SIZE(arr)
22+
2023
if i < 0:
21-
i += cnp.PyArray_SIZE(arr)
24+
i += sz
25+
elif i >= sz:
26+
raise IndexError('index out of bounds')
2227
data_ptr = cnp.PyArray_GETPTR1(arr, i)
2328
return cnp.PyArray_GETITEM(arr, data_ptr)
2429

25-
cpdef inline set_value_at(ndarray arr, object loc, object value):
30+
cdef inline set_value_at(ndarray arr, object loc, object value):
2631
cdef:
27-
Py_ssize_t i
32+
Py_ssize_t i, sz
2833
if is_float_object(loc):
2934
casted = int(loc)
3035
if casted == loc:
3136
loc = casted
3237
i = <Py_ssize_t> loc
38+
sz = cnp.PyArray_SIZE(arr)
39+
3340
if i < 0:
34-
i += cnp.PyArray_SIZE(arr)
41+
i += sz
42+
elif i >= sz:
43+
raise IndexError('index out of bounds')
3544

3645
assign_value_1d(arr, i, value)

pandas/tests/test_series.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,10 @@ def test_getitem_setitem_boolean_corner(self):
355355
self.assertRaises(Exception, ts.ix.__getitem__, mask_shifted)
356356
self.assertRaises(Exception, ts.ix.__setitem__, mask_shifted, 1)
357357

358+
def test_getitem_out_of_bounds(self):
359+
# don't segfault, GH #495
360+
self.assertRaises(IndexError, self.ts.__getitem__, len(self.ts))
361+
358362
def test_slice(self):
359363
numSlice = self.series[10:20]
360364
numSliceEnd = self.series[-10:]
@@ -531,7 +535,7 @@ def test_to_string(self):
531535
# pass float_format
532536
format = '%.4f'.__mod__
533537
result = self.ts.to_string(float_format=format)
534-
result = [x.split()[1] for x in result.split('\n')[:-1]]
538+
result = [x.split()[1] for x in result.split('\n')]
535539
expected = [format(x) for x in self.ts]
536540
self.assertEqual(result, expected)
537541

setup.py

+1
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ def srcpath(name=None, suffix='.pyx', subdir='src'):
290290
if suffix == '.pyx':
291291
tseries_depends = [srcpath(f, suffix='.pyx')
292292
for f in tseries_depends]
293+
tseries_depends.append('util.pxd')
293294
else:
294295
tseries_depends = []
295296

0 commit comments

Comments
 (0)