##// END OF EJS Templates
First prototype of component, traitlets and a config loader.
First prototype of component, traitlets and a config loader.

File last commit:

r2157:8bb438f1
r2157:8bb438f1
Show More
component.py
184 lines | 5.8 KiB | text/x-python | PythonLexer
#!/usr/bin/env python
# encoding: utf-8
"""
A lightweight component system for IPython.
Authors:
* Brian Granger
* Fernando Perez
"""
#-----------------------------------------------------------------------------
# Copyright (C) 2008-2009 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 weakref import WeakValueDictionary
from IPython.utils.traitlets import (
HasTraitlets, TraitletError, MetaHasTraitlets,
Int, Float, Str, Bool, Unicode
)
#-----------------------------------------------------------------------------
# Helper classes for Components
#-----------------------------------------------------------------------------
class Config(object):
pass
class MetaComponentTracker(type):
"""A metaclass that tracks instances of Components and its subclasses."""
def __init__(cls, name, bases, d):
super(MetaComponentTracker, cls).__init__(name, bases, d)
cls.__instance_refs = WeakValueDictionary()
cls.__numcreated = 0
def __call__(cls, *args, **kw):
"""Called when class is called (instantiated)!!!
Then a Component or subclass is instantiated, this is called and
the instance is saved in a WeakValueDictionary for tracking.
"""
instance = super(MetaComponentTracker, cls).__call__(*args, **kw)
for c in cls.__mro__:
if issubclass(cls, c) and issubclass(c, Component):
c.__numcreated += 1
c.__instance_refs[c.__numcreated] = instance
return instance
def get_instances(cls):
"""Get all instances of cls and its subclasses."""
return cls.__instance_refs.values()
def get_instances_by_name(cls, name):
"""Get all instances of cls and its subclasses by name."""
return [i for i in cls.get_instances() if i.name == name]
def get_instances_by_subclass(cls, thisclass):
"""Get all instances of cls that are instances of thisclass.
This includes all instances of subclasses of thisclass.
"""
return [i for i in cls.get_instances() if isinstance(i, thisclass)]
def get_instances_by_class(cls, thisclass):
"""Get all instances of cls that are instances of thisclass.
This exclused instances of thisclass subclasses.
"""
return [i for i in cls.get_instances() if type(i) is thisclass]
def get_instances_by_condition(cls, call):
"""Get all instances of cls, i such that call(i)==True."""
return [i for i in cls.get_instances() if call(i)]
class ComponentNameGenerator(object):
"""A Singleton to generate unique component names."""
def __init__(self, prefix):
self.prefix = prefix
self.i = 0
def __call__(self):
count = self.i
self.i += 1
return "%s%s" % (self.prefix, count)
ComponentNameGenerator = ComponentNameGenerator('ipython.component')
class MetaComponent(MetaHasTraitlets, MetaComponentTracker):
pass
#-----------------------------------------------------------------------------
# Component implementation
#-----------------------------------------------------------------------------
class Component(HasTraitlets):
__metaclass__ = MetaComponent
config = Config()
def __init__(self, parent, name=None, config=None):
"""Create a component given a parent.
Parameters
----------
parent : Component subclass
The parent in the component graph. The parent is used
to get the root of the component graph.
name : str
The unique name of the component. If empty, then a unique
one will be autogenerated.
config : Config
If this is empty, self.config = root.config, otherwise
self.config = config and root.config is ignored. This argument
should be used to pass the config to the root. Otherwise, it
can be used to *override* the inheritance of root.config. If a
caller wants to modify root.config (not override), the caller
should make a copy and change attributes and then pass the copy
to this argument. We might think about changing this behavior.
"""
super(Component, self).__init__()
if name is None:
self._name = ComponentNameGenerator()
else:
self._name = name
self.parent = parent # this uses the property and handles None
if config is not None:
self.config = config
else:
if self.parent is not None:
self.config = self.parent.config
#-------------------------------------------------------------------------
# Properties
#-------------------------------------------------------------------------
def _set_name(self, name):
# This should use the ComponentNameGenerator to test for uniqueness
self._name = name
def _get_name(self):
return self._name
name = property(_get_name, _set_name)
def _set_parent(self, parent):
if parent is None:
self._parent = None
self._root = self
else:
assert isinstance(parent, Component), 'parent must be a component'
self._parent = parent
self._root = parent.root
def _get_parent(self):
return self._parent
parent = property(_get_parent, _set_parent)
@property
def root(self):
return self._root