From 33a38b3abb005928231c982208b5d15f954ca14e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Ogam?= Date: Tue, 4 May 2021 00:17:18 +0200 Subject: [PATCH 1/8] Use common TypeError message for built-in functions getattr and hasattr --- Python/bltinmodule.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 66a74cbdef6104..a800315bcfcfb5 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1099,11 +1099,6 @@ builtin_getattr(PyObject *self, PyObject *const *args, Py_ssize_t nargs) v = args[0]; name = args[1]; - if (!PyUnicode_Check(name)) { - PyErr_SetString(PyExc_TypeError, - "getattr(): attribute name must be string"); - return NULL; - } if (nargs > 2) { if (_PyObject_LookupAttr(v, name, &result) == 0) { PyObject *dflt = args[2]; @@ -1164,11 +1159,6 @@ builtin_hasattr_impl(PyObject *module, PyObject *obj, PyObject *name) { PyObject *v; - if (!PyUnicode_Check(name)) { - PyErr_SetString(PyExc_TypeError, - "hasattr(): attribute name must be string"); - return NULL; - } if (_PyObject_LookupAttr(obj, name, &v) < 0) { return NULL; } From 12ce6caf1cb84911a3a9fb60e2eacdf610fe1304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Ogam?= Date: Tue, 4 May 2021 21:36:37 +0200 Subject: [PATCH 2/8] Add unit tests for TypeError messages --- Lib/test/test_call.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py index 3f459222748b3a..7db6756fa132cb 100644 --- a/Lib/test/test_call.py +++ b/Lib/test/test_call.py @@ -169,6 +169,36 @@ def test_oldargs1_2_kw(self): msg = r"count\(\) takes no keyword arguments" self.assertRaisesRegex(TypeError, msg, [].count, x=2, y=2) + def test_argtype0(self): + msg = r"^attribute name must be string, not 'int'$" + self.assertRaisesRegex(TypeError, msg, getattr, 'foo', 123) + + def test_argtype1(self): + msg = r"^attribute name must be string, not 'int'$" + self.assertRaisesRegex(TypeError, msg, hasattr, 'foo', 123) + + def test_argtype2(self): + msg = r"^attribute name must be string, not 'int'$" + self.assertRaisesRegex(TypeError, msg, setattr, 'foo', 123, 'bar') + + def test_argtype3(self): + msg = r"^attribute name must be string, not 'int'$" + self.assertRaisesRegex(TypeError, msg, delattr, 'foo', 123) + + def test_argtype4(self): + msg = r"^attribute name must be string, not 'int'$" + with self.assertRaisesRegex(TypeError, msg): + object.__getattribute__('foo', 123) + + def test_argtype5(self): + msg = r"^attribute name must be string, not 'int'$" + with self.assertRaisesRegex(TypeError, msg): + object.__setattr__('foo', 123, 'bar') + + def test_argtype6(self): + msg = r"^attribute name must be string, not 'int'$" + with self.assertRaisesRegex(TypeError, msg): + object.__delattr__('foo', 123) class TestCallingConventions(unittest.TestCase): From caefee1b7114d3b6fd792fae77889490882ebbb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Ogam?= Date: Tue, 4 May 2021 21:56:38 +0200 Subject: [PATCH 3/8] Add a NEWS entry --- .../Core and Builtins/2021-05-04-21-55-49.bpo-44024.M9m8Qd.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2021-05-04-21-55-49.bpo-44024.M9m8Qd.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-05-04-21-55-49.bpo-44024.M9m8Qd.rst b/Misc/NEWS.d/next/Core and Builtins/2021-05-04-21-55-49.bpo-44024.M9m8Qd.rst new file mode 100644 index 00000000000000..741a83ceb3b8b9 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2021-05-04-21-55-49.bpo-44024.M9m8Qd.rst @@ -0,0 +1,3 @@ +Use the common :exc:`TypeError` message for non-string second arguments +passed to the built-in functions :func:`getattr` and :func:`hasattr`. Patch +by Géry Ogam. From 46c3a0fa061377ca2cfe64880d42abed4ce4b960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Ogam?= Date: Tue, 4 May 2021 22:38:57 +0200 Subject: [PATCH 4/8] Update Misc/NEWS.d/next/Core and Builtins/2021-05-04-21-55-49.bpo-44024.M9m8Qd.rst Co-authored-by: Jelle Zijlstra --- .../Core and Builtins/2021-05-04-21-55-49.bpo-44024.M9m8Qd.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-05-04-21-55-49.bpo-44024.M9m8Qd.rst b/Misc/NEWS.d/next/Core and Builtins/2021-05-04-21-55-49.bpo-44024.M9m8Qd.rst index 741a83ceb3b8b9..b06aa1665b970e 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2021-05-04-21-55-49.bpo-44024.M9m8Qd.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2021-05-04-21-55-49.bpo-44024.M9m8Qd.rst @@ -1,3 +1,3 @@ -Use the common :exc:`TypeError` message for non-string second arguments +Improve the error message for non-string second arguments passed to the built-in functions :func:`getattr` and :func:`hasattr`. Patch by Géry Ogam. From 87813e858b1d1bf962fa8bf479f9fc8eb77a9700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Ogam?= Date: Tue, 4 May 2021 23:10:04 +0200 Subject: [PATCH 5/8] Update 2021-05-04-21-55-49.bpo-44024.M9m8Qd.rst --- .../2021-05-04-21-55-49.bpo-44024.M9m8Qd.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-05-04-21-55-49.bpo-44024.M9m8Qd.rst b/Misc/NEWS.d/next/Core and Builtins/2021-05-04-21-55-49.bpo-44024.M9m8Qd.rst index b06aa1665b970e..e80d2d64cc85ab 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2021-05-04-21-55-49.bpo-44024.M9m8Qd.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2021-05-04-21-55-49.bpo-44024.M9m8Qd.rst @@ -1,3 +1,2 @@ -Improve the error message for non-string second arguments -passed to the built-in functions :func:`getattr` and :func:`hasattr`. Patch -by Géry Ogam. +Improve the exc:`TypeError` message for non-string second arguments passed to +the built-in functions :func:`getattr` and :func:`hasattr`. Patch by Géry Ogam. From ad724dd8737e58709efcbdc8c85982f6ec28ac1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Ogam?= Date: Wed, 5 May 2021 14:19:26 +0200 Subject: [PATCH 6/8] Update 2021-05-04-21-55-49.bpo-44024.M9m8Qd.rst --- .../Core and Builtins/2021-05-04-21-55-49.bpo-44024.M9m8Qd.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-05-04-21-55-49.bpo-44024.M9m8Qd.rst b/Misc/NEWS.d/next/Core and Builtins/2021-05-04-21-55-49.bpo-44024.M9m8Qd.rst index e80d2d64cc85ab..5037413353d28f 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2021-05-04-21-55-49.bpo-44024.M9m8Qd.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2021-05-04-21-55-49.bpo-44024.M9m8Qd.rst @@ -1,2 +1,2 @@ -Improve the exc:`TypeError` message for non-string second arguments passed to +Improve the exc:`TypeError` message for non-string second arguments passed to the built-in functions :func:`getattr` and :func:`hasattr`. Patch by Géry Ogam. From 4f175376d1973902532b8fdfffb2a2db89ca4592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Ogam?= Date: Wed, 5 Jan 2022 13:01:35 +0100 Subject: [PATCH 7/8] Move tests from test_call.py to test_builtin.py --- Lib/test/test_builtin.py | 18 ++++++++++++++---- Lib/test/test_call.py | 31 ------------------------------- 2 files changed, 14 insertions(+), 35 deletions(-) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index bd8353d038b6fe..7d75973eb72563 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -509,6 +509,9 @@ def test_delattr(self): sys.spam = 1 delattr(sys, 'spam') self.assertRaises(TypeError, delattr) + self.assertRaises(TypeError, delattr, sys) + msg = r"^attribute name must be string, not 'int'$" + self.assertRaisesRegex(TypeError, msg, delattr, sys, 1) def test_dir(self): # dir(wrong number of arguments) @@ -801,17 +804,21 @@ def test_filter_pickle(self): def test_getattr(self): self.assertTrue(getattr(sys, 'stdout') is sys.stdout) - self.assertRaises(TypeError, getattr, sys, 1) - self.assertRaises(TypeError, getattr, sys, 1, "foo") self.assertRaises(TypeError, getattr) + self.assertRaises(TypeError, getattr, sys) + msg = r"^attribute name must be string, not 'int'$" + self.assertRaisesRegex(TypeError, msg, getattr, sys, 1) + self.assertRaisesRegex(TypeError, msg, getattr, sys, 1, 'spam') self.assertRaises(AttributeError, getattr, sys, chr(sys.maxunicode)) # unicode surrogates are not encodable to the default encoding (utf8) self.assertRaises(AttributeError, getattr, 1, "\uDAD1\uD51E") def test_hasattr(self): self.assertTrue(hasattr(sys, 'stdout')) - self.assertRaises(TypeError, hasattr, sys, 1) self.assertRaises(TypeError, hasattr) + self.assertRaises(TypeError, hasattr, sys) + msg = r"^attribute name must be string, not 'int'$" + self.assertRaisesRegex(TypeError, msg, hasattr, sys, 1) self.assertEqual(False, hasattr(sys, chr(sys.maxunicode))) # Check that hasattr propagates all exceptions outside of @@ -1457,8 +1464,11 @@ def test_bug_27936(self): def test_setattr(self): setattr(sys, 'spam', 1) self.assertEqual(sys.spam, 1) - self.assertRaises(TypeError, setattr, sys, 1, 'spam') self.assertRaises(TypeError, setattr) + self.assertRaises(TypeError, setattr, sys) + self.assertRaises(TypeError, setattr, sys, 'spam') + msg = r"^attribute name must be string, not 'int'$" + self.assertRaisesRegex(TypeError, msg, setattr, sys, 1, 'spam') # test_str(): see test_unicode.py and test_bytes.py for str() tests. diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py index 7db6756fa132cb..d08f7fc5073257 100644 --- a/Lib/test/test_call.py +++ b/Lib/test/test_call.py @@ -169,37 +169,6 @@ def test_oldargs1_2_kw(self): msg = r"count\(\) takes no keyword arguments" self.assertRaisesRegex(TypeError, msg, [].count, x=2, y=2) - def test_argtype0(self): - msg = r"^attribute name must be string, not 'int'$" - self.assertRaisesRegex(TypeError, msg, getattr, 'foo', 123) - - def test_argtype1(self): - msg = r"^attribute name must be string, not 'int'$" - self.assertRaisesRegex(TypeError, msg, hasattr, 'foo', 123) - - def test_argtype2(self): - msg = r"^attribute name must be string, not 'int'$" - self.assertRaisesRegex(TypeError, msg, setattr, 'foo', 123, 'bar') - - def test_argtype3(self): - msg = r"^attribute name must be string, not 'int'$" - self.assertRaisesRegex(TypeError, msg, delattr, 'foo', 123) - - def test_argtype4(self): - msg = r"^attribute name must be string, not 'int'$" - with self.assertRaisesRegex(TypeError, msg): - object.__getattribute__('foo', 123) - - def test_argtype5(self): - msg = r"^attribute name must be string, not 'int'$" - with self.assertRaisesRegex(TypeError, msg): - object.__setattr__('foo', 123, 'bar') - - def test_argtype6(self): - msg = r"^attribute name must be string, not 'int'$" - with self.assertRaisesRegex(TypeError, msg): - object.__delattr__('foo', 123) - class TestCallingConventions(unittest.TestCase): """Test calling using various C calling conventions (METH_*) from Python From c648edc0193dbdc40a7ab0f604596f5777fda71e Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 10 Jan 2022 11:53:42 +0200 Subject: [PATCH 8/8] Remove change in unrelated file. --- Lib/test/test_call.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py index d08f7fc5073257..3f459222748b3a 100644 --- a/Lib/test/test_call.py +++ b/Lib/test/test_call.py @@ -170,6 +170,7 @@ def test_oldargs1_2_kw(self): self.assertRaisesRegex(TypeError, msg, [].count, x=2, y=2) + class TestCallingConventions(unittest.TestCase): """Test calling using various C calling conventions (METH_*) from Python