summaryrefslogtreecommitdiffstats
path: root/Lib/test
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2022-07-28 04:40:36 (GMT)
committerGitHub <noreply@github.com>2022-07-28 04:40:36 (GMT)
commitebad53a4dc1bb591820724a22cef9b8459185b5f (patch)
treefa878ab07860517d3ace9ee9ef8ed8ef998a2415 /Lib/test
parent0fe645d6fd22a6f57e777a29e65cf9a4ff9785ae (diff)
downloadcpython-ebad53a4dc1bb591820724a22cef9b8459185b5f.zip
cpython-ebad53a4dc1bb591820724a22cef9b8459185b5f.tar.gz
cpython-ebad53a4dc1bb591820724a22cef9b8459185b5f.tar.bz2
gh-94938: Fix errror detection of unexpected keyword arguments (GH-94999)
When keyword argument name is an instance of a str subclass with overloaded methods __eq__ and __hash__, the former code could not find the name of an extraneous keyword argument to report an error, and _PyArg_UnpackKeywords() returned success without setting the corresponding cell in the linearized arguments array. But since the number of expected initialized cells is determined as the total number of passed arguments, this lead to reading NULL as a keyword parameter value, that caused SystemError or crash or other undesired behavior.
Diffstat (limited to 'Lib/test')
-rw-r--r--Lib/test/test_call.py25
-rw-r--r--Lib/test/test_getargs2.py27
2 files changed, 52 insertions, 0 deletions
diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py
index 07355e8..a2eec41 100644
--- a/Lib/test/test_call.py
+++ b/Lib/test/test_call.py
@@ -11,6 +11,19 @@ import gc
import contextlib
+class BadStr(str):
+ def __eq__(self, other):
+ return True
+ def __hash__(self):
+ # Guaranteed different hash
+ return str.__hash__(self) ^ 3
+
+ def __eq__(self, other):
+ return False
+ def __hash__(self):
+ return str.__hash__(self)
+
+
class FunctionCalls(unittest.TestCase):
def test_kwargs_order(self):
@@ -145,6 +158,18 @@ class CFunctionCallsErrorMessages(unittest.TestCase):
self.assertRaisesRegex(TypeError, msg,
print, 0, sep=1, end=2, file=3, flush=4, foo=5)
+ def test_varargs18_kw(self):
+ # _PyArg_UnpackKeywordsWithVararg()
+ msg = r"invalid keyword argument for print\(\)$"
+ with self.assertRaisesRegex(TypeError, msg):
+ print(0, 1, **{BadStr('foo'): ','})
+
+ def test_varargs19_kw(self):
+ # _PyArg_UnpackKeywords()
+ msg = r"invalid keyword argument for round\(\)$"
+ with self.assertRaisesRegex(TypeError, msg):
+ round(1.75, **{BadStr('foo'): 1})
+
def test_oldargs0_1(self):
msg = r"keys\(\) takes no arguments \(1 given\)"
self.assertRaisesRegex(TypeError, msg, {}.keys, 0)
diff --git a/Lib/test/test_getargs2.py b/Lib/test/test_getargs2.py
index 899e546..1d5c7fb 100644
--- a/Lib/test/test_getargs2.py
+++ b/Lib/test/test_getargs2.py
@@ -746,6 +746,33 @@ class KeywordOnly_TestCase(unittest.TestCase):
"'\udc80' is an invalid keyword argument for this function"):
getargs_keyword_only(1, 2, **{'\uDC80': 10})
+ def test_weird_str_subclass(self):
+ class BadStr(str):
+ def __eq__(self, other):
+ return True
+ def __hash__(self):
+ # Guaranteed different hash
+ return str.__hash__(self) ^ 3
+ with self.assertRaisesRegex(TypeError,
+ "invalid keyword argument for this function"):
+ getargs_keyword_only(1, 2, **{BadStr("keyword_only"): 3})
+ with self.assertRaisesRegex(TypeError,
+ "invalid keyword argument for this function"):
+ getargs_keyword_only(1, 2, **{BadStr("monster"): 666})
+
+ def test_weird_str_subclass2(self):
+ class BadStr(str):
+ def __eq__(self, other):
+ return False
+ def __hash__(self):
+ return str.__hash__(self)
+ with self.assertRaisesRegex(TypeError,
+ "invalid keyword argument for this function"):
+ getargs_keyword_only(1, 2, **{BadStr("keyword_only"): 3})
+ with self.assertRaisesRegex(TypeError,
+ "invalid keyword argument for this function"):
+ getargs_keyword_only(1, 2, **{BadStr("monster"): 666})
+
class PositionalOnlyAndKeywords_TestCase(unittest.TestCase):
from _testcapi import getargs_positional_only_and_keywords as getargs