Skip to content

Commit 85f0dd6

Browse files
authored
Merge pull request #11398 from anntzer/gethome
Simplify retrieval of cache and config directories
2 parents 632305e + 8be4eae commit 85f0dd6

File tree

4 files changed

+72
-106
lines changed

4 files changed

+72
-106
lines changed

lib/matplotlib/__init__.py

Lines changed: 64 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -217,13 +217,6 @@ def compare_versions(a, b):
217217
sys.argv = ['modpython']
218218

219219

220-
def _is_writable_dir(p):
221-
"""
222-
p is a string pointing to a putative writable dir -- return True p
223-
is such a string, else False
224-
"""
225-
return os.access(p, os.W_OK) and os.path.isdir(p)
226-
227220
_verbose_msg = """\
228221
matplotlib.verbose is deprecated;
229222
Command line argument --verbose-LEVEL is deprecated.
@@ -394,27 +387,34 @@ def ge(self, level):
394387
verbose = Verbose()
395388

396389

397-
def _wrap(fmt, func, level=logging.DEBUG, always=True):
390+
def _logged_cached(fmt, func=None):
398391
"""
399-
return a callable function that wraps func and reports its
400-
output through logger
392+
Decorator that logs a function's return value, and memoizes that value.
401393
402-
if always is True, the report will occur on every function
403-
call; otherwise only on the first time the function is called
404-
"""
405-
assert callable(func)
394+
After ::
406395
407-
def wrapper(*args, **kwargs):
408-
ret = func(*args, **kwargs)
396+
@_logged_cached(fmt)
397+
def func(): ...
409398
410-
if (always or not wrapper._spoke):
411-
_log.log(level, fmt % ret)
412-
spoke = True
413-
if not wrapper._spoke:
414-
wrapper._spoke = spoke
399+
the first call to *func* will log its return value at the DEBUG level using
400+
%-format string *fmt*, and memoize it; later calls to *func* will directly
401+
return that value.
402+
"""
403+
if func is None: # Return the actual decorator.
404+
return functools.partial(_logged_cached, fmt)
405+
406+
called = False
407+
ret = None
408+
409+
@functools.wraps(func)
410+
def wrapper():
411+
nonlocal called, ret
412+
if not called:
413+
ret = func()
414+
called = True
415+
_log.debug(fmt, ret)
415416
return ret
416-
wrapper._spoke = False
417-
wrapper.__doc__ = func.__doc__
417+
418418
return wrapper
419419

420420

@@ -552,49 +552,39 @@ def checkdep_usetex(s):
552552
return flag
553553

554554

555-
def _get_home():
556-
"""Find user's home directory if possible.
557-
Otherwise, returns None.
555+
@_logged_cached('$HOME=%s')
556+
def get_home():
557+
"""
558+
Return the user's home directory.
558559
559-
:see:
560-
http://mail.python.org/pipermail/python-list/2005-February/325395.html
560+
If the user's home directory cannot be found, return None.
561561
"""
562-
path = os.path.expanduser("~")
563-
if os.path.isdir(path):
564-
return path
565-
for evar in ('HOME', 'USERPROFILE', 'TMP'):
566-
path = os.environ.get(evar)
567-
if path is not None and os.path.isdir(path):
568-
return path
569-
return None
562+
try:
563+
return str(Path.home())
564+
except Exception:
565+
return None
570566

571567

572568
def _create_tmp_config_dir():
573569
"""
574-
If the config directory can not be created, create a temporary
575-
directory.
570+
If the config directory can not be created, create a temporary directory.
576571
"""
577572
configdir = os.environ['MPLCONFIGDIR'] = (
578573
tempfile.mkdtemp(prefix='matplotlib-'))
579574
atexit.register(shutil.rmtree, configdir)
580575
return configdir
581576

582577

583-
get_home = _wrap('$HOME=%s', _get_home, always=False)
584-
585-
586578
def _get_xdg_config_dir():
587579
"""
588580
Returns the XDG configuration directory, according to the `XDG
589581
base directory spec
590582
<http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html>`_.
591583
"""
592-
path = os.environ.get('XDG_CONFIG_HOME')
593-
if path is None:
594-
path = get_home()
595-
if path is not None:
596-
path = os.path.join(path, '.config')
597-
return path
584+
return (os.environ.get('XDG_CONFIG_HOME')
585+
or (str(Path(get_home(), ".config"))
586+
if get_home()
587+
else None))
598588

599589

600590
def _get_xdg_cache_dir():
@@ -603,60 +593,46 @@ def _get_xdg_cache_dir():
603593
base directory spec
604594
<http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html>`_.
605595
"""
606-
path = os.environ.get('XDG_CACHE_HOME')
607-
if path is None:
608-
path = get_home()
609-
if path is not None:
610-
path = os.path.join(path, '.cache')
611-
return path
596+
return (os.environ.get('XDG_CACHE_HOME')
597+
or (str(Path(get_home(), ".cache"))
598+
if get_home()
599+
else None))
612600

613601

