diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 997e86f40c93..fdc351e59cd1 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -213,16 +213,28 @@ def _is_writable_dir(p): p is a string pointing to a putative writable dir -- return True p is such a string, else False """ - try: p + '' # test is string like - except TypeError: return False + try: + p + '' # test is string like + except TypeError: + return False + + # Test whether the operating system thinks it's a writable directory. + # Note that this check is necessary on Google App Engine, because the + # subsequent check will succeed even though p may not be writable. + if not os.access(p, os.W_OK) or not os.path.isdir(p): + return False + + # Also test that it is actually possible to write to a file here. try: t = tempfile.TemporaryFile(dir=p) try: t.write(ascii('1')) finally: t.close() - except OSError: return False - else: return True + except OSError: + return False + + return True class Verbose: """ @@ -475,38 +487,42 @@ def checkdep_usetex(s): def _get_home(): """Find user's home directory if possible. - Otherwise raise error. + Otherwise, returns None. - :see: http://mail.python.org/pipermail/python-list/2005-February/263921.html + :see: http://mail.python.org/pipermail/python-list/2005-February/325395.html """ - path='' try: - path=os.path.expanduser("~") - except: + path = os.path.expanduser("~") + except ImportError: + # This happens on Google App Engine (pwd module is not present). pass - if not os.path.isdir(path): - for evar in ('HOME', 'USERPROFILE', 'TMP'): - try: - path = os.environ[evar] - if os.path.isdir(path): - break - except: pass - if path: - return path else: - raise RuntimeError('please define environment variable $HOME') + 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 def _create_tmp_config_dir(): """ If the config directory can not be created, create a temporary directory. + + Returns None if a writable temporary directory could not be created. """ import getpass import tempfile - tempdir = os.path.join( - tempfile.gettempdir(), 'matplotlib-%s' % getpass.getuser()) + try: + tempdir = tempfile.gettempdir() + except NotImplementedError: + # Some restricted platforms (such as Google App Engine) do not provide + # gettempdir. + return None + tempdir = os.path.join(tempdir, 'matplotlib-%s' % getpass.getuser()) os.environ['MPLCONFIGDIR'] = tempdir return tempdir @@ -518,35 +534,42 @@ def _get_configdir(): """ Return the string representing the configuration directory. - Default is HOME/.matplotlib. You can override this with the - MPLCONFIGDIR environment variable. If the default is not - writable, and MPLCONFIGDIR is not set, then - tempfile.gettempdir() is used to provide a directory in - which a matplotlib subdirectory is created as the configuration - directory. + The directory is chosen as follows: + + 1. If the MPLCONFIGDIR environment variable is supplied, choose that. Else, + choose the '.matplotlib' subdirectory of the user's home directory (and + create it if necessary). + 2. If the chosen directory exists and is writable, use that as the + configuration directory. + 3. If possible, create a temporary directory, and use it as the + configuration directory. + 4. A writable directory could not be found or created; return None. """ configdir = os.environ.get('MPLCONFIGDIR') if configdir is not None: if not os.path.exists(configdir): - os.makedirs(configdir) + mkdirs(configdir) if not _is_writable_dir(configdir): return _create_tmp_config_dir() return configdir h = get_home() - p = os.path.join(get_home(), '.matplotlib') + if h is not None: + p = os.path.join(h, '.matplotlib') - if os.path.exists(p): - if not _is_writable_dir(p): - return _create_tmp_config_dir() - else: - if not _is_writable_dir(h): - return _create_tmp_config_dir() - from matplotlib.cbook import mkdirs - mkdirs(p) + if os.path.exists(p): + if not _is_writable_dir(p): + return _create_tmp_config_dir() + else: + if not _is_writable_dir(h): + return _create_tmp_config_dir() + from matplotlib.cbook import mkdirs + mkdirs(p) - return p + return p + + return _create_tmp_config_dir() get_configdir = verbose.wrap('CONFIGDIR=%s', _get_configdir, always=False) @@ -636,27 +659,39 @@ def matplotlib_fname(): """ - oldname = os.path.join( os.getcwd(), '.matplotlibrc') + oldname = os.path.join(os.getcwd(), '.matplotlibrc') if os.path.exists(oldname): - print("""\ -WARNING: Old rc filename ".matplotlibrc" found in working dir - and and renamed to new default rc file name "matplotlibrc" - (no leading"dot"). """, file=sys.stderr) - shutil.move('.matplotlibrc', 'matplotlibrc') + try: + shutil.move('.matplotlibrc', 'matplotlibrc') + except IOError as e: + warnings.warn('File could not be renamed: %s' % e) + else: + warnings.warn("""\ +Old rc filename ".matplotlibrc" found in working dir and and renamed to new + default rc file name "matplotlibrc" (no leading ".").""") home = get_home() - oldname = os.path.join( home, '.matplotlibrc') - if os.path.exists(oldname): - configdir = get_configdir() - newname = os.path.join(configdir, 'matplotlibrc') - print("""\ -WARNING: Old rc filename "%s" found and renamed to - new default rc file name "%s"."""%(oldname, newname), file=sys.stderr) - - shutil.move(oldname, newname) - + configdir = get_configdir() + if home: + oldname = os.path.join(home, '.matplotlibrc') + if os.path.exists(oldname): + if configdir is not None: + newname = os.path.join(configdir, 'matplotlibrc') + + try: + shutil.move(oldname, newname) + except IOError as e: + warnings.warn('File could not be renamed: %s' % e) + else: + warnings.warn("""\ +Old rc filename "%s" found and renamed to new default rc file name "%s".""" + % (oldname, newname)) + else: + warnings.warn("""\ +Could not rename old rc file "%s": a suitable configuration directory could not + be found.""" % oldname) - fname = os.path.join( os.getcwd(), 'matplotlibrc') + fname = os.path.join(os.getcwd(), 'matplotlibrc') if os.path.exists(fname): return fname if 'MATPLOTLIBRC' in os.environ: @@ -666,9 +701,10 @@ def matplotlib_fname(): if os.path.exists(fname): return fname - fname = os.path.join(get_configdir(), 'matplotlibrc') - if os.path.exists(fname): return fname - + if configdir is not None: + fname = os.path.join(configdir, 'matplotlibrc') + if os.path.exists(fname): + return fname path = get_data_path() # guaranteed to exist or raise fname = os.path.join(path, 'matplotlibrc') diff --git a/lib/matplotlib/finance.py b/lib/matplotlib/finance.py index faa3175be3f5..c456acbf43f0 100644 --- a/lib/matplotlib/finance.py +++ b/lib/matplotlib/finance.py @@ -28,7 +28,13 @@ configdir = get_configdir() -cachedir = os.path.join(configdir, 'finance.cache') +# cachedir will be None if there is no writable directory. +if configdir is not None: + cachedir = os.path.join(configdir, 'finance.cache') +else: + # Should only happen in a restricted environment (such as Google App + # Engine). Deal with this gracefully by not caching finance data. + cachedir = None stock_dt = np.dtype([('date', object), @@ -178,20 +184,25 @@ def fetch_historical_yahoo(ticker, date1, date2, cachename=None,dividends=False) d2[0], d2[1], d2[2], ticker, g) - if cachename is None: + # Cache the finance data if cachename is supplied, or there is a writable + # cache directory. + if cachename is None and cachedir is not None: cachename = os.path.join(cachedir, md5(url).hexdigest()) - if os.path.exists(cachename): - fh = open(cachename) - verbose.report('Using cachefile %s for %s'%(cachename, ticker)) + if cachename is not None: + if os.path.exists(cachename): + fh = open(cachename) + verbose.report('Using cachefile %s for %s'%(cachename, ticker)) + else: + mkdirs(os.path.abspath(os.path.dirname(cachename))) + with contextlib.closing(urlopen(url)) as urlfh: + with open(cachename, 'wb') as fh: + fh.write(urlfh.read()) + verbose.report('Saved %s data to cache file %s'%(ticker, cachename)) + fh = open(cachename, 'r') + + return fh else: - mkdirs(os.path.abspath(os.path.dirname(cachename))) - with contextlib.closing(urlopen(url)) as urlfh: - with open(cachename, 'wb') as fh: - fh.write(urlfh.read()) - verbose.report('Saved %s data to cache file %s'%(ticker, cachename)) - fh = open(cachename, 'r') - - return fh + return urlopen(url) def quotes_historical_yahoo(ticker, date1, date2, asobject=False, diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index 28ac73453e26..5684fc6263f8 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -1311,28 +1311,38 @@ def findfont(prop, fontext='ttf'): return result else: - if sys.version_info[0] >= 3: - _fmcache = os.path.join(get_configdir(), 'fontList.py3k.cache') + configdir = get_configdir() + if configdir is not None: + if sys.version_info[0] >= 3: + _fmcache = os.path.join(configdir, 'fontList.py3k.cache') + else: + _fmcache = os.path.join(configdir, 'fontList.cache') else: - _fmcache = os.path.join(get_configdir(), 'fontList.cache') + # Should only happen in a restricted environment (such as Google App + # Engine). Deal with this gracefully by not caching fonts. + _fmcache = None fontManager = None def _rebuild(): global fontManager fontManager = FontManager() - pickle_dump(fontManager, _fmcache) + if _fmcache: + pickle_dump(fontManager, _fmcache) verbose.report("generated new fontManager") - try: - fontManager = pickle_load(_fmcache) - if (not hasattr(fontManager, '_version') or - fontManager._version != FontManager.__version__): + if _fmcache: + try: + fontManager = pickle_load(_fmcache) + if (not hasattr(fontManager, '_version') or + fontManager._version != FontManager.__version__): + _rebuild() + else: + fontManager.default_size = None + verbose.report("Using fontManager instance from %s" % _fmcache) + except: _rebuild() - else: - fontManager.default_size = None - verbose.report("Using fontManager instance from %s" % _fmcache) - except: + else: _rebuild() def findfont(prop, **kw): diff --git a/lib/matplotlib/testing/compare.py b/lib/matplotlib/testing/compare.py index 17d247746bf7..7f649e58befd 100644 --- a/lib/matplotlib/testing/compare.py +++ b/lib/matplotlib/testing/compare.py @@ -99,7 +99,10 @@ def compare_float( expected, actual, relTol = None, absTol = None ): # parameters old and new to a list that can be passed to Popen to # convert files with that extension to png format. def get_cache_dir(): - cache_dir = os.path.join(_get_configdir(), 'test_cache') + configdir = _get_configdir() + if configdir is None: + raise RuntimeError('Could not find a suitable configuration directory') + cache_dir = os.path.join(configdir, 'test_cache') if not os.path.exists(cache_dir): try: os.makedirs(cache_dir) diff --git a/lib/matplotlib/texmanager.py b/lib/matplotlib/texmanager.py index da666b1a138e..b3fcc8df0247 100644 --- a/lib/matplotlib/texmanager.py +++ b/lib/matplotlib/texmanager.py @@ -41,6 +41,7 @@ import os import shutil import sys +import warnings from hashlib import md5 @@ -94,16 +95,30 @@ class TexManager: oldcache = os.path.join(oldpath, '.tex.cache') configdir = mpl.get_configdir() - texcache = os.path.join(configdir, 'tex.cache') + if configdir is not None: + texcache = os.path.join(configdir, 'tex.cache') + else: + # Should only happen in a restricted environment (such as Google App + # Engine). Deal with this gracefully by not creating a cache directory. + texcache = None if os.path.exists(oldcache): - # FIXME raise proper warning - print("""\ -WARNING: found a TeX cache dir in the deprecated location "%s". - Moving it to the new default location "%s".""" % (oldcache, texcache), - file=sys.stderr) - shutil.move(oldcache, texcache) - mkdirs(texcache) + if texcache is not None: + try: + shutil.move(oldcache, texcache) + except IOError as e: + warnings.warn('File could not be renamed: %s' % e) + else: + warnings.warn("""\ +Found a TeX cache dir in the deprecated location "%s". + Moving it to the new default location "%s".""" % (oldcache, texcache)) + else: + warnings.warn("""\ +Could not rename old TeX cache dir "%s": a suitable configuration + directory could not be found.""" % oldcache) + + if texcache is not None: + mkdirs(texcache) _dvipng_hack_alpha = None #_dvipng_hack_alpha = dvipng_hack_alpha() @@ -145,6 +160,11 @@ class TexManager: def __init__(self): + if self.texcache is None: + raise RuntimeError( + ('Cannot create TexManager, as there is no cache directory ' + 'available')) + mkdirs(self.texcache) ff = rcParams['font.family'].lower() if ff in self.font_families: