Skip to content

Commit ad779b4

Browse files
hershentimhoffm
authored andcommitted
Replaced warnings.warn with either logging.warnings or cbook._warn_external
1) Replaced warnings.warn with either logging.warnings or cbook._warn_external. (#12006) 2) Updated contributions guidelines to use cbook._warn_external.
1 parent 41f72fb commit ad779b4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+399
-340
lines changed

doc/devel/contributing.rst

+60-18
Original file line numberDiff line numberDiff line change
@@ -449,29 +449,71 @@ Then they will receive messages like::
449449
Which logging level to use?
450450
~~~~~~~~~~~~~~~~~~~~~~~~~~~
451451

452-
There are five levels at which you can emit messages. `logging.critical` and
453-
`logging.error` are really only there for errors that will end the use of the
454-
library but not kill the interpreter. `logging.warning` overlaps with the
455-
`warnings` library. The `logging tutorial`_ suggests that the difference
456-
between `logging.warning` and `warnings.warn` is that `warnings.warn` should
457-
be used for things the user must change to stop the warning (typically in the
458-
source), whereas `logging.warning` can be more persistent. Moreover, note
459-
that `warnings.warn` will by default only emit a given warning *once*, whereas
460-
`logging.warning` will display the message every time it is called.
452+
There are five levels at which you can emit messages.
453+
454+
- `logging.critical` and `logging.error` are really only there for errors that
455+
will end the use of the library but not kill the interpreter.
456+
- `logging.warning` and `cbook._warn_external` are used to warn the user,
457+
see below.
458+
- `logging.info` is for information that the user may want to know if the
459+
program behaves oddly. They are not displayed by default. For instance, if
460+
an object isn't drawn because its position is ``NaN``, that can usually
461+
be ignored, but a mystified user could call
462+
``logging.basicConfig(level=logging.INFO)`` and get an error message that
463+
says why.
464+
- `logging.debug` is the least likely to be displayed, and hence can be the
465+
most verbose. "Expected" code paths (e.g., reporting normal intermediate
466+
steps of layouting or rendering) should only log at this level.
461467

462468
By default, `logging` displays all log messages at levels higher than
463469
`logging.WARNING` to `sys.stderr`.
464470

465-
Calls to `logging.info` are not displayed by default. They are for
466-
information that the user may want to know if the program behaves oddly.
467-
For instance, if an object isn't drawn because its position is ``NaN``,
468-
that can usually be ignored, but a mystified user could call
469-
``logging.basicConfig(level=logging.INFO)`` and get an error message that
470-
says why.
471+
The `logging tutorial`_ suggests that the difference
472+
between `logging.warning` and `cbook._warn_external` (which uses
473+
`warnings.warn`) is that `cbook._warn_external` should be used for things the
474+
user must change to stop the warning (typically in the source), whereas
475+
`logging.warning` can be more persistent. Moreover, note that
476+
`cbook._warn_external` will by default only emit a given warning *once* for
477+
each line of user code, whereas `logging.warning` will display the message
478+
every time it is called.
471479

472-
`logging.debug` is the least likely to be displayed, and hence can be the most
473-
verbose. "Expected" code paths (e.g., reporting normal intermediate steps of
474-
layouting or rendering) should only log at this level.
480+
By default, `warnings.warn` displays the line of code that has the `warn` call.
481+
This usually isn't more informative than the warning message itself. Therefore,
482+
Matplotlib uses `cbook._warn_external` which uses `warnings.warn`, but goes
483+
up the stack and displays the first line of code outside of Matplotlib.
484+
For example, for the module::
485+
486+
# in my_matplotlib_module.py
487+
import warnings
488+
489+
def set_range(bottom, top):
490+
if bottom == top:
491+
warnings.warn('Attempting to set identical bottom==top')
492+
493+
494+
running the script::
495+
496+
from matplotlib import my_matplotlib_module
497+
my_matplotlib_module.set_range(0, 0) #set range
498+
499+
500+
will display::
501+
502+
UserWarning: Attempting to set identical bottom==top
503+
warnings.warn('Attempting to set identical bottom==top')
504+
505+
Modifying the module to use `cbook._warn_external`::
506+
507+
from matplotlib import cbook
508+
509+
def set_range(bottom, top):
510+
if bottom == top:
511+
cbook._warn_external('Attempting to set identical bottom==top')
512+
513+
and running the same script will display::
514+
515+
UserWarning: Attempting to set identical bottom==top
516+
my_matplotlib_module.set_range(0, 0) #set range
475517

476518
.. _logging tutorial: https://docs.python.org/3/howto/logging.html#logging-basic-tutorial
477519

lib/matplotlib/__init__.py

+36-39
Original file line numberDiff line numberDiff line change
@@ -244,9 +244,8 @@ def _set_logger_verbose_level(level_str='silent', file_str='sys.stdout'):
244244
fileo = open(file_str, 'w')
245245
# if this fails, we will just write to stdout
246246
except IOError:
247-
warnings.warn('could not open log file "{0}"'
248-
'for writing. Check your '
249-
'matplotlibrc'.format(file_str))
247+
_log.warning('could not open log file "{0}" for writing. '
248+
'Check your matplotlibrc'.format(file_str))
250249
console = logging.StreamHandler(fileo)
251250
console.setLevel(newlev)
252251
_log.addHandler(console)
@@ -307,8 +306,9 @@ def set_level(self, level):
307306
if self._commandLineVerbose is not None:
308307
level = self._commandLineVerbose
309308
if level not in self.levels:
310-
warnings.warn('matplotlib: unrecognized --verbose-* string "%s".'
311-
' Legal values are %s' % (level, self.levels))
309+
cbook._warn_external('matplotlib: unrecognized --verbose-* '
310+
'string "%s". Legal values are %s' %
311+
(level, self.levels))
312312
else:
313313
self.level = level
314314

@@ -487,9 +487,9 @@ def checkdep_ps_distiller(s):
487487
gs_exec, gs_v = checkdep_ghostscript()
488488
if not gs_exec:
489489
flag = False
490-
warnings.warn('matplotlibrc ps.usedistiller option can not be used '
491-
'unless ghostscript 9.0 or later is installed on your '
492-
'system')
490+
_log.warning('matplotlibrc ps.usedistiller option can not be used '
491+
'unless ghostscript 9.0 or later is installed on your '
492+
'system')
493493

494494
if s == 'xpdf':
495495
pdftops_req = '3.0'
@@ -502,9 +502,9 @@ def checkdep_ps_distiller(s):
502502
pass
503503
else:
504504
flag = False
505-
warnings.warn(('matplotlibrc ps.usedistiller can not be set to '
506-
'xpdf unless xpdf-%s or later is installed on '
507-
'your system') % pdftops_req)
505+
_log.warning(('matplotlibrc ps.usedistiller can not be set to '
506+
'xpdf unless xpdf-%s or later is installed on '
507+
'your system') % pdftops_req)
508508

509509
if flag:
510510
return s
@@ -522,22 +522,22 @@ def checkdep_usetex(s):
522522

523523
if shutil.which("tex") is None:
524524
flag = False
525-
warnings.warn('matplotlibrc text.usetex option can not be used unless '
526-
'TeX is installed on your system')
525+
_log.warning('matplotlibrc text.usetex option can not be used unless '
526+
'TeX is installed on your system')
527527

528528
dvipng_v = checkdep_dvipng()
529529
if not compare_versions(dvipng_v, dvipng_req):
530530
flag = False
531-
warnings.warn('matplotlibrc text.usetex can not be used with *Agg '
532-
'backend unless dvipng-%s or later is installed on '
533-
'your system' % dvipng_req)
531+
_log.warning('matplotlibrc text.usetex can not be used with *Agg '
532+
'backend unless dvipng-%s or later is installed on '
533+
'your system' % dvipng_req)
534534

535535
gs_exec, gs_v = checkdep_ghostscript()
536536
if not compare_versions(gs_v, gs_req):
537537
flag = False
538-
warnings.warn('matplotlibrc text.usetex can not be used unless '
539-
'ghostscript-%s or later is installed on your system'
540-
% gs_req)
538+
_log.warning('matplotlibrc text.usetex can not be used unless '
539+
'ghostscript-%s or later is installed on your system'
540+
% gs_req)
541541

542542
return flag
543543

@@ -962,17 +962,17 @@ def _rc_params_in_file(fname, fail_on_error=False):
962962
tup = strippedline.split(':', 1)
963963
if len(tup) != 2:
964964
error_details = _error_details_fmt % (cnt, line, fname)
965-
warnings.warn('Illegal %s' % error_details)
965+
_log.warning('Illegal %s' % error_details)
966966
continue
967967
key, val = tup
968968
key = key.strip()
969969
val = val.strip()
970970
if key in rc_temp:
971-
warnings.warn('Duplicate key in file "%s", line #%d' %
972-
(fname, cnt))
971+
_log.warning('Duplicate key in file "%s", line #%d' %
972+
(fname, cnt))
973973
rc_temp[key] = (val, line, cnt)
974974
except UnicodeDecodeError:
975-
warnings.warn(
975+
_log.warning(
976976
('Cannot decode configuration file %s with '
977977
'encoding %s, check LANG and LC_* variables')
978978
% (fname, locale.getpreferredencoding(do_setlocale=False) or
@@ -991,8 +991,8 @@ def _rc_params_in_file(fname, fail_on_error=False):
991991
config[key] = val # try to convert to proper type or skip
992992
except Exception as msg:
993993
error_details = _error_details_fmt % (cnt, line, fname)
994-
warnings.warn('Bad val "%s" on %s\n\t%s' %
995-
(val, error_details, msg))
994+
_log.warning('Bad val "%s" on %s\n\t%s' %
995+
(val, error_details, msg))
996996

997997
for key, (val, line, cnt) in rc_temp.items():
998998
if key in defaultParams:
@@ -1003,8 +1003,8 @@ def _rc_params_in_file(fname, fail_on_error=False):
10031003
config[key] = val # try to convert to proper type or skip
10041004
except Exception as msg:
10051005
error_details = _error_details_fmt % (cnt, line, fname)
1006-
warnings.warn('Bad val "%s" on %s\n\t%s' %
1007-
(val, error_details, msg))
1006+
_log.warning('Bad val "%s" on %s\n\t%s' %
1007+
(val, error_details, msg))
10081008
elif key in _deprecated_ignore_map:
10091009
version, alt_key = _deprecated_ignore_map[key]
10101010
cbook.warn_deprecated(
@@ -1347,10 +1347,9 @@ def use(arg, warn=False, force=True):
13471347
# If we are going to force the switch, never warn, else, if warn
13481348
# is True, then direct users to `plt.switch_backend`
13491349
if (not force) and warn:
1350-
warnings.warn(
1350+
cbook._warn_external(
13511351
("matplotlib.pyplot as already been imported, "
1352-
"this call will have no effect."),
1353-
stacklevel=2)
1352+
"this call will have no effect."))
13541353

13551354
# if we are going to force switching the backend, pull in
13561355
# `switch_backend` from pyplot. This will only happen if
@@ -1430,7 +1429,7 @@ def _init_tests():
14301429
from matplotlib import ft2font
14311430
if (ft2font.__freetype_version__ != LOCAL_FREETYPE_VERSION or
14321431
ft2font.__freetype_build_type__ != 'local'):
1433-
warnings.warn(
1432+
_log.warning(
14341433
"Matplotlib is not built with the correct FreeType version to run "
14351434
"tests. Set local_freetype=True in setup.cfg and rebuild. "
14361435
"Expect many image comparison failures below. "
@@ -1439,9 +1438,7 @@ def _init_tests():
14391438
"Freetype build type is {2}local".format(
14401439
LOCAL_FREETYPE_VERSION,
14411440
ft2font.__freetype_version__,
1442-
"" if ft2font.__freetype_build_type__ == 'local' else "not "
1443-
)
1444-
)
1441+
"" if ft2font.__freetype_build_type__ == 'local' else "not "))
14451442

14461443
try:
14471444
import pytest
@@ -1771,12 +1768,12 @@ def inner(ax, *args, data=None, **kwargs):
17711768
elif label_namer in kwargs:
17721769
kwargs['label'] = get_label(kwargs[label_namer], label)
17731770
else:
1774-
warnings.warn(
1771+
cbook._warn_external(
17751772
"Tried to set a label via parameter %r in func %r but "
1776-
"couldn't find such an argument.\n"
1777-
"(This is a programming error, please report to "
1778-
"the Matplotlib list!)" % (label_namer, func.__name__),
1779-
RuntimeWarning, stacklevel=2)
1773+
"couldn't find such an argument.\n(This is a "
1774+
"programming error, please report to the Matplotlib "
1775+
"list!)" % (label_namer, func.__name__),
1776+
RuntimeWarning)
17801777
return func(ax, *args, **kwargs)
17811778

17821779
inner.__doc__ = _add_data_doc(inner.__doc__,

lib/matplotlib/_constrained_layout.py

+8-7
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,11 @@
4545
# Todo: AnchoredOffsetbox connected to gridspecs or axes. This would
4646
# be more general way to add extra-axes annotations.
4747

48-
import numpy as np
4948
import logging
50-
import warnings
5149

50+
import numpy as np
51+
52+
import matplotlib.cbook as cbook
5253
import matplotlib._layoutbox as layoutbox
5354

5455
_log = logging.getLogger(__name__)
@@ -153,9 +154,9 @@ def do_constrained_layout(fig, renderer, h_pad, w_pad,
153154
if gs._layoutbox is not None:
154155
gss.add(gs)
155156
if len(gss) == 0:
156-
warnings.warn('There are no gridspecs with layoutboxes. '
157-
'Possibly did not call parent GridSpec with the figure= '
158-
'keyword')
157+
cbook._warn_external('There are no gridspecs with layoutboxes. '
158+
'Possibly did not call parent GridSpec with the'
159+
' figure= keyword')
159160

160161
if fig._layoutbox.constrained_layout_called < 1:
161162
for gs in gss:
@@ -221,8 +222,8 @@ def do_constrained_layout(fig, renderer, h_pad, w_pad,
221222
# so this does the same w/o zeroing layout.
222223
ax._set_position(newpos, which='original')
223224
else:
224-
warnings.warn('constrained_layout not applied. At least '
225-
'one axes collapsed to zero width or height.')
225+
cbook._warn_external('constrained_layout not applied. At least '
226+
'one axes collapsed to zero width or height.')
226227

227228

228229
def _make_ghost_gridspec_slots(fig, gs):

lib/matplotlib/artist.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from collections import OrderedDict, namedtuple
22
from functools import wraps
33
import inspect
4+
import logging
45
import re
56
import warnings
67

@@ -12,6 +13,8 @@
1213
from .transforms import (Bbox, IdentityTransform, Transform, TransformedBbox,
1314
TransformedPatchPath, TransformedPath)
1415

16+
_log = logging.getLogger(__name__)
17+
1518

1619
def allow_rasterization(draw):
1720
"""
@@ -400,7 +403,7 @@ def contains(self, mouseevent):
400403
"""
401404
if callable(self._contains):
402405
return self._contains(self, mouseevent)
403-
warnings.warn("'%s' needs 'contains' method" % self.__class__.__name__)
406+
_log.warning("'%s' needs 'contains' method" % self.__class__.__name__)
404407
return False, {}
405408

406409
def set_contains(self, picker):
@@ -850,7 +853,8 @@ def set_rasterized(self, rasterized):
850853
rasterized : bool or None
851854
"""
852855
if rasterized and not hasattr(self.draw, "_supports_rasterization"):
853-
warnings.warn("Rasterization of '%s' will be ignored" % self)
856+
cbook._warn_external(
857+
"Rasterization of '%s' will be ignored" % self)
854858

855859
self._rasterized = rasterized
856860

lib/matplotlib/axes/_axes.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -4623,8 +4623,9 @@ def hexbin(self, x, y, C=None, gridsize=100, bins=None,
46234623
# Set normalizer if bins is 'log'
46244624
if bins == 'log':
46254625
if norm is not None:
4626-
warnings.warn("Only one of 'bins' and 'norm' arguments can be "
4627-
"supplied, ignoring bins={}".format(bins))
4626+
cbook._warn_external("Only one of 'bins' and 'norm' "
4627+
"arguments can be supplied, ignoring "
4628+
"bins={}".format(bins))
46284629
else:
46294630
norm = mcolors.LogNorm()
46304631
bins = None

0 commit comments

Comments
 (0)