Skip to content

Some more fixes for the verbose -> logging switch. #9761

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 10 additions & 11 deletions doc/devel/contributing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -472,19 +472,18 @@ Then they will receive messages like::
Which logging level to use?
~~~~~~~~~~~~~~~~~~~~~~~~~~~

There are five levels at which you can emit messages.
`logging.critical` and `logging.error`
are really only there for errors that will end the use of the library but
not kill the interpreter. `logging.warning` overlaps with the
``warnings`` library. The
`logging tutorial <https://docs.python.org/3/howto/logging.html#logging-basic-tutorial>`_
suggests that the difference
between `logging.warning` and `warnings.warn` is that
`warnings.warn` be used for things the user must change to stop
the warning, whereas `logging.warning` can be more persistent.
There are five levels at which you can emit messages. `logging.critical` and
`logging.error` are really only there for errors that will end the use of the
library but not kill the interpreter. `logging.warning` overlaps with the
`warnings` library. The `logging tutorial`_ suggests that the difference
between `logging.warning` and `warnings.warn` is that `warnings.warn` be used
for things the user must change to stop the warning, whereas `logging.warning`
can be more persistent.

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

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

Calls to `logging.info` are not displayed by default. They are for
information that the user may want to know if the program behaves oddly.
Expand Down
68 changes: 28 additions & 40 deletions lib/matplotlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,31 +396,6 @@ def ge(self, level):
return self.vald[self.level] >= self.vald[level]


def _wrap(fmt, func, level='DEBUG', always=True):
"""
return a callable function that wraps func and reports its
output through logger

if always is True, the report will occur on every function
call; otherwise only on the first time the function is called
"""
assert callable(func)

def wrapper(*args, **kwargs):
ret = func(*args, **kwargs)

if (always or not wrapper._spoke):
lvl = logging.getLevelName(level.upper())
_log.log(lvl, fmt % ret)
spoke = True
if not wrapper._spoke:
wrapper._spoke = spoke
return ret
wrapper._spoke = False
wrapper.__doc__ = func.__doc__
return wrapper


def checkdep_dvipng():
try:
s = subprocess.Popen([str('dvipng'), '-version'],
Expand Down Expand Up @@ -589,7 +564,27 @@ def checkdep_usetex(s):
return flag


def _get_home():
def _log_and_cache_result(fmt, level="DEBUG", func=None):
"""Decorator that logs & caches the result of a function with no arguments.

The first time the decorated *func* is called, its result is logged at
level *level* using log format *fmt*, and cached. Later calls immediately
return the cached result without logging.
"""
if func is None:
return functools.partial(_log_and_cache_result, fmt, level)

@functools.lru_cache(1) # Calls after the 1st return the cached result.
@functools.wraps(func)
def wrapper():
retval = func()
_log.log(logging.getLevelName(level.upper()), fmt, retval)
return retval
return wrapper


@_log_and_cache_result("HOME=%s")
def get_home():
"""Find user's home directory if possible.
Otherwise, returns None.

Expand Down Expand Up @@ -620,9 +615,6 @@ def _create_tmp_config_dir():
return configdir


get_home = _wrap('$HOME=%s', _get_home, always=False)


def _get_xdg_config_dir():
"""
Returns the XDG configuration directory, according to the `XDG
Expand Down Expand Up @@ -684,7 +676,8 @@ def _get_config_or_cache_dir(xdg_base):
return _create_tmp_config_dir()


def _get_configdir():
@_log_and_cache_result("CONFIGDIR=%s")
def get_configdir():
"""
Return the string representing the configuration directory.

Expand All @@ -705,10 +698,9 @@ def _get_configdir():
"""
return _get_config_or_cache_dir(_get_xdg_config_dir())

get_configdir = _wrap('CONFIGDIR=%s', _get_configdir, always=False)


def _get_cachedir():
@_log_and_cache_result("CACHEDIR=%s")
def get_cachedir():
"""
Return the location of the cache directory.

Expand All @@ -717,8 +709,6 @@ def _get_cachedir():
"""
return _get_config_or_cache_dir(_get_xdg_cache_dir())

get_cachedir = _wrap('CACHEDIR=%s', _get_cachedir, always=False)


def _decode_filesystem_path(path):
if not isinstance(path, str):
Expand Down Expand Up @@ -770,14 +760,12 @@ def _get_data_path():
raise RuntimeError('Could not find the matplotlib data files')


def _get_data_path_cached():
@_log_and_cache_result('rcParams["datapath"]=%s')
def get_data_path():
if defaultParams['datapath'][0] is None:
defaultParams['datapath'][0] = _get_data_path()
return defaultParams['datapath'][0]

get_data_path = _wrap('matplotlib data path %s', _get_data_path_cached,
always=False)


def get_py2exe_datafiles():
datapath = get_data_path()
Expand Down Expand Up @@ -835,7 +823,7 @@ def gen_candidates():
else:
yield matplotlibrc
yield os.path.join(matplotlibrc, 'matplotlibrc')
yield os.path.join(_get_configdir(), 'matplotlibrc')
yield os.path.join(get_configdir(), 'matplotlibrc')
yield os.path.join(get_data_path(), 'matplotlibrc')

for fname in gen_candidates():
Expand Down
2 changes: 1 addition & 1 deletion lib/matplotlib/style/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

BASE_LIBRARY_PATH = os.path.join(mpl.get_data_path(), 'stylelib')
# Users may want multiple library paths, so store a list of paths.
USER_LIBRARY_PATHS = [os.path.join(mpl._get_configdir(), 'stylelib')]
USER_LIBRARY_PATHS = [os.path.join(mpl.get_configdir(), 'stylelib')]
STYLE_EXTENSION = 'mplstyle'
STYLE_FILE_PATTERN = re.compile(r'([\S]+).%s$' % STYLE_EXTENSION)

Expand Down
5 changes: 2 additions & 3 deletions lib/matplotlib/testing/compare.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
from matplotlib.compat import subprocess
from matplotlib.testing.exceptions import ImageComparisonFailure
from matplotlib import _png
from matplotlib import _get_cachedir
from matplotlib import cbook

__all__ = ['compare_float', 'compare_images', 'comparable_formats']
Expand Down Expand Up @@ -82,7 +81,7 @@ def compare_float(expected, actual, relTol=None, absTol=None):


def get_cache_dir():
cachedir = _get_cachedir()
cachedir = matplotlib.get_cachedir()
if cachedir is None:
raise RuntimeError('Could not find a suitable configuration directory')
cache_dir = os.path.join(cachedir, 'test_cache')
Expand Down Expand Up @@ -272,7 +271,7 @@ def convert(filename, cache):
created file.

If *cache* is True, the result of the conversion is cached in
`matplotlib._get_cachedir() + '/test_cache/'`. The caching is based
`matplotlib.get_cachedir() + '/test_cache/'`. The caching is based
on a hash of the exact contents of the input file. The is no limit
on the size of the cache, so it may need to be manually cleared
periodically.
Expand Down