614602
def _get_config_or_cache_dir(xdg_base):
615603
configdir = os.environ.get('MPLCONFIGDIR')
616-
if configdir is not None:
617-
configdir = os.path.abspath(configdir)
618-
Path(configdir).mkdir(parents=True, exist_ok=True)
619-
if not _is_writable_dir(configdir):
620-
return _create_tmp_config_dir()
621-
return configdir
622-
623-
p = None
624-
h = get_home()
625-
if h is not None:
626-
p = os.path.join(h, '.matplotlib')
627-
if sys.platform.startswith(('linux', 'freebsd')):
628-
p = None
629-
if xdg_base is not None:
630-
p = os.path.join(xdg_base, 'matplotlib')
631-
632-
if p is not None:
633-
if os.path.exists(p):
634-
if _is_writable_dir(p):
635-
return p
604+
if configdir:
605+
configdir = Path(configdir).resolve()
606+
elif sys.platform.startswith(('linux', 'freebsd')) and xdg_base:
607+
configdir = Path(xdg_base, "matplotlib")
608+
elif get_home():
609+
configdir = Path(get_home(), ".matplotlib")
610+
else:
611+
configdir = None
612+
613+
if configdir:
614+
try:
615+
configdir.mkdir(parents=True, exist_ok=True)
616+
except OSError:
617+
pass
636618
else:
637-
try:
638-
Path(p).mkdir(parents=True, exist_ok=True)
639-
except OSError:
640-
pass
641-
else:
642-
return p
619+
if os.access(str(configdir), os.W_OK) and configdir.is_dir():
620+
return str(configdir)
643621

644622
return _create_tmp_config_dir()
645623

646624

647-
def _get_configdir():
625+
@_logged_cached('CONFIGDIR=%s')
626+
def get_configdir():
648627
"""
649628
Return the string representing the configuration directory.
650629
651630
The directory is chosen as follows:
652631
653632
1. If the MPLCONFIGDIR environment variable is supplied, choose that.
654-
655633
2a. On Linux, follow the XDG specification and look first in
656634
`$XDG_CONFIG_HOME`, if defined, or `$HOME/.config`.
657-
658635
2b. On other platforms, choose `$HOME/.matplotlib`.
659-
660636
3. If the chosen directory exists and is writable, use that as the
661637
configuration directory.
662638
4. If possible, create a temporary directory, and use it as the
@@ -665,10 +641,9 @@ def _get_configdir():
665641
"""
666642
return _get_config_or_cache_dir(_get_xdg_config_dir())
667643

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

670-
671-
def _get_cachedir():
645+
@_logged_cached('CACHEDIR=%s')
646+
def get_cachedir():
672647
"""
673648
Return the location of the cache directory.
674649
@@ -677,8 +652,6 @@ def _get_cachedir():
677652
"""
678653
return _get_config_or_cache_dir(_get_xdg_cache_dir())
679654

680-
get_cachedir = _wrap('CACHEDIR=%s', _get_cachedir, always=False)
681-
682655

683656
def _decode_filesystem_path(path):
684657
if not isinstance(path, str):
@@ -730,14 +703,12 @@ def _get_data_path():
730703
raise RuntimeError('Could not find the matplotlib data files')
731704

732705

733-
def _get_data_path_cached():
706+
@_logged_cached('matplotlib data path: %s')
707+
def get_data_path():
734708
if defaultParams['datapath'][0] is None:
735709
defaultParams['datapath'][0] = _get_data_path()
736710
return defaultParams['datapath'][0]
737711

738-
get_data_path = _wrap('matplotlib data path %s', _get_data_path_cached,
739-
always=False)
740-
741712

742713
def get_py2exe_datafiles():
743714
data_path = Path(get_data_path())
@@ -788,7 +759,7 @@ def gen_candidates():
788759
else:
789760
yield matplotlibrc
790761
yield os.path.join(matplotlibrc, 'matplotlibrc')
791-
yield os.path.join(_get_configdir(), 'matplotlibrc')
762+
yield os.path.join(get_configdir(), 'matplotlibrc')
792763
yield os.path.join(get_data_path(), 'matplotlibrc')
793764

794765
for fname in gen_candidates():

lib/matplotlib/cbook/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1984,7 +1984,7 @@ class so far, an alias named ``get_alias`` will be defined; the same will
19841984
can be used by `~.normalize_kwargs` (which assumes that higher priority
19851985
aliases come last).
19861986
"""
1987-
if cls is None:
1987+
if cls is None: # Return the actual class decorator.
19881988
return functools.partial(_define_aliases, alias_d)
19891989

19901990
def make_alias(name): # Enforce a closure over *name*.

lib/matplotlib/style/core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

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

lib/matplotlib/testing/compare.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@
1818

1919
import matplotlib
2020
from matplotlib.testing.exceptions import ImageComparisonFailure
21-
from matplotlib import _png
22-
from matplotlib import _get_cachedir
23-
from matplotlib import cbook
21+
from matplotlib import _png, cbook
2422

2523
__all__ = ['compare_float', 'compare_images', 'comparable_formats']
2624

@@ -79,7 +77,7 @@ def compare_float(expected, actual, relTol=None, absTol=None):
7977

8078

8179
def get_cache_dir():
82-
cachedir = _get_cachedir()
80+
cachedir = matplotlib.get_cachedir()
8381
if cachedir is None:
8482
raise RuntimeError('Could not find a suitable configuration directory')
8583
cache_dir = os.path.join(cachedir, 'test_cache')
@@ -293,15 +291,12 @@ def comparable_formats():
293291

294292
def convert(filename, cache):
295293
"""
296-
Convert the named file into a png file. Returns the name of the
297-
created file.
294+
Convert the named file to png; return the name of the created file.
298295
299296
If *cache* is True, the result of the conversion is cached in
300-
`matplotlib._get_cachedir() + '/test_cache/'`. The caching is based
301-
on a hash of the exact contents of the input file. The is no limit
302-
on the size of the cache, so it may need to be manually cleared
303-
periodically.
304-
297+
`matplotlib.get_cachedir() + '/test_cache/'`. The caching is based on a
298+
hash of the exact contents of the input file. There is no limit on the
299+
size of the cache, so it may need to be manually cleared periodically.
305300
"""
306301
base, extension = filename.rsplit('.', 1)
307302
if extension not in converter:

0 commit comments

Comments
 (0)