Skip to content

Commit e86c1c6

Browse files
committed
Change verbose to standard logging library
Change verbose to standard logging library PEP8 fixes and formatting of log messages @anntzer PEP8 fixes and formatting of log messages @anntzer Small fixes as suggested by @aantzer and @tacaswell Documentation Fix on merge Number small fixes Small fix for docs Small fix for error in animate.py fixed import order and small doc fix Small doc fix Changed log. to _log. Fix on merge Number small fixes Small fix for docs Small fix for error in animate.py fixed import order and small doc fix Small doc fix Changed log. to _log. Changed log. to _log. fixed fonts gah gah gah gah gah gah PEP8 doc fix revert _base.py fix missing _log Fixes for @efiring Fixes for @anntzer
1 parent c465993 commit e86c1c6

24 files changed

+434
-241
lines changed

.appveyor.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ after_test:
144144
# After the tests were a success, build packages (wheels and conda)
145145

146146
# Build the wheel with the static libs
147-
# Hide the output, the copied files really clutter the build log...
147+
# Hide the output, the copied files really clutter the build_log...
148148
- '%CMD_IN_ENV% python setup.py bdist_wheel > NUL:'
149149

150150
# And now the conda build after a cleanup...

doc/devel/contributing.rst

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,73 @@ forced to use ``**kwargs``. An example is
437437
elif len(args) == 1:
438438
...etc...
439439

440+
.. _using_logging:
441+
442+
Using logging for debug messages
443+
--------------------------------
444+
445+
Matplotlib uses the standard python logging_ library to write verbose
446+
warnings, information, and
447+
debug messages. Please use it! In all those places you write :func:`print()`
448+
statements to do your debugging, try using :func:`log.debug()` instead!
449+
450+
.. _logging: https://docs.python.org/3/library/logging.html
451+
452+
To include logging_ in your module, at the top of the module, you need to
453+
``import logging``. Then calls in your code like::
454+
455+
_log = logging.getLogger(__name__) # right after the imports
456+
457+
# code
458+
# more code
459+
_log.info('Here is some information')
460+
_log.debug('Here is some more detailed information')
461+
462+
will log to a logger named ``matplotlib.yourmodulename``.
463+
464+
If an end-user of your module sets up logging_ to display at levels
465+
more verbose than `logger.WARNING` in their code as follows::
466+
467+
import logging
468+
Format = '%(name)s:%(lineno)5d - %(levelname)s - %(message)s'
469+
logging.basicConfig(level=logging.DEBUG,
470+
format=Format)
471+
import matplotlib.pyplot as plt
472+
473+
Then they will receive messages like::
474+
475+
matplotlib.backends: 89 - INFO - backend MacOSX version unknown
476+
matplotlib.yourmodulename: 347 - INFO - Here is some information
477+
matplotlib.yourmodulename: 348 - DEBUG - Here is some more detailed information
478+
479+
Which logging level to use?
480+
~~~~~~~~~~~~~~~~~~~~~~~~~~~
481+
482+
There are five logging levels. :func:`logging.critical()` and
483+
:func:`logging.error()`
484+
are really only there for errors that will end the use of the library but
485+
not kill the interpreter. :func:`logging.warning()` overlaps with the
486+
``warnings`` library. The
487+
`logging tutorial <https://docs.python.org/3/howto/logging.html#logging-basic-tutorial>`_
488+
suggests that the differentiation
489+
between :func:`logging.warning()` and :func:`warnings.warn()` is that
490+
:func:`warnings.warn()` be used for things the user must change to stop
491+
the warning, whereas :func:`logging.warning()` can be more persistent.
492+
493+
By default, logging_ displays all log messages at levels higher than
494+
``logging.WARNING`` to ``sys.stderr``.
495+
496+
Calls to :func:`logging.info()` are not displayed by default. They are for
497+
information that the user may want to know if the program behaves oddly.
498+
For instance, if an object isn't drawn because its position is ``NaN``,
499+
that can usually be ignored, but a mystified user could set
500+
``logging.basicConfig(level=logging.INFO)`` and get an error message that
501+
says why.
502+
503+
:func:`logging.debug()` is the least likely to be displayed, and hence can
504+
be the most verbose.
505+
506+
440507
.. _custom_backend:
441508

