Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Include/internal/pycore_abstract.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ extern "C" {
static inline int
_PyIndex_Check(PyObject *obj)
{
PyNumberMethods *tp_as_number = Py_TYPE(obj)->tp_as_number;
return (tp_as_number != NULL && tp_as_number->nb_index != NULL);
return Py_TYPE(obj)->tp_as_number->nb_index != NULL;
}

// Exported for external JIT support
Expand Down
23 changes: 23 additions & 0 deletions Include/internal/pycore_typeobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,29 @@ extern "C" {
#define _Py_MAX_GLOBAL_TYPE_VERSION_TAG (_Py_TYPE_BASE_VERSION_TAG - 1)


extern const PyNumberMethods _PyType_EmptyNumberMethods;
extern const PySequenceMethods _PyType_EmptySequenceMethods;
extern const PyMappingMethods _PyType_EmptyMappingMethods;

static inline int
_PyType_HasOwnNumberMethods(PyTypeObject *tp)
{
return tp->tp_as_number != &_PyType_EmptyNumberMethods;
}

static inline int
_PyType_HasOwnSequenceMethods(PyTypeObject *tp)
{
return tp->tp_as_sequence != &_PyType_EmptySequenceMethods;
}

static inline int
_PyType_HasOwnMappingMethods(PyTypeObject *tp)
{
return tp->tp_as_mapping != &_PyType_EmptyMappingMethods;
}


/* runtime lifecycle */

extern PyStatus _PyTypes_InitTypes(PyInterpreterState *);
Expand Down
135 changes: 135 additions & 0 deletions Lib/test/test_descr.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from copy import deepcopy
from contextlib import redirect_stdout
from test import support
from test.support import import_helper
from test.support.script_helper import assert_python_ok

try:
Expand Down Expand Up @@ -6319,5 +6320,139 @@ class IntSubclass(int):
weakref_descriptor.__get__(IntSubclass(), IntSubclass)


class TestSubtableDispatchInvariants(unittest.TestCase):

def test_int_times_list(self):
self.assertEqual(2 * [1], [1, 1])

def test_list_times_int(self):
self.assertEqual([1] * 2, [1, 1])

def test_int_times_tuple(self):
self.assertEqual(2 * (1,), (1, 1))

def test_tuple_times_int(self):
self.assertEqual((1,) * 2, (1, 1))

def test_int_times_str(self):
self.assertEqual(2 * "ab", "abab")

def test_str_times_int(self):
self.assertEqual("ab" * 2, "abab")

def test_int_times_bytes(self):
self.assertEqual(2 * b"ab", b"abab")

def test_int_imul_list(self):
a = 2
a *= [1]
self.assertEqual(a, [1, 1])

def test_list_imul_int(self):
a = [1]
a *= 2
self.assertEqual(a, [1, 1])

def test_str_imul_int(self):
a = "ab"
a *= 2
self.assertEqual(a, "abab")

def test_class_without_mul_imul_seq_raises(self):
class NoSeq:
pass
a = NoSeq()
with self.assertRaises(TypeError):
a *= [1]

def test_class_with_empty_seq_methods_imul_seq_raises(self):
class HasMulOnly:
def __mul__(self, other):
return NotImplemented
def __index__(self):
return 2
a = HasMulOnly()
with self.assertRaises(TypeError):
a *= [1]

def test_int_getitem_raises_type_error(self):
x = 5
with self.assertRaises(TypeError):
x[0]

def test_int_setitem_raises_type_error(self):
x = 5
with self.assertRaises(TypeError):
x[0] = 1

def test_object_setitem_does_not_coerce_index(self):
class BadIndex:
called = False

def __index__(self):
self.called = True
raise AssertionError("__index__ called")

obj = object()
key = BadIndex()
with self.assertRaisesRegex(TypeError,
"does not support item assignment"):
obj[key] = 1
self.assertFalse(key.called)

def test_object_delitem_does_not_coerce_index(self):
class BadIndex:
called = False

def __index__(self):
self.called = True
raise AssertionError("__index__ called")

obj = object()
key = BadIndex()
with self.assertRaisesRegex(TypeError,
"does not support item deletion"):
del obj[key]
self.assertFalse(key.called)

def test_object_bool_default(self):
class C:
pass
self.assertTrue(bool(C()))

def test_object_bool_with_len(self):
class C:
def __len__(self):
return 0
self.assertFalse(bool(C()))

def test_object_bool_with_dunder_bool(self):
class C:
def __bool__(self):
return False
self.assertFalse(bool(C()))

def test_sequence_check_int_is_false(self):
_testlimitedcapi = import_helper.import_module("_testlimitedcapi")
self.assertFalse(_testlimitedcapi.sequence_check(5))

def test_mapping_check_int_is_false(self):
_testlimitedcapi = import_helper.import_module("_testlimitedcapi")
self.assertFalse(_testlimitedcapi.mapping_check(5))

def test_int_index_protocol(self):
class MyInt(int):
pass
a = [10, 20, 30]
self.assertEqual(a[MyInt(1)], 20)

def test_class_without_index_protocol_subscript_raises(self):
class NoIndex:
pass
a = [10, 20, 30]
with self.assertRaises(TypeError):
a[NoIndex()]


if __name__ == "__main__":
unittest.main()
4 changes: 2 additions & 2 deletions Modules/_bisectmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ get_sq_item(PyObject *s)
// The parts of PySequence_GetItem that we only need to do once
PyTypeObject *tp = Py_TYPE(s);
PySequenceMethods *m = tp->tp_as_sequence;
if (m && m->sq_item) {
if (m->sq_item) {
return m->sq_item;
}
const char *msg;
if (tp->tp_as_mapping && tp->tp_as_mapping->mp_subscript) {
if (tp->tp_as_mapping->mp_subscript) {
msg = "%.200s is not a sequence";
}
else {
Expand Down
10 changes: 0 additions & 10 deletions Modules/_testinternalcapi/test_cases.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading