Skip to content
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

Split IPython specific functions out of utils.path #8214

Merged
merged 5 commits into from
Apr 2, 2015
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
246 changes: 246 additions & 0 deletions IPython/core/tests/test_paths.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
from contextlib import contextmanager
import errno
import os
import shutil
import sys
import tempfile
import warnings

try:
reload
except NameError: # Python 3
from imp import reload

from nose import with_setup
import nose.tools as nt

import IPython
from IPython import paths
from IPython.testing.decorators import skip_win32
from IPython.utils.tempdir import TemporaryDirectory

env = os.environ
TMP_TEST_DIR = tempfile.mkdtemp()
HOME_TEST_DIR = os.path.join(TMP_TEST_DIR, "home_test_dir")
XDG_TEST_DIR = os.path.join(HOME_TEST_DIR, "xdg_test_dir")
XDG_CACHE_DIR = os.path.join(HOME_TEST_DIR, "xdg_cache_dir")
IP_TEST_DIR = os.path.join(HOME_TEST_DIR,'.ipython')

def setup():
"""Setup testenvironment for the module:

- Adds dummy home dir tree
"""
# Do not mask exceptions here. In particular, catching WindowsError is a
# problem because that exception is only defined on Windows...
os.makedirs(IP_TEST_DIR)
os.makedirs(os.path.join(XDG_TEST_DIR, 'ipython'))
os.makedirs(os.path.join(XDG_CACHE_DIR, 'ipython'))


def teardown():
"""Teardown testenvironment for the module:

- Remove dummy home dir tree
"""
# Note: we remove the parent test dir, which is the root of all test
# subdirs we may have created. Use shutil instead of os.removedirs, so
# that non-empty directories are all recursively removed.
shutil.rmtree(TMP_TEST_DIR)


def setup_environment():
"""Setup testenvironment for some functions that are tested
in this module. In particular this functions stores attributes
and other things that we need to stub in some test functions.
This needs to be done on a function level and not module level because
each testfunction needs a pristine environment.
"""
global oldstuff, platformstuff
oldstuff = (env.copy(), os.name, sys.platform, paths.get_home_dir, IPython.__file__, os.getcwd())

def teardown_environment():
"""Restore things that were remembered by the setup_environment function
"""
(oldenv, os.name, sys.platform, paths.get_home_dir, IPython.__file__, old_wd) = oldstuff
os.chdir(old_wd)
reload(paths)

for key in list(env):
if key not in oldenv:
del env[key]
env.update(oldenv)
if hasattr(sys, 'frozen'):
del sys.frozen

# Build decorator that uses the setup_environment/setup_environment
with_environment = with_setup(setup_environment, teardown_environment)

@contextmanager
def patch_get_home_dir(dirpath):
orig_get_home_dir = paths.get_home_dir
paths.get_home_dir = lambda : dirpath
try:
yield
finally:
paths.get_home_dir = orig_get_home_dir


@with_environment
def test_get_ipython_dir_1():
"""test_get_ipython_dir_1, Testcase to see if we can call get_ipython_dir without Exceptions."""
env_ipdir = os.path.join("someplace", ".ipython")
paths._writable_dir = lambda path: True
env['IPYTHONDIR'] = env_ipdir
ipdir = paths.get_ipython_dir()
nt.assert_equal(ipdir, env_ipdir)


@with_environment
def test_get_ipython_dir_2():
"""test_get_ipython_dir_2, Testcase to see if we can call get_ipython_dir without Exceptions."""
with patch_get_home_dir('someplace'):
paths.get_xdg_dir = lambda : None
paths._writable_dir = lambda path: True
os.name = "posix"
env.pop('IPYTHON_DIR', None)
env.pop('IPYTHONDIR', None)
env.pop('XDG_CONFIG_HOME', None)
ipdir = paths.get_ipython_dir()
nt.assert_equal(ipdir, os.path.join("someplace", ".ipython"))

@with_environment
def test_get_ipython_dir_3():
"""test_get_ipython_dir_3, move XDG if defined, and .ipython doesn't exist."""
tmphome = TemporaryDirectory()
try:
with patch_get_home_dir(tmphome.name):
os.name = "posix"
env.pop('IPYTHON_DIR', None)
env.pop('IPYTHONDIR', None)
env['XDG_CONFIG_HOME'] = XDG_TEST_DIR

with warnings.catch_warnings(record=True) as w:
ipdir = paths.get_ipython_dir()

nt.assert_equal(ipdir, os.path.join(tmphome.name, ".ipython"))
if sys.platform != 'darwin':
nt.assert_equal(len(w), 1)
nt.assert_in('Moving', str(w[0]))
finally:
tmphome.cleanup()

