Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 477c1dc

Browse files
authored
Merge pull request #9313 from jklymak/logging
Replace verbose class with standard logging library
2 parents 7252158 + 844877c commit 477c1dc

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)