diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 2e4ee4edb393..07beae316f5a 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -216,13 +216,6 @@ def compare_versions(a, b): sys.argv = ['modpython'] -def _is_writable_dir(p): - """ - p is a string pointing to a putative writable dir -- return True p - is such a string, else False - """ - return os.access(p, os.W_OK) and os.path.isdir(p) - _verbose_msg = """\ matplotlib.verbose is deprecated; Command line argument --verbose-LEVEL is deprecated. @@ -393,27 +386,34 @@ def ge(self, level): verbose = Verbose() -def _wrap(fmt, func, level=logging.DEBUG, always=True): +def _logged_cached(fmt, func=None): """ - return a callable function that wraps func and reports its - output through logger + Decorator that logs a function's return value, and memoizes that value. - 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) + After :: - def wrapper(*args, **kwargs): - ret = func(*args, **kwargs) + @_logged_cached(fmt) + def func(): ... - if (always or not wrapper._spoke): - _log.log(level, fmt % ret) - spoke = True - if not wrapper._spoke: - wrapper._spoke = spoke + the first call to *func* will log its return value at the DEBUG level using + %-format string *fmt*, and memoize it; later calls to *func* will directly + return that value. + """ + if func is None: # Return the actual decorator. + return functools.partial(_logged_cached, fmt) + + called = False + ret = None + + @functools.wraps(func) + def wrapper(): + nonlocal called, ret + if not called: + ret = func() + called = True + _log.debug(fmt, ret) return ret - wrapper._spoke = False - wrapper.__doc__ = func.__doc__ + return wrapper @@ -551,27 +551,22 @@ def checkdep_usetex(s): return flag -def _get_home(): - """Find user's home directory if possible. - Otherwise, returns None. +@_logged_cached('$HOME=%s') +def get_home(): + """ + Return the user's home directory. - :see: - http://mail.python.org/pipermail/python-list/2005-February/325395.html + If the user's home directory cannot be found, return None. """ - path = os.path.expanduser("~") - if os.path.isdir(path): - return path - for evar in ('HOME', 'USERPROFILE', 'TMP'): - path = os.environ.get(evar) - if path is not None and os.path.isdir(path): - return path - return None + try: + return str(Path.home()) + except Exception: + return None def _create_tmp_config_dir(): """ - If the config directory can not be created, create a temporary - directory. + If the config directory can not be created, create a temporary directory. """ configdir = os.environ['MPLCONFIGDIR'] = ( tempfile.mkdtemp(prefix='matplotlib-')) @@ -579,21 +574,16 @@ 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 base directory spec `_. """ - path = os.environ.get('XDG_CONFIG_HOME') - if path is None: - path = get_home() - if path is not None: - path = os.path.join(path, '.config') - return path + return (os.environ.get('XDG_CONFIG_HOME') + or (str(Path(get_home(), ".config")) + if get_home() + else None)) def _get_xdg_cache_dir(): @@ -602,60 +592,46 @@ def _get_xdg_cache_dir(): base directory spec `_. """ - path = os.environ.get('XDG_CACHE_HOME') - if path is None: - path = get_home() - if path is not None: - path = os.path.join(path, '.cache') - return path + return (os.environ.get('XDG_CACHE_HOME') + or (str(Path(get_home(), ".cache")) + if get_home() + else None)) def _get_config_or_cache_dir(xdg_base): configdir = os.environ.get('MPLCONFIGDIR') - if configdir is not None: - configdir = os.path.abspath(configdir) - Path(configdir).mkdir(parents=True, exist_ok=True) - if not _is_writable_dir(configdir): - return _create_tmp_config_dir() - return configdir - - p = None - h = get_home() - if h is not None: - p = os.path.join(h, '.matplotlib') - if sys.platform.startswith(('linux', 'freebsd')): - p = None - if xdg_base is not None: - p = os.path.join(xdg_base, 'matplotlib') - - if p is not None: - if os.path.exists(p): - if _is_writable_dir(p): - return p + if configdir: + configdir = Path(configdir).resolve() + elif sys.platform.startswith(('linux', 'freebsd')) and xdg_base: + configdir = Path(xdg_base, "matplotlib") + elif get_home(): + configdir = Path(get_home(), ".matplotlib") + else: + configdir = None + + if configdir: + try: + configdir.mkdir(parents=True, exist_ok=True) + except OSError: + pass else: - try: - Path(p).mkdir(parents=True, exist_ok=True) - except OSError: - pass - else: - return p + if os.access(str(configdir), os.W_OK) and configdir.is_dir(): + return str(configdir) return _create_tmp_config_dir() -def _get_configdir(): +@_logged_cached('CONFIGDIR=%s') +def get_configdir(): """ Return the string representing the configuration directory. The directory is chosen as follows: 1. If the MPLCONFIGDIR environment variable is supplied, choose that. - 2a. On Linux, follow the XDG specification and look first in `$XDG_CONFIG_HOME`, if defined, or `$HOME/.config`. - 2b. On other platforms, choose `$HOME/.matplotlib`. - 3. If the chosen directory exists and is writable, use that as the configuration directory. 4. If possible, create a temporary directory, and use it as the @@ -664,10 +640,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(): +@_logged_cached('CACHEDIR=%s') +def get_cachedir(): """ Return the location of the cache directory. @@ -676,8 +651,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): @@ -729,14 +702,12 @@ def _get_data_path(): raise RuntimeError('Could not find the matplotlib data files') -def _get_data_path_cached(): +@_logged_cached('matplotlib data path: %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(): data_path = Path(get_data_path()) @@ -787,7 +758,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(): diff --git a/lib/matplotlib/cbook/__init__.py b/lib/matplotlib/cbook/__init__.py index 7abcac3e66d8..02f8c1ff6c63 100644 --- a/lib/matplotlib/cbook/__init__.py +++ b/lib/matplotlib/cbook/__init__.py @@ -1984,7 +1984,7 @@ class so far, an alias named ``get_alias`` will be defined; the same will can be used by `~.normalize_kwargs` (which assumes that higher priority aliases come last). """ - if cls is None: + if cls is None: # Return the actual class decorator. return functools.partial(_define_aliases, alias_d) def make_alias(name): # Enforce a closure over *name*. diff --git a/lib/matplotlib/style/core.py b/lib/matplotlib/style/core.py index 27cc8339c242..d7c59f844563 100644 --- a/lib/matplotlib/style/core.py +++ b/lib/matplotlib/style/core.py @@ -25,7 +25,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) diff --git a/lib/matplotlib/testing/compare.py b/lib/matplotlib/testing/compare.py index 0f5e149a567b..d9528e3a4e2b 100644 --- a/lib/matplotlib/testing/compare.py +++ b/lib/matplotlib/testing/compare.py @@ -18,9 +18,7 @@ import matplotlib from matplotlib.testing.exceptions import ImageComparisonFailure -from matplotlib import _png -from matplotlib import _get_cachedir -from matplotlib import cbook +from matplotlib import _png, cbook __all__ = ['compare_float', 'compare_images', 'comparable_formats'] @@ -79,7 +77,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') @@ -293,15 +291,12 @@ def comparable_formats(): def convert(filename, cache): """ - Convert the named file into a png file. Returns the name of the - created file. + Convert the named file to png; return the name of the created file. If *cache* is True, the result of the conversion is cached in - `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. - + `matplotlib.get_cachedir() + '/test_cache/'`. The caching is based on a + hash of the exact contents of the input file. There is no limit on the + size of the cache, so it may need to be manually cleared periodically. """ base, extension = filename.rsplit('.', 1) if extension not in converter: