Skip to content
Merged
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: 3 additions & 0 deletions Lib/test/seq_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ def __getitem__(self, i):
self.assertEqual(self.type2test(LyingTuple((2,))), self.type2test((1,)))
self.assertEqual(self.type2test(LyingList([2])), self.type2test([1]))

with self.assertRaises(TypeError):
self.type2test(unsupported_arg=[])

def test_truth(self):
self.assertFalse(self.type2test())
self.assertTrue(self.type2test([42]))
Expand Down
27 changes: 27 additions & 0 deletions Lib/test/test_float.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,33 @@ def test_keyword_args(self):
with self.assertRaisesRegex(TypeError, 'keyword argument'):
float(x='3.14')

def test_keywords_in_subclass(self):
class subclass(float):
pass
u = subclass(2.5)
self.assertIs(type(u), subclass)
self.assertEqual(float(u), 2.5)
with self.assertRaises(TypeError):
subclass(x=0)

class subclass_with_init(float):
def __init__(self, arg, newarg=None):
self.newarg = newarg
u = subclass_with_init(2.5, newarg=3)
self.assertIs(type(u), subclass_with_init)
self.assertEqual(float(u), 2.5)
self.assertEqual(u.newarg, 3)

class subclass_with_new(float):
def __new__(cls, arg, newarg=None):
self = super().__new__(cls, arg)
self.newarg = newarg
return self
u = subclass_with_new(2.5, newarg=3)
self.assertIs(type(u), subclass_with_new)
self.assertEqual(float(u), 2.5)
self.assertEqual(u.newarg, 3)

def test_is_integer(self):
self.assertFalse((1.1).is_integer())
self.assertTrue((1.).is_integer())
Expand Down
61 changes: 51 additions & 10 deletions Lib/test/test_itertools.py
Original file line number Diff line number Diff line change
Expand Up @@ -2229,16 +2229,57 @@ def __eq__(self, other):
class SubclassWithKwargsTest(unittest.TestCase):
def test_keywords_in_subclass(self):
# count is not subclassable...
for cls in (repeat, zip, filter, filterfalse, chain, map,
starmap, islice, takewhile, dropwhile, cycle, compress):
class Subclass(cls):
def __init__(self, newarg=None, *args):
cls.__init__(self, *args)
try:
Subclass(newarg=1)
except TypeError as err:
# we expect type errors because of wrong argument count
self.assertNotIn("keyword arguments", err.args[0])
testcases = [
(repeat, (1, 2), [1, 1]),
(zip, ([1, 2], 'ab'), [(1, 'a'), (2, 'b')]),
(filter, (None, [0, 1]), [1]),
(filterfalse, (None, [0, 1]), [0]),
(chain, ([1, 2], [3, 4]), [1, 2, 3]),
(map, (str, [1, 2]), ['1', '2']),
(starmap, (operator.pow, ((2, 3), (3, 2))), [8, 9]),
(islice, ([1, 2, 3, 4], 1, 3), [2, 3]),
(takewhile, (isEven, [2, 3, 4]), [2]),
(dropwhile, (isEven, [2, 3, 4]), [3, 4]),
(cycle, ([1, 2],), [1, 2, 1]),
(compress, ('ABC', [1, 0, 1]), ['A', 'C']),
]
for cls, args, result in testcases:
with self.subTest(cls):
class subclass(cls):
pass
u = subclass(*args)
self.assertIs(type(u), subclass)
self.assertEqual(list(islice(u, 0, 3)), result)
with self.assertRaises(TypeError):
subclass(*args, newarg=3)

for cls, args, result in testcases:
# Constructors of repeat, zip, compress accept keyword arguments.
# Their subclasses need overriding __new__ to support new
# keyword arguments.
if cls in [repeat, zip, compress]:
continue
with self.subTest(cls):
class subclass_with_init(cls):
def __init__(self, *args, newarg=None):
self.newarg = newarg
u = subclass_with_init(*args, newarg=3)
self.assertIs(type(u), subclass_with_init)
self.assertEqual(list(islice(u, 0, 3)), result)
self.assertEqual(u.newarg, 3)

for cls, args, result in testcases:
with self.subTest(cls):
class subclass_with_new(cls):
def __new__(cls, *args, newarg=None):
self = super().__new__(cls, *args)
self.newarg = newarg
return self
u = subclass_with_new(*args, newarg=3)
self.assertIs(type(u), subclass_with_new)
self.assertEqual(list(islice(u, 0, 3)), result)
self.assertEqual(u.newarg, 3)


@support.cpython_only
class SizeofTest(unittest.TestCase):
Expand Down
28 changes: 28 additions & 0 deletions Lib/test/test_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,34 @@ def test_keyword_args(self):
with self.assertRaisesRegex(TypeError, 'keyword argument'):
list(sequence=[])

