From 041dd8eef105f981693f7084b270481816dd9a7a Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 21 May 2015 20:15:40 +0300 Subject: [PATCH] Issue #15836: assertRaises(), assertRaisesRegex(), assertWarns() and assertWarnsRegex() assertments now check the type of the first argument to prevent possible user error. Based on patch by Daniel Wagner-Hall. --- .../test_importlib/builtin/test_loader.py | 1 - Lib/unittest/case.py | 13 +++++ Lib/unittest/test/test_case.py | 56 +++++++++++++++++++ Misc/ACKS | 1 + Misc/NEWS | 4 ++ 5 files changed, 74 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_importlib/builtin/test_loader.py b/Lib/test/test_importlib/builtin/test_loader.py index 1684ab6eb4..b1349ec5da 100644 --- a/Lib/test/test_importlib/builtin/test_loader.py +++ b/Lib/test/test_importlib/builtin/test_loader.py @@ -97,7 +97,6 @@ class InspectLoaderTests: method = getattr(self.machinery.BuiltinImporter, meth_name) with self.assertRaises(ImportError) as cm: method(util.BUILTINS.bad_name) - self.assertRaises(util.BUILTINS.bad_name) (Frozen_InspectLoaderTests, diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py index befad61946..7701ad3adc 100644 --- a/Lib/unittest/case.py +++ b/Lib/unittest/case.py @@ -119,6 +119,10 @@ def expectedFailure(test_item): test_item.__unittest_expecting_failure__ = True return test_item +def _is_subtype(expected, basetype): + if isinstance(expected, tuple): + return all(_is_subtype(e, basetype) for e in expected) + return isinstance(expected, type) and issubclass(expected, basetype) class _BaseTestCaseContext: @@ -148,6 +152,9 @@ class _AssertRaisesBaseContext(_BaseTestCaseContext): If args is not empty, call a callable passing positional and keyword arguments. """ + if not _is_subtype(self.expected, self._base_type): + raise TypeError('%s() arg 1 must be %s' % + (name, self._base_type_str)) if args and args[0] is None: warnings.warn("callable is None", DeprecationWarning, 3) @@ -172,6 +179,9 @@ class _AssertRaisesBaseContext(_BaseTestCaseContext): class _AssertRaisesContext(_AssertRaisesBaseContext): """A context manager used to implement TestCase.assertRaises* methods.""" + _base_type = BaseException + _base_type_str = 'an exception type or tuple of exception types' + def __enter__(self): return self @@ -206,6 +216,9 @@ class _AssertRaisesContext(_AssertRaisesBaseContext): class _AssertWarnsContext(_AssertRaisesBaseContext): """A context manager used to implement TestCase.assertWarns* methods.""" + _base_type = Warning + _base_type_str = 'a warning type or tuple of warning types' + def __enter__(self): # The __warningregistry__'s need to be in a pristine state for tests # to work properly. diff --git a/Lib/unittest/test/test_case.py b/Lib/unittest/test/test_case.py index a05cc64890..ada733b1ff 100644 --- a/Lib/unittest/test/test_case.py +++ b/Lib/unittest/test/test_case.py @@ -1185,6 +1185,18 @@ test case with self.assertRaises(ExceptionMock): self.assertRaises(ValueError, Stub) + def testAssertRaisesNoExceptionType(self): + with self.assertRaises(TypeError): + self.assertRaises() + with self.assertRaises(TypeError): + self.assertRaises(1) + with self.assertRaises(TypeError): + self.assertRaises(object) + with self.assertRaises(TypeError): + self.assertRaises((ValueError, 1)) + with self.assertRaises(TypeError): + self.assertRaises((ValueError, object)) + def testAssertRaisesRegex(self): class ExceptionMock(Exception): pass @@ -1258,6 +1270,20 @@ test case self.assertIsInstance(e, ExceptionMock) self.assertEqual(e.args[0], v) + def testAssertRaisesRegexNoExceptionType(self): + with self.assertRaises(TypeError): + self.assertRaisesRegex() + with self.assertRaises(TypeError): + self.assertRaisesRegex(ValueError) + with self.assertRaises(TypeError): + self.assertRaisesRegex(1, 'expect') + with self.assertRaises(TypeError): + self.assertRaisesRegex(object, 'expect') + with self.assertRaises(TypeError): + self.assertRaisesRegex((ValueError, 1), 'expect') + with self.assertRaises(TypeError): + self.assertRaisesRegex((ValueError, object), 'expect') + def testAssertWarnsCallable(self): def _runtime_warn(): warnings.warn("foo", RuntimeWarning) @@ -1336,6 +1362,20 @@ test case with self.assertWarns(DeprecationWarning): _runtime_warn() + def testAssertWarnsNoExceptionType(self): + with self.assertRaises(TypeError): + self.assertWarns() + with self.assertRaises(TypeError): + self.assertWarns(1) + with self.assertRaises(TypeError): + self.assertWarns(object) + with self.assertRaises(TypeError): + self.assertWarns((UserWarning, 1)) + with self.assertRaises(TypeError): + self.assertWarns((UserWarning, object)) + with self.assertRaises(TypeError): + self.assertWarns((UserWarning, Exception)) + def testAssertWarnsRegexCallable(self): def _runtime_warn(msg): warnings.warn(msg, RuntimeWarning) @@ -1414,6 +1454,22 @@ test case with self.assertWarnsRegex(RuntimeWarning, "o+"): _runtime_warn("barz") + def testAssertWarnsRegexNoExceptionType(self): + with self.assertRaises(TypeError): + self.assertWarnsRegex() + with self.assertRaises(TypeError): + self.assertWarnsRegex(UserWarning) + with self.assertRaises(TypeError): + self.assertWarnsRegex(1, 'expect') + with self.assertRaises(TypeError): + self.assertWarnsRegex(object, 'expect') + with self.assertRaises(TypeError): + self.assertWarnsRegex((UserWarning, 1), 'expect') + with self.assertRaises(TypeError): + self.assertWarnsRegex((UserWarning, object), 'expect') + with self.assertRaises(TypeError): + self.assertWarnsRegex((UserWarning, Exception), 'expect') + @contextlib.contextmanager def assertNoStderr(self): with captured_stderr() as buf: diff --git a/Misc/ACKS b/Misc/ACKS index 807eeb27a6..4d3e290043 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1472,6 +1472,7 @@ Alex Volkov Martijn Vries Sjoerd de Vries Guido Vranken +Daniel Wagner-Hall Niki W. Waibel Wojtek Walczak Charles Waldman diff --git a/Misc/NEWS b/Misc/NEWS index 5838a63046..0f35d3f97b 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -52,6 +52,10 @@ Core and Builtins Library ------- +- Issue #15836: assertRaises(), assertRaisesRegex(), assertWarns() and + assertWarnsRegex() assertments now check the type of the first argument + to prevent possible user error. Based on patch by Daniel Wagner-Hall. + - Issue #9858: Add missing method stubs to _io.RawIOBase. Patch by Laura Rupprecht. -- 2.40.0