Skip to content

Commit b00921b

Browse files
committed
1) Replaced warnings.warn with either logging.warnings or cbook._warn_external. 2) Updated contributions guidelines to use cbook._warn_external.
1 parent 566bd8b commit b00921b

Some content is hidden

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

48 files changed

+359
-304
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_extrenal` 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 it 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+
Modifiying the module to use `cbook._warn_extermal`::
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

+26-24
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ 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}"'
247+
logging.warning('could not open log file "{0}"'
248248
'for writing. Check your '
249249
'matplotlibrc'.format(file_str))
250250
console = logging.StreamHandler(fileo)
@@ -307,8 +307,10 @@ def set_level(self, level):
307307
if self._commandLineVerbose is not None:
308308
level = self._commandLineVerbose
309309
if level not in self.levels:
310-
warnings.warn('matplotlib: unrecognized --verbose-* string "%s".'
311-
' Legal values are %s' % (level, self.levels))
310+
cbook._warn_external(
311+
'matplotlib: unrecognized --verbose-* string "%s". Legal '
312+
'values are %s' % (
313+
level, self.levels))
312314
else:
313315
self.level = level
314316

@@ -487,7 +489,8 @@ def checkdep_ps_distiller(s):
487489
gs_exec, gs_v = checkdep_ghostscript()
488490
if not gs_exec:
489491
flag = False
490-
warnings.warn('matplotlibrc ps.usedistiller option can not be used '
492+
493+
logging.warning('matplotlibrc ps.usedistiller option can not be used '
491494
'unless ghostscript 9.0 or later is installed on your '
492495
'system')
493496

@@ -502,7 +505,7 @@ def checkdep_ps_distiller(s):
502505
pass
503506
else:
504507
flag = False
505-
warnings.warn(('matplotlibrc ps.usedistiller can not be set to '
508+
logging.warning(('matplotlibrc ps.usedistiller can not be set to '
506509
'xpdf unless xpdf-%s or later is installed on '
507510
'your system') % pdftops_req)
508511

@@ -522,20 +525,21 @@ def checkdep_usetex(s):
522525

523526
if shutil.which("tex") is None:
524527
flag = False
525-
warnings.warn('matplotlibrc text.usetex option can not be used unless '
526-
'TeX is installed on your system')
528+
logging.warning(
529+
'matplotlibrc text.usetex option can not be used unless TeX is '
530+
'installed on your system')
527531

528532
dvipng_v = checkdep_dvipng()
529533
if not compare_versions(dvipng_v, dvipng_req):
530534
flag = False
531-
warnings.warn('matplotlibrc text.usetex can not be used with *Agg '
535+
logging.warning('matplotlibrc text.usetex can not be used with *Agg '
532536
'backend unless dvipng-%s or later is installed on '
533537
'your system' % dvipng_req)
534538

535539
gs_exec, gs_v = checkdep_ghostscript()
536540
if not compare_versions(gs_v, gs_req):
537541
flag = False
538-
warnings.warn('matplotlibrc text.usetex can not be used unless '
542+
logging.warning('matplotlibrc text.usetex can not be used unless '
539543
'ghostscript-%s or later is installed on your system'
540544
% gs_req)
541545

@@ -954,17 +958,17 @@ def _rc_params_in_file(fname, fail_on_error=False):
954958
tup = strippedline.split(':', 1)
955959
if len(tup) != 2:
956960
error_details = _error_details_fmt % (cnt, line, fname)
957-
warnings.warn('Illegal %s' % error_details)
961+
logging.warning('Illegal %s' % error_details)
958962
continue
959963
key, val = tup
960964
key = key.strip()
961965
val = val.strip()
962966
if key in rc_temp:
963-
warnings.warn('Duplicate key in file "%s", line #%d' %
967+
logging.warning('Duplicate key in file "%s", line #%d' %
964968
(fname, cnt))
965969
rc_temp[key] = (val, line, cnt)
966970
except UnicodeDecodeError:
967-
warnings.warn(
971+
logging.warning(
968972
('Cannot decode configuration file %s with '
969973
'encoding %s, check LANG and LC_* variables')
970974
% (fname, locale.getpreferredencoding(do_setlocale=False) or
@@ -983,7 +987,7 @@ def _rc_params_in_file(fname, fail_on_error=False):
983987
config[key] = val # try to convert to proper type or skip
984988
except Exception as msg:
985989
error_details = _error_details_fmt % (cnt, line, fname)
986-
warnings.warn('Bad val "%s" on %s\n\t%s' %
990+
logging.warning('Bad val "%s" on %s\n\t%s' %
987991
(val, error_details, msg))
988992

989993
for key, (val, line, cnt) in rc_temp.items():
@@ -995,7 +999,7 @@ def _rc_params_in_file(fname, fail_on_error=False):
995999
config[key] = val # try to convert to proper type or skip
9961000
except Exception as msg:
9971001
error_details = _error_details_fmt % (cnt, line, fname)
998-
warnings.warn('Bad val "%s" on %s\n\t%s' %
1002+
logging.warning('Bad val "%s" on %s\n\t%s' %
9991003
(val, error_details, msg))
10001004
elif key in _deprecated_ignore_map:
10011005
version, alt_key = _deprecated_ignore_map[key]
@@ -1338,10 +1342,9 @@ def use(arg, warn=True, force=False):
13381342
# If we are going to force the switch, never warn, else, if warn
13391343
# is True, then direct users to `plt.switch_backend`
13401344
if (not force) and warn:
1341-
warnings.warn(
1345+
cbook._warn_external(
13421346
("matplotlib.pyplot as already been imported, "
1343-
"this call will have no effect."),
1344-
stacklevel=2)
1347+
"this call will have no effect."))
13451348

13461349
# if we are going to force switching the backend, pull in
13471350
# `switch_backend` from pyplot. This will only happen if
@@ -1421,7 +1424,7 @@ def _init_tests():
14211424
from matplotlib import ft2font
14221425
if (ft2font.__freetype_version__ != LOCAL_FREETYPE_VERSION or
14231426
ft2font.__freetype_build_type__ != 'local'):
1424-
warnings.warn(
1427+
logging.warning(
14251428
"Matplotlib is not built with the correct FreeType version to run "
14261429
"tests. Set local_freetype=True in setup.cfg and rebuild. "
14271430
"Expect many image comparison failures below. "
@@ -1430,9 +1433,7 @@ def _init_tests():
14301433
"Freetype build type is {2}local".format(
14311434
LOCAL_FREETYPE_VERSION,
14321435
ft2font.__freetype_version__,
1433-
"" if ft2font.__freetype_build_type__ == 'local' else "not "
1434-
)
1435-
)
1436+
"" if ft2font.__freetype_build_type__ == 'local' else "not "))
14361437

14371438
try:
14381439
import pytest
@@ -1762,12 +1763,13 @@ def inner(ax, *args, data=None, **kwargs):
17621763
elif label_namer in kwargs:
17631764
kwargs['label'] = get_label(kwargs[label_namer], label)
17641765
else:
1765-
warnings.warn(
1766+
cbook._warn_external(
17661767
"Tried to set a label via parameter %r in func %r but "
17671768
"couldn't find such an argument.\n"
17681769
"(This is a programming error, please report to "
1769-
"the Matplotlib list!)" % (label_namer, func.__name__),
1770-
RuntimeWarning, stacklevel=2)
1770+
"the Matplotlib list!)" % (label_namer,
1771+
func.__name__),
1772+
RuntimeWarning)
17711773
return func(ax, *args, **kwargs)
17721774

17731775
inner.__doc__ = _add_data_doc(inner.__doc__,

lib/matplotlib/_constrained_layout.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747

4848
import numpy as np
4949
import logging
50-
import warnings
50+
import matplotlib.cbook as cbook
5151

5252
import matplotlib._layoutbox as layoutbox
5353

@@ -153,7 +153,7 @@ def do_constrained_layout(fig, renderer, h_pad, w_pad,
153153
if gs._layoutbox is not None:
154154
gss.add(gs)
155155
if len(gss) == 0:
156-
warnings.warn('There are no gridspecs with layoutboxes. '
156+
cbook._warn_external('There are no gridspecs with layoutboxes. '
157157
'Possibly did not call parent GridSpec with the figure= '
158158
'keyword')
159159

@@ -221,7 +221,7 @@ def do_constrained_layout(fig, renderer, h_pad, w_pad,
221221
# so this does the same w/o zeroing layout.
222222
ax._set_position(newpos, which='original')
223223
else:
224-
warnings.warn('constrained_layout not applied. At least '
224+
cbook._warn_ext('constrained_layout not applied. At least '
225225
'one axes collapsed to zero width or height.')
226226

227227

lib/matplotlib/artist.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import inspect
44
import re
55
import warnings
6+
import logging
67

78
import numpy as np
89

@@ -400,7 +401,8 @@ def contains(self, mouseevent):
400401
"""
401402
if callable(self._contains):
402403
return self._contains(self, mouseevent)
403-
warnings.warn("'%s' needs 'contains' method" % self.__class__.__name__)
404+
logging.warning("'%s' needs 'contains' method" %
405+
self.__class__.__name__)
404406
return False, {}
405407

406408
def set_contains(self, picker):
@@ -850,7 +852,7 @@ def set_rasterized(self, rasterized):
850852
rasterized : bool or None
851853
"""
852854
if rasterized and not hasattr(self.draw, "_supports_rasterization"):
853-
warnings.warn("Rasterization of '%s' will be ignored" % self)
855+
logging.warning("Rasterization of '%s' will be ignored" % self)
854856

855857
self._rasterized = rasterized
856858

lib/matplotlib/axes/_axes.py

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

0 commit comments

Comments
 (0)