def test_keywords_in_subclass(self):
class subclass(list):
pass
u = subclass([1, 2])
self.assertIs(type(u), subclass)
self.assertEqual(list(u), [1, 2])
with self.assertRaises(TypeError):
subclass(sequence=())

class subclass_with_init(list):
def __init__(self, seq, newarg=None):
super().__init__(seq)
self.newarg = newarg
u = subclass_with_init([1, 2], newarg=3)
self.assertIs(type(u), subclass_with_init)
self.assertEqual(list(u), [1, 2])
self.assertEqual(u.newarg, 3)

class subclass_with_new(list):
def __new__(cls, seq, newarg=None):
self = super().__new__(cls, seq)
self.newarg = newarg
return self
u = subclass_with_new([1, 2], newarg=3)
self.assertIs(type(u), subclass_with_new)
self.assertEqual(list(u), [1, 2])
self.assertEqual(u.newarg, 3)

def test_truth(self):
super().test_truth()
self.assertTrue(not [])
Expand Down
62 changes: 54 additions & 8 deletions Lib/test/test_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -644,15 +644,34 @@ class TestSetSubclass(TestSet):
thetype = SetSubclass
basetype = set

class SetSubclassWithKeywordArgs(set):
def __init__(self, iterable=[], newarg=None):
set.__init__(self, iterable)

class TestSetSubclassWithKeywordArgs(TestSet):

def test_keywords_in_subclass(self):
'SF bug #1486663 -- this used to erroneously raise a TypeError'
SetSubclassWithKeywordArgs(newarg=1)
class subclass(set):
pass
u = subclass([1, 2])
self.assertIs(type(u), subclass)
self.assertEqual(set(u), {1, 2})
with self.assertRaises(TypeError):
subclass(sequence=())

class subclass_with_init(set):
def __init__(self, arg, newarg=None):
super().__init__(arg)
self.newarg = newarg
u = subclass_with_init([1, 2], newarg=3)
self.assertIs(type(u), subclass_with_init)
self.assertEqual(set(u), {1, 2})
self.assertEqual(u.newarg, 3)

class subclass_with_new(set):
def __new__(cls, arg, newarg=None):
self = super().__new__(cls, arg)
self.newarg = newarg
return self
u = subclass_with_new([1, 2], newarg=3)
self.assertIs(type(u), subclass_with_new)
self.assertEqual(set(u), {1, 2})
self.assertEqual(u.newarg, 3)


class TestFrozenSet(TestJointOps, unittest.TestCase):
thetype = frozenset
Expand Down Expand Up @@ -734,6 +753,33 @@ class TestFrozenSetSubclass(TestFrozenSet):
thetype = FrozenSetSubclass
basetype = frozenset

def test_keywords_in_subclass(self):
class subclass(frozenset):
pass
u = subclass([1, 2])
self.assertIs(type(u), subclass)
self.assertEqual(set(u), {1, 2})
with self.assertRaises(TypeError):
subclass(sequence=())

class subclass_with_init(frozenset):
def __init__(self, arg, newarg=None):
self.newarg = newarg
u = subclass_with_init([1, 2], newarg=3)
self.assertIs(type(u), subclass_with_init)
self.assertEqual(set(u), {1, 2})
self.assertEqual(u.newarg, 3)

class subclass_with_new(frozenset):
def __new__(cls, arg, newarg=None):
self = super().__new__(cls, arg)
self.newarg = newarg
return self
u = subclass_with_new([1, 2], newarg=3)
self.assertIs(type(u), subclass_with_new)
self.assertEqual(set(u), {1, 2})
self.assertEqual(u.newarg, 3)

def test_constructor_identity(self):
s = self.thetype(range(3))
t = self.thetype(s)
Expand Down
27 changes: 27 additions & 0 deletions Lib/test/test_tuple.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,33 @@ def test_keyword_args(self):
with self.assertRaisesRegex(TypeError, 'keyword argument'):
tuple(sequence=())

def test_keywords_in_subclass(self):
class subclass(tuple):
pass
u = subclass([1, 2])
self.assertIs(type(u), subclass)
self.assertEqual(list(u), [1, 2])
with self.assertRaises(TypeError):
subclass(sequence=())

class subclass_with_init(tuple):
def __init__(self, arg, newarg=None):
self.newarg = newarg
u = subclass_with_init([1, 2], newarg=3)
self.assertIs(type(u), subclass_with_init)
self.assertEqual(list(u), [1, 2])
self.assertEqual(u.newarg, 3)

class subclass_with_new(tuple):
def __new__(cls, arg, newarg=None):
self = super().__new__(cls, arg)
self.newarg = newarg
return self
u = subclass_with_new([1, 2], newarg=3)
self.assertIs(type(u), subclass_with_new)
self.assertEqual(list(u), [1, 2])
self.assertEqual(u.newarg, 3)

