|
16 | 16 |
|
17 | 17 | //------------------------------------------------------------------------------
|
18 | 18 | // Given a PyObject, raise if not an array.
|
19 |
| -# define AK_CHECK_NUMPY_ARRAY(O) \ |
20 |
| - if (!PyArray_Check(O)) { \ |
21 |
| - return PyErr_Format(PyExc_TypeError, "Expected NumPy array (got %s)", \ |
22 |
| - Py_TYPE(O)->tp_name); \ |
| 19 | +# define AK_CHECK_NUMPY_ARRAY(O) \ |
| 20 | + if (!PyArray_Check(O)) { \ |
| 21 | + return PyErr_Format(PyExc_TypeError, \ |
| 22 | + "Expected NumPy array, not %s.", \ |
| 23 | + Py_TYPE(O)->tp_name); \ |
23 | 24 | }
|
24 | 25 |
|
25 | 26 | // Given a PyObject, raise if not an array or is not one or two dimensional.
|
|
29 | 30 | int ndim = PyArray_NDIM((PyArrayObject *)O); \
|
30 | 31 | if (ndim != 1 && ndim != 2) { \
|
31 | 32 | return PyErr_Format(PyExc_NotImplementedError,\
|
32 |
| - "expected 1D or 2D array (got %i)", \ |
| 33 | + "Expected 1D or 2D array, not %i.", \ |
33 | 34 | ndim); \
|
34 | 35 | } \
|
35 | 36 | } while (0)
|
@@ -3322,6 +3323,53 @@ row_1d_filter(PyObject *Py_UNUSED(m), PyObject *a)
|
3322 | 3323 | return a;
|
3323 | 3324 | }
|
3324 | 3325 |
|
| 3326 | + |
| 3327 | +// Convert any slice to an ascending slice that covers the same values. |
| 3328 | +static PyObject * |
| 3329 | +slice_to_ascending_slice(PyObject *Py_UNUSED(m), PyObject *args) { |
| 3330 | + |
| 3331 | + PyObject* slice; |
| 3332 | + PyObject* size; |
| 3333 | + if (!PyArg_ParseTuple(args, |
| 3334 | + "O!O!:slice_to_ascending_slice", |
| 3335 | + &PySlice_Type, &slice, |
| 3336 | + &PyLong_Type, &size)) { |
| 3337 | + return NULL; |
| 3338 | + } |
| 3339 | + |
| 3340 | + Py_ssize_t step_count = -1; |
| 3341 | + Py_ssize_t start = 0; |
| 3342 | + Py_ssize_t stop = 0; |
| 3343 | + Py_ssize_t step = 0; |
| 3344 | + |
| 3345 | + if (PySlice_Unpack(slice, &start, &stop, &step)) { |
| 3346 | + return NULL; |
| 3347 | + } |
| 3348 | + if (step > 0) { |
| 3349 | + Py_INCREF(slice); |
| 3350 | + return slice; |
| 3351 | + } |
| 3352 | + step_count = PySlice_AdjustIndices( |
| 3353 | + PyLong_AsSsize_t(size), |
| 3354 | + &start, |
| 3355 | + &stop, |
| 3356 | + step); |
| 3357 | + |
| 3358 | + PyObject* asc_stop = PyLong_FromSsize_t(start + 1); |
| 3359 | + // step will be negative; shift original start value down to find new start |
| 3360 | + PyObject* asc_start = PyLong_FromSsize_t(start + (step * (step_count - 1))); |
| 3361 | + PyObject* asc_step = PyLong_FromSsize_t(-step); |
| 3362 | + |
| 3363 | + // might be NULL, let return |
| 3364 | + PyObject* asc = PySlice_New(asc_start, asc_stop, asc_step); |
| 3365 | + |
| 3366 | + Py_DECREF(asc_start); |
| 3367 | + Py_DECREF(asc_stop); |
| 3368 | + Py_DECREF(asc_step); |
| 3369 | + |
| 3370 | + return asc; |
| 3371 | +} |
| 3372 | + |
3325 | 3373 | //------------------------------------------------------------------------------
|
3326 | 3374 | // array utility
|
3327 | 3375 |
|
@@ -5348,6 +5396,7 @@ static PyMethodDef arraykit_methods[] = {
|
5348 | 5396 | {"column_2d_filter", column_2d_filter, METH_O, NULL},
|
5349 | 5397 | {"column_1d_filter", column_1d_filter, METH_O, NULL},
|
5350 | 5398 | {"row_1d_filter", row_1d_filter, METH_O, NULL},
|
| 5399 | + {"slice_to_ascending_slice", slice_to_ascending_slice, METH_VARARGS, NULL}, |
5351 | 5400 | {"array_deepcopy",
|
5352 | 5401 | (PyCFunction)array_deepcopy,
|
5353 | 5402 | METH_VARARGS | METH_KEYWORDS,
|
|
0 commit comments