Skip to content

Commit

Permalink
Merge 'XDG', introducing basic XDG_CONFIG_HOME support
Browse files Browse the repository at this point in the history
  • Loading branch information
minrk committed Feb 23, 2011
2 parents 49dbd35 + facbe2a commit 370e5c6
Show file tree
Hide file tree
Showing 18 changed files with 183 additions and 43 deletions.
2 changes: 1 addition & 1 deletion IPython/core/application.py
Expand Up @@ -105,7 +105,7 @@ class Application(object):
default_log_level = logging.WARN
#: Set by --profile option
profile_name = None
#: User's ipython directory, typically ~/.ipython/
#: User's ipython directory, typically ~/.ipython or ~/.config/ipython/
ipython_dir = None
#: Internal defaults, implemented in code.
default_config = None
Expand Down
2 changes: 1 addition & 1 deletion IPython/core/magic.py
Expand Up @@ -395,7 +395,7 @@ def magic_magic(self, parameter_s = ''):
You can define your own magic functions to extend the system. See the supplied
ipythonrc and example-magic.py files for details (in your ipython
configuration directory, typically $HOME/.ipython/).
configuration directory, typically $HOME/.config/ipython on Linux or $HOME/.ipython elsewhere).
You can also define your own aliased names for magic functions. In your
ipythonrc file, placing a line like:
Expand Down
7 changes: 4 additions & 3 deletions IPython/core/usage.py
Expand Up @@ -30,9 +30,10 @@
command line, simply because they are not practical here. Look into your
ipython_config.py configuration file for details on those.
This file typically installed in the $HOME/.ipython directory. For Windows
users, $HOME resolves to C:\\Documents and Settings\\YourUserName in most
instances.
This file is typically installed in the IPYTHON_DIR directory. For Linux
users, this will be $HOME/.config/ipython, and for other users it will be
$HOME/.ipython. For Windows users, $HOME resolves to C:\\Documents and
Settings\\YourUserName in most instances.
In IPython's documentation, we will refer to this directory as IPYTHON_DIR,
you can change its default location by setting any path you want in this
Expand Down
2 changes: 1 addition & 1 deletion IPython/kernel/controllerservice.py
Expand Up @@ -186,7 +186,7 @@ def _logEngineInfoToFile(self, id, ip, port, pid):
This method takes the assigned id, ip/port and pid of the engine
and saves it to a file of the form:
~/.ipython/log/ipcontroller-###-engine-info.log
IPYTHON_DIR/log/ipcontroller-###-engine-info.log
where ### is the pid of the controller.
Expand Down
2 changes: 1 addition & 1 deletion IPython/kernel/ipcontrollerapp.py
Expand Up @@ -49,7 +49,7 @@
clients. The controller needs to be started before the engines and can be
configured using command line options or using a cluster directory. Cluster
directories contain config, log and security files and are usually located in
your .ipython directory and named as "cluster_<profile>". See the --profile
your ipython directory and named as "cluster_<profile>". See the --profile
and --cluster-dir options for details.
"""

Expand Down
2 changes: 1 addition & 1 deletion IPython/kernel/ipengineapp.py
Expand Up @@ -60,7 +60,7 @@ class SimpleStruct:
and controller. A controller needs to be started before the engines. The
engine can be configured using command line options or using a cluster
directory. Cluster directories contain config, log and security files and are
usually located in your .ipython directory and named as "cluster_<profile>".
usually located in your ipython directory and named as "cluster_<profile>".
See the --profile and --cluster-dir options for details.
"""

Expand Down
48 changes: 43 additions & 5 deletions IPython/utils/path.py
Expand Up @@ -246,21 +246,59 @@ def get_home_dir():
else:
raise HomeDirError('No valid home directory could be found for your OS')

def get_xdg_dir():
"""Return the XDG_CONFIG_HOME, if it is defined and exists, else None.
This is only for posix (Linux,Unix,OS X, etc) systems.
"""

isdir = os.path.isdir
env = os.environ

if os.name == 'posix':
# Linux, Unix, AIX, OS X
# use ~/.config if not set OR empty
xdg = env.get("XDG_CONFIG_HOME", None) or os.path.join(get_home_dir(), '.config')
if xdg and isdir(xdg):
return xdg.decode(sys.getfilesystemencoding())

return None


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 the adds .ipython to the end of the path.
"""

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

ipdir_def = '.ipython'
xdg_def = 'ipython'

home_dir = get_home_dir()
xdg_dir = get_xdg_dir()
# import pdb; pdb.set_trace() # dbg
ipdir = os.environ.get(
'IPYTHON_DIR', os.environ.get(
'IPYTHONDIR', os.path.join(home_dir, ipdir_def)
)
)
ipdir = env.get('IPYTHON_DIR', env.get('IPYTHONDIR', None))
if ipdir is None:
# not set explicitly, use XDG_CONFIG_HOME or HOME
home_ipdir = pjoin(home_dir, ipdir_def)
if xdg_dir:
# use XDG, as long as the user isn't already
# using $HOME/.ipython and *not* XDG/ipython

xdg_ipdir = pjoin(xdg_dir, xdg_def)

if exists(xdg_ipdir) or not exists(home_ipdir):
ipdir = xdg_ipdir

if ipdir is None:
# not using XDG
ipdir = home_ipdir

return ipdir.decode(sys.getfilesystemencoding())


Expand Down
86 changes: 86 additions & 0 deletions IPython/utils/tests/test_path.py
Expand Up @@ -46,6 +46,7 @@
TEST_FILE_PATH = split(abspath(__file__))[0]
TMP_TEST_DIR = tempfile.mkdtemp()
HOME_TEST_DIR = join(TMP_TEST_DIR, "home_test_dir")
XDG_TEST_DIR = join(HOME_TEST_DIR, "xdg_test_dir")
IP_TEST_DIR = join(HOME_TEST_DIR,'.ipython')
#
# Setup/teardown functions/decorators
Expand All @@ -59,6 +60,7 @@ def setup():
# 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'))


def teardown():
Expand Down Expand Up @@ -236,9 +238,93 @@ def test_get_ipython_dir_2():
os.name = "posix"
env.pop('IPYTHON_DIR', None)
env.pop('IPYTHONDIR', None)
env.pop('XDG_CONFIG_HOME', None)
ipdir = path.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, use XDG if defined, and .ipython doesn't exist."""
path.get_home_dir = lambda : "someplace"
os.name = "posix"
env.pop('IPYTHON_DIR', None)
env.pop('IPYTHONDIR', None)
env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
ipdir = path.get_ipython_dir()
nt.assert_equal(ipdir, os.path.join(XDG_TEST_DIR, "ipython"))

@with_environment
def test_get_ipython_dir_4():
"""test_get_ipython_dir_4, use XDG if both exist."""
path.get_home_dir = lambda : HOME_TEST_DIR
os.name = "posix"
env.pop('IPYTHON_DIR', None)
env.pop('IPYTHONDIR', None)
env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
xdg_ipdir = os.path.join(XDG_TEST_DIR, "ipython")
ipdir = path.get_ipython_dir()
nt.assert_equal(ipdir, xdg_ipdir)

@with_environment
def test_get_ipython_dir_5():
"""test_get_ipython_dir_5, use .ipython if exists and XDG defined, but doesn't exist."""
os.name = "posix"
env.pop('IPYTHON_DIR', None)
env.pop('IPYTHONDIR', None)
env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
os.rmdir(os.path.join(XDG_TEST_DIR, 'ipython'))
ipdir = path.get_ipython_dir()
nt.assert_equal(ipdir, IP_TEST_DIR)

@with_environment
def test_get_ipython_dir_6():
"""test_get_ipython_dir_6, use XDG if defined and neither exist."""
path.get_home_dir = lambda : 'somehome'
path.get_xdg_dir = lambda : 'somexdg'
os.name = "posix"
env.pop('IPYTHON_DIR', None)
env.pop('IPYTHONDIR', None)
xdg_ipdir = os.path.join("somexdg", "ipython")
ipdir = path.get_ipython_dir()
nt.assert_equal(ipdir, xdg_ipdir)

@with_environment
def test_get_xdg_dir_1():
"""test_get_xdg_dir_1, check xdg_dir"""
reload(path)
path.get_home_dir = lambda : 'somewhere'
os.name = "posix"
env.pop('IPYTHON_DIR', None)
env.pop('IPYTHONDIR', None)
env.pop('XDG_CONFIG_HOME', None)

