Commit 1d75a79c authored by Thomas Wouters's avatar Thomas Wouters

Apply SF patch #101029: call __getitem__ with a proper slice object if there

is no __getslice__ available. Also does the same for C extension types.
Includes rudimentary documentation (it could use a cross reference to the
section on slice objects, I couldn't figure out how to do that) and a test
suite for all Python __hooks__ I could think of, including the new
behaviour.
parent 68add2e9
...@@ -1042,11 +1042,12 @@ objects. The first set of methods is used either to emulate a ...@@ -1042,11 +1042,12 @@ objects. The first set of methods is used either to emulate a
sequence or to emulate a mapping; the difference is that for a sequence or to emulate a mapping; the difference is that for a
sequence, the allowable keys should be the integers \var{k} for which sequence, the allowable keys should be the integers \var{k} for which
\code{0 <= \var{k} < \var{N}} where \var{N} is the length of the \code{0 <= \var{k} < \var{N}} where \var{N} is the length of the
sequence, and the method \method{__getslice__()} (see below) should be sequence, or slice objects, which define a range of items. (For backwards
defined. It is also recommended that mappings provide methods compatibility, the method \method{__getslice__()} (see below) can also be
\method{keys()}, \method{values()}, \method{items()}, defined to handle simple, but not extended slices.) It is also recommended
\method{has_key()}, \method{get()}, \method{clear()}, \method{copy()}, that mappings provide methods \method{keys()}, \method{values()},
and \method{update()} behaving similar to those for \method{items()}, \method{has_key()}, \method{get()}, \method{clear()},
\method{copy()}, and \method{update()} behaving similar to those for
Python's standard dictionary objects; mutable sequences should provide Python's standard dictionary objects; mutable sequences should provide
methods \method{append()}, \method{count()}, \method{index()}, methods \method{append()}, \method{count()}, \method{index()},
\method{insert()}, \method{pop()}, \method{remove()}, \method{reverse()} \method{insert()}, \method{pop()}, \method{remove()}, \method{reverse()}
...@@ -1141,22 +1142,30 @@ If the instance does not implement the \method{__len__()} method, an ...@@ -1141,22 +1142,30 @@ If the instance does not implement the \method{__len__()} method, an
No guarantee is made that indexes adjusted this way are not still No guarantee is made that indexes adjusted this way are not still
negative. Indexes which are greater than the length of the sequence negative. Indexes which are greater than the length of the sequence
are not modified. are not modified.
This method is deprecated. If no \method{__getslice__()} is found, a slice
object is created instead, and passed to \method{__getitem__()} instead.
\end{methoddesc} \end{methoddesc}
\begin{methoddesc}[sequence object]{__setslice__}{self, i, j, sequence} \begin{methoddesc}[sequence object]{__setslice__}{self, i, j, sequence}
Called to implement assignment to \code{\var{self}[\var{i}:\var{j}]}. Called to implement assignment to \code{\var{self}[\var{i}:\var{j}]}.
Same notes for \var{i} and \var{j} as for \method{__getslice__()}. Same notes for \var{i} and \var{j} as for \method{__getslice__()}.
This method is deprecated. If no \method{__setslice__()} is found, a slice
object is created instead, and passed to \method{__setitem__()} instead.
\end{methoddesc} \end{methoddesc}
\begin{methoddesc}[sequence object]{__delslice__}{self, i, j} \begin{methoddesc}[sequence object]{__delslice__}{self, i, j}
Called to implement deletion of \code{\var{self}[\var{i}:\var{j}]}. Called to implement deletion of \code{\var{self}[\var{i}:\var{j}]}.
Same notes for \var{i} and \var{j} as for \method{__getslice__()}. Same notes for \var{i} and \var{j} as for \method{__getslice__()}.
This method is deprecated. If no \method{__delslice__()} is found, a slice
object is created instead, and passed to \method{__delitem__()} instead.
\end{methoddesc} \end{methoddesc}
Notice that these methods are only invoked when a single slice with a Notice that these methods are only invoked when a single slice with a single
single colon is used. For slice operations involving extended slice colon is used, and the slice method is available. For slice operations
notation, \method{__getitem__()}, \method{__setitem__()} involving extended slice notation, or in absence of the slice methods,
or\method{__delitem__()} is called. \method{__getitem__()}, \method{__setitem__()} or \method{__delitem__()} is
called with a slice object as argument.
\subsection{Emulating numeric types\label{numeric-types}} \subsection{Emulating numeric types\label{numeric-types}}
......
test_class
__init__: ()
__coerce__: (1,)
__add__: (1,)
__coerce__: (1,)
__radd__: (1,)
__coerce__: (1,)
__sub__: (1,)
__coerce__: (1,)
__rsub__: (1,)
__coerce__: (1,)
__mul__: (1,)
__coerce__: (1,)
__rmul__: (1,)
__coerce__: (1,)
__div__: (1,)
__coerce__: (1,)
__rdiv__: (1,)
__coerce__: (1,)
__mod__: (1,)
__coerce__: (1,)
__rmod__: (1,)
__coerce__: (1,)
__divmod__: (1,)
__coerce__: (1,)
__rdivmod__: (1,)
__coerce__: (1,)
__pow__: (1,)
__coerce__: (1,)
__rpow__: (1,)
__coerce__: (1,)
__rshift__: (1,)
__coerce__: (1,)
__rrshift__: (1,)
__coerce__: (1,)
__lshift__: (1,)
__coerce__: (1,)
__rlshift__: (1,)
__coerce__: (1,)
__and__: (1,)
__coerce__: (1,)
__rand__: (1,)
__coerce__: (1,)
__or__: (1,)
__coerce__: (1,)
__ror__: (1,)
__coerce__: (1,)
__xor__: (1,)
__coerce__: (1,)
__rxor__: (1,)
__contains__: (1,)
__getitem__: (1,)
__setitem__: (1, 1)
__delitem__: (1,)
__getslice__: (0, 42)
__setslice__: (0, 42, 'The Answer')
__delslice__: (0, 42)
__getitem__: (slice(2, 1024, 10),)
__setitem__: (slice(2, 1024, 10), 'A lot')
__delitem__: (slice(2, 1024, 10),)
__getitem__: ((slice(None, 42, None), Ellipsis, slice(None, 24, None), 24, 100),)
__setitem__: ((slice(None, 42, None), Ellipsis, slice(None, 24, None), 24, 100), 'Strange')
__delitem__: ((slice(None, 42, None), Ellipsis, slice(None, 24, None), 24, 100),)
__getitem__: (slice(0, 42, None),)
__setitem__: (slice(0, 42, None), 'The Answer')
__delitem__: (slice(0, 42, None),)
__neg__: ()
__pos__: ()
__abs__: ()
__int__: ()
__long__: ()
__float__: ()
__oct__: ()
__hex__: ()
__hash__: ()
__repr__: ()
__str__: ()
__coerce__: (1,)
__cmp__: (1,)
__coerce__: (1,)
__cmp__: (1,)
__coerce__: (1,)
__cmp__: (1,)
__coerce__: (1,)
__cmp__: (1,)
__coerce__: (1,)
__cmp__: (1,)
__coerce__: (1,)
__cmp__: (1,)
__coerce__: (1,)
__cmp__: (1,)
__coerce__: (1,)
__cmp__: (1,)
__coerce__: (1,)
__cmp__: (1,)
__coerce__: (1,)
__cmp__: (1,)
__del__: ()
__getattr__: ('spam',)
__setattr__: ('eggs', 'spam, spam, spam and ham')
__delattr__: ('cardinal',)
"Test the functionality of Python classes implementing operators."
testmeths = [
# Binary operations
"add",
"radd",
"sub",
"rsub",
"mul",
"rmul",
"div",
"rdiv",
"mod",
"rmod",
"divmod",
"rdivmod",
"pow",
"rpow",
"rshift",
"rrshift",
"lshift",
"rlshift",
"and",
"rand",
"or",
"ror",
"xor",
"rxor",
# List/dict operations
"contains",
"getitem",
"getslice",
"setitem",
"setslice",
"delitem",
"delslice",
# Unary operations
"neg",
"pos",
"abs",
"int",
"long",
"float",
"oct",
"hex",
# generic operations
"init",
"del",
]
# These need to return something other than None
# "coerce",
# "hash",
# "str",
# "repr",
# These are separate because they can influence the test of other methods.
# "getattr",
# "setattr",
# "delattr",
class AllTests:
def __coerce__(self, *args):
print "__coerce__:", args
return (self,) + args
def __hash__(self, *args):
print "__hash__:", args
return id(self)
def __str__(self, *args):
print "__str__:", args
return "AllTests"
def __repr__(self, *args):
print "__repr__:", args
return "AllTests"
def __cmp__(self, *args):
print "__cmp__:", args
return 0
for method in testmeths:
exec("""def __%(method)s__(self, *args):
print "__%(method)s__:", args
"""%locals(), AllTests.__dict__);
# this also tests __init__ of course.
testme = AllTests()
# Binary operations
testme + 1
1 + testme
testme - 1
1 - testme
testme * 1
1 * testme
testme / 1
1 / testme
testme % 1
1 % testme
divmod(testme,1)
divmod(1, testme)
testme ** 1
1 ** testme
testme >> 1
1 >> testme
testme << 1
1 << testme
testme & 1
1 & testme
testme | 1
1 | testme
testme ^ 1
1 ^ testme
# List/dict operations
1 in testme
testme[1]
testme[1] = 1
del testme[1]
testme[:42]
testme[:42] = "The Answer"
del testme[:42]
testme[2:1024:10]
testme[2:1024:10] = "A lot"
del testme[2:1024:10]
testme[:42, ..., :24:, 24, 100]
testme[:42, ..., :24:, 24, 100] = "Strange"
del testme[:42, ..., :24:, 24, 100]
# Now remove the slice hooks to see if converting normal slices to slice
# object works.
del AllTests.__getslice__
del AllTests.__setslice__
del AllTests.__delslice__
testme[:42]
testme[:42] = "The Answer"
del testme[:42]
# Unary operations
-testme
+testme
abs(testme)
int(testme)
long(testme)
float(testme)
oct(testme)
hex(testme)
# And the rest...
hash(testme)
repr(testme)
str(testme)
testme == 1
testme < 1
testme > 1
testme <> 1
testme != 1
1 == testme
1 < testme
1 > testme
1 <> testme
1 != testme
# This test has to be last (duh.)
del testme
# Interfering tests
class ExtraTests:
def __getattr__(self, *args):
print "__getattr__:", args
return "SomeVal"
def __setattr__(self, *args):
print "__setattr__:", args
def __delattr__(self, *args):
print "__delattr__:", args
testme = ExtraTests()
testme.spam
testme.eggs = "spam, spam, spam and ham"
del testme.cardinal
...@@ -876,10 +876,29 @@ PySequence_GetItem(PyObject *s, int i) ...@@ -876,10 +876,29 @@ PySequence_GetItem(PyObject *s, int i)
return type_error("unindexable object"); return type_error("unindexable object");
} }
static PyObject *
sliceobj_from_intint(int i, int j)
{
PyObject *start, *end, *slice;
start = PyInt_FromLong((long)i);
if (!start)
return NULL;
end = PyInt_FromLong((long)j);
if (!end) {
Py_DECREF(start);
return NULL;
}
slice = PySlice_New(start, end, NULL);
Py_DECREF(start);
Py_DECREF(end);
return slice;
}
PyObject * PyObject *
PySequence_GetSlice(PyObject *s, int i1, int i2) PySequence_GetSlice(PyObject *s, int i1, int i2)
{ {
PySequenceMethods *m; PySequenceMethods *m;
PyMappingMethods *mp;
if (!s) return null_error(); if (!s) return null_error();
...@@ -897,6 +916,14 @@ PySequence_GetSlice(PyObject *s, int i1, int i2) ...@@ -897,6 +916,14 @@ PySequence_GetSlice(PyObject *s, int i1, int i2)
} }
} }
return m->sq_slice(s, i1, i2); return m->sq_slice(s, i1, i2);
} else if ((mp = s->ob_type->tp_as_mapping) && mp->mp_subscript) {
PyObject *res;
PyObject *slice = sliceobj_from_intint(i1, i2);
if (!slice)
return NULL;
res = mp->mp_subscript(s, slice);
Py_DECREF(slice);
return res;
} }
return type_error("unsliceable object"); return type_error("unsliceable object");
...@@ -960,6 +987,7 @@ int ...@@ -960,6 +987,7 @@ int
PySequence_SetSlice(PyObject *s, int i1, int i2, PyObject *o) PySequence_SetSlice(PyObject *s, int i1, int i2, PyObject *o)
{ {
PySequenceMethods *m; PySequenceMethods *m;
PyMappingMethods *mp;
if (s == NULL) { if (s == NULL) {
null_error(); null_error();
...@@ -980,7 +1008,16 @@ PySequence_SetSlice(PyObject *s, int i1, int i2, PyObject *o) ...@@ -980,7 +1008,16 @@ PySequence_SetSlice(PyObject *s, int i1, int i2, PyObject *o)
} }
} }
return m->sq_ass_slice(s, i1, i2, o); return m->sq_ass_slice(s, i1, i2, o);
} else if ((mp = s->ob_type->tp_as_mapping) && mp->mp_ass_subscript) {
int res;
PyObject *slice = sliceobj_from_intint(i1, i2);
if (!slice)
return -1;
res = mp->mp_ass_subscript(s, slice, o);
Py_DECREF(slice);
return res;
} }
type_error("object doesn't support slice assignment"); type_error("object doesn't support slice assignment");
return -1; return -1;
} }
......
...@@ -971,6 +971,27 @@ instance_item(PyInstanceObject *inst, int i) ...@@ -971,6 +971,27 @@ instance_item(PyInstanceObject *inst, int i)
return res; return res;
} }
static PyObject *
sliceobj_from_intint(int i, int j)
{
PyObject *start, *end, *res;
start = PyInt_FromLong((long)i);
if (!start)
return NULL;
end = PyInt_FromLong((long)j);
if (!end) {
Py_DECREF(start);
return NULL;
}
res = PySlice_New(start, end, NULL);
Py_DECREF(start);
Py_DECREF(end);
return res;
}
static PyObject * static PyObject *
instance_slice(PyInstanceObject *inst, int i, int j) instance_slice(PyInstanceObject *inst, int i, int j)
{ {
...@@ -980,9 +1001,19 @@ instance_slice(PyInstanceObject *inst, int i, int j) ...@@ -980,9 +1001,19 @@ instance_slice(PyInstanceObject *inst, int i, int j)
if (getslicestr == NULL) if (getslicestr == NULL)
getslicestr = PyString_InternFromString("__getslice__"); getslicestr = PyString_InternFromString("__getslice__");
func = instance_getattr(inst, getslicestr); func = instance_getattr(inst, getslicestr);
if (func == NULL)
return NULL; if (func == NULL) {
arg = Py_BuildValue("(ii)", i, j); PyErr_Clear();
if (getitemstr == NULL)
getitemstr = PyString_InternFromString("__getitem__");
func = instance_getattr(inst, getitemstr);
if (func == NULL)
return NULL;
arg = Py_BuildValue("(N)", sliceobj_from_intint(i, j));
} else
arg = Py_BuildValue("(ii)", i, j);
if (arg == NULL) { if (arg == NULL) {
Py_DECREF(func); Py_DECREF(func);
return NULL; return NULL;
...@@ -1038,19 +1069,39 @@ instance_ass_slice(PyInstanceObject *inst, int i, int j, PyObject *value) ...@@ -1038,19 +1069,39 @@ instance_ass_slice(PyInstanceObject *inst, int i, int j, PyObject *value)
delslicestr = delslicestr =
PyString_InternFromString("__delslice__"); PyString_InternFromString("__delslice__");
func = instance_getattr(inst, delslicestr); func = instance_getattr(inst, delslicestr);
if (func == NULL) {
PyErr_Clear();
if (delitemstr == NULL)
delitemstr =
PyString_InternFromString("__delitem__");
func = instance_getattr(inst, delitemstr);
if (func == NULL)
return -1;
arg = Py_BuildValue("(N)",
sliceobj_from_intint(i, j));
} else
arg = Py_BuildValue("(ii)", i, j);
} }
else { else {
if (setslicestr == NULL) if (setslicestr == NULL)
setslicestr = setslicestr =
PyString_InternFromString("__setslice__"); PyString_InternFromString("__setslice__");
func = instance_getattr(inst, setslicestr); func = instance_getattr(inst, setslicestr);
if (func == NULL) {
PyErr_Clear();
if (setitemstr == NULL)
setitemstr =
PyString_InternFromString("__setitem__");
func = instance_getattr(inst, setitemstr);
if (func == NULL)
return -1;
arg = Py_BuildValue("(NO)",
sliceobj_from_intint(i, j), value);
} else
arg = Py_BuildValue("(iiO)", i, j, value);
} }
if (func == NULL)
return -1;
if (value == NULL)
arg = Py_BuildValue("(ii)", i, j);
else
arg = Py_BuildValue("(iiO)", i, j, value);
if (arg == NULL) { if (arg == NULL) {
Py_DECREF(func); Py_DECREF(func);
return -1; return -1;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment