Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ordered argument to ShouldWarn context manager #185

Merged
merged 4 commits into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions docs/warnings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ causing the test in which it occurs to fail:
... warn("sorry dave, I can't let you do that")
Traceback (most recent call last):
...
AssertionError: sequence not as expected:
<BLANKLINE>
AssertionError:...
<SequenceComparison(ordered=True, partial=False)(failed)>
same:
[]
<BLANKLINE>
Expand All @@ -40,6 +40,7 @@ attributes differ:
<BLANKLINE>
actual:
[UserWarning("sorry dave, I can't let you do that"...)]
</SequenceComparison(ordered=True, partial=False)> (expected) != [UserWarning("sorry dave, I can't let you do that"...)] (actual)

You can check multiple warnings in a particular piece of code:

Expand All @@ -50,6 +51,16 @@ You can check multiple warnings in a particular piece of code:
... warn('you should fix that')
... warn('and that too')

If you don't care about the order of issued warnings, you can use ``order_matters=False``:

>>> from warnings import warn
>>> from testfixtures import ShouldWarn
>>> with ShouldWarn(UserWarning('you should fix that'),
... UserWarning('and that too'),
... order_matters=False):
... warn('and that too')
... warn('you should fix that')

If you want to inspect more details of the warnings issued, you can capture
them into a list as follows:

Expand Down Expand Up @@ -78,8 +89,8 @@ context it manages, it will raise an :class:`AssertionError` to indicate this:
... warn("woah dude")
Traceback (most recent call last):
...
AssertionError: sequence not as expected:
<BLANKLINE>
AssertionError:...
<SequenceComparison(ordered=True, partial=False)(failed)>
same:
[]
<BLANKLINE>
Expand All @@ -88,3 +99,4 @@ expected:
<BLANKLINE>
actual:
[UserWarning('woah dude'...)]
</SequenceComparison(ordered=True, partial=False)> (expected) != [UserWarning('woah dude'...)] (actual)
19 changes: 15 additions & 4 deletions testfixtures/shouldwarn.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import warnings
from typing import Union, Type

from testfixtures import Comparison as C, compare
from testfixtures import Comparison, SequenceComparison, compare


WarningOrType = Union[Warning, Type[Warning]]
Expand All @@ -26,6 +26,13 @@ class ShouldWarn(warnings.catch_warnings):
If no expected warnings are passed, you will need to inspect
the contents of the list returned by the context manager.


:param order_matters:

A keyword-only parameter that controls whether the order of the
captured entries is required to match those of the expected entries.
Defaults to ``True``.

:param filters:
If passed, these are used to create a filter such that only warnings you
are interested in will be considered by this :class:`ShouldWarn`
Expand All @@ -36,9 +43,10 @@ class ShouldWarn(warnings.catch_warnings):

_empty_okay = False

def __init__(self, *expected: WarningOrType, **filters):
def __init__(self, *expected: WarningOrType, order_matters: bool = True, **filters):
super(ShouldWarn, self).__init__(record=True)
self.expected = [C(e) for e in expected]
self.order_matters = order_matters
self.expected = [Comparison(e) for e in expected]
self.filters = filters

def __enter__(self):
Expand All @@ -52,7 +60,10 @@ def __exit__(self, exc_type, exc_val, exc_tb):
return
if not self.expected and self.recorded and not self._empty_okay:
return
compare(self.expected, actual=[wm.message for wm in self.recorded])
compare(
expected=SequenceComparison(*self.expected, ordered=self.order_matters),
actual=[wm.message for wm in self.recorded]
)


class ShouldNotWarn(ShouldWarn):
Expand Down
51 changes: 41 additions & 10 deletions testfixtures/tests/test_shouldwarn.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@ def test_warn_expected(self):

def test_warn_not_expected(self):
with ShouldAssert(
"sequence not as expected:\n\n"
"\n<SequenceComparison(ordered=True, partial=False)(failed)>\n"
"same:\n[]\n\n"
"expected:\n[]\n\n"
"actual:\n[UserWarning('foo'"+comma+")]"
"actual:\n[UserWarning('foo'"+comma+")]\n"
"</SequenceComparison(ordered=True, partial=False)> (expected) "
"!= [UserWarning('foo'"+comma+")] (actual)"
):
with warnings.catch_warnings(record=True) as backstop:
with ShouldNotWarn():
Expand All @@ -41,10 +43,11 @@ def test_no_warn_expected(self):

