summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael W. Hudson <mwh@python.net>2002-06-11 10:55:12 (GMT)
committerMichael W. Hudson <mwh@python.net>2002-06-11 10:55:12 (GMT)
commit5efaf7eac8c1dbbf82a96dc5d9b87fddd5d17b58 (patch)
tree7358a0479ab98893ad5a5457ae0df328d0e60251
parentf90ae20354ceb501f0ba0b6459df17f1a8005a47 (diff)
downloadcpython-5efaf7eac8c1dbbf82a96dc5d9b87fddd5d17b58.zip
cpython-5efaf7eac8c1dbbf82a96dc5d9b87fddd5d17b58.tar.gz
cpython-5efaf7eac8c1dbbf82a96dc5d9b87fddd5d17b58.tar.bz2
This is my nearly two year old patch
[ 400998 ] experimental support for extended slicing on lists somewhat spruced up and better tested than it was when I wrote it. Includes docs & tests. The whatsnew section needs expanding, and arrays should support extended slices -- later.
-rw-r--r--Doc/api/concrete.tex26
-rw-r--r--Doc/lib/libstdtypes.tex49
-rw-r--r--Doc/ref/ref3.tex7
-rw-r--r--Doc/whatsnew/whatsnew23.tex9
-rw-r--r--Include/sliceobject.h3
-rw-r--r--Lib/test/test_bool.py2
-rw-r--r--Lib/test/test_types.py72
-rw-r--r--Misc/NEWS4
-rw-r--r--Objects/listobject.c188
-rw-r--r--Objects/sliceobject.c53
-rw-r--r--Objects/stringobject.c64
-rw-r--r--Objects/tupleobject.c59
-rw-r--r--Objects/unicodeobject.c56
13 files changed, 570 insertions, 22 deletions
diff --git a/Doc/api/concrete.tex b/Doc/api/concrete.tex
index 8316b2d..2face20 100644
--- a/Doc/api/concrete.tex
+++ b/Doc/api/concrete.tex
@@ -2288,6 +2288,32 @@ They are found in the dictionary of type objects.
\begin{cfuncdesc}{int}{PySlice_GetIndices}{PySliceObject *slice, int length,
int *start, int *stop, int *step}
+Retrieve the start, stop and step indices from the slice object
+\var{slice}, assuming a sequence of length \var{length}. Treats
+indices greater than \var{length} as errors.
+
+Returns 0 on success and -1 on error with no exception set (unless one
+of the indices was not \constant{None} and failed to be converted to
+an integer, in which case -1 is returned with an exception set).
+
+You probably do not want to use this function. If you want to use
+slice objects in versions of Python prior to 2.3, you would probably
+do well to incorporate the source of \cfunction{PySlice_GetIndicesEx},
+suitably renamed, in the source of your extension.
+\end{cfuncdesc}
+
+\begin{cfuncdesc}{int}{PySlice_GetIndicesEx}{PySliceObject *slice, int length,
+ int *start, int *stop, int *step,
+ int *slicelength}
+Usable replacement for \cfunction{PySlice_GetIndices}. Retrieve the
+start, stop, and step indices from the slice object \var{slice}
+assuming a sequence of length \var{length}, and store the length of
+the slice in \var{slicelength}. Out of bounds indices are clipped in
+a manner consistent with the handling of normal slices.
+
+Returns 0 on success and -1 on error with exception set.
+
+\versionadded{2.3}
\end{cfuncdesc}
diff --git a/Doc/lib/libstdtypes.tex b/Doc/lib/libstdtypes.tex
index 55e77a7..f7db943 100644
--- a/Doc/lib/libstdtypes.tex
+++ b/Doc/lib/libstdtypes.tex
@@ -433,6 +433,7 @@ equal to \var{x}, else \code{1}}{}
\hline
\lineiii{\var{s}[\var{i}]}{\var{i}'th item of \var{s}, origin 0}{(2)}
\lineiii{\var{s}[\var{i}:\var{j}]}{slice of \var{s} from \var{i} to \var{j}}{(2), (3)}
+ \lineiii{\var{s}[\var{i}:\var{j}:\var{k}]}{slice of \var{s} from \var{i} to \var{j} with step \var{k}}{(2), (4)}
\hline
\lineiii{len(\var{s})}{length of \var{s}}{}
\lineiii{min(\var{s})}{smallest item of \var{s}}{}
@@ -446,6 +447,7 @@ equal to \var{x}, else \code{1}}{}
\indexii{repetition}{operation}
\indexii{subscript}{operation}
\indexii{slice}{operation}
+\indexii{extended slice}{operation}
\opindex{in}
\opindex{not in}
@@ -492,6 +494,15 @@ Notes:
\code{len(\var{s})}, use \code{len(\var{s})}. If \var{i} is omitted,
use \code{0}. If \var{j} is omitted, use \code{len(\var{s})}. If
\var{i} is greater than or equal to \var{j}, the slice is empty.
+
+\item[(4)] The slice of \var{s} from \var{i} to \var{j} with step \var{k}
+ is defined as the sequence of items with index \code{\var{x} =
+ \var{i} + \var{n}*\var{k}} such that \var{n} \code{>=} \code{0} and
+ \code{\var{i} <= \var{x} < \var{j}}. If \var{i} or \var{j} is
+ greater than \code{len(\var{s})}, use \code{len(\var{s})}. If
+ \var{i} or \var{j} are ommitted then they become ``end'' values
+ (which end depends on the sign of \var{k}).
+
\end{description}
@@ -875,31 +886,36 @@ The following operations are defined on mutable sequence types (where
{slice of \var{s} from \var{i} to \var{j} is replaced by \var{t}}{}
\lineiii{del \var{s}[\var{i}:\var{j}]}
{same as \code{\var{s}[\var{i}:\var{j}] = []}}{}
+ \lineiii{\var{s}[\var{i}:\var{j}:\var{k}] = \var{t}}
+ {the elements of \code{\var{s}[\var{i}:\var{j}:\var{k}]} are replaced by those of \var{t}}{(1)}
+ \lineiii{del \var{s}[\var{i}:\var{j}:\var{k}]}
+ {removes the elements of \code{\var{s}[\var{i}:\var{j}:\var{k}]} from the list}{}
\lineiii{\var{s}.append(\var{x})}
- {same as \code{\var{s}[len(\var{s}):len(\var{s})] = [\var{x}]}}{(1)}
+ {same as \code{\var{s}[len(\var{s}):len(\var{s})] = [\var{x}]}}{(2)}
\lineiii{\var{s}.extend(\var{x})}
- {same as \code{\var{s}[len(\var{s}):len(\var{s})] = \var{x}}}{(2)}
+ {same as \code{\var{s}[len(\var{s}):len(\var{s})] = \var{x}}}{(3)}
\lineiii{\var{s}.count(\var{x})}
{return number of \var{i}'s for which \code{\var{s}[\var{i}] == \var{x}}}{}
\lineiii{\var{s}.index(\var{x})}
- {return smallest \var{i} such that \code{\var{s}[\var{i}] == \var{x}}}{(3)}
+ {return smallest \var{i} such that \code{\var{s}[\var{i}] == \var{x}}}{(4)}
\lineiii{\var{s}.insert(\var{i}, \var{x})}
{same as \code{\var{s}[\var{i}:\var{i}] = [\var{x}]}
- if \code{\var{i} >= 0}}{(4)}
+ if \code{\var{i} >= 0}}{(5)}
\lineiii{\var{s}.pop(\optional{\var{i}})}
- {same as \code{\var{x} = \var{s}[\var{i}]; del \var{s}[\var{i}]; return \var{x}}}{(5)}
+ {same as \code{\var{x} = \var{s}[\var{i}]; del \var{s}[\var{i}]; return \var{x}}}{(6)}
\lineiii{\var{s}.remove(\var{x})}
- {same as \code{del \var{s}[\var{s}.index(\var{x})]}}{(3)}
+ {same as \code{del \var{s}[\var{s}.index(\var{x})]}}{(4)}
\lineiii{\var{s}.reverse()}
- {reverses the items of \var{s} in place}{(6)}
+ {reverses the items of \var{s} in place}{(7)}
\lineiii{\var{s}.sort(\optional{\var{cmpfunc}})}
- {sort the items of \var{s} in place}{(6), (7)}
+ {sort the items of \var{s} in place}{(7), (8)}
\end{tableiii}
\indexiv{operations on}{mutable}{sequence}{types}
\indexiii{operations on}{sequence}{types}
\indexiii{operations on}{list}{type}
\indexii{subscript}{assignment}
\indexii{slice}{assignment}
+\indexii{extended slice}{assignment}
\stindex{del}
\withsubitem{(list method)}{
\ttindex{append()}\ttindex{extend()}\ttindex{count()}\ttindex{index()}
@@ -908,32 +924,35 @@ The following operations are defined on mutable sequence types (where
\noindent
Notes:
\begin{description}
-\item[(1)] The C implementation of Python has historically accepted
+\item[(1)] \var{t} must have the same length as the slice it is
+ replacing.
+
+\item[(2)] The C implementation of Python has historically accepted
multiple parameters and implicitly joined them into a tuple; this
no longer works in Python 2.0. Use of this misfeature has been
deprecated since Python 1.4.
-\item[(2)] Raises an exception when \var{x} is not a list object. The
+\item[(3)] Raises an exception when \var{x} is not a list object. The
\method{extend()} method is experimental and not supported by
mutable sequence types other than lists.
-\item[(3)] Raises \exception{ValueError} when \var{x} is not found in
+\item[(4)] Raises \exception{ValueError} when \var{x} is not found in
\var{s}.
-\item[(4)] When a negative index is passed as the first parameter to
+\item[(5)] When a negative index is passed as the first parameter to
the \method{insert()} method, the new element is prepended to the
sequence.
-\item[(5)] The \method{pop()} method is only supported by the list and
+\item[(6)] The \method{pop()} method is only supported by the list and
array types. The optional argument \var{i} defaults to \code{-1},
so that by default the last item is removed and returned.
-\item[(6)] The \method{sort()} and \method{reverse()} methods modify the
+\item[(7)] The \method{sort()} and \method{reverse()} methods modify the
list in place for economy of space when sorting or reversing a large
list. To remind you that they operate by side effect, they don't return
the sorted or reversed list.
-\item[(7)] The \method{sort()} method takes an optional argument
+\item[(8)] The \method{sort()} method takes an optional argument
specifying a comparison function of two arguments (list items) which
should return a negative, zero or positive number depending on whether
the first argument is considered smaller than, equal to, or larger
diff --git a/Doc/ref/ref3.tex b/Doc/ref/ref3.tex
index 2eb5739..c319fb8 100644
--- a/Doc/ref/ref3.tex
+++ b/Doc/ref/ref3.tex
@@ -252,6 +252,13 @@ sequence of the same type. This implies that the index set is
renumbered so that it starts at 0.
\index{slicing}
+Some sequences also support ``extended slicing'' with a third ``step''
+parameter: \code{\var{a}[\var{i}:\var{j}:\var{k}]} selects all items
+of \var{a} with index \var{x} where \code{\var{x} = \var{i} +
+\var{n}*\var{k}}, \var{n} \code{>=} \code{0} and \var{i} \code{<=}
+\var{x} \code{<} \var{j}.
+\index{extended slicing}
+
Sequences are distinguished according to their mutability:
\begin{description}
diff --git a/Doc/whatsnew/whatsnew23.tex b/Doc/whatsnew/whatsnew23.tex
index 2a34faf..0759c9d 100644
--- a/Doc/whatsnew/whatsnew23.tex
+++ b/Doc/whatsnew/whatsnew23.tex
@@ -336,6 +336,15 @@ strings \samp{True} and \samp{False} instead of \samp{1} and \samp{0}.
\end{seealso}
+\section{Extended Slices\label{extended-slices}}
+
+Ever since Python 1.4 the slice syntax has supported a third
+``stride'' argument, but the builtin sequence types have not supported
+this feature (it was initially included at the behest of the
+developers of the Numerical Python package). This changes with Python
+2.3.
+
+% XXX examples, etc.
%======================================================================
%\section{Other Language Changes}
diff --git a/Include/sliceobject.h b/Include/sliceobject.h
index d9d6934..cd40a1b 100644
--- a/Include/sliceobject.h
+++ b/Include/sliceobject.h
@@ -32,6 +32,9 @@ DL_IMPORT(PyObject *) PySlice_New(PyObject* start, PyObject* stop,
PyObject* step);
DL_IMPORT(int) PySlice_GetIndices(PySliceObject *r, int length,
int *start, int *stop, int *step);
+DL_IMPORT(int) PySlice_GetIndicesEx(PySliceObject *r, int length,
+ int *start, int *stop,
+ int *step, int *slicelength);
#ifdef __cplusplus
}
diff --git a/Lib/test/test_bool.py b/Lib/test/test_bool.py
index 4a8bef1..cadf23a 100644
--- a/Lib/test/test_bool.py
+++ b/Lib/test/test_bool.py
@@ -219,7 +219,7 @@ veris(operator.isSequenceType(0), False)
veris(operator.isSequenceType([]), True)
veris(operator.contains([], 1), False)
veris(operator.contains([1], 1), True)
-veris(operator.isMappingType([]), False)
+veris(operator.isMappingType(1), False)
veris(operator.isMappingType({}), True)
veris(operator.lt(0, 0), False)
veris(operator.lt(0, 1), True)
diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py
index bc13c5f..8452cec 100644
--- a/Lib/test/test_types.py
+++ b/Lib/test/test_types.py
@@ -188,6 +188,31 @@ else: raise TestFailed, 'in/not in string'
x = 'x'*103
if '%s!'%x != x+'!': raise TestFailed, 'nasty string formatting bug'
+#extended slices for strings
+a = '0123456789'
+vereq(a[::], a)
+vereq(a[::2], '02468')
+vereq(a[1::2], '13579')
+vereq(a[::-1],'9876543210')
+vereq(a[::-2], '97531')
+vereq(a[3::-2], '31')
+vereq(a[-100:100:], a)
+vereq(a[100:-100:-1], a[::-1])
+vereq(a[-100L:100L:2L], '02468')
+
+if have_unicode:
+ a = unicode('0123456789', 'ascii')
+ vereq(a[::], a)
+ vereq(a[::2], unicode('02468', 'ascii'))
+ vereq(a[1::2], unicode('13579', 'ascii'))
+ vereq(a[::-1], unicode('9876543210', 'ascii'))
+ vereq(a[::-2], unicode('97531', 'ascii'))
+ vereq(a[3::-2], unicode('31', 'ascii'))
+ vereq(a[-100:100:], a)
+ vereq(a[100:-100:-1], a[::-1])
+ vereq(a[-100L:100L:2L], unicode('02468', 'ascii'))
+
+
print '6.5.2 Tuples'
if len(()) != 0: raise TestFailed, 'len(())'
if len((1,)) != 1: raise TestFailed, 'len((1,))'
@@ -207,6 +232,19 @@ if x != (): raise TestFailed, 'tuple inplace add from () to () failed'
x += (1,)
if x != (1,): raise TestFailed, 'tuple resize from () failed'
+# extended slicing - subscript only for tuples
+a = (0,1,2,3,4)
+vereq(a[::], a)
+vereq(a[::2], (0,2,4))
+vereq(a[1::2], (1,3))
+vereq(a[::-1], (4,3,2,1,0))
+vereq(a[::-2], (4,2,0))
+vereq(a[3::-2], (3,1))
+vereq(a[-100:100:], a)
+vereq(a[100:-100:-1], a[::-1])
+vereq(a[-100L:100L:2L], (0,2,4))
+
+
print '6.5.3 Lists'
if len([]) != 0: raise TestFailed, 'len([])'
if len([1,]) != 1: raise TestFailed, 'len([1,])'
@@ -322,6 +360,40 @@ if a[ -pow(2,128L): 3 ] != [0,1,2]:
if a[ 3: pow(2,145L) ] != [3,4]:
raise TestFailed, "list slicing with too-large long integer"
+
+# extended slicing
+
+# subscript
+a = [0,1,2,3,4]
+vereq(a[::], a)
+vereq(a[::2], [0,2,4])
+vereq(a[1::2], [1,3])
+vereq(a[::-1], [4,3,2,1,0])
+vereq(a[::-2], [4,2,0])
+vereq(a[3::-2], [3,1])
+vereq(a[-100:100:], a)
+vereq(a[100:-100:-1], a[::-1])
+vereq(a[-100L:100L:2L], [0,2,4])
+# deletion
+del a[::2]
+vereq(a, [1,3])
+a = range(5)
+del a[1::2]
+vereq(a, [0,2,4])
+a = range(5)
+del a[1::-2]
+vereq(a, [0,2,3,4])
+# assignment
+a = range(10)
+a[::2] = [-1]*5
+vereq(a, [-1, 1, -1, 3, -1, 5, -1, 7, -1, 9])
+a = range(10)
+a[::-4] = [10]*3
+vereq(a, [0, 10, 2, 3, 4, 10, 6, 7, 8 ,10])
+a = range(4)
+a[::-1] = a
+vereq(a, [3, 2, 1, 0])
+
print '6.6 Mappings == Dictionaries'
d = {}
if d.keys() != []: raise TestFailed, '{}.keys()'
diff --git a/Misc/NEWS b/Misc/NEWS
index 752082b..9c51c1f 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -6,6 +6,10 @@ Type/class unification and new-style classes
Core and builtins
+- Most builtin sequences now support "extended slices", i.e. slices
+ with a third "stride" parameter. For example, "range(10)[1:6:2]"
+ evaluates to [1, 3, 5].
+
- Cycles going through the __class__ link of a new-style instance are
now detected by the garbage collector.
diff --git a/Objects/listobject.c b/Objects/listobject.c
index bd391af..3ddf032 100644
--- a/Objects/listobject.c
+++ b/Objects/listobject.c
@@ -1684,6 +1684,192 @@ static char list_doc[] =
staticforward PyObject * list_iter(PyObject *seq);
+static PyObject*
+list_subscript(PyListObject* self, PyObject* item)
+{
+ if (PyInt_Check(item)) {
+ long i = PyInt_AS_LONG(item);
+ if (i < 0)
+ i += PyList_GET_SIZE(self);
+ return list_item(self, i);
+ }
+ else if (PyLong_Check(item)) {
+ long i = PyLong_AsLong(item);
+ if (i == -1 && PyErr_Occurred())
+ return NULL;
+ if (i < 0)
+ i += PyList_GET_SIZE(self);
+ return list_item(self, i);
+ }
+ else if (PySlice_Check(item)) {
+ int start, stop, step, slicelength, cur, i;
+ PyObject* result;
+ PyObject* it;
+
+ if (PySlice_GetIndicesEx((PySliceObject*)item, self->ob_size,
+ &start, &stop, &step, &slicelength) < 0) {
+ return NULL;
+ }
+
+ if (slicelength <= 0) {
+ return PyList_New(0);
+ }
+ else {
+ result = PyList_New(slicelength);
+ if (!result) return NULL;
+
+ for (cur = start, i = 0; i < slicelength;
+ cur += step, i++) {
+ it = PyList_GET_ITEM(self, cur);
+ Py_INCREF(it);
+ PyList_SET_ITEM(result, i, it);
+ }
+
+ return result;
+ }
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError,
+ "list indices must be integers");
+ return NULL;
+ }
+}
+
+static int
+list_ass_subscript(PyListObject* self, PyObject* item, PyObject* value)
+{
+ if (PyInt_Check(item)) {
+ long i = PyInt_AS_LONG(item);
+ if (i < 0)
+ i += PyList_GET_SIZE(self);
+ return list_ass_item(self, i, value);
+ }
+ else if (PyLong_Check(item)) {
+ long i = PyLong_AsLong(item);
+ if (i == -1 && PyErr_Occurred())
+ return -1;
+ if (i < 0)
+ i += PyList_GET_SIZE(self);
+ return list_ass_item(self, i, value);
+ }
+ else if (PySlice_Check(item)) {
+ int start, stop, step, slicelength;
+
+ if (PySlice_GetIndicesEx((PySliceObject*)item, self->ob_size,
+ &start, &stop, &step, &slicelength) < 0) {
+ return -1;
+ }
+
+ if (value == NULL) {
+ /* delete slice */
+ PyObject **garbage, **item;
+ int cur, i, j;
+
+ if (slicelength <= 0)
+ return 0;
+
+ if (step < 0) {
+ stop = start + 1;
+ start = stop + step*(slicelength - 1) - 1;
+ step = -step;
+ }
+
+ garbage = (PyObject**)
+ PyMem_MALLOC(slicelength*sizeof(PyObject*));
+
+ /* drawing pictures might help
+ understand these for loops */
+ for (cur = start, i = 0; cur < stop; cur += step, i++) {
+ garbage[i] = PyList_GET_ITEM(self, cur);
+
+ for (j = 0; j < step; j++) {
+ PyList_SET_ITEM(self, cur + j - i,
+ PyList_GET_ITEM(self, cur + j + 1));
+ }
+ }
+ for (cur = start + slicelength*step + 1;
+ cur < self->ob_size; cur++) {
+ PyList_SET_ITEM(self, cur - slicelength,
+ PyList_GET_ITEM(self, cur));
+ }
+ self->ob_size -= slicelength;
+ item = self->ob_item;
+ NRESIZE(item, PyObject*, self->ob_size);
+ self->ob_item = item;
+
+ for (i = 0; i < slicelength; i++) {
+ Py_DECREF(garbage[i]);
+ }
+ PyMem_FREE(garbage);
+
+ return 0;
+ }
+ else {
+ /* assign slice */
+ PyObject **garbage, *ins;
+ int cur, i;
+
+ if (!PyList_Check(value)) {
+ PyErr_Format(PyExc_TypeError,
+ "must assign list (not \"%.200s\") to slice",
+ value->ob_type->tp_name);
+ return -1;
+ }
+
+ if (PyList_GET_SIZE(value) != slicelength) {
+ PyErr_Format(PyExc_ValueError,
+ "attempt to assign list of size %d to extended slice of size %d",
+ PyList_Size(value), slicelength);
+ return -1;
+ }
+
+ if (!slicelength)
+ return 0;
+
+ /* protect against a[::-1] = a */
+ if (self == (PyListObject*)value) {
+ value = list_slice((PyListObject*)value, 0,
+ PyList_GET_SIZE(value));
+ }
+ else {
+ Py_INCREF(value);
+ }
+
+ garbage = (PyObject**)
+ PyMem_MALLOC(slicelength*sizeof(PyObject*));
+
+ for (cur = start, i = 0; i < slicelength;
+ cur += step, i++) {
+ garbage[i] = PyList_GET_ITEM(self, cur);
+
+ ins = PyList_GET_ITEM(value, i);
+ Py_INCREF(ins);
+ PyList_SET_ITEM(self, cur, ins);
+ }
+
+ for (i = 0; i < slicelength; i++) {
+ Py_DECREF(garbage[i]);
+ }
+
+ PyMem_FREE(garbage);
+ Py_DECREF(value);
+
+ return 0;
+ }
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError,
+ "list indices must be integers");
+ return -1;
+ }
+}
+
+static PyMappingMethods list_as_mapping = {
+ (inquiry)list_length,
+ (binaryfunc)list_subscript,
+ (objobjargproc)list_ass_subscript
+};
+
PyTypeObject PyList_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0,
@@ -1698,7 +1884,7 @@ PyTypeObject PyList_Type = {
(reprfunc)list_repr, /* tp_repr */
0, /* tp_as_number */
&list_as_sequence, /* tp_as_sequence */
- 0, /* tp_as_mapping */
+ &list_as_mapping, /* tp_as_mapping */
list_nohash, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
diff --git a/Objects/sliceobject.c b/Objects/sliceobject.c
index 7562d34..7499d31 100644
--- a/Objects/sliceobject.c
+++ b/Objects/sliceobject.c
@@ -109,6 +109,59 @@ PySlice_GetIndices(PySliceObject *r, int length,
return 0;
}
+int
+PySlice_GetIndicesEx(PySliceObject *r, int length,
+ int *start, int *stop, int *step, int *slicelength)
+{
+ /* this is harder to get right than you might think */
+ int defstart, defstop;
+
+ if (r->step == Py_None) {
+ *step = 1;
+ } else {
+ *step = PyInt_AsLong(r->step);
+ if (*step == -1 && PyErr_Occurred()) {
+ return -1;
+ }
+ else if (*step == 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "slice step cannot be zero");
+ return -1;
+ }
+ }
+
+ defstart = *step < 0 ? length-1 : 0;
+ defstop = *step < 0 ? -1 : length;
+
+ if (r->start == Py_None) {
+ *start = defstart;
+ } else {
+ if (!_PyEval_SliceIndex(r->start, start)) return -1;
+ if (*start < 0) *start += length;
+ if (*start < 0) *start = (*step < 0) ? -1 : 0;
+ if (*start >= length)
+ *start = (*step < 0) ? length - 1 : length;
+ }
+
+ if (r->stop == Py_None) {
+ *stop = defstop;
+ } else {
+ if (!_PyEval_SliceIndex(r->stop, stop)) return -1;
+ if (*stop < 0) *stop += length;
+ if (*stop < 0) *stop = -1;
+ if (*stop > length) *stop = length;
+ }
+
+ if (*step < 0) {
+ *slicelength = (*stop-*start+1)/(*step)+1;
+ } else {
+ *slicelength = (*stop-*start-1)/(*step)+1;
+ }
+ if (*slicelength < 0) *slicelength = 0;
+
+ return 0;
+}
+
static void
slice_dealloc(PySliceObject *r)
{
diff --git a/Objects/stringobject.c b/Objects/stringobject.c
index 89e414a..b88778e 100644
--- a/Objects/stringobject.c
+++ b/Objects/stringobject.c
@@ -940,6 +940,60 @@ string_hash(PyStringObject *a)
return x;
}
+static PyObject*
+string_subscript(PyStringObject* self, PyObject* item)
+{
+ if (PyInt_Check(item)) {
+ long i = PyInt_AS_LONG(item);
+ if (i < 0)
+ i += PyString_GET_SIZE(self);
+ return string_item(self,i);
+ }
+ else if (PyLong_Check(item)) {
+ long i = PyLong_AsLong(item);
+ if (i == -1 && PyErr_Occurred())
+ return NULL;
+ if (i < 0)
+ i += PyString_GET_SIZE(self);
+ return string_item(self,i);
+ }
+ else if (PySlice_Check(item)) {
+ int start, stop, step, slicelength, cur, i;
+ char* source_buf;
+ char* result_buf;
+ PyObject* result;
+
+ if (PySlice_GetIndicesEx((PySliceObject*)item,
+ PyString_GET_SIZE(self),
+ &start, &stop, &step, &slicelength) < 0) {
+ return NULL;
+ }
+
+ if (slicelength <= 0) {
+ return PyString_FromStringAndSize("", 0);
+ }
+ else {
+ source_buf = PyString_AsString((PyObject*)self);
+ result_buf = PyMem_Malloc(slicelength);
+
+ for (cur = start, i = 0; i < slicelength;
+ cur += step, i++) {
+ result_buf[i] = source_buf[cur];
+ }
+
+ result = PyString_FromStringAndSize(result_buf,
+ slicelength);
+ PyMem_Free(result_buf);
+ return result;
+ }
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError,
+ "string indices must be integers");
+ return NULL;
+ }
+}
+
static int
string_buffer_getreadbuf(PyStringObject *self, int index, const void **ptr)
{
@@ -991,6 +1045,12 @@ static PySequenceMethods string_as_sequence = {
(objobjproc)string_contains /*sq_contains*/
};
+static PyMappingMethods string_as_mapping = {
+ (inquiry)string_length,
+ (binaryfunc)string_subscript,
+ 0,
+};
+
static PyBufferProcs string_as_buffer = {
(getreadbufferproc)string_buffer_getreadbuf,
(getwritebufferproc)string_buffer_getwritebuf,
@@ -2929,7 +2989,7 @@ PyTypeObject PyString_Type = {
(reprfunc)string_repr, /* tp_repr */
0, /* tp_as_number */
&string_as_sequence, /* tp_as_sequence */
- 0, /* tp_as_mapping */
+ &string_as_mapping, /* tp_as_mapping */
(hashfunc)string_hash, /* tp_hash */
0, /* tp_call */
(reprfunc)string_str, /* tp_str */
@@ -3349,7 +3409,7 @@ PyString_Format(PyObject *format, PyObject *args)
arglen = -1;
argidx = -2;
}
- if (args->ob_type->tp_as_mapping)
+ if (args->ob_type->tp_as_mapping && !PyTuple_Check(args))
dict = args;
while (--fmtcnt >= 0) {
if (*fmt != '%') {
diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c
index 581ccf9..203801e 100644
--- a/Objects/tupleobject.c
+++ b/Objects/tupleobject.c
@@ -541,6 +541,63 @@ static PySequenceMethods tuple_as_sequence = {
(objobjproc)tuplecontains, /* sq_contains */
};
+static PyObject*
+tuplesubscript(PyTupleObject* self, PyObject* item)
+{
+ if (PyInt_Check(item)) {
+ long i = PyInt_AS_LONG(item);
+ if (i < 0)
+ i += PyTuple_GET_SIZE(self);
+ return tupleitem(self, i);
+ }
+ else if (PyLong_Check(item)) {
+ long i = PyLong_AsLong(item);
+ if (i == -1 && PyErr_Occurred())
+ return NULL;
+ if (i < 0)
+ i += PyTuple_GET_SIZE(self);
+ return tupleitem(self, i);
+ }
+ else if (PySlice_Check(item)) {
+ int start, stop, step, slicelength, cur, i;
+ PyObject* result;
+ PyObject* it;
+
+ if (PySlice_GetIndicesEx((PySliceObject*)item,
+ PyTuple_GET_SIZE(self),
+ &start, &stop, &step, &slicelength) < 0) {
+ return NULL;
+ }
+
+ if (slicelength <= 0) {
+ return PyTuple_New(0);
+ }
+ else {
+ result = PyTuple_New(slicelength);
+
+ for (cur = start, i = 0; i < slicelength;
+ cur += step, i++) {
+ it = PyTuple_GET_ITEM(self, cur);
+ Py_INCREF(it);
+ PyTuple_SET_ITEM(result, i, it);
+ }
+
+ return result;
+ }
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError,
+ "tuple indices must be integers");
+ return NULL;
+ }
+}
+
+static PyMappingMethods tuple_as_mapping = {
+ (inquiry)tuplelength,
+ (binaryfunc)tuplesubscript,
+ 0
+};
+
PyTypeObject PyTuple_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0,
@@ -555,7 +612,7 @@ PyTypeObject PyTuple_Type = {
(reprfunc)tuplerepr, /* tp_repr */
0, /* tp_as_number */
&tuple_as_sequence, /* tp_as_sequence */
- 0, /* tp_as_mapping */
+ &tuple_as_mapping, /* tp_as_mapping */
(hashfunc)tuplehash, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c
index 2cb97bc..6e0fd9f 100644
--- a/Objects/unicodeobject.c
+++ b/Objects/unicodeobject.c
@@ -5061,6 +5061,58 @@ static PySequenceMethods unicode_as_sequence = {
(objobjproc)PyUnicode_Contains, /*sq_contains*/
};
+static PyObject*
+unicode_subscript(PyUnicodeObject* self, PyObject* item)
+{
+ if (PyInt_Check(item)) {
+ long i = PyInt_AS_LONG(item);
+ if (i < 0)
+ i += PyString_GET_SIZE(self);
+ return unicode_getitem(self, i);
+ } else if (PyLong_Check(item)) {
+ long i = PyLong_AsLong(item);
+ if (i == -1 && PyErr_Occurred())
+ return NULL;
+ if (i < 0)
+ i += PyString_GET_SIZE(self);
+ return unicode_getitem(self, i);
+ } else if (PySlice_Check(item)) {
+ int start, stop, step, slicelength, cur, i;
+ Py_UNICODE* source_buf;
+ Py_UNICODE* result_buf;
+ PyObject* result;
+
+ if (PySlice_GetIndicesEx((PySliceObject*)item, PyString_GET_SIZE(self),
+ &start, &stop, &step, &slicelength) < 0) {
+ return NULL;
+ }
+
+ if (slicelength <= 0) {
+ return PyUnicode_FromUnicode(NULL, 0);
+ } else {
+ source_buf = PyUnicode_AS_UNICODE((PyObject*)self);
+ result_buf = PyMem_MALLOC(slicelength*sizeof(Py_UNICODE));
+
+ for (cur = start, i = 0; i < slicelength; cur += step, i++) {
+ result_buf[i] = source_buf[cur];
+ }
+
+ result = PyUnicode_FromUnicode(result_buf, slicelength);
+ PyMem_FREE(result_buf);
+ return result;
+ }
+ } else {
+ PyErr_SetString(PyExc_TypeError, "string indices must be integers");
+ return NULL;
+ }
+}
+
+static PyMappingMethods unicode_as_mapping = {
+ (inquiry)unicode_length, /* mp_length */
+ (binaryfunc)unicode_subscript, /* mp_subscript */
+ (objobjargproc)0, /* mp_ass_subscript */
+};
+
static int
unicode_buffer_getreadbuf(PyUnicodeObject *self,
int index,
@@ -5355,7 +5407,7 @@ PyObject *PyUnicode_Format(PyObject *format,
arglen = -1;
argidx = -2;
}
- if (args->ob_type->tp_as_mapping)
+ if (args->ob_type->tp_as_mapping && !PyTuple_Check(args))
dict = args;
while (--fmtcnt >= 0) {
@@ -5817,7 +5869,7 @@ PyTypeObject PyUnicode_Type = {
(reprfunc) unicode_repr, /* tp_repr */
0, /* tp_as_number */
&unicode_as_sequence, /* tp_as_sequence */
- 0, /* tp_as_mapping */
+ &unicode_as_mapping, /* tp_as_mapping */
(hashfunc) unicode_hash, /* tp_hash*/
0, /* tp_call*/
(reprfunc) unicode_str, /* tp_str */