Commit df573d6a authored by Serhiy Storchaka's avatar Serhiy Storchaka

Issue #24134: assertRaises(), assertRaisesRegex(), assertWarns() and

assertWarnsRegex() checks now emits a deprecation warning when callable is
None or keyword arguments except msg is passed in the context manager mode.
parent 6b680cd6
...@@ -129,37 +129,42 @@ class _BaseTestCaseContext: ...@@ -129,37 +129,42 @@ class _BaseTestCaseContext:
msg = self.test_case._formatMessage(self.msg, standardMsg) msg = self.test_case._formatMessage(self.msg, standardMsg)
raise self.test_case.failureException(msg) raise self.test_case.failureException(msg)
def _sentinel(*args, **kwargs):
raise AssertionError('Should never be called')
class _AssertRaisesBaseContext(_BaseTestCaseContext): class _AssertRaisesBaseContext(_BaseTestCaseContext):
def __init__(self, expected, test_case, callable_obj=_sentinel, def __init__(self, expected, test_case, expected_regex=None):
expected_regex=None):
_BaseTestCaseContext.__init__(self, test_case) _BaseTestCaseContext.__init__(self, test_case)
self.expected = expected self.expected = expected
self.test_case = test_case self.test_case = test_case
if callable_obj is not _sentinel:
try:
self.obj_name = callable_obj.__name__
except AttributeError:
self.obj_name = str(callable_obj)
else:
self.obj_name = None
if expected_regex is not None: if expected_regex is not None:
expected_regex = re.compile(expected_regex) expected_regex = re.compile(expected_regex)
self.expected_regex = expected_regex self.expected_regex = expected_regex
self.obj_name = None
self.msg = None self.msg = None
def handle(self, name, callable_obj, args, kwargs): def handle(self, name, args, kwargs):
""" """
If callable_obj is _sentinel, assertRaises/Warns is being used as a If args is empty, assertRaises/Warns is being used as a
context manager, so check for a 'msg' kwarg and return self. context manager, so check for a 'msg' kwarg and return self.
If callable_obj is not _sentinel, call it passing args and kwargs. If args is not empty, call a callable passing positional and keyword
arguments.
""" """
if callable_obj is _sentinel: if args and args[0] is None:
warnings.warn("callable is None",
DeprecationWarning, 3)
args = ()
if not args:
self.msg = kwargs.pop('msg', None) self.msg = kwargs.pop('msg', None)
if kwargs:
warnings.warn('%r is an invalid keyword argument for '
'this function' % next(iter(kwargs)),
DeprecationWarning, 3)
return self return self
callable_obj, *args = args
try:
self.obj_name = callable_obj.__name__
except AttributeError:
self.obj_name = str(callable_obj)
with self: with self:
callable_obj(*args, **kwargs) callable_obj(*args, **kwargs)
...@@ -676,15 +681,15 @@ class TestCase(object): ...@@ -676,15 +681,15 @@ class TestCase(object):
except UnicodeDecodeError: except UnicodeDecodeError:
return '%s : %s' % (safe_repr(standardMsg), safe_repr(msg)) return '%s : %s' % (safe_repr(standardMsg), safe_repr(msg))
def assertRaises(self, excClass, callableObj=_sentinel, *args, **kwargs): def assertRaises(self, expected_exception, *args, **kwargs):
"""Fail unless an exception of class excClass is raised """Fail unless an exception of class expected_exception is raised
by callableObj when invoked with arguments args and keyword by the callable when invoked with specified positional and
arguments kwargs. If a different type of exception is keyword arguments. If a different type of exception is
raised, it will not be caught, and the test case will be raised, it will not be caught, and the test case will be
deemed to have suffered an error, exactly as for an deemed to have suffered an error, exactly as for an
unexpected exception. unexpected exception.
If called with callableObj omitted, will return a If called with the callable and arguments omitted, will return a
context object used like this:: context object used like this::
with self.assertRaises(SomeException): with self.assertRaises(SomeException):
...@@ -702,18 +707,18 @@ class TestCase(object): ...@@ -702,18 +707,18 @@ class TestCase(object):
the_exception = cm.exception the_exception = cm.exception
self.assertEqual(the_exception.error_code, 3) self.assertEqual(the_exception.error_code, 3)
""" """
context = _AssertRaisesContext(excClass, self, callableObj) context = _AssertRaisesContext(expected_exception, self)
return context.handle('assertRaises', callableObj, args, kwargs) return context.handle('assertRaises', args, kwargs)
def assertWarns(self, expected_warning, callable_obj=_sentinel, *args, **kwargs): def assertWarns(self, expected_warning, *args, **kwargs):
"""Fail unless a warning of class warnClass is triggered """Fail unless a warning of class warnClass is triggered
by callable_obj when invoked with arguments args and keyword by the callable when invoked with specified positional and
arguments kwargs. If a different type of warning is keyword arguments. If a different type of warning is
triggered, it will not be handled: depending on the other triggered, it will not be handled: depending on the other
warning filtering rules in effect, it might be silenced, printed warning filtering rules in effect, it might be silenced, printed
out, or raised as an exception. out, or raised as an exception.
If called with callable_obj omitted, will return a If called with the callable and arguments omitted, will return a
context object used like this:: context object used like this::
with self.assertWarns(SomeWarning): with self.assertWarns(SomeWarning):
...@@ -733,8 +738,8 @@ class TestCase(object): ...@@ -733,8 +738,8 @@ class TestCase(object):
the_warning = cm.warning the_warning = cm.warning
self.assertEqual(the_warning.some_attribute, 147) self.assertEqual(the_warning.some_attribute, 147)
""" """
context = _AssertWarnsContext(expected_warning, self, callable_obj) context = _AssertWarnsContext(expected_warning, self)
return context.handle('assertWarns', callable_obj, args, kwargs) return context.handle('assertWarns', args, kwargs)
def assertLogs(self, logger=None, level=None): def assertLogs(self, logger=None, level=None):
"""Fail unless a log message of level *level* or higher is emitted """Fail unless a log message of level *level* or higher is emitted
...@@ -1221,26 +1226,23 @@ class TestCase(object): ...@@ -1221,26 +1226,23 @@ class TestCase(object):
self.fail(self._formatMessage(msg, standardMsg)) self.fail(self._formatMessage(msg, standardMsg))
def assertRaisesRegex(self, expected_exception, expected_regex, def assertRaisesRegex(self, expected_exception, expected_regex,
callable_obj=_sentinel, *args, **kwargs): *args, **kwargs):
"""Asserts that the message in a raised exception matches a regex. """Asserts that the message in a raised exception matches a regex.
Args: Args:
expected_exception: Exception class expected to be raised. expected_exception: Exception class expected to be raised.
expected_regex: Regex (re pattern object or string) expected expected_regex: Regex (re pattern object or string) expected
to be found in error message. to be found in error message.
callable_obj: Function to be called. args: Function to be called and extra positional args.
kwargs: Extra kwargs.
msg: Optional message used in case of failure. Can only be used msg: Optional message used in case of failure. Can only be used
when assertRaisesRegex is used as a context manager. when assertRaisesRegex is used as a context manager.
args: Extra args.
kwargs: Extra kwargs.
""" """
context = _AssertRaisesContext(expected_exception, self, callable_obj, context = _AssertRaisesContext(expected_exception, self, expected_regex)
expected_regex) return context.handle('assertRaisesRegex', args, kwargs)
return context.handle('assertRaisesRegex', callable_obj, args, kwargs)
def assertWarnsRegex(self, expected_warning, expected_regex, def assertWarnsRegex(self, expected_warning, expected_regex,
callable_obj=_sentinel, *args, **kwargs): *args, **kwargs):
"""Asserts that the message in a triggered warning matches a regexp. """Asserts that the message in a triggered warning matches a regexp.
Basic functioning is similar to assertWarns() with the addition Basic functioning is similar to assertWarns() with the addition
that only warnings whose messages also match the regular expression that only warnings whose messages also match the regular expression
...@@ -1250,15 +1252,13 @@ class TestCase(object): ...@@ -1250,15 +1252,13 @@ class TestCase(object):
expected_warning: Warning class expected to be triggered. expected_warning: Warning class expected to be triggered.
expected_regex: Regex (re pattern object or string) expected expected_regex: Regex (re pattern object or string) expected
to be found in error message. to be found in error message.
callable_obj: Function to be called. args: Function to be called and extra positional args.
kwargs: Extra kwargs.
msg: Optional message used in case of failure. Can only be used msg: Optional message used in case of failure. Can only be used
when assertWarnsRegex is used as a context manager. when assertWarnsRegex is used as a context manager.
args: Extra args.
kwargs: Extra kwargs.
""" """
context = _AssertWarnsContext(expected_warning, self, callable_obj, context = _AssertWarnsContext(expected_warning, self, expected_regex)
expected_regex) return context.handle('assertWarnsRegex', args, kwargs)
return context.handle('assertWarnsRegex', callable_obj, args, kwargs)
def assertRegex(self, text, expected_regex, msg=None): def assertRegex(self, text, expected_regex, msg=None):
"""Fail the test unless the text matches the regular expression.""" """Fail the test unless the text matches the regular expression."""
......
...@@ -1146,7 +1146,7 @@ test case ...@@ -1146,7 +1146,7 @@ test case
with self.assertRaises(self.failureException): with self.assertRaises(self.failureException):
self.assertRaises(ExceptionMock, lambda: 0) self.assertRaises(ExceptionMock, lambda: 0)
# Failure when the function is None # Failure when the function is None
with self.assertRaises(TypeError): with self.assertWarns(DeprecationWarning):
self.assertRaises(ExceptionMock, None) self.assertRaises(ExceptionMock, None)
# Failure when another exception is raised # Failure when another exception is raised
with self.assertRaises(ExceptionMock): with self.assertRaises(ExceptionMock):
...@@ -1172,6 +1172,15 @@ test case ...@@ -1172,6 +1172,15 @@ test case
with self.assertRaises(self.failureException): with self.assertRaises(self.failureException):
with self.assertRaises(ExceptionMock): with self.assertRaises(ExceptionMock):
pass pass
# Custom message
with self.assertRaisesRegex(self.failureException, 'foobar'):
with self.assertRaises(ExceptionMock, msg='foobar'):
pass
# Invalid keyword argument
with self.assertWarnsRegex(DeprecationWarning, 'foobar'), \
self.assertRaises(AssertionError):
with self.assertRaises(ExceptionMock, foobar=42):
pass
# Failure when another exception is raised # Failure when another exception is raised
with self.assertRaises(ExceptionMock): with self.assertRaises(ExceptionMock):
self.assertRaises(ValueError, Stub) self.assertRaises(ValueError, Stub)
...@@ -1185,7 +1194,7 @@ test case ...@@ -1185,7 +1194,7 @@ test case
self.assertRaisesRegex(ExceptionMock, re.compile('expect$'), Stub) self.assertRaisesRegex(ExceptionMock, re.compile('expect$'), Stub)
self.assertRaisesRegex(ExceptionMock, 'expect$', Stub) self.assertRaisesRegex(ExceptionMock, 'expect$', Stub)
with self.assertRaises(TypeError): with self.assertWarns(DeprecationWarning):
self.assertRaisesRegex(ExceptionMock, 'expect$', None) self.assertRaisesRegex(ExceptionMock, 'expect$', None)
def testAssertNotRaisesRegex(self): def testAssertNotRaisesRegex(self):
...@@ -1197,6 +1206,15 @@ test case ...@@ -1197,6 +1206,15 @@ test case
self.failureException, '^Exception not raised by <lambda>$', self.failureException, '^Exception not raised by <lambda>$',
self.assertRaisesRegex, Exception, 'x', self.assertRaisesRegex, Exception, 'x',
lambda: None) lambda: None)
# Custom message
with self.assertRaisesRegex(self.failureException, 'foobar'):
with self.assertRaisesRegex(Exception, 'expect', msg='foobar'):
pass
# Invalid keyword argument
with self.assertWarnsRegex(DeprecationWarning, 'foobar'), \
self.assertRaises(AssertionError):
with self.assertRaisesRegex(Exception, 'expect', foobar=42):
pass
def testAssertRaisesRegexInvalidRegex(self): def testAssertRaisesRegexInvalidRegex(self):
# Issue 20145. # Issue 20145.
...@@ -1255,7 +1273,7 @@ test case ...@@ -1255,7 +1273,7 @@ test case
with self.assertRaises(self.failureException): with self.assertRaises(self.failureException):
self.assertWarns(RuntimeWarning, lambda: 0) self.assertWarns(RuntimeWarning, lambda: 0)
# Failure when the function is None # Failure when the function is None
with self.assertRaises(TypeError): with self.assertWarns(DeprecationWarning):
self.assertWarns(RuntimeWarning, None) self.assertWarns(RuntimeWarning, None)
# Failure when another warning is triggered # Failure when another warning is triggered
with warnings.catch_warnings(): with warnings.catch_warnings():
...@@ -1295,6 +1313,15 @@ test case ...@@ -1295,6 +1313,15 @@ test case
with self.assertRaises(self.failureException): with self.assertRaises(self.failureException):
with self.assertWarns(RuntimeWarning): with self.assertWarns(RuntimeWarning):
pass pass
# Custom message
with self.assertRaisesRegex(self.failureException, 'foobar'):
with self.assertWarns(RuntimeWarning, msg='foobar'):
pass
# Invalid keyword argument
with self.assertWarnsRegex(DeprecationWarning, 'foobar'), \
self.assertRaises(AssertionError):
with self.assertWarns(RuntimeWarning, foobar=42):
pass
# Failure when another warning is triggered # Failure when another warning is triggered
with warnings.catch_warnings(): with warnings.catch_warnings():
# Force default filter (in case tests are run with -We) # Force default filter (in case tests are run with -We)
...@@ -1319,7 +1346,7 @@ test case ...@@ -1319,7 +1346,7 @@ test case
self.assertWarnsRegex(RuntimeWarning, "o+", self.assertWarnsRegex(RuntimeWarning, "o+",
lambda: 0) lambda: 0)
# Failure when the function is None # Failure when the function is None
with self.assertRaises(TypeError): with self.assertWarns(DeprecationWarning):
self.assertWarnsRegex(RuntimeWarning, "o+", None) self.assertWarnsRegex(RuntimeWarning, "o+", None)
# Failure when another warning is triggered # Failure when another warning is triggered
with warnings.catch_warnings(): with warnings.catch_warnings():
...@@ -1357,6 +1384,15 @@ test case ...@@ -1357,6 +1384,15 @@ test case
with self.assertRaises(self.failureException): with self.assertRaises(self.failureException):
with self.assertWarnsRegex(RuntimeWarning, "o+"): with self.assertWarnsRegex(RuntimeWarning, "o+"):
pass pass
# Custom message
with self.assertRaisesRegex(self.failureException, 'foobar'):
with self.assertWarnsRegex(RuntimeWarning, 'o+', msg='foobar'):
pass
# Invalid keyword argument
with self.assertWarnsRegex(DeprecationWarning, 'foobar'), \
self.assertRaises(AssertionError):
with self.assertWarnsRegex(RuntimeWarning, 'o+', foobar=42):
pass
# Failure when another warning is triggered # Failure when another warning is triggered
with warnings.catch_warnings(): with warnings.catch_warnings():
# Force default filter (in case tests are run with -We) # Force default filter (in case tests are run with -We)
......
...@@ -76,7 +76,8 @@ Library ...@@ -76,7 +76,8 @@ Library
usernames and passwords to UTF8. usernames and passwords to UTF8.
- Issue #24134: assertRaises(), assertRaisesRegex(), assertWarns() and - Issue #24134: assertRaises(), assertRaisesRegex(), assertWarns() and
assertWarnsRegex() checks are not longer successful if the callable is None. assertWarnsRegex() checks now emits a deprecation warning when callable is
None or keyword arguments except msg is passed in the context manager mode.
- Issue #24018: Add a collections.abc.Generator abstract base class. - Issue #24018: Add a collections.abc.Generator abstract base class.
Contributed by Stefan Behnel. Contributed by Stefan Behnel.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment