|
|
#!/usr/bin/env python
|
|
|
# encoding: utf-8
|
|
|
"""
|
|
|
A base class for objects that are configurable.
|
|
|
|
|
|
Authors:
|
|
|
|
|
|
* Brian Granger
|
|
|
* Fernando Perez
|
|
|
"""
|
|
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
# Copyright (C) 2008-2010 The IPython Development Team
|
|
|
#
|
|
|
# Distributed under the terms of the BSD License. The full license is in
|
|
|
# the file COPYING, distributed as part of this software.
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
# Imports
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
|
|
from copy import deepcopy
|
|
|
import datetime
|
|
|
from weakref import WeakValueDictionary
|
|
|
|
|
|
from IPython.utils.importstring import import_item
|
|
|
from loader import Config
|
|
|
from IPython.utils.traitlets import HasTraits, Instance
|
|
|
|
|
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
# Helper classes for Configurables
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
class ConfigurableError(Exception):
|
|
|
pass
|
|
|
|
|
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
# Configurable implementation
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
class Configurable(HasTraits):
|
|
|
|
|
|
config = Instance(Config,(),{})
|
|
|
created = None
|
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
"""Create a conigurable given a config config.
|
|
|
|
|
|
Parameters
|
|
|
----------
|
|
|
config : Config
|
|
|
If this is empty, default values are used. If config is a
|
|
|
:class:`Config` instance, it will be used to configure the
|
|
|
instance.
|
|
|
|
|
|
Notes
|
|
|
-----
|
|
|
Subclasses of Configurable must call the :meth:`__init__` method of
|
|
|
:class:`Configurable` *before* doing anything else and using
|
|
|
:func:`super`::
|
|
|
|
|
|
class MyConfigurable(Configurable):
|
|
|
def __init__(self, config=None):
|
|
|
super(MyConfigurable, self).__init__(config)
|
|
|
# Then any other code you need to finish initialization.
|
|
|
|
|
|
This ensures that instances will be configured properly.
|
|
|
"""
|
|
|
config = kwargs.pop('config', None)
|
|
|
if config is not None:
|
|
|
# We used to deepcopy, but for now we are trying to just save
|
|
|
# by reference. This *could* have side effects as all components
|
|
|
# will share config. In fact, I did find such a side effect in
|
|
|
# _config_changed below. If a config attribute value was a mutable type
|
|
|
# all instances of a component were getting the same copy, effectively
|
|
|
# making that a class attribute.
|
|
|
# self.config = deepcopy(config)
|
|
|
self.config = config
|
|
|
# This should go second so individual keyword arguments override
|
|
|
# the values in config.
|
|
|
super(Configurable, self).__init__(**kwargs)
|
|
|
self.created = datetime.datetime.now()
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
|
# Static trait notifiations
|
|
|
#-------------------------------------------------------------------------
|
|
|
|
|
|
def _config_changed(self, name, old, new):
|
|
|
"""Update all the class traits having ``config=True`` as metadata.
|
|
|
|
|
|
For any class trait with a ``config`` metadata attribute that is
|
|
|
``True``, we update the trait with the value of the corresponding
|
|
|
config entry.
|
|
|
"""
|
|
|
# Get all traits with a config metadata entry that is True
|
|
|
traits = self.traits(config=True)
|
|
|
|
|
|
# We auto-load config section for this class as well as any parent
|
|
|
# classes that are Configurable subclasses. This starts with Configurable
|
|
|
# and works down the mro loading the config for each section.
|
|
|
section_names = [cls.__name__ for cls in \
|
|
|
reversed(self.__class__.__mro__) if
|
|
|
issubclass(cls, Configurable) and issubclass(self.__class__, cls)]
|
|
|
|
|
|
for sname in section_names:
|
|
|
# Don't do a blind getattr as that would cause the config to
|
|
|
# dynamically create the section with name self.__class__.__name__.
|
|
|
if new._has_section(sname):
|
|
|
my_config = new[sname]
|
|
|
for k, v in traits.items():
|
|
|
# Don't allow traitlets with config=True to start with
|
|
|
# uppercase. Otherwise, they are confused with Config
|
|
|
# subsections. But, developers shouldn't have uppercase
|
|
|
# attributes anyways! (PEP 6)
|
|
|
if k[0].upper()==k[0] and not k.startswith('_'):
|
|
|
raise ConfigurableError('Configurable traitlets with '
|
|
|
'config=True must start with a lowercase so they are '
|
|
|
'not confused with Config subsections: %s.%s' % \
|
|
|
(self.__class__.__name__, k))
|
|
|
try:
|
|
|
# Here we grab the value from the config
|
|
|
# If k has the naming convention of a config
|
|
|
# section, it will be auto created.
|
|
|
config_value = my_config[k]
|
|
|
except KeyError:
|
|
|
pass
|
|
|
else:
|
|
|
# print "Setting %s.%s from %s.%s=%r" % \
|
|
|
# (self.__class__.__name__,k,sname,k,config_value)
|
|
|
# We have to do a deepcopy here if we don't deepcopy the entire
|
|
|
# config object. If we don't, a mutable config_value will be
|
|
|
# shared by all instances, effectively making it a class attribute.
|
|
|
setattr(self, k, deepcopy(config_value))
|
|
|
|
|
|
|