Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
pelson merged 17 commits intomatplotlib:masterfrommgiuca-google:no-home-dir
Apr 16, 2013
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
17 commits
Select commitHold shift + click to select a range
8083f25
shutil.move: Guard against IOError and print warnings instead of cras…
mgiuca-googleMar 6, 2013
a697a26
matplotlib: _is_writable_dir uses os.access instead of TemporaryFile.
mgiuca-googleMar 6, 2013
4d65400
Matplotlib now works when the user has no home directory.
mgiuca-googleMar 6, 2013
21921a3
get_configdir returns None if tempfile.gettempdir() is not available.
mgiuca-googleMar 6, 2013
4987dcd
Deal with all cases where get_configdir might return None.
mgiuca-googleMar 6, 2013
64c797b
font_manager: Gracefully handle the case of there being no config dir.
mgiuca-googleMar 6, 2013
1dbd6de
texmanager: Gracefully handle the case of there being no config dir u…
mgiuca-googleMar 6, 2013
1adfc85
finance: Gracefully handle the case of there being no config dir.
mgiuca-googleMar 8, 2013
941efd4
Fix formatting and other misc code tweaks.
mgiuca-googleMar 17, 2013
ca6cd19
matplotlib.get_home: Removing catch-all except blocks.
mgiuca-googleMar 18, 2013
cc8cd1b
matplotlib, texmanager: Change WARNING prints into real warnings.
mgiuca-googleMar 18, 2013
f01ebe1
matplotlib, texmanager: Only print the rename message if it actually …
mgiuca-googleMar 18, 2013
018ce26
finance: Fixed caching when cachename is supplied.
mgiuca-googleMar 18, 2013
6a4f1e7
matplotlib: Use cbook.mkdirs instead of os.makedirs.
mgiuca-googleMar 18, 2013
4f55a27
matplotlib: Remove catch for OSError.
mgiuca-googleMar 18, 2013
81639a1
matplotlib: _is_writable_dir checks that it is a directory.
mgiuca-googleMar 18, 2013
8335773
matplotlib: _is_writable_dir tests with os.access and TemporaryFile.
mgiuca-googleMar 18, 2013
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 94 additions & 58 deletionslib/matplotlib/__init__.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -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:
"""
Expand DownExpand Up@@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

👍

"""
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
Expand All@@ -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)
Copy link
Member

Choose a reason for hiding this comment

The 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 betweencbook.mkdirs andos.makedirs. Any ideas?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

The difference is thatcbook.mkdirs doesn't complain if the directory already exists.

Copy link
Member

Choose a reason for hiding this comment

The 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.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

So why does it useos.makedirs above? (It's a little bit above the context shown in this patch, but the first block ofget_configdir callsos.makedirs, whereas the second block callscbook.mkdirs.) Can we change it so it consistently callscbook.mkdirs?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Yes -- let's change this tocbook.mkdirs in both cases.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The 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)


Expand DownExpand Up@@ -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:
Expand All@@ -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')
Expand Down
37 changes: 24 additions & 13 deletionslib/matplotlib/finance.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -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),
Expand DownExpand Up@@ -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,
Expand Down
34 changes: 22 additions & 12 deletionslib/matplotlib/font_manager.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -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):
Expand Down
5 changes: 4 additions & 1 deletionlib/matplotlib/testing/compare.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -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)
Expand Down
36 changes: 28 additions & 8 deletionslib/matplotlib/texmanager.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,6 +41,7 @@
import os
import shutil
import sys
import warnings

from hashlib import md5

Expand DownExpand Up@@ -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()
Expand DownExpand Up@@ -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'))
Copy link
Member

Choose a reason for hiding this comment

The 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.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The 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:
Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp