##// END OF EJS Templates
Added diagnostics printout at the end of the test suite....
Added diagnostics printout at the end of the test suite. This will make it easier for us to understand problem reports from users.

File last commit:

r2322:65ca6e29
r2496:f440a2cd
Show More
overview.txt
331 lines | 14.6 KiB | text/plain | TextLexer
.. _config_overview:
============================================
Overview of the IPython configuration system
============================================
This section describes the IPython configuration system. Starting with version
0.11, IPython has a completely new configuration system that is quite
different from the older :file:`ipythonrc` or :file:`ipy_user_conf.py`
approaches. The new configuration system was designed from scratch to address
the particular configuration needs of IPython. While there are many
other excellent configuration systems out there, we found that none of them
met our requirements.
.. warning::
If you are upgrading to version 0.11 of IPython, you will need to migrate
your old :file:`ipythonrc` or :file:`ipy_user_conf.py` configuration files
to the new system. Read on for information on how to do this.
The discussion that follows is focused on teaching user's how to configure
IPython to their liking. Developer's who want to know more about how they
can enable their objects to take advantage of the configuration system
should consult our :ref:`developer guide <developer_guide>`
The main concepts
=================
There are a number of abstractions that the IPython configuration system uses.
Each of these abstractions is represented by a Python class.
Configuration object: :class:`~IPython.config.loader.Config`
A configuration object is a simple dictionary-like class that holds
configuration attributes and sub-configuration objects. These classes
support dotted attribute style access (``Foo.bar``) in addition to the
regular dictionary style access (``Foo['bar']``). Configuration objects
are smart. They know how to merge themselves with other configuration
objects and they automatically create sub-configuration objects.
Application: :class:`~IPython.core.application.Application`
An application is a process that does a specific job. The most obvious
application is the :command:`ipython` command line program. Each
application reads a *single* configuration file and command line options
and then produces a master configuration object for the application. This
configuration object is then passed to the components that the application
creates. Components implement the actual logic of the application and know
how to configure themselves given the configuration object.
Component: :class:`~IPython.core.component.Component`
A component is a regular Python class that serves as a base class for all
main classes in an application. The
:class:`~IPython.core.component.Component` base class is lightweight and
only does two main things.
First, it keeps track of all instances of itself and provides an
interfaces for querying those instances. This enables components to get
references to other components, even though they are not "nearby" in the
runtime object graph.
Second, it declares what class attributes are configurable and specifies
the default types and values of those attributes. This information is used
to automatically configure instances given the applications configuration
object.
Developers create :class:`~IPython.core.component.Component` subclasses
that implement all of the logic in the application. Each of these
subclasses has its own configuration information that controls how
instances are created.
Having described these main concepts, we can now state the main idea in our
configuration system: *"configuration" allows the default values of class
attributes to be controlled on a class by class basis*. Thus all instances of
a given class are configured in the same way. Furthermore, if two instances
need to be configured differently, they need to be instances of two different
classes. While this model may seem a bit restrictive, we have found that it
expresses most things that need to be configured extremely well.
Now, we show what our configuration objects and files look like.
Configuration objects and files
===============================
A configuration file is simply a pure Python file that sets the attributes
of a global, pre-created configuration object. This configuration object is a
:class:`~IPython.config.loader.Config` instance. While in a configuration
file, to get a reference to this object, simply call the :func:`get_config`
function. We inject this function into the global namespace that the
configuration file is executed in.
Here is an example of a super simple configuration file that does nothing::
c = get_config()
Once you get a reference to the configuration object, you simply set
attributes on it. All you have to know is:
* The name of each attribute.
* The type of each attribute.
The answers to these two questions are provided by the various
:class:`~IPython.core.component.Component` subclasses that an application
uses. Let's look at how this would work for a simple component subclass::
# Sample component that can be configured.
from IPython.core.component import Component
from IPython.utils.traitlets import Int, Float, Str, Bool
class MyComponent(Component):
name = Str('defaultname', config=True)
ranking = Int(0, config=True)
value = Float(99.0)
# The rest of the class implementation would go here..
In this example, we see that :class:`MyComponent` has three attributes, two
of whom (``name``, ``ranking``) can be configured. All of the attributes
are given types and default values. If a :class:`MyComponent` is instantiated,
but not configured, these default values will be used. But let's see how
to configure this class in a configuration file::
# Sample config file
c = get_config()
c.MyComponent.name = 'coolname'
c.MyComponent.ranking = 10
After this configuration file is loaded, the values set in it will override
the class defaults anytime a :class:`MyComponent` is created. Furthermore,
these attributes will be type checked and validated anytime they are set.
This type checking is handled by the :mod:`IPython.utils.traitlets` module,
which provides the :class:`Str`, :class:`Int` and :class:`Float` types. In
addition to these traitlets, the :mod:`IPython.utils.traitlets` provides
traitlets for a number of other types.
.. note::
Underneath the hood, the :class:`Component` base class is a subclass of
:class:`IPython.utils.traitlets.HasTraitlets`. The
:mod:`IPython.utils.traitlets` module is a lightweight version of
:mod:`enthought.traits`. Our implementation is a pure Python subset
(mostly API compatible) of :mod:`enthought.traits` that does not have any
of the automatic GUI generation capabilities. Our plan is to achieve 100%
API compatibility to enable the actual :mod:`enthought.traits` to
eventually be used instead. Currently, we cannot use
:mod:`enthought.traits` as we are committed to the core of IPython being
pure Python.
It should be very clear at this point what the naming convention is for
configuration attributes::
c.ClassName.attribute_name = attribute_value
Here, ``ClassName`` is the name of the class whose configuration attribute you
want to set, ``attribute_name`` is the name of the attribute you want to set
and ``attribute_value`` the the value you want it to have. The ``ClassName``
attribute of ``c`` is not the actual class, but instead is another
:class:`~IPython.config.loader.Config` instance.
.. note::
The careful reader may wonder how the ``ClassName`` (``MyComponent`` in
the above example) attribute of the configuration object ``c`` gets
created. These attributes are created on the fly by the
:class:`~IPython.config.loader.Config` instance, using a simple naming
convention. Any attribute of a :class:`~IPython.config.loader.Config`
instance whose name begins with an uppercase character is assumed to be a
sub-configuration and a new empty :class:`~IPython.config.loader.Config`
instance is dynamically created for that attribute. This allows deeply
hierarchical information created easily (``c.Foo.Bar.value``) on the
fly.
Configuration files inheritance
===============================
Let's say you want to have different configuration files for various purposes.
Our configuration system makes it easy for one configuration file to inherit
the information in another configuration file. The :func:`load_subconfig`
command can be used in a configuration file for this purpose. Here is a simple
example that loads all of the values from the file :file:`base_config.py`::
# base_config.py
c = get_config()
c.MyComponent.name = 'coolname'
c.MyComponent.ranking = 100
into the configuration file :file:`main_config.py`::
# main_config.py
c = get_config()
# Load everything from base_config.py
load_subconfig('base_config.py')
# Now override one of the values
c.MyComponent.name = 'bettername'
In a situation like this the :func:`load_subconfig` makes sure that the
search path for sub-configuration files is inherited from that of the parent.
Thus, you can typically put the two in the same directory and everything will
just work.
Class based configuration inheritance
=====================================
There is another aspect of configuration where inheritance comes into play.
Sometimes, your classes will have an inheritance hierarchy that you want
to be reflected in the configuration system. Here is a simple example::
from IPython.core.component import Component
from IPython.utils.traitlets import Int, Float, Str, Bool
class Foo(Component):
name = Str('fooname', config=True)
value = Float(100.0, config=True)
class Bar(Foo):
name = Str('barname', config=True)
othervalue = Int(0, config=True)
Now, we can create a configuration file to configure instances of :class:`Foo`
and :class:`Bar`::
# config file
c = get_config()
c.Foo.name = 'bestname'
c.Bar.othervalue = 10
This class hierarchy and configuration file accomplishes the following:
* The default value for :attr:`Foo.name` and :attr:`Bar.name` will be
'bestname'. Because :class:`Bar` is a :class:`Foo` subclass it also
picks up the configuration information for :class:`Foo`.
* The default value for :attr:`Foo.value` and :attr:`Bar.value` will be
``100.0``, which is the value specified as the class default.
* The default value for :attr:`Bar.othervalue` will be 10 as set in the
configuration file. Because :class:`Foo` is the parent of :class:`Bar`
it doesn't know anything about the :attr:`othervalue` attribute.
Configuration file location
===========================
So where should you put your configuration files? By default, all IPython
applications look in the so called "IPython directory". The location of
this directory is determined by the following algorithm:
* If the ``--ipython-dir`` command line flag is given, its value is used.
* If not, the value returned by :func:`IPython.utils.genutils.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`.
For most users, the default value will simply be something like
:file:`$HOME/.ipython`.
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
each application has its own default configuration filename. The default named
used by the :command:`ipython` command line program is
:file:`ipython_config.py`. This value can be overriden by the ``-config_file``
command line flag. A sample :file:`ipython_config.py` file can be found
in :mod:`IPython.config.default.ipython_config.py`. Simple copy it to your
IPython directory to begin using it.
.. _Profiles:
Profiles
========
A profile is simply a configuration file that follows a simple naming
convention and can be loaded using a simplified syntax. The idea is
that users often want to maintain a set of configuration files for different
purposes: one for doing numerical computing with NumPy and SciPy and
another for doing symbolic computing with SymPy. Profiles make it easy
to keep a separate configuration file for each of these purposes.
Let's start by showing how a profile is used:
.. code-block:: bash
$ ipython -p sympy
This tells the :command:`ipython` command line program to get its
configuration from the "sympy" profile. The search path for profiles is the
same as that of regular configuration files. The only difference is that
profiles are named in a special way. In the case above, the "sympy" profile
would need to have the name :file:`ipython_config_sympy.py`.
The general pattern is this: simply add ``_profilename`` to the end of the
normal configuration file name. Then load the profile by adding ``-p
profilename`` to your command line options.
IPython ships with some sample profiles in :mod:`IPython.config.profile`.
Simply copy these to your IPython directory to begin using them.
Design requirements
===================
Here are the main requirements we wanted our configuration system to have:
* Support for hierarchical configuration information.
* Full integration with command line option parsers. Often, you want to read
a configuration file, but then override some of the values with command line
options. Our configuration system automates this process and allows each
command line option to be linked to a particular attribute in the
configuration hierarchy that it will override.
* Configuration files that are themselves valid Python code. This accomplishes
many things. First, it becomes possible to put logic in your configuration
files that sets attributes based on your operating system, network setup,
Python version, etc. Second, Python has a super simple syntax for accessing
hierarchical data structures, namely regular attribute access
(``Foo.Bar.Bam.name``). Third, using Python makes it easy for users to
import configuration attributes from one configuration file to another.
Forth, even though Python is dynamically typed, it does have types that can
be checked at runtime. Thus, a ``1`` in a config file is the integer '1',
while a ``'1'`` is a string.
* A fully automated method for getting the configuration information to the
classes that need it at runtime. Writing code that walks a configuration
hierarchy to extract a particular attribute is painful. When you have
complex configuration information with hundreds of attributes, this makes
you want to cry.
* Type checking and validation that doesn't require the entire configuration
hierarchy to be specified statically before runtime. Python is a very
dynamic language and you don't always know everything that needs to be
configured when a program starts.