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.
This commit is contained in:
Thomas Wouters 2000-08-17 22:37:32 +00:00
parent 68add2e938
commit 1d75a79c00
5 changed files with 435 additions and 18 deletions

View File

@ -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, 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
sequence, and the method \method{__getslice__()} (see below) should be
defined. It is also recommended that mappings provide methods
\method{keys()}, \method{values()}, \method{items()},
\method{has_key()}, \method{get()}, \method{clear()}, \method{copy()},
and \method{update()} behaving similar to those for
sequence, or slice objects, which define a range of items. (For backwards
compatibility, the method \method{__getslice__()} (see below) can also be
defined to handle simple, but not extended slices.) It is also recommended
that mappings provide methods \method{keys()}, \method{values()},
\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
methods \method{append()}, \method{count()}, \method{index()},
\method{insert()}, \method{pop()}, \method{remove()}, \method{reverse()}
@ -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
negative. Indexes which are greater than the length of the sequence
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}
\begin{methoddesc}[sequence object]{__setslice__}{self, i, j, sequence}
Called to implement assignment to \code{\var{self}[\var{i}:\var{j}]}.
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}
\begin{methoddesc}[sequence object]{__delslice__}{self, i, 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__()}.
This method is deprecated. If no \method{__delslice__()} is found, a slice
object is created instead, and passed to \method{__delitem__()} instead.
\end{methoddesc}
Notice that these methods are only invoked when a single slice with a
single colon is used. For slice operations involving extended slice
notation, \method{__getitem__()}, \method{__setitem__()}
or\method{__delitem__()} is called.
Notice that these methods are only invoked when a single slice with a single
colon is used, and the slice method is available. For slice operations
involving extended slice notation, or in absence of the slice methods,
\method{__getitem__()}, \method{__setitem__()} or \method{__delitem__()} is
called with a slice object as argument.
\subsection{Emulating numeric types\label{numeric-types}}

101
Lib/test/output/test_class Normal file
View File

@ -0,0 +1,101 @@
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',)

219
Lib/test/test_class.py Normal file
View File

@ -0,0 +1,219 @@
"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

View File

@ -876,10 +876,29 @@ PySequence_GetItem(PyObject *s, int i)
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 *
PySequence_GetSlice(PyObject *s, int i1, int i2)
{
PySequenceMethods *m;
PyMappingMethods *mp;
if (!s) return null_error();
@ -897,6 +916,14 @@ PySequence_GetSlice(PyObject *s, int i1, int 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");
@ -960,6 +987,7 @@ int
PySequence_SetSlice(PyObject *s, int i1, int i2, PyObject *o)
{
PySequenceMethods *m;
PyMappingMethods *mp;
if (s == NULL) {
null_error();
@ -980,7 +1008,16 @@ PySequence_SetSlice(PyObject *s, int i1, int i2, PyObject *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");
return -1;
}

View File

@ -971,6 +971,27 @@ instance_item(PyInstanceObject *inst, int i)
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 *
instance_slice(PyInstanceObject *inst, int i, int j)
{
@ -980,9 +1001,19 @@ instance_slice(PyInstanceObject *inst, int i, int j)
if (getslicestr == NULL)
getslicestr = PyString_InternFromString("__getslice__");
func = instance_getattr(inst, getslicestr);
if (func == NULL)
return NULL;
arg = Py_BuildValue("(ii)", i, j);
if (func == NULL) {
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) {
Py_DECREF(func);
return NULL;
@ -1038,19 +1069,39 @@ instance_ass_slice(PyInstanceObject *inst, int i, int j, PyObject *value)
delslicestr =
PyString_InternFromString("__delslice__");
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 {
if (setslicestr == NULL)
setslicestr =
PyString_InternFromString("__setslice__");
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) {
Py_DECREF(func);
return -1;