442509
Developing a new backend

doc/faq/troubleshooting_faq.rst

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,37 @@ provide the following information in your e-mail to the `mailing list
114114
the error will help you find a bug in *your* code that is causing the
115115
problem.
116116

117-
* You can get very helpful debugging output from matlotlib by running your
118-
script with a ``verbose-helpful`` or ``--verbose-debug`` flags and posting
119-
the verbose output the lists::
117+
* You can get helpful debugging output from matlotlib by using the logging_
118+
library in your code and posting the verbose output the lists. For a
119+
command-line version of this, try::
120120

121-
python simple_plot.py --verbose-helpful > output.txt
121+
python -c "from logging import *; basicConfig(level=DEBUG); from pylab import *; plot(); show()"
122+
123+
124+
If you want to put the debugging hooks in your own code, then the
125+
most simple way to do so is to insert the following *before* any calls
126+
to ``import matplotlib``::
127+
128+
import logging
129+
logging.basicConfig(level=logging.DEBUG)
130+
131+
import matplotlib.pyplot as plt
132+
133+
Note that if you want to use logging_ in your own code, but do not
134+
want verbose Matplotlib output, you can set the logging level
135+
for Matplotlib independently::
136+
137+
import logging
138+
# set DEBUG for everything
139+
logging.basicConfig(level=logging.DEBUG)
140+
logger = logging.getLogger('matplotlib')
141+
# set WARNING for Matplotlib
142+
logger.setLevel(logging.WARNING)
143+
144+
The logging module is very flexible, and can be a valuable tool in chasing
145+
down errors.
146+
147+
.. _logging: https://docs.python.org/3/library/logging.html
122148

123149
If you compiled Matplotlib yourself, please also provide:
124150

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
Logging for debug output
2+
------------------------
3+
4+
Matplotlib has in the past (sporadically) used an internal
5+
verbose-output reporter. This version converted those calls to using the
6+
standard python logging_ library.
7+
8+
Support for the old ``rcParams`` ``verbose.level`` and ``verbose.fileo`` is
9+
dropped.
10+
11+
The command-line options ``--verbose-helpful`` and ``--verbose-debug`` are
12+
still accepted, but deprecated. They are now equivalent to setting
13+
``logging.INFO`` and ``logging.DEBUG``.
14+
15+
The logger's root name is ``matplotlib`` and can be accessed from programs
16+
as::
17+
18+
import logging
19+
mlog = logging.getLogger('matplotlib')
20+
21+
Instructions for basic usage are in :ref:`troubleshooting-faq` and for
22+
developers in :ref:`contributing`.
23+
24+
.. _logging: https://docs.python.org/3/library/logging.html

lib/matplotlib/__init__.py

Lines changed: 114 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@
112112
import io
113113
import inspect
114114
import itertools
115+
import logging
115116
import locale
116117
import os
117118
import re
@@ -123,7 +124,8 @@
123124
# definitions, so it is safe to import from it here.
124125
from . import cbook
125126
from matplotlib.cbook import (
126-
_backports, mplDeprecation, dedent, get_label, sanitize_sequence)
127+
_backports, mplDeprecation, dedent, get_label, sanitize_sequence,
128+
deprecated)
127129
from matplotlib.compat import subprocess
128130
from matplotlib.rcsetup import defaultParams, validate_backend, cycler
129131

@@ -137,6 +139,8 @@
137139
__version__ = str(get_versions()['version'])
138140
del get_versions
139141

142+
_log = logging.getLogger(__name__)
143+
140144
__version__numpy__ = str('1.7.1') # minimum required numpy version
141145

142146
__bibtex__ = r"""@Article{Hunter:2007,
@@ -160,6 +164,9 @@
160164
if not (_python27 or _python34):
161165
raise ImportError("Matplotlib requires Python 2.7 or 3.4 or later")
162166

167+
if _python27:
168+
_log.addHandler(logging.NullHandler())
169+
163170

164171
def compare_versions(a, b):
165172
"return True if a is greater than or equal to b"
@@ -215,7 +222,76 @@ def _is_writable_dir(p):
215222
"""
216223
return os.access(p, os.W_OK) and os.path.isdir(p)
217224

225+
_verbose_msg = """\
226+
--verbose-LEVEL and the Verbose class are deprecated.
227+
This functionality is now provided by the standard
228+
python logging library. To get more (or less) logging output:"
229+
import logging
230+
logger = logging.getLogger('matplotlib')
231+
logger.set_level(logging.INFO)"""
232+
233+
234+
def _set_logger_verbose_level(level_str='silent', file_str='sys.stdout'):
235+
"""
236+
Use a --verbose-LEVEL level to set the logging level:
218237
238+
"""
239+
levelmap = {'silent': logging.WARNING, 'helpful': logging.INFO,
240+
'debug': logging.DEBUG, 'debug-annoying': logging.DEBUG,
241+
'info': logging.INFO, 'warning': logging.WARNING}
242+
# Check that current state of logger isn't already more verbose
243+
# than the requested level. If its more verbose, then leave more
244+
# verbose.
245+
if level_str in levelmap:
246+
newlev = levelmap[level_str]
247+
oldlev = _log.getEffectiveLevel()
248+
if newlev < oldlev:
249+
_log.setLevel(newlev)
250+
std = {
251+
'sys.stdout': sys.stdout,
252+
'sys.stderr': sys.stderr,
253+
}
254+
if file_str in std:
255+
fileo = std[file_str]
256+
else:
257+
fileo = sys.stdout
258+
try:
259+
fileo = open(file_str, 'w')
260+
# if this fails, we will just write to stdout
261+
except IOError:
262+
warnings.warn('could not open log file "{0}"'
263+
'for writing. Check your '
264+
'matplotlibrc'.format(file_str))
265+
console = logging.StreamHandler(fileo)
266+
console.setLevel(newlev)
267+
_log.addHandler(console)
268+
269+
270+
def _parse_commandline():
271+
"""
272+
Check for --verbose-LEVEL type command line arguments and
273+
set logging level appropriately.
274+
"""
275+
276+
levels = ('silent', 'helpful', 'debug', 'debug-annoying',
277+
'info', 'warning')
278+
279+
for arg in sys.argv[1:]:
280+
# cast to str because we are using unicode_literals,
281+
# and argv is always str
282+
283+
if arg.startswith(str('--verbose-')):
284+
level_str = arg[10:]
285+
# If it doesn't match one of ours, then don't even
286+
# bother noting it, we are just a 3rd-party library
287+
# to somebody else's script.
288+
if level_str in levels:
289+
_set_logger_verbose_level(level_str)
290+
291+
_parse_commandline()
292+
293+
294+
@deprecated("2.2", message=_verbose_msg)
219295
class Verbose(object):
220296
"""
221297
A class to handle reporting. Set the fileo attribute to any file
@@ -311,7 +387,31 @@ def ge(self, level):
311387
return self.vald[self.level] >= self.vald[level]
312388

313389

314-
verbose = Verbose()
390+
def _wrap(fmt, func, level='INFO', always=True):
391+
"""
392+
return a callable function that wraps func and reports it
393+
output through logger
394+
395+
if always is True, the report will occur on every function
396+
call; otherwise only on the first time the function is called
397+
"""
398+
assert callable(func)
399+
400+
def wrapper(*args, **kwargs):
401+
ret = func(*args, **kwargs)
402+
403+
if (always or not wrapper._spoke):
404+
lvl = logging.getLevelName(level.upper())
405+
_log.log(lvl, fmt % ret)
406+
spoke = True
407+
if not wrapper._spoke:
408+
wrapper._spoke = spoke
409+
return ret
410+
wrapper._spoke = False
411+
wrapper.__doc__ = func.__doc__
412+
return wrapper
413+
414+
# verbose = Verbose()
315415

316416

317417
def checkdep_dvipng():
@@ -512,7 +612,7 @@ def _create_tmp_config_dir():
512612
return configdir
513613

514614

515-
get_home = verbose.wrap('$HOME=%s', _get_home, always=False)
615+
get_home = _wrap('$HOME=%s', _get_home, always=False)
516616

517617

518618
def _get_xdg_config_dir():
@@ -601,7 +701,7 @@ def _get_configdir():
601701
"""
602702
return _get_config_or_cache_dir(_get_xdg_config_dir())
603703

604-
get_configdir = verbose.wrap('CONFIGDIR=%s', _get_configdir, always=False)
704+
get_configdir = _wrap('CONFIGDIR=%s', _get_configdir, always=False)
605705

606706

607707
def _get_cachedir():
@@ -613,7 +713,7 @@ def _get_cachedir():
613713
"""
614714
return _get_config_or_cache_dir(_get_xdg_cache_dir())
615715

616-
get_cachedir = verbose.wrap('CACHEDIR=%s', _get_cachedir, always=False)
716+
get_cachedir = _wrap('CACHEDIR=%s', _get_cachedir, always=False)
617717

618718

619719
def _decode_filesystem_path(path):
@@ -671,7 +771,7 @@ def _get_data_path_cached():
671771
defaultParams['datapath'][0] = _get_data_path()
672772
return defaultParams['datapath'][0]
673773

674-
get_data_path = verbose.wrap('matplotlib data path %s', _get_data_path_cached,
774+
get_data_path = _wrap('matplotlib data path %s', _get_data_path_cached,
675775
always=False)
676776

677777

@@ -1035,22 +1135,18 @@ def rc_params_from_file(fname, fail_on_error=False, use_default_template=True):
10351135
if key not in _all_deprecated])
10361136
config.update(config_from_file)
10371137

1038-
verbose.set_level(config['verbose.level'])
1039-
verbose.set_fileo(config['verbose.fileo'])
1040-
10411138
if config['datapath'] is None:
10421139
config['datapath'] = get_data_path()
10431140

10441141
if "".join(config['text.latex.preamble']):
1045-
verbose.report("""
1142+
_log.info("""
10461143
*****************************************************************
10471144
You have the following UNSUPPORTED LaTeX preamble customizations:
10481145
%s
10491146
Please do not ask for support with these customizations active.
10501147
*****************************************************************
1051-
""" % '\n'.join(config['text.latex.preamble']), 'helpful')
1052-
1053-
verbose.report('loaded rc file %s' % fname)
1148+
""", '\n'.join(config['text.latex.preamble']))
1149+
_log.info('loaded rc file %s', fname)
10541150

10551151
return config
10561152

@@ -1736,9 +1832,8 @@ def inner(ax, *args, **kwargs):
17361832
return inner
17371833
return param
17381834

1739-
1740-
verbose.report('matplotlib version %s' % __version__)
1741-
verbose.report('verbose.level %s' % verbose.level)
1742-
verbose.report('interactive is %s' % is_interactive())
1743-
verbose.report('platform is %s' % sys.platform)
1744-
verbose.report('loaded modules: %s' % list(sys.modules), 'debug')
1835+
_log.info('matplotlib version %s', __version__)
1836+
#_log.info('verbose.level %s' % verbose.level)
1837+
_log.info('interactive is %s', is_interactive())
1838+
_log.info('platform is %s', sys.platform)
1839+
_log.debug('loaded modules: %s', list(sys.modules))

0 commit comments

Comments
 (0)