Skip to content

Commit

Permalink
Add warns() context manager.
Browse files Browse the repository at this point in the history
  • Loading branch information
bradleyayers committed May 12, 2012
1 parent 8fb6e4c commit aa9d3fc
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 1 deletion.
60 changes: 59 additions & 1 deletion attest/contexts.py
Expand Up @@ -18,6 +18,7 @@
'Error',
'raises',
'tempdir',
'warns',
]


Expand Down Expand Up @@ -101,7 +102,7 @@ def __str__(self):
return str(self.exc)

def __repr__(self):
return '<Error %s>' % repr(self.exc)
return u'<Error %s>' % repr(self.exc)


@contextmanager
Expand Down Expand Up @@ -160,3 +161,60 @@ def tempdir(*args, **kwargs):
yield d
finally:
rmtree(d)


@contextmanager
def warns(*warnings, **opts):
"""Context manager that succeeds if all `warnings` are seen inside the
context. Yields a list of matching captured warnings as
:class:`warnings.WarningMessage` objects.
.. testsetup::
from attest import warns
import warnings
>>> with warns(UserWarning) as captured:
... warnings.warn("Example warning", UserWarning)
...
>>> captured[0].message == "Example warning"
True
:param any: Require only *one* of the warnings to be observed.
.. note::
1. :mod:`warnings` filtering is overridden to ``"always"`` for
monitored warnings.
2. :attr:`WarningMessage.message` differs from Python's default.
Rather than being the warning instance, it is ``unicode(warning)``.
"""
import warnings as mod

captured = []
old_filters, old_showwarning = mod.filters, mod.showwarning
mod.filters = old_filters[:]

def showwarning(message, category, *args, **kwargs):
if category not in warnings:
old_showwarning(message, category, *args, **kwargs)
return
# It's more useful to have the message as the text representation of
# the warning, rather than an exception instance
message = unicode(message)
captured.append(mod.WarningMessage(message, category, *args, **kwargs))
mod.showwarning = showwarning

for warning in warnings:
mod.simplefilter("always", warning)

try:
yield captured
if opts.get("any", False):
assert captured
else:
assert set(warnings) == set([w.category for w in captured])
finally:
mod.filters = old_filters
mod.showwarning = old_showwarning
28 changes: 28 additions & 0 deletions attest/tests/contexts.py
Expand Up @@ -3,6 +3,7 @@
import sys
import os
from os import path
import warnings

from attest import Tests, assert_hook, Assert
import attest
Expand Down Expand Up @@ -86,3 +87,30 @@ def tempdir():
open(path.join(d, 'tempfile'), 'w').close()
assert os.listdir(d) == ['tempfile']
assert not path.exists(d)


@suite.test
def warns():
with attest.warns(UserWarning) as captured:
warnings.warn("foo", UserWarning)

assert unicode(captured[0].message) == "foo"

with attest.raises(AssertionError):
with attest.warns(UserWarning):
pass

with attest.raises(AssertionError):
with attest.warns(UserWarning, DeprecationWarning):
warnings.warn("foo", UserWarning)

with attest.warns(UserWarning, DeprecationWarning, any=True):
warnings.warn("foo", UserWarning)

if hasattr(warnings, "catch_warnings"): # not available in Python 2.5
with warnings.catch_warnings():
warnings.simplefilter("error", UserWarning)
with attest.warns(UserWarning):
warnings.warn("foo")
with attest.raises(UserWarning):
warnings.warn("bar")
2 changes: 2 additions & 0 deletions docs/api/contexts.rst
Expand Up @@ -7,6 +7,8 @@ Attest provides some context managers that are useful for writing tests.

.. autofunction:: raises(\*exceptions)

.. autofunction:: warns(\*warnings)

.. autofunction:: capture_output()

.. autofunction:: disable_imports(\*names)
Expand Down

0 comments on commit aa9d3fc

Please sign in to comment.