Commit 3ee5e622 authored by Guido van Rossum's avatar Guido van Rossum

Hopefully this addresses the remaining issues of SF bugs 459235 and

473985.  Through a subtle rearrangement of some members in the etype
struct (!), mapping methods are now preferred over sequence methods,
which is necessary to support str.__getitem__("hello", slice(4)) etc.
parent 012a3ab4
...@@ -3099,6 +3099,51 @@ def copy_setstate(): ...@@ -3099,6 +3099,51 @@ def copy_setstate():
vereq(b.foo, 24) vereq(b.foo, 24)
vereq(b.getfoo(), 24) vereq(b.getfoo(), 24)
def slices():
if verbose:
print "Testing cases with slices and overridden __getitem__ ..."
# Strings
vereq("hello"[:4], "hell")
vereq("hello"[slice(4)], "hell")
vereq(str.__getitem__("hello", slice(4)), "hell")
class S(str):
def __getitem__(self, x):
return str.__getitem__(self, x)
vereq(S("hello")[:4], "hell")
vereq(S("hello")[slice(4)], "hell")
vereq(S("hello").__getitem__(slice(4)), "hell")
# Tuples
vereq((1,2,3)[:2], (1,2))
vereq((1,2,3)[slice(2)], (1,2))
vereq(tuple.__getitem__((1,2,3), slice(2)), (1,2))
class T(tuple):
def __getitem__(self, x):
return tuple.__getitem__(self, x)
vereq(T((1,2,3))[:2], (1,2))
vereq(T((1,2,3))[slice(2)], (1,2))
vereq(T((1,2,3)).__getitem__(slice(2)), (1,2))
# Lists
vereq([1,2,3][:2], [1,2])
vereq([1,2,3][slice(2)], [1,2])
vereq(list.__getitem__([1,2,3], slice(2)), [1,2])
class L(list):
def __getitem__(self, x):
return list.__getitem__(self, x)
vereq(L([1,2,3])[:2], [1,2])
vereq(L([1,2,3])[slice(2)], [1,2])
vereq(L([1,2,3]).__getitem__(slice(2)), [1,2])
# Now do lists and __setitem__
a = L([1,2,3])
a[slice(1, 3)] = [3,2]
vereq(a, [1,3,2])
a[slice(0, 2, 1)] = [3,1]
vereq(a, [3,1,2])
a.__setitem__(slice(1, 3), [2,1])
vereq(a, [3,2,1])
a.__setitem__(slice(0, 2, 1), [2,3])
vereq(a, [2,3,1])
def do_this_first(): def do_this_first():
if verbose: if verbose:
print "Testing SF bug 551412 ..." print "Testing SF bug 551412 ..."
...@@ -3182,6 +3227,7 @@ def test_main(): ...@@ -3182,6 +3227,7 @@ def test_main():
docdescriptor() docdescriptor()
string_exceptions() string_exceptions()
copy_setstate() copy_setstate()
slices()
if verbose: print "All OK" if verbose: print "All OK"
if __name__ == "__main__": if __name__ == "__main__":
......
...@@ -8,10 +8,16 @@ ...@@ -8,10 +8,16 @@
/* The *real* layout of a type object when allocated on the heap */ /* The *real* layout of a type object when allocated on the heap */
/* XXX Should we publish this in a header file? */ /* XXX Should we publish this in a header file? */
typedef struct { typedef struct {
/* Note: there's a dependency on the order of these members
in slotptr() below. */
PyTypeObject type; PyTypeObject type;
PyNumberMethods as_number; PyNumberMethods as_number;
PySequenceMethods as_sequence;
PyMappingMethods as_mapping; PyMappingMethods as_mapping;
PySequenceMethods as_sequence; /* as_sequence comes after as_mapping,
so that the mapping wins when both
the mapping and the sequence define
a given operator (e.g. __getitem__).
see add_operators() below. */
PyBufferProcs as_buffer; PyBufferProcs as_buffer;
PyObject *name, *slots; PyObject *name, *slots;
PyMemberDef members[1]; PyMemberDef members[1];
...@@ -3870,16 +3876,17 @@ slotptr(PyTypeObject *type, int offset) ...@@ -3870,16 +3876,17 @@ slotptr(PyTypeObject *type, int offset)
{ {
char *ptr; char *ptr;
/* Note: this depends on the order of the members of etype! */
assert(offset >= 0); assert(offset >= 0);
assert(offset < offsetof(etype, as_buffer)); assert(offset < offsetof(etype, as_buffer));
if (offset >= offsetof(etype, as_mapping)) { if (offset >= offsetof(etype, as_sequence)) {
ptr = (void *)type->tp_as_mapping;
offset -= offsetof(etype, as_mapping);
}
else if (offset >= offsetof(etype, as_sequence)) {
ptr = (void *)type->tp_as_sequence; ptr = (void *)type->tp_as_sequence;
offset -= offsetof(etype, as_sequence); offset -= offsetof(etype, as_sequence);
} }
else if (offset >= offsetof(etype, as_mapping)) {
ptr = (void *)type->tp_as_mapping;
offset -= offsetof(etype, as_mapping);
}
else if (offset >= offsetof(etype, as_number)) { else if (offset >= offsetof(etype, as_number)) {
ptr = (void *)type->tp_as_number; ptr = (void *)type->tp_as_number;
offset -= offsetof(etype, as_number); offset -= offsetof(etype, as_number);
...@@ -4113,24 +4120,32 @@ fixup_slot_dispatchers(PyTypeObject *type) ...@@ -4113,24 +4120,32 @@ fixup_slot_dispatchers(PyTypeObject *type)
/* This function is called by PyType_Ready() to populate the type's /* This function is called by PyType_Ready() to populate the type's
dictionary with method descriptors for function slots. For each dictionary with method descriptors for function slots. For each
function slot (like tp_repr) that's defined in the type, one or function slot (like tp_repr) that's defined in the type, one or more
more corresponding descriptors are added in the type's tp_dict corresponding descriptors are added in the type's tp_dict dictionary
dictionary under the appropriate name (like __repr__). Some under the appropriate name (like __repr__). Some function slots
function slots cause more than one descriptor to be added (for cause more than one descriptor to be added (for example, the nb_add
example, the nb_add slot adds both __add__ and __radd__ slot adds both __add__ and __radd__ descriptors) and some function
descriptors) and some function slots compete for the same slots compete for the same descriptor (for example both sq_item and
descriptor (for example both sq_item and mp_subscript generate a mp_subscript generate a __getitem__ descriptor).
__getitem__ descriptor). This only adds new descriptors and
doesn't overwrite entries in tp_dict that were previously In the latter case, the first slotdef entry encoutered wins. Since
defined. The descriptors contain a reference to the C function slotdef entries are sorted by the offset of the slot in the etype
they must call, so that it's safe if they are copied into a struct, this gives us some control over disambiguating between
subtype's __dict__ and the subtype has a different C function in competing slots: the members of struct etype are listed from most
its slot -- calling the method defined by the descriptor will call general to least general, so the most general slot is preferred. In
the C function that was used to create it, rather than the C particular, because as_mapping comes before as_sequence, for a type
function present in the slot when it is called. (This is important that defines both mp_subscript and sq_item, mp_subscript wins.
because a subtype may have a C function in the slot that calls the
method from the dictionary, and we want to avoid infinite recursion This only adds new descriptors and doesn't overwrite entries in
here.) */ tp_dict that were previously defined. The descriptors contain a
reference to the C function they must call, so that it's safe if they
are copied into a subtype's __dict__ and the subtype has a different
C function in its slot -- calling the method defined by the
descriptor will call the C function that was used to create it,
rather than the C function present in the slot when it is called.
(This is important because a subtype may have a C function in the
slot that calls the method from the dictionary, and we want to avoid
infinite recursion here.) */
static int static int
add_operators(PyTypeObject *type) add_operators(PyTypeObject *type)
......
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