diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py index ee0e24f6e86ae33..6c12d4cbe625585 100644 --- a/Lib/test/test_xmlrpc.py +++ b/Lib/test/test_xmlrpc.py @@ -16,6 +16,7 @@ import contextlib from test import support from test.support import os_helper +from test.support import script_helper from test.support import socket_helper from test.support import threading_helper from test.support import ALWAYS_EQ, LARGEST, SMALLEST @@ -129,6 +130,56 @@ def test_dump_big_long(self): def test_dump_bad_dict(self): self.assertRaises(TypeError, xmlrpclib.dumps, ({(1,2,3): 1},)) + def test_dump_invalid_params(self): + for params in ([], ["x"], {"x": 1}, "abc", 1): + with self.subTest(params=params): + with self.assertRaisesRegex( + TypeError, + "^argument must be tuple or Fault instance$", + ): + xmlrpclib.dumps(params) + + def test_dump_invalid_methodresponse(self): + for params in ((), (1, 2)): + with self.subTest(params=params): + with self.assertRaisesRegex( + ValueError, + "^response tuple must be a singleton$", + ): + xmlrpclib.dumps(params, methodresponse=True) + + def test_dump_invalid_params_optimized(self): + code = r""" +import xmlrpc.client as xmlrpclib + +if __debug__: + raise AssertionError("expected optimized mode") + +def check(exc_type, message, func): + try: + func() + except exc_type as exc: + if str(exc) != message: + raise AssertionError(str(exc)) + else: + raise AssertionError(f"{exc_type.__name__} not raised") + +for params in [], ["x"], {"x": 1}, "abc", 1: + check( + TypeError, + "argument must be tuple or Fault instance", + lambda params=params: xmlrpclib.dumps(params), + ) + +for params in (), (1, 2): + check( + ValueError, + "response tuple must be a singleton", + lambda params=params: xmlrpclib.dumps(params, methodresponse=True), + ) +""" + script_helper.assert_python_ok("-O", "-c", code) + def test_dump_recursive_seq(self): l = [1,2,3] t = [3,4,5,l] diff --git a/Lib/xmlrpc/client.py b/Lib/xmlrpc/client.py index 84e4e4d11a7319e..a2c0696e5c322ab 100644 --- a/Lib/xmlrpc/client.py +++ b/Lib/xmlrpc/client.py @@ -938,11 +938,12 @@ def dumps(params, methodname=None, methodresponse=None, encoding=None, where necessary. """ - assert isinstance(params, (tuple, Fault)), "argument must be tuple or Fault instance" + if not isinstance(params, (tuple, Fault)): + raise TypeError("argument must be tuple or Fault instance") if isinstance(params, Fault): methodresponse = 1 - elif methodresponse and isinstance(params, tuple): - assert len(params) == 1, "response tuple must be a singleton" + elif methodresponse and len(params) != 1: + raise ValueError("response tuple must be a singleton") if not encoding: encoding = "utf-8" diff --git a/Misc/NEWS.d/next/Library/2026-06-16-05-55-00.gh-issue-151532.xmlrpc.rst b/Misc/NEWS.d/next/Library/2026-06-16-05-55-00.gh-issue-151532.xmlrpc.rst new file mode 100644 index 000000000000000..42b5ef816beb1b5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-16-05-55-00.gh-issue-151532.xmlrpc.rst @@ -0,0 +1,3 @@ +Validate :func:`xmlrpc.client.dumps` arguments with explicit runtime checks +so invalid arguments are rejected consistently when Python runs with +optimization enabled.