@with_environment
def test_get_ipython_dir_4():
"""test_get_ipython_dir_4, warn if XDG and home both exist."""
with patch_get_home_dir(HOME_TEST_DIR):
os.name = "posix"
env.pop('IPYTHON_DIR', None)
env.pop('IPYTHONDIR', None)
env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
try:
os.mkdir(os.path.join(XDG_TEST_DIR, 'ipython'))
except OSError as e:
if e.errno != errno.EEXIST:
raise

with warnings.catch_warnings(record=True) as w:
ipdir = paths.get_ipython_dir()

nt.assert_equal(ipdir, os.path.join(HOME_TEST_DIR, ".ipython"))
if sys.platform != 'darwin':
nt.assert_equal(len(w), 1)
nt.assert_in('Ignoring', str(w[0]))

@with_environment
def test_get_ipython_dir_5():
"""test_get_ipython_dir_5, use .ipython if exists and XDG defined, but doesn't exist."""
with patch_get_home_dir(HOME_TEST_DIR):
os.name = "posix"
env.pop('IPYTHON_DIR', None)
env.pop('IPYTHONDIR', None)
env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
try:
os.rmdir(os.path.join(XDG_TEST_DIR, 'ipython'))
except OSError as e:
if e.errno != errno.ENOENT:
raise
ipdir = paths.get_ipython_dir()
nt.assert_equal(ipdir, IP_TEST_DIR)

@with_environment
def test_get_ipython_dir_6():
"""test_get_ipython_dir_6, use home over XDG if defined and neither exist."""
xdg = os.path.join(HOME_TEST_DIR, 'somexdg')
os.mkdir(xdg)
shutil.rmtree(os.path.join(HOME_TEST_DIR, '.ipython'))
with patch_get_home_dir(HOME_TEST_DIR):
orig_get_xdg_dir = paths.get_xdg_dir
paths.get_xdg_dir = lambda : xdg
try:
os.name = "posix"
env.pop('IPYTHON_DIR', None)
env.pop('IPYTHONDIR', None)
env.pop('XDG_CONFIG_HOME', None)
with warnings.catch_warnings(record=True) as w:
ipdir = paths.get_ipython_dir()

nt.assert_equal(ipdir, os.path.join(HOME_TEST_DIR, '.ipython'))
nt.assert_equal(len(w), 0)
finally:
paths.get_xdg_dir = orig_get_xdg_dir

@with_environment
def test_get_ipython_dir_7():
"""test_get_ipython_dir_7, test home directory expansion on IPYTHONDIR"""
paths._writable_dir = lambda path: True
home_dir = os.path.normpath(os.path.expanduser('~'))
env['IPYTHONDIR'] = os.path.join('~', 'somewhere')
ipdir = paths.get_ipython_dir()
nt.assert_equal(ipdir, os.path.join(home_dir, 'somewhere'))

@skip_win32
@with_environment
def test_get_ipython_dir_8():
"""test_get_ipython_dir_8, test / home directory"""
old = paths._writable_dir, paths.get_xdg_dir
try:
paths._writable_dir = lambda path: bool(path)
paths.get_xdg_dir = lambda: None
env.pop('IPYTHON_DIR', None)
env.pop('IPYTHONDIR', None)
env['HOME'] = '/'
nt.assert_equal(paths.get_ipython_dir(), '/.ipython')
finally:
paths._writable_dir, paths.get_xdg_dir = old


@with_environment
def test_get_ipython_cache_dir():
os.environ["HOME"] = HOME_TEST_DIR
if os.name == 'posix' and sys.platform != 'darwin':
# test default
os.makedirs(os.path.join(HOME_TEST_DIR, ".cache"))
os.environ.pop("XDG_CACHE_HOME", None)
ipdir = paths.get_ipython_cache_dir()
nt.assert_equal(os.path.join(HOME_TEST_DIR, ".cache", "ipython"),
ipdir)
nt.assert_true(os.path.isdir(ipdir))

# test env override
os.environ["XDG_CACHE_HOME"] = XDG_CACHE_DIR
ipdir = paths.get_ipython_cache_dir()
nt.assert_true(os.path.isdir(ipdir))
nt.assert_equal(ipdir, os.path.join(XDG_CACHE_DIR, "ipython"))
else:
nt.assert_equal(paths.get_ipython_cache_dir(),
paths.get_ipython_dir())

def test_get_ipython_package_dir():
ipdir = paths.get_ipython_package_dir()
nt.assert_true(os.path.isdir(ipdir))


def test_get_ipython_module_path():
ipapp_path = paths.get_ipython_module_path('IPython.terminal.ipapp')
nt.assert_true(os.path.isfile(ipapp_path))
149 changes: 149 additions & 0 deletions IPython/paths.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import os.path
import shutil
import tempfile
from warnings import warn

import IPython
from IPython.utils.importstring import import_item
from IPython.utils.path import (
get_home_dir, get_xdg_dir, get_xdg_cache_dir, compress_user, _writable_dir,
ensure_dir_exists, fs_encoding, filefind
)
from IPython.utils import py3compat

