overview.txt
331 lines
| 14.6 KiB
| text/plain
|
TextLexer
Brian Granger
|
r2277 | .. _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: | ||||
Brian Granger
|
r2322 | * If the ``--ipython-dir`` command line flag is given, its value is used. | ||
Brian Granger
|
r2277 | |||
* If not, the value returned by :func:`IPython.utils.genutils.get_ipython_dir` | ||||
Brian Granger
|
r2322 | is used. This function will first look at the :envvar:`IPYTHON_DIR` | ||
Brian Granger
|
r2277 | environment variable and then default to the directory | ||
Brian Granger
|
r2322 | :file:`$HOME/.ipython`. | ||
Brian Granger
|
r2277 | |||
For most users, the default value will simply be something like | ||||
Brian Granger
|
r2322 | :file:`$HOME/.ipython`. | ||
Brian Granger
|
r2277 | |||
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. | ||||