Skip to content

Commit a828aa4

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 minor Doc link fixes @anntzer Small doc change Many small changes, thanks @QuLogic and @anntzer Link error docs
1 parent c465993 commit a828aa4

24 files changed

+436
-250
lines changed

doc/devel/contributing.rst

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ repository <https://github.com/matplotlib/matplotlib/>`__ on GitHub,
171171
then submit a "pull request" (PR).
172172

173173
The best practices for using GitHub to make PRs to Matplotlib are
174-
documented in the :ref:`development-workflow` section.
174+
documented in the :ref:`development-workflow` section.
175175

176176
A brief overview is:
177177

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

442506
Developing a new backend

doc/faq/troubleshooting_faq.rst

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ Troubleshooting
99

1010
.. _matplotlib-version:
1111

12-
Obtaining matplotlib version
12+
Obtaining Matplotlib version
1313
============================
1414

15-
To find out your matplotlib version number, import it and print the
15+
To find out your Matplotlib version number, import it and print the
1616
``__version__`` attribute::
1717

1818
>>> import matplotlib
@@ -25,7 +25,7 @@ To find out your matplotlib version number, import it and print the
2525
:file:`matplotlib` install location
2626
===================================
2727

28-
You can find what directory matplotlib is installed in by importing it
28+
You can find what directory Matplotlib is installed in by importing it
2929
and printing the ``__file__`` attribute::
3030

3131
>>> import matplotlib
@@ -37,7 +37,7 @@ and printing the ``__file__`` attribute::
3737
:file:`matplotlib` configuration and cache directory locations
3838
==============================================================
3939

40-
Each user has a matplotlib configuration directory which may contain a
40+
Each user has a Matplotlib configuration directory which may contain a
4141
:ref:`matplotlibrc <customizing-with-matplotlibrc-files>` file. To
4242
locate your :file:`matplotlib/` configuration directory, use
4343
:func:`matplotlib.get_configdir`::
@@ -79,7 +79,7 @@ directory and the cache directory.
7979
Getting help
8080
============
8181

82-
There are a number of good resources for getting help with matplotlib.
82+
There are a number of good resources for getting help with Matplotlib.
8383
There is a good chance your question has already been asked:
8484

8585
- The `mailing list archive <http://matplotlib.1069221.n5.nabble.com/>`_.
@@ -114,11 +114,35 @@ 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 to 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.
122146

123147
If you compiled Matplotlib yourself, please also provide:
124148

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 converts 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: 109 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@
113113
import inspect
114114
import itertools
115115
import locale
116+
import logging
116117
import os
117118
import re
118119
import sys
@@ -137,6 +138,8 @@
137138
__version__ = str(get_versions()['version'])
138139
del get_versions
139140

141+
_log = logging.getLogger(__name__)
142+
140143
__version__numpy__ = str('1.7.1') # minimum required numpy version
141144

142145
__bibtex__ = r"""@Article{Hunter:2007,
@@ -160,6 +163,9 @@
160163
if not (_python27 or _python34):
161164
raise ImportError("Matplotlib requires Python 2.7 or 3.4 or later")
162165

166+
if _python27:
167+
_log.addHandler(logging.NullHandler())
168+
163169

164170
def compare_versions(a, b):
165171
"return True if a is greater than or equal to b"
@@ -215,7 +221,75 @@ def _is_writable_dir(p):
215221
"""
216222
return os.access(p, os.W_OK) and os.path.isdir(p)
217223

224+
_verbose_msg = """\
225+
Command line argument --verbose-LEVEL is deprecated.
226+
This functionality is now provided by the standard
227+
python logging library. To get more (or less) logging output:
228+
import logging
229+
logger = logging.getLogger('matplotlib')
230+
logger.set_level(logging.INFO)"""
231+
232+
233+
def _set_logger_verbose_level(level_str='silent', file_str='sys.stdout'):
234+
"""
235+
Use a --verbose-LEVEL level to set the logging level:
236+
237+
"""
238+
levelmap = {'silent': logging.WARNING, 'helpful': logging.INFO,
239+
'debug': logging.DEBUG, 'debug-annoying': logging.DEBUG,
240+
'info': logging.INFO, 'warning': logging.WARNING}
241+
# Check that current state of logger isn't already more verbose
242+
# than the requested level. If it is more verbose, then leave more
243+
# verbose.
244+
newlev = levelmap[level_str]
245+
oldlev = _log.getEffectiveLevel()
246+
if newlev < oldlev:
247+
_log.setLevel(newlev)
248+
std = {
249+
'sys.stdout': sys.stdout,
250+
'sys.stderr': sys.stderr,
251+
}
252+
if file_str in std:
253+
fileo = std[file_str]
254+
else:
255+
fileo = sys.stdout
256+
try:
257+
fileo = open(file_str, 'w')
258+
# if this fails, we will just write to stdout
259+
except IOError:
260+
warnings.warn('could not open log file "{0}"'
261+
'for writing. Check your '
262+
'matplotlibrc'.format(file_str))
263+
console = logging.StreamHandler(fileo)
264+
console.setLevel(newlev)
265+
_log.addHandler(console)
266+
267+
268+
def _parse_commandline():
269+
"""
270+
Check for --verbose-LEVEL type command line arguments and
271+
set logging level appropriately.
272+
"""
218273

