Skip to content

Commit e1fefb2

Browse files
authored
Merge pull request #2692 from devitocodes/JDBetteridge/devito_warnings
misc: Add custom warnings functionality
2 parents c95f40b + f804f38 commit e1fefb2

File tree

4 files changed

+117
-0
lines changed

4 files changed

+117
-0
lines changed

devito/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from devito.builtins import * # noqa
2828
from devito.data.allocators import * # noqa
2929
from devito.logger import error, warning, info, set_log_level # noqa
30+
from devito.warnings import warn # noqa
3031
from devito.mpi import MPI, CustomTopology # noqa
3132
try:
3233
from devito.checkpointing import DevitoCheckpoint, CheckpointOperator # noqa

devito/logger.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
CRITICAL = logging.CRITICAL
2323

2424
logging.addLevelName(PERF, "PERF")
25+
# Note: Do not use
26+
# logging.captureWarnings(True)
27+
# here as it will swallow all warnings (not just Devito warnings)
28+
# Instead use the `devito.warnings` module to log warnings
2529

2630
logger_registry = {
2731
'DEBUG': DEBUG,

devito/warnings.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import warnings
2+
3+
from devito.logger import warning as log_warning
4+
5+
6+
class DevitoWarning(Warning):
7+
pass
8+
9+
10+
def warn(message, category=None, stacklevel=2, source=None):
11+
"""
12+
`devito.warn` follows the Python call signature for `warning.warn`:
13+
https://docs.python.org/3/library/warnings.html#warnings.warn
14+
15+
Parameters
16+
----------
17+
message: str or Warning
18+
Message to display
19+
category: None or Warning
20+
Leave as None to get a `DevitoWarning`
21+
stacklevel: int
22+
Set a custom stack level
23+
source: None or object
24+
the destroyed object which emitted a `ResourceWarning`
25+
"""
26+
warning_type = None
27+
if isinstance(message, Warning):
28+
warning_type = message.__class__.__name__
29+
elif category is not None:
30+
warning_type = category.__name__
31+
32+
if warning_type is not None:
33+
message = f'from {warning_type}: {str(message)}'
34+
35+
log_warning(message)
36+
warnings.warn(message, category=DevitoWarning, stacklevel=stacklevel, source=source)

tests/test_warnings.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import pytest
2+
import warnings
3+
4+
from devito.warnings import warn, DevitoWarning
5+
6+
7+
class NewWarning(UserWarning):
8+
"""
9+
A custom warning class
10+
"""
11+
pass
12+
13+
14+
@pytest.fixture
15+
def custom_warning():
16+
# Instance of custom warning class
17+
return NewWarning('** NEW ** A fun new kind of warning')
18+
19+
20+
class TestDevitoWarnings:
21+
"""
22+
In all cases check that the `DevitoWarning` type of Warning is raised
23+
__in all cases__, even if a custom type is provided
24+
"""
25+
def test_raise(self):
26+
with pytest.warns(DevitoWarning) as w:
27+
warn('Let this be a warning to you')
28+
29+
assert len(w) == 1
30+
assert 'DevitoWarning' in repr(w[0].message)
31+
assert w[0].filename == __file__
32+
33+
def test_raise_from_user(self):
34+
with pytest.warns(DevitoWarning) as w:
35+
warn('Let this be another warning to you', UserWarning)
36+
37+
assert len(w) == 1
38+
assert 'UserWarning:' in str(w[0].message)
39+
assert w[0].filename == __file__
40+
41+
def test_raise_from_user_kw(self):
42+
with pytest.warns(DevitoWarning) as w:
43+
warn('Let this be another warning to you', category=UserWarning)
44+
45+
assert len(w) == 1
46+
assert 'UserWarning:' in str(w[0].message)
47+
assert w[0].filename == __file__
48+
49+
def test_raise_from_custom(self, custom_warning):
50+
with pytest.warns(DevitoWarning) as w:
51+
warn(custom_warning)
52+
53+
assert len(w) == 1
54+
assert 'NewWarning:' in str(w[0].message)
55+
assert w[0].filename == __file__
56+
57+
58+
class TestWarning:
59+
"""
60+
Check that the custom DevitoWarning does not interfere with Python warnings
61+
"""
62+
def test_raise(self):
63+
with pytest.warns(UserWarning):
64+
warnings.warn('Let this be a warning to you')
65+
66+
def test_raise_devito(self):
67+
with pytest.warns(DevitoWarning):
68+
warnings.warn('Let this be another warning to you', DevitoWarning)
69+
70+
def test_raise_devito_kw(self):
71+
with pytest.warns(DevitoWarning):
72+
warn('Let this be another warning to you', category=DevitoWarning)
73+
74+
def test_raise_from_custom(self, custom_warning):
75+
with pytest.warns(NewWarning):
76+
warnings.warn(custom_warning)

0 commit comments

Comments
 (0)