-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Support environments without a home dir or writable file system #1824
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
Changes from all commits
8083f25
a697a26
4d65400
21921a3
4987dcd
64c797b
1dbd6de
1adfc85
941efd4
ca6cd19
cc8cd1b
f01ebe1
018ce26
6a4f1e7
4f55a27
81639a1
8335773
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There must be a subtlety that I'm missing here - I can't see the difference between There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The difference is that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cbook.mkdirs() is even safer than os.makedirs(). Imagine two processes, one needing to make A/B, and the other needing to make A/C. There is a race condition in os.makedirs() where one of the two processes will fail to make their directory because the exception was thrown while dealing with the parent directory. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So why does it use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes -- let's change this to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
|
||
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') | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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')) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This probably doesn't affect Google AppEngine (which I suspect doesn't have a TeX installation anyway), but it would be nice to continue to be able to get TeX snippets without them being cached in this case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From my reading of the code, this whole module doesn't work if there is no writable directory. The so-called "texcache" is not really a cache at all, it's also a working directory, where intermediate files are generated and processed with dvipng. So even if we assume a system that does have a TeX installation and the ability to launch external commands, but does not have any writable file system, we won't be able to run these commands. (This isn't a simple matter of disabling caching.) Correct me if I'm wrong about that. |
||
|
||
mkdirs(self.texcache) | ||
ff = rcParams['font.family'].lower() | ||
if ff in self.font_families: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