274+
levels = ('silent', 'helpful', 'debug', 'debug-annoying',
275+
'info', 'warning')
276+
277+
for arg in sys.argv[1:]:
278+
# cast to str because we are using unicode_literals,
279+
# and argv is always str
280+
281+
if arg.startswith(str('--verbose-')):
282+
level_str = arg[10:]
283+
# If it doesn't match one of ours, then don't even
284+
# bother noting it, we are just a 3rd-party library
285+
# to somebody else's script.
286+
if level_str in levels:
287+
_set_logger_verbose_level(level_str)
288+
289+
_parse_commandline()
290+
291+
292+
@cbook.deprecated("2.2", message=_verbose_msg)
219293
class Verbose(object):
220294
"""
221295
A class to handle reporting. Set the fileo attribute to any file
@@ -311,7 +385,29 @@ def ge(self, level):
311385
return self.vald[self.level] >= self.vald[level]
312386

313387

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

316412

317413
def checkdep_dvipng():
@@ -512,7 +608,7 @@ def _create_tmp_config_dir():
512608
return configdir
513609

514610

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

517613

518614
def _get_xdg_config_dir():
@@ -601,7 +697,7 @@ def _get_configdir():
601697
"""
602698
return _get_config_or_cache_dir(_get_xdg_config_dir())
603699

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

606702

607703
def _get_cachedir():
@@ -613,7 +709,7 @@ def _get_cachedir():
613709
"""
614710
return _get_config_or_cache_dir(_get_xdg_cache_dir())
615711

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

618714

619715
def _decode_filesystem_path(path):
@@ -671,8 +767,8 @@ def _get_data_path_cached():
671767
defaultParams['datapath'][0] = _get_data_path()
672768
return defaultParams['datapath'][0]
673769

674-
get_data_path = verbose.wrap('matplotlib data path %s', _get_data_path_cached,
675-
always=False)
770+
get_data_path = _wrap('matplotlib data path %s', _get_data_path_cached,
771+
always=False)
676772

677773

678774
def get_py2exe_datafiles():
@@ -1035,22 +1131,18 @@ def rc_params_from_file(fname, fail_on_error=False, use_default_template=True):
10351131
if key not in _all_deprecated])
10361132
config.update(config_from_file)
10371133

1038-
verbose.set_level(config['verbose.level'])
1039-
verbose.set_fileo(config['verbose.fileo'])
1040-
10411134
if config['datapath'] is None:
10421135
config['datapath'] = get_data_path()
10431136

10441137
if "".join(config['text.latex.preamble']):
1045-
verbose.report("""
1138+
_log.info("""
10461139
*****************************************************************
10471140
You have the following UNSUPPORTED LaTeX preamble customizations:
10481141
%s
10491142
Please do not ask for support with these customizations active.
10501143
*****************************************************************
1051-
""" % '\n'.join(config['text.latex.preamble']), 'helpful')
1052-
1053-
verbose.report('loaded rc file %s' % fname)
1144+
""", '\n'.join(config['text.latex.preamble']))
1145+
_log.info('loaded rc file %s', fname)
10541146

10551147
return config
10561148

@@ -1736,9 +1828,7 @@ def inner(ax, *args, **kwargs):
17361828
return inner
17371829
return param
17381830

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')
1831+
_log.info('matplotlib version %s', __version__)
1832+
_log.info('interactive is %s', is_interactive())
1833+
_log.info('platform is %s', sys.platform)
1834+
_log.debug('loaded modules: %s', list(sys.modules))

0 commit comments

Comments
 (0)