nt.assert_equal(path.get_xdg_dir(), os.path.join('somewhere', '.config'))


@with_environment
def test_get_xdg_dir_1():
"""test_get_xdg_dir_1, check nonexistant xdg_dir"""
reload(path)
path.get_home_dir = lambda : HOME_TEST_DIR
os.name = "posix"
env.pop('IPYTHON_DIR', None)
env.pop('IPYTHONDIR', None)
env.pop('XDG_CONFIG_HOME', None)
nt.assert_equal(path.get_xdg_dir(), None)

@with_environment
def test_get_xdg_dir_2():
"""test_get_xdg_dir_2, check xdg_dir default to ~/.config"""
reload(path)
path.get_home_dir = lambda : HOME_TEST_DIR
os.name = "posix"
env.pop('IPYTHON_DIR', None)
env.pop('IPYTHONDIR', None)
env.pop('XDG_CONFIG_HOME', None)
cfgdir=os.path.join(path.get_home_dir(), '.config')
os.makedirs(cfgdir)

nt.assert_equal(path.get_xdg_dir(), cfgdir)

def test_filefind():
"""Various tests for filefind"""
Expand Down
5 changes: 3 additions & 2 deletions docs/source/config/old.txt
Expand Up @@ -12,7 +12,8 @@ Outdated configuration information that might still be useful
This section will help you set various things in your environment for
your IPython sessions to be as efficient as possible. All of IPython's
configuration information, along with several example files, is stored
in a directory named by default $HOME/.ipython. You can change this by
in a directory named by default $HOME/.config/ipython if $HOME/.config
exists (Linux), or $HOME/.ipython as a secondary default. You can change this by
defining the environment variable IPYTHONDIR, or at runtime with the
command line option -ipythondir.

Expand Down Expand Up @@ -94,7 +95,7 @@ sequences. You can go to a 'no color' mode by typing '%colors NoColor'.

You can try using a different terminal emulator program (Emacs users,
see below). To permanently set your color preferences, edit the file
$HOME/.ipython/ipythonrc and set the colors option to the desired value.
$IPYTHON_DIR/ipythonrc and set the colors option to the desired value.


Object details (types, docstrings, source code, etc.)
Expand Down
19 changes: 16 additions & 3 deletions docs/source/config/overview.txt
Expand Up @@ -235,6 +235,9 @@ This class hierarchy and configuration file accomplishes the following:
configuration file. Because :class:`Foo` is the parent of :class:`Bar`
it doesn't know anything about the :attr:`othervalue` attribute.


.. _ipython_dir:

Configuration file location
===========================

Expand All @@ -246,11 +249,19 @@ this directory is determined by the following algorithm:

* If not, the value returned by :func:`IPython.utils.path.get_ipython_dir`
is used. This function will first look at the :envvar:`IPYTHON_DIR`
environment variable and then default to the directory
:file:`$HOME/.ipython`.
environment variable and then default to a platform-specific default.

On posix systems (Linux, Unix, etc.), IPython respects the ``$XDG_CONFIG_HOME``
part of the `XDG Base Directory`_ specification. If ``$XDG_CONFIG_HOME`` is
defined and exists ( ``XDG_CONFIG_HOME`` has a default interpretation of
:file:`$HOME/.config`), then IPython's config directory will be located in
:file:`$XDG_CONFIG_HOME/ipython`. If users still have an IPython directory
in :file:`$HOME/.ipython`, then that will be used. in preference to the
system default.

For most users, the default value will simply be something like
:file:`$HOME/.ipython`.
:file:`$HOME/.config/ipython` on Linux, or :file:`$HOME/.ipython`
elsewhere.

Once the location of the IPython directory has been determined, you need to
know what filename to use for the configuration file. The basic idea is that
Expand Down Expand Up @@ -327,3 +338,5 @@ Here are the main requirements we wanted our configuration system to have:
dynamic language and you don't always know everything that needs to be
configured when a program starts.


.. _`XDG Base Directory`: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
2 changes: 1 addition & 1 deletion docs/source/development/doc_guide.txt
Expand Up @@ -51,7 +51,7 @@ An interactive Python session::

>>> from IPython.utils.path import get_ipython_dir
>>> get_ipython_dir()
'/home/fperez/.ipython'
'/home/fperez/.config/ipython'

An IPython session:

