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

Skip to content

Commit 91b1d9f

Browse files
committed
Merge pull request #1824 from mgiuca-google/no-home-dir
Support environments without a home dir or writable file system
2 parents 9e477b3 + 8335773 commit 91b1d9f

File tree

5 files changed

+172
-92
lines changed

5 files changed

+172
-92
lines changed

lib/matplotlib/__init__.py

Lines changed: 94 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -213,16 +213,28 @@ def _is_writable_dir(p):
213213
p is a string pointing to a putative writable dir -- return True p
214214
is such a string, else False
215215
"""
216-
try: p + '' # test is string like
217-
except TypeError: return False
216+
try:
217+
p + '' # test is string like
218+
except TypeError:
219+
return False
220+
221+
# Test whether the operating system thinks it's a writable directory.
222+
# Note that this check is necessary on Google App Engine, because the
223+
# subsequent check will succeed even though p may not be writable.
224+
if not os.access(p, os.W_OK) or not os.path.isdir(p):
225+
return False
226+
227+
# Also test that it is actually possible to write to a file here.
218228
try:
219229
t = tempfile.TemporaryFile(dir=p)
220230
try:
221231
t.write(ascii('1'))
222232
finally:
223233
t.close()
224-
except OSError: return False
225-
else: return True
234+
except OSError:
235+
return False
236+
237+
return True
226238

227239
class Verbose:
228240
"""
@@ -475,38 +487,42 @@ def checkdep_usetex(s):
475487

476488
def _get_home():
477489
"""Find user's home directory if possible.
478-
Otherwise raise error.
490+
Otherwise, returns None.
479491
480-
:see: http://mail.python.org/pipermail/python-list/2005-February/263921.html
492+
:see: http://mail.python.org/pipermail/python-list/2005-February/325395.html
481493
"""
482-
path=''
483494
try:
484-
path=os.path.expanduser("~")
485-
except:
495+
path = os.path.expanduser("~")
496+
except ImportError:
497+
# This happens on Google App Engine (pwd module is not present).
486498
pass
487-
if not os.path.isdir(path):
488-
for evar in ('HOME', 'USERPROFILE', 'TMP'):
489-
try:
490-
path = os.environ[evar]
491-
if os.path.isdir(path):
492-
break
493-
except: pass
494-
if path:
495-
return path
496499
else:
497-
raise RuntimeError('please define environment variable $HOME')
500+
if os.path.isdir(path):
501+
return path
502+
for evar in ('HOME', 'USERPROFILE', 'TMP'):
503+
path = os.environ.get(evar)
504+
if path is not None and os.path.isdir(path):
505+
return path
506+
return None
498507

499508

500509
def _create_tmp_config_dir():
501510
"""
502511
If the config directory can not be created, create a temporary
503512
directory.
513+
514+
Returns None if a writable temporary directory could not be created.
504515
"""
505516
import getpass
506517
import tempfile
507518

508-
tempdir = os.path.join(
509-
tempfile.gettempdir(), 'matplotlib-%s' % getpass.getuser())
519+
try:
520+
tempdir = tempfile.gettempdir()
521+
except NotImplementedError:
522+
# Some restricted platforms (such as Google App Engine) do not provide
523+
# gettempdir.
524+
return None
525+
tempdir = os.path.join(tempdir, 'matplotlib-%s' % getpass.getuser())
510526
os.environ['MPLCONFIGDIR'] = tempdir
511527

512528
return tempdir
@@ -518,35 +534,42 @@ def _get_configdir():
518534
"""
519535
Return the string representing the configuration directory.
520536
521-
Default is HOME/.matplotlib. You can override this with the
522-
MPLCONFIGDIR environment variable. If the default is not
523-
writable, and MPLCONFIGDIR is not set, then
524-
tempfile.gettempdir() is used to provide a directory in
525-
which a matplotlib subdirectory is created as the configuration
526-
directory.
537+
The directory is chosen as follows:
538+
539+
1. If the MPLCONFIGDIR environment variable is supplied, choose that. Else,
540+
choose the '.matplotlib' subdirectory of the user's home directory (and
541+
create it if necessary).
542+
2. If the chosen directory exists and is writable, use that as the
543+
configuration directory.
544+
3. If possible, create a temporary directory, and use it as the
545+
configuration directory.
546+
4. A writable directory could not be found or created; return None.
527547
"""
528548