def get_ipython_dir():
"""Get the IPython directory for this platform and user.

This uses the logic in `get_home_dir` to find the home directory
and then adds .ipython to the end of the path.
"""

env = os.environ
pjoin = os.path.join


ipdir_def = '.ipython'

home_dir = get_home_dir()
xdg_dir = get_xdg_dir()

# import pdb; pdb.set_trace() # dbg
if 'IPYTHON_DIR' in env:
warn('The environment variable IPYTHON_DIR is deprecated. '
'Please use IPYTHONDIR instead.')
ipdir = env.get('IPYTHONDIR', env.get('IPYTHON_DIR', None))
if ipdir is None:
# not set explicitly, use ~/.ipython
ipdir = pjoin(home_dir, ipdir_def)
if xdg_dir:
# Several IPython versions (up to 1.x) defaulted to .config/ipython
# on Linux. We have decided to go back to using .ipython everywhere
xdg_ipdir = pjoin(xdg_dir, 'ipython')

if _writable_dir(xdg_ipdir):
cu = compress_user
if os.path.exists(ipdir):
warn(('Ignoring {0} in favour of {1}. Remove {0} to '
'get rid of this message').format(cu(xdg_ipdir), cu(ipdir)))
elif os.path.islink(xdg_ipdir):
warn(('{0} is deprecated. Move link to {1} to '
'get rid of this message').format(cu(xdg_ipdir), cu(ipdir)))
else:
warn('Moving {0} to {1}'.format(cu(xdg_ipdir), cu(ipdir)))
shutil.move(xdg_ipdir, ipdir)

ipdir = os.path.normpath(os.path.expanduser(ipdir))

if os.path.exists(ipdir) and not _writable_dir(ipdir):
# ipdir exists, but is not writable
warn("IPython dir '{0}' is not a writable location,"
" using a temp directory.".format(ipdir))
ipdir = tempfile.mkdtemp()
elif not os.path.exists(ipdir):
parent = os.path.dirname(ipdir)
if not _writable_dir(parent):
# ipdir does not exist and parent isn't writable
warn("IPython parent '{0}' is not a writable location,"
" using a temp directory.".format(parent))
ipdir = tempfile.mkdtemp()

return py3compat.cast_unicode(ipdir, fs_encoding)


def get_ipython_cache_dir():
"""Get the cache directory it is created if it does not exist."""
xdgdir = get_xdg_cache_dir()
if xdgdir is None:
return get_ipython_dir()
ipdir = os.path.join(xdgdir, "ipython")
if not os.path.exists(ipdir) and _writable_dir(xdgdir):
ensure_dir_exists(ipdir)
elif not _writable_dir(xdgdir):
return get_ipython_dir()

return py3compat.cast_unicode(ipdir, fs_encoding)


def get_ipython_package_dir():
"""Get the base directory where IPython itself is installed."""
ipdir = os.path.dirname(IPython.__file__)
return py3compat.cast_unicode(ipdir, fs_encoding)


def get_ipython_module_path(module_str):
"""Find the path to an IPython module in this version of IPython.

This will always find the version of the module that is in this importable
IPython package. This will always return the path to the ``.py``
version of the module.
"""
if module_str == 'IPython':
return os.path.join(get_ipython_package_dir(), '__init__.py')
mod = import_item(module_str)
the_path = mod.__file__.replace('.pyc', '.py')
the_path = the_path.replace('.pyo', '.py')
return py3compat.cast_unicode(the_path, fs_encoding)

def locate_profile(profile='default'):
"""Find the path to the folder associated with a given profile.

I.e. find $IPYTHONDIR/profile_whatever.
"""
from IPython.core.profiledir import ProfileDir, ProfileDirError
try:
pd = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile)
except ProfileDirError:
# IOError makes more sense when people are expecting a path
raise IOError("Couldn't find profile %r" % profile)
return pd.location

def get_security_file(filename, profile='default'):
"""Return the absolute path of a security file given by filename and profile

This allows users and developers to find security files without
knowledge of the IPython directory structure. The search path
will be ['.', profile.security_dir]

Parameters
----------

filename : str
The file to be found. If it is passed as an absolute path, it will
simply be returned.
profile : str [default: 'default']
The name of the profile to search. Leaving this unspecified
The file to be found. If it is passed as an absolute path, fname will
simply be returned.

Returns
-------
Raises :exc:`IOError` if file not found or returns absolute path to file.
"""
# import here, because profiledir also imports from utils.path
from IPython.core.profiledir import ProfileDir
try:
pd = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile)
except Exception:
# will raise ProfileDirError if no such profile
raise IOError("Profile %r not found")
return filefind(filename, ['.', pd.security_dir])