def test_no_warn_not_expected(self):
with ShouldAssert(
"sequence not as expected:\n\n"
"\n<SequenceComparison(ordered=True, partial=False)(failed)>\n"
"same:\n[]\n\n"
"expected:\n[<C:builtins.UserWarning>args: ('foo',)</>]"
"\n\nactual:\n[]"
"\n\nactual:\n[]\n"
"</SequenceComparison(ordered=True, partial=False)> (expected) != [] (actual)"
):
with ShouldWarn(UserWarning('foo')):
pass
Expand All @@ -64,17 +67,42 @@ def test_multiple_warnings(self):
self.assertTrue('foo' in content)
self.assertTrue('bar' in content)

def test_multiple_warnings_ordered(self):
with warnings.catch_warnings(record=True) as backstop:
with ShouldWarn(UserWarning('foo'), UserWarning('bar')):
warnings.warn('foo')
warnings.warn('bar')
compare(len(backstop), expected=0)

def test_multiple_warnings_wrong_order(self):
with ShouldRaise(AssertionError) as s:
with ShouldWarn(UserWarning('foo'), UserWarning('bar')):
warnings.warn('bar')
warnings.warn('foo')
content = str(s.raised)
self.assertTrue('foo' in content)
self.assertTrue('bar' in content)

def test_multiple_warnings_ignore_order(self):
with warnings.catch_warnings(record=True) as backstop:
with ShouldWarn(UserWarning('foo'), UserWarning('bar'), order_matters=False):
warnings.warn('bar')
warnings.warn('foo')
compare(len(backstop), expected=0)

def test_minimal_ok(self):
with ShouldWarn(UserWarning):
warnings.warn('foo')

def test_minimal_bad(self):
with ShouldAssert(
"sequence not as expected:\n\n"
"\n<SequenceComparison(ordered=True, partial=False)(failed)>\n"
"same:\n[]\n\n"
"expected:\n"
"[<C:builtins.DeprecationWarning(failed)>wrong type</>]\n\n"
"actual:\n[UserWarning('foo'"+comma+")]"
"actual:\n[UserWarning('foo'"+comma+")]\n"
"</SequenceComparison(ordered=True, partial=False)> (expected) "
"!= [UserWarning('foo'"+comma+")] (actual)"
):
with ShouldWarn(DeprecationWarning):
warnings.warn('foo')
Expand All @@ -87,14 +115,16 @@ def test_maximal_ok(self):

def test_maximal_bad(self):
with ShouldAssert(
"sequence not as expected:\n\n"
"\n<SequenceComparison(ordered=True, partial=False)(failed)>\n"
"same:\n[]\n\n"
"expected:\n[\n"
"<C:builtins.DeprecationWarning(failed)>\n"
"attributes differ:\n"
"'args': ('bar',) (Comparison) != ('foo',) (actual)\n"
"</C:builtins.DeprecationWarning>]\n\n"
"actual:\n[DeprecationWarning('foo'"+comma+")]"
"actual:\n[DeprecationWarning('foo'"+comma+")]\n"
"</SequenceComparison(ordered=True, partial=False)> (expected) "
"!= [DeprecationWarning('foo'"+comma+")] (actual)"
):
with ShouldWarn(DeprecationWarning('bar')):
warnings.warn_explicit(
Expand Down Expand Up @@ -130,10 +160,11 @@ def test_filter_present(self):

def test_filter_missing(self):
with ShouldAssert(
"sequence not as expected:\n\n"
"\n<SequenceComparison(ordered=True, partial=False)(failed)>\n"
"same:\n[]\n\n"
"expected:\n[<C:builtins.DeprecationWarning>]\n\n"
"actual:\n[]"
"actual:\n[]\n"
"</SequenceComparison(ordered=True, partial=False)> (expected) != [] (actual)"
):
with ShouldWarn(DeprecationWarning,
message="This function is deprecated."):
Expand Down