529549
configdir = os.environ.get('MPLCONFIGDIR')
530550
if configdir is not None:
531551
if not os.path.exists(configdir):
532-
os.makedirs(configdir)
552+
mkdirs(configdir)
533553
if not _is_writable_dir(configdir):
534554
return _create_tmp_config_dir()
535555
return configdir
536556

537557
h = get_home()
538-
p = os.path.join(get_home(), '.matplotlib')
558+
if h is not None:
559+
p = os.path.join(h, '.matplotlib')
539560

540-
if os.path.exists(p):
541-
if not _is_writable_dir(p):
542-
return _create_tmp_config_dir()
543-
else:
544-
if not _is_writable_dir(h):
545-
return _create_tmp_config_dir()
546-
from matplotlib.cbook import mkdirs
547-
mkdirs(p)
561+
if os.path.exists(p):
562+
if not _is_writable_dir(p):
563+
return _create_tmp_config_dir()
564+
else:
565+
if not _is_writable_dir(h):
566+
return _create_tmp_config_dir()
567+
from matplotlib.cbook import mkdirs
568+
mkdirs(p)
548569

549-
return p
570+
return p
571+
572+
return _create_tmp_config_dir()
550573
get_configdir = verbose.wrap('CONFIGDIR=%s', _get_configdir, always=False)
551574

552575