Expand Down
17 changes: 9 additions & 8 deletions docs/source/interactive/reference.txt
Expand Up @@ -27,11 +27,12 @@ file and ignore your configuration setup.

Please note that some of the configuration options are not available at
the command line, simply because they are not practical here. Look into
your ipythonrc configuration file for details on those. This file
typically installed in the $HOME/.ipython directory. For Windows users,
$HOME resolves to C:\\Documents and Settings\\YourUserName in most
instances. In the rest of this text, we will refer to this directory as
IPYTHON_DIR.
your ipythonrc configuration file for details on those. This file is typically
installed in the IPYTHON_DIR directory. For Linux
users, this will be $HOME/.config/ipython, and for other users it will be
$HOME/.ipython. For Windows users, $HOME resolves to C:\\Documents and
Settings\\YourUserName in most instances.




Expand Down Expand Up @@ -218,9 +219,9 @@ All options with a [no] prepended can be specified in negated form
include this one and load extra things for particular
tasks. For example:

1. $HOME/.ipython/ipythonrc : load basic things you always want.
2. $HOME/.ipython/ipythonrc-math : load (1) and basic math-related modules.
3. $HOME/.ipython/ipythonrc-numeric : load (1) and Numeric and plotting modules.
1. $IPYTHON_DIR/ipythonrc : load basic things you always want.
2. $IPYTHON_DIR/ipythonrc-math : load (1) and basic math-related modules.
3. $IPYTHON_DIR/ipythonrc-numeric : load (1) and Numeric and plotting modules.

Since it is possible to create an endless loop by having
circular file inclusions, IPython will stop if it reaches 15
Expand Down
2 changes: 1 addition & 1 deletion docs/source/interactive/shell.txt
Expand Up @@ -25,7 +25,7 @@ the "pysh" shortcut in start menu.
If you want to use the features of sh profile as your defaults (which
might be a good idea if you use other profiles a lot of the time but
still want the convenience of sh profile), add ``import ipy_profile_sh``
to your ~/.ipython/ipy_user_conf.py.
to your $IPYTHON_DIR/ipy_user_conf.py.

The 'sh' profile is different from the default profile in that:

Expand Down
6 changes: 3 additions & 3 deletions docs/source/parallel/parallel_intro.txt
Expand Up @@ -138,13 +138,13 @@ these keys are known as Foolscap URLs, or FURLs, because of the underlying
network protocol we are using. As a user, you don't need to know anything
about the details of these FURLs, other than that when the controller starts,
it saves a set of FURLs to files named :file:`something.furl`. The default
location of these files is the :file:`~./ipython/security` directory.
location of these files is the :file:`$IPYTHON_DIR/cluster_<profile>/security` directory.

To connect and authenticate to the controller an engine or client simply needs
to present an appropriate FURL (that was originally created by the controller)
to the controller. Thus, the FURL files need to be copied to a location where
the clients and engines can find them. Typically, this is the
:file:`~./ipython/security` directory on the host where the client/engine is
:file:`$IPYTHON_DIR/cluster_<profile>/security` directory on the host where the client/engine is
running (which could be a different host than the controller). Once the FURL
files are copied over, everything should work fine.

Expand Down Expand Up @@ -212,7 +212,7 @@ everything is working correctly, try the following commands:
Remember, a client also needs to present a FURL file to the controller. How
does this happen? When a multiengine client is created with no arguments, the
client tries to find the corresponding FURL file in the local
:file:`~./ipython/security` directory. If it finds it, you are set. If you
:file:`$IPYTHON_DIR/cluster_<profile>/security` directory. If it finds it, you are set. If you
have put the FURL file in a different location or it has a different name,
create the client like this::

Expand Down
2 changes: 1 addition & 1 deletion docs/source/parallel/parallel_multiengine.txt
Expand Up @@ -37,7 +37,7 @@ and then create a :class:`MultiEngineClient` instance:
In [2]: mec = client.MultiEngineClient()

This form assumes that the :file:`ipcontroller-mec.furl` is in the
:file:`~./ipython/security` directory on the client's host. If not, the
:file:`$IPYTHON_DIR/cluster_<profile>/security` directory on the client's host. If not, the
location of the FURL file must be given as an argument to the
constructor:

Expand Down

0 comments on commit 370e5c6

Please sign in to comment.