def test_truth(self):
super().test_truth()
self.assertTrue(not ())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Constructors of subclasses of some buitin classes (e.g. :class:`tuple`,
:class:`list`, :class:`frozenset`) no longer accept arbitrary keyword
arguments. Subclass of :class:`set` can now define a ``__new__()`` method
with additional keyword parameters without overriding also ``__init__()``.
5 changes: 3 additions & 2 deletions Modules/_io/clinic/bufferedio.c.h
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,8 @@ _io_BufferedRWPair___init__(PyObject *self, PyObject *args, PyObject *kwargs)
PyObject *writer;
Py_ssize_t buffer_size = DEFAULT_BUFFER_SIZE;

if (Py_IS_TYPE(self, &PyBufferedRWPair_Type) &&
if ((Py_IS_TYPE(self, &PyBufferedRWPair_Type) ||
Py_TYPE(self)->tp_new == PyBufferedRWPair_Type.tp_new) &&
!_PyArg_NoKeywords("BufferedRWPair", kwargs)) {
goto exit;
}
Expand Down Expand Up @@ -637,4 +638,4 @@ _io_BufferedRandom___init__(PyObject *self, PyObject *args, PyObject *kwargs)
exit:
return return_value;
}
/*[clinic end generated code: output=98ccf7610c0e82ba input=a9049054013a1b77]*/
/*[clinic end generated code: output=79138a088729b5ee input=a9049054013a1b77]*/
5 changes: 3 additions & 2 deletions Modules/_randommodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -523,8 +523,9 @@ random_init(RandomObject *self, PyObject *args, PyObject *kwds)
PyObject *arg = NULL;
_randomstate *state = _randomstate_type(Py_TYPE(self));

if (Py_IS_TYPE(self, (PyTypeObject *)state->Random_Type) &&
!_PyArg_NoKeywords("Random()", kwds)) {
if ((Py_IS_TYPE(self, (PyTypeObject *)state->Random_Type) ||
Py_TYPE(self)->tp_init == ((PyTypeObject*)state->Random_Type)->tp_init) &&
!_PyArg_NoKeywords("Random", kwds)) {
return -1;
}

Expand Down
5 changes: 3 additions & 2 deletions Modules/_sqlite/clinic/cursor.c.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ pysqlite_cursor_init(PyObject *self, PyObject *args, PyObject *kwargs)
int return_value = -1;
pysqlite_Connection *connection;

if (Py_IS_TYPE(self, clinic_state()->CursorType) &&
if ((Py_IS_TYPE(self, clinic_state()->CursorType) ||
Py_TYPE(self)->tp_new == clinic_state()->CursorType->tp_new) &&
!_PyArg_NoKeywords("Cursor", kwargs)) {
goto exit;
}
Expand Down Expand Up @@ -299,4 +300,4 @@ pysqlite_cursor_close(pysqlite_Cursor *self, PyTypeObject *cls, PyObject *const
exit:
return return_value;
}
/*[clinic end generated code: output=ace31a7481aa3f41 input=a9049054013a1b77]*/
/*[clinic end generated code: output=3b5328c1619b7626 input=a9049054013a1b77]*/
5 changes: 3 additions & 2 deletions Modules/_sqlite/clinic/row.c.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ pysqlite_row_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
pysqlite_Cursor *cursor;
PyObject *data;

if ((type == clinic_state()->RowType) &&
if ((type == clinic_state()->RowType ||
type->tp_init == clinic_state()->RowType->tp_init) &&
!_PyArg_NoKeywords("Row", kwargs)) {
goto exit;
}
Expand Down Expand Up @@ -53,4 +54,4 @@ pysqlite_row_keys(pysqlite_Row *self, PyObject *Py_UNUSED(ignored))
{
return pysqlite_row_keys_impl(self);
}
/*[clinic end generated code: output=0382771b4fc85f36 input=a9049054013a1b77]*/
/*[clinic end generated code: output=9d54919dbb4ba5f1 input=a9049054013a1b77]*/
4 changes: 3 additions & 1 deletion Modules/arraymodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -2617,7 +2617,9 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
PyObject *initial = NULL, *it = NULL;
const struct arraydescr *descr;

if (type == state->ArrayType && !_PyArg_NoKeywords("array.array", kwds))
if ((type == state->ArrayType ||
type->tp_init == state->ArrayType->tp_init) &&
!_PyArg_NoKeywords("array.array", kwds))
return NULL;

if (!PyArg_ParseTuple(args, "C|O:array", &c, &initial))
Expand Down
5 changes: 3 additions & 2 deletions Modules/clinic/_collectionsmodule.c.h

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

8 changes: 5 additions & 3 deletions Modules/clinic/_queuemodule.c.h

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

Loading