@@ -636,27 +659,39 @@ def matplotlib_fname():
636659
637660
"""
638661

639-
oldname = os.path.join( os.getcwd(), '.matplotlibrc')
662+
oldname = os.path.join(os.getcwd(), '.matplotlibrc')
640663
if os.path.exists(oldname):
641-
print("""\
642-
WARNING: Old rc filename ".matplotlibrc" found in working dir
643-
and and renamed to new default rc file name "matplotlibrc"
644-
(no leading"dot"). """, file=sys.stderr)
645-
shutil.move('.matplotlibrc', 'matplotlibrc')
664+
try:
665+
shutil.move('.matplotlibrc', 'matplotlibrc')
666+
except IOError as e:
667+
warnings.warn('File could not be renamed: %s' % e)
668+
else:
669+
warnings.warn("""\
670+
Old rc filename ".matplotlibrc" found in working dir and and renamed to new
671+
default rc file name "matplotlibrc" (no leading ".").""")
646672

647673
home = get_home()
648-
oldname = os.path.join( home, '.matplotlibrc')
649-
if os.path.exists(oldname):
650-
configdir = get_configdir()
651-
newname = os.path.join(configdir, 'matplotlibrc')
652-
print("""\
653-
WARNING: Old rc filename "%s" found and renamed to
654-
new default rc file name "%s"."""%(oldname, newname), file=sys.stderr)
655-
656-
shutil.move(oldname, newname)
657-
674+
configdir = get_configdir()
675+
if home:
676+
oldname = os.path.join(home, '.matplotlibrc')
677+
if os.path.exists(oldname):
678+
if configdir is not None:
679+
newname = os.path.join(configdir, 'matplotlibrc')
680+
681+
try:
682+
shutil.move(oldname, newname)
683+
except IOError as e:
684+
warnings.warn('File could not be renamed: %s' % e)
685+
else:
686+
warnings.warn("""\
687+
Old rc filename "%s" found and renamed to new default rc file name "%s"."""
688+
% (oldname, newname))
689+
else:
690+
warnings.warn("""\
691+
Could not rename old rc file "%s": a suitable configuration directory could not
692+
be found.""" % oldname)
658693

659-
fname = os.path.join( os.getcwd(), 'matplotlibrc')
694+
fname = os.path.join(os.getcwd(), 'matplotlibrc')
660695
if os.path.exists(fname): return fname
661696

662697
if 'MATPLOTLIBRC' in os.environ:
@@ -666,9 +701,10 @@ def matplotlib_fname():
666701
if os.path.exists(fname):
667702
return fname
668703

669-
fname = os.path.join(get_configdir(), 'matplotlibrc')
670-
if os.path.exists(fname): return fname
671-
704+
if configdir is not None:
705+
fname = os.path.join(configdir, 'matplotlibrc')
706+
if os.path.exists(fname):
707+
return fname
672708

673709
path = get_data_path() # guaranteed to exist or raise
674710
fname = os.path.join(path, 'matplotlibrc')

lib/matplotlib/finance.py

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,13 @@
2828

2929

3030
configdir = get_configdir()
31-
cachedir = os.path.join(configdir, 'finance.cache')
31+
# cachedir will be None if there is no writable directory.
32+
if configdir is not None:
33+
cachedir = os.path.join(configdir, 'finance.cache')
34+
else:
35+
# Should only happen in a restricted environment (such as Google App
36+
# Engine). Deal with this gracefully by not caching finance data.
37+
cachedir = None
3238

3339

3440
stock_dt = np.dtype([('date', object),
@@ -178,20 +184,25 @@ def fetch_historical_yahoo(ticker, date1, date2, cachename=None,dividends=False)
178184
d2[0], d2[1], d2[2], ticker, g)
179185

180186

181-
if cachename is None:
187+
# Cache the finance data if cachename is supplied, or there is a writable
188+
# cache directory.
189+
if cachename is None and cachedir is not None:
182190
cachename = os.path.join(cachedir, md5(url).hexdigest())
183-
if os.path.exists(cachename):
184-
fh = open(cachename)
185-
verbose.report('Using cachefile %s for %s'%(cachename, ticker))
191+
if cachename is not None:
192+
if os.path.exists(cachename):
193+
fh = open(cachename)
194+
verbose.report('Using cachefile %s for %s'%(cachename, ticker))
195+
else:
196+
mkdirs(os.path.abspath(os.path.dirname(cachename)))
197+
with contextlib.closing(urlopen(url)) as urlfh:
198+
with open(cachename, 'wb') as fh:
199+
fh.write(urlfh.read())
200+
verbose.report('Saved %s data to cache file %s'%(ticker, cachename))
201+
fh = open(cachename, 'r')
202+
203+
return fh
186204
else:
187-
mkdirs(os.path.abspath(os.path.dirname(cachename)))
188-
with contextlib.closing(urlopen(url)) as urlfh:
189-
with open(cachename, 'wb') as fh:
190-
fh.write(urlfh.read())
191-
verbose.report('Saved %s data to cache file %s'%(ticker, cachename))
192-
fh = open(cachename, 'r')
193-
194-
return fh
205+
return urlopen(url)
195206

196207

197208
def quotes_historical_yahoo(ticker, date1, date2, asobject=False,

lib/matplotlib/font_manager.py

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,28 +1311,38 @@ def findfont(prop, fontext='ttf'):
13111311
return result
13121312

13131313
else:
1314-
if sys.version_info[0] >= 3:
1315-
_fmcache = os.path.join(get_configdir(), 'fontList.py3k.cache')
1314+
configdir = get_configdir()
1315+
if configdir is not None:
1316+
if sys.version_info[0] >= 3:
1317+
_fmcache = os.path.join(configdir, 'fontList.py3k.cache')
1318+
else:
1319+
_fmcache = os.path.join(configdir, 'fontList.cache')
13161320
else:
1317-
_fmcache = os.path.join(get_configdir(), 'fontList.cache')
1321+
# Should only happen in a restricted environment (such as Google App
1322+
# Engine). Deal with this gracefully by not caching fonts.
1323+
_fmcache = None
13181324

13191325
fontManager = None
13201326

13211327
def _rebuild():
13221328
global fontManager
13231329
fontManager = FontManager()
1324-
pickle_dump(fontManager, _fmcache)
1330+
if _fmcache:
1331+
pickle_dump(fontManager, _fmcache)
13251332
verbose.report("generated new fontManager")
13261333

1327-
try:
1328-
fontManager = pickle_load(_fmcache)
1329-
if (not hasattr(fontManager, '_version') or
1330-
fontManager._version != FontManager.__version__):
1334+
if _fmcache:
1335+
try:
1336+
fontManager = pickle_load(_fmcache)
1337+
if (not hasattr(fontManager, '_version') or
1338+
fontManager._version != FontManager.__version__):
1339+
_rebuild()
1340+
else:
1341+
fontManager.default_size = None
1342+
verbose.report("Using fontManager instance from %s" % _fmcache)
1343+
except:
13311344
_rebuild()
1332-
else:
1333-
fontManager.default_size = None
1334-
verbose.report("Using fontManager instance from %s" % _fmcache)
1335-
except:
1345+
else:
13361346
_rebuild()
13371347

13381348
def findfont(prop, **kw):

lib/matplotlib/testing/compare.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,10 @@ def compare_float( expected, actual, relTol = None, absTol = None ):
9999
# parameters old and new to a list that can be passed to Popen to
100100
# convert files with that extension to png format.
101101
def get_cache_dir():
102-
cache_dir = os.path.join(_get_configdir(), 'test_cache')
102+
configdir = _get_configdir()
103+
if configdir is None:
104+
raise RuntimeError('Could not find a suitable configuration directory')
105+
cache_dir = os.path.join(configdir, 'test_cache')
103106
if not os.path.exists(cache_dir):
104107
try:
105108
os.makedirs(cache_dir)

lib/matplotlib/texmanager.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import os
4242
import shutil
4343
import sys
44+
import warnings
4445

4546
from hashlib import md5
4647

@@ -94,16 +95,30 @@ class TexManager:
9495
oldcache = os.path.join(oldpath, '.tex.cache')
9596

9697
configdir = mpl.get_configdir()
97-
texcache = os.path.join(configdir, 'tex.cache')
98+
if configdir is not None:
99+
texcache = os.path.join(configdir, 'tex.cache')
100+
else:
101+
# Should only happen in a restricted environment (such as Google App
102+
# Engine). Deal with this gracefully by not creating a cache directory.
103+
texcache = None
98104

99105
if os.path.exists(oldcache):
100-
# FIXME raise proper warning
101-
print("""\
102-
WARNING: found a TeX cache dir in the deprecated location "%s".
103-
Moving it to the new default location "%s".""" % (oldcache, texcache),
104-
file=sys.stderr)
105-
shutil.move(oldcache, texcache)
106-
mkdirs(texcache)
106+
if texcache is not None:
107+
try:
108+
shutil.move(oldcache, texcache)
109+
except IOError as e:
110+
warnings.warn('File could not be renamed: %s' % e)
111+
else:
112+
warnings.warn("""\
113+
Found a TeX cache dir in the deprecated location "%s".
114+
Moving it to the new default location "%s".""" % (oldcache, texcache))
115+
else:
116+
warnings.warn("""\
117+
Could not rename old TeX cache dir "%s": a suitable configuration
118+
directory could not be found.""" % oldcache)
119+
120+
if texcache is not None:
121+
mkdirs(texcache)
107122

108123
_dvipng_hack_alpha = None
109124
#_dvipng_hack_alpha = dvipng_hack_alpha()
@@ -145,6 +160,11 @@ class TexManager:
145160

146161
def __init__(self):
147162

163+
if self.texcache is None:
164+
raise RuntimeError(
165+
('Cannot create TexManager, as there is no cache directory '
166+
'available'))
167+
148168
mkdirs(self.texcache)
149169
ff = rcParams['font.family'].lower()
150170
if ff in self.font_families:

0 commit comments

Comments
 (0)