##// END OF EJS Templates
Transformers have been added to the prefilter....
Transformers have been added to the prefilter. Transformers are run first before the checkers/handlers and simple transform lines of input. In the long run, we are going to move away from checkers/handlers and just use transformers. The new transformers are used to implement the a=!ls and b=%who syntax.

File last commit:

r2259:0ba23227
r2273:01effcc5
Show More
component.py
317 lines | 11.3 KiB | text/x-python | PythonLexer
Brian Granger
First prototype of component, traitlets and a config loader.
r2157 #!/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
#-----------------------------------------------------------------------------
Brian Granger
Improvement to how config is handled in Components....
r2184 from copy import deepcopy
Brian Granger
Continuing a massive refactor of everything.
r2205 import datetime
Brian Granger
First prototype of component, traitlets and a config loader.
r2157 from weakref import WeakValueDictionary
Brian Granger
A number of changes to how traitlets and components work....
r2229 from IPython.utils.importstring import import_item
Brian Granger
Massive refactoring of of the core....
r2245 from IPython.config.loader import Config
Brian Granger
First prototype of component, traitlets and a config loader.
r2157 from IPython.utils.traitlets import (
Brian Granger
Improvements to component.py....
r2180 HasTraitlets, TraitletError, MetaHasTraitlets, Instance, This
Brian Granger
First prototype of component, traitlets and a config loader.
r2157 )
#-----------------------------------------------------------------------------
# Helper classes for Components
#-----------------------------------------------------------------------------
Brian Granger
Fixing subtle bug in the traitlets with This....
r2183 class ComponentError(Exception):
pass
Brian Granger
First prototype of component, traitlets and a config loader.
r2157 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):
Brian Granger
More work on componentizing everything....
r2243 """Called when a class is called (instantiated)!!!
Brian Granger
First prototype of component, traitlets and a config loader.
r2157
Brian Granger
Improvements to component.py....
r2180 When a Component or subclass is instantiated, this is called and
Brian Granger
First prototype of component, traitlets and a config loader.
r2157 the instance is saved in a WeakValueDictionary for tracking.
"""
Brian Granger
Created context manager for the things injected into __builtin__.
r2227 instance = cls.__new__(cls, *args, **kw)
Brian Granger
More work on componentizing everything....
r2243
# Register the instance before __init__ is called so get_instances
# works inside __init__ methods!
indices = cls.register_instance(instance)
# This is in a try/except because of the __init__ method fails, the
# instance is discarded and shouldn't be tracked.
try:
if isinstance(instance, cls):
cls.__init__(instance, *args, **kw)
except:
# Unregister the instance because __init__ failed!
cls.unregister_instances(indices)
raise
else:
return instance
def register_instance(cls, instance):
"""Register instance with cls and its subclasses."""
# indices is a list of the keys used to register the instance
# with. This list is needed if the instance needs to be unregistered.
indices = []
Brian Granger
First prototype of component, traitlets and a config loader.
r2157 for c in cls.__mro__:
if issubclass(cls, c) and issubclass(c, Component):
c.__numcreated += 1
Brian Granger
More work on componentizing everything....
r2243 indices.append(c.__numcreated)
Brian Granger
First prototype of component, traitlets and a config loader.
r2157 c.__instance_refs[c.__numcreated] = instance
Brian Granger
More work on componentizing everything....
r2243 else:
break
return indices
def unregister_instances(cls, indices):
"""Unregister instance with cls and its subclasses."""
for c, index in zip(cls.__mro__, indices):
try:
del c.__instance_refs[index]
except KeyError:
pass
Brian Granger
Created context manager for the things injected into __builtin__.
r2227
Brian Granger
More work on componentizing everything....
r2243 def clear_instances(cls):
"""Clear all instances tracked by cls."""
cls.__instance_refs.clear()
cls.__numcreated = 0
Brian Granger
First prototype of component, traitlets and a config loader.
r2157
Brian Granger
A number of changes to how traitlets and components work....
r2229 def get_instances(cls, name=None, root=None, klass=None):
Brian Granger
Improvements to component.py....
r2180 """Get all instances of cls and its subclasses.
Brian Granger
First prototype of component, traitlets and a config loader.
r2157
Brian Granger
Improvements to component.py....
r2180 Parameters
----------
name : str
Limit to components with this name.
root : Component or subclass
Limit to components having this root.
Brian Granger
A number of changes to how traitlets and components work....
r2229 klass : class or str
Limits to instances of the class or its subclasses. If a str
is given ut must be in the form 'foo.bar.MyClass'. The str
form of this argument is useful for forward declarations.
Brian Granger
First prototype of component, traitlets and a config loader.
r2157 """
Brian Granger
A number of changes to how traitlets and components work....
r2229 if klass is not None:
if isinstance(klass, basestring):
klass = import_item(klass)
Brian Granger
More work on componentizing everything....
r2243 # Limit search to instances of klass for performance
if issubclass(klass, Component):
return klass.get_instances(name=name, root=root)
Brian Granger
Improvements to component.py....
r2180 instances = cls.__instance_refs.values()
if name is not None:
instances = [i for i in instances if i.name == name]
Brian Granger
A number of changes to how traitlets and components work....
r2229 if klass is not None:
instances = [i for i in instances if isinstance(i, klass)]
Brian Granger
Improvements to component.py....
r2180 if root is not None:
instances = [i for i in instances if i.root == root]
return instances
Brian Granger
Created context manager for the things injected into __builtin__.
r2227 def get_instances_by_condition(cls, call, name=None, root=None,
Brian Granger
A number of changes to how traitlets and components work....
r2229 klass=None):
Brian Granger
Improvements to component.py....
r2180 """Get all instances of cls, i such that call(i)==True.
Brian Granger
Created context manager for the things injected into __builtin__.
r2227 This also takes the ``name`` and ``root`` and ``classname``
arguments of :meth:`get_instance`
Brian Granger
First prototype of component, traitlets and a config loader.
r2157 """
Brian Granger
A number of changes to how traitlets and components work....
r2229 return [i for i in cls.get_instances(name, root, klass) if call(i)]
Brian Granger
First prototype of component, traitlets and a config loader.
r2157
Brian Granger
More work on componentizing everything....
r2243 def masquerade_as(instance, cls):
"""Let instance masquerade as an instance of cls.
Sometimes, such as in testing code, it is useful to let a class
masquerade as another. Python, being duck typed, allows this by
default. But, instances of components are tracked by their class type.
After calling this, cls.get_instances() will return ``instance``. This
does not, however, cause isinstance(instance, cls) to return ``True``.
Parameters
----------
instance : an instance of a Component or Component subclass
The instance that will pretend to be a cls.
cls : subclass of Component
The Component subclass that instance will pretend to be.
"""
cls.register_instance(instance)
Brian Granger
First prototype of component, traitlets and a config loader.
r2157 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
Brian Granger
Improvements to component.py....
r2180 # Traitlets are fun!
Brian Granger
Massive refactoring of of the core....
r2245 config = Instance(Config,(),{})
Brian Granger
Fixing subtle bug in the traitlets with This....
r2183 parent = This()
root = This()
Brian Granger
Continuing a massive refactor of everything.
r2205 created = None
Brian Granger
First prototype of component, traitlets and a config loader.
r2157
def __init__(self, parent, name=None, config=None):
Brian Granger
Fixing subtle bug in the traitlets with This....
r2183 """Create a component given a parent and possibly and name and config.
Brian Granger
First prototype of component, traitlets and a config loader.
r2157
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.
Brian Granger
Massive refactoring of of the core....
r2245 config : Config
Brian Granger
Fixing subtle bug in the traitlets with This....
r2183 If this is empty, self.config = parent.config, otherwise
self.config = config and root.config is ignored. This argument
should only be used to *override* the automatic inheritance of
parent.config. If a caller wants to modify parent.config
(not override), the caller should make a copy and change
attributes and then pass the copy to this argument.
Notes
-----
Subclasses of Component must call the :meth:`__init__` method of
:class:`Component` *before* doing anything else and using
:func:`super`::
class MyComponent(Component):
def __init__(self, parent, name=None, config=None):
super(MyComponent, self).__init__(parent, name, config)
# Then any other code you need to finish initialization.
This ensures that the :attr:`parent`, :attr:`name` and :attr:`config`
attributes are handled properly.
Brian Granger
First prototype of component, traitlets and a config loader.
r2157 """
super(Component, self).__init__()
Brian Granger
Improvements to component.py....
r2180 self._children = []
Brian Granger
First prototype of component, traitlets and a config loader.
r2157 if name is None:
Brian Granger
Improvements to component.py....
r2180 self.name = ComponentNameGenerator()
Brian Granger
First prototype of component, traitlets and a config loader.
r2157 else:
Brian Granger
Improvements to component.py....
r2180 self.name = name
self.root = self # This is the default, it is set when parent is set
Brian Granger
Massive refactoring of of the core....
r2245 self.parent = parent
Brian Granger
First prototype of component, traitlets and a config loader.
r2157 if config is not None:
Brian Granger
Improvement to how config is handled in Components....
r2184 self.config = deepcopy(config)
Brian Granger
First prototype of component, traitlets and a config loader.
r2157 else:
if self.parent is not None:
Brian Granger
Improvement to how config is handled in Components....
r2184 self.config = deepcopy(self.parent.config)
Brian Granger
First prototype of component, traitlets and a config loader.
r2157
Brian Granger
Continuing a massive refactor of everything.
r2205 self.created = datetime.datetime.now()
Brian Granger
First prototype of component, traitlets and a config loader.
r2157 #-------------------------------------------------------------------------
Brian Granger
Improvements to component.py....
r2180 # Static traitlet notifiations
Brian Granger
First prototype of component, traitlets and a config loader.
r2157 #-------------------------------------------------------------------------
Brian Granger
Improvements to component.py....
r2180 def _parent_changed(self, name, old, new):
if old is not None:
old._remove_child(self)
if new is not None:
new._add_child(self)
Brian Granger
First prototype of component, traitlets and a config loader.
r2157
Brian Granger
Improvements to component.py....
r2180 if new is None:
self.root = self
Brian Granger
First prototype of component, traitlets and a config loader.
r2157 else:
Brian Granger
Improvements to component.py....
r2180 self.root = new.root
Brian Granger
First prototype of component, traitlets and a config loader.
r2157
Brian Granger
Fixing subtle bug in the traitlets with This....
r2183 def _root_changed(self, name, old, new):
if self.parent is None:
if not (new is self):
raise ComponentError("Root not self, but parent is None.")
else:
if not self.parent.root is new:
raise ComponentError("Error in setting the root attribute: "
"root != parent.root")
Brian Granger
Improvement to how config is handled in Components....
r2184
def _config_changed(self, name, old, new):
Brian Granger
Components now auto-load the configs of parent classes as well.
r2259 """Update all the class traits having ``config=True`` as metadata.
Brian Granger
Minor doc updates.
r2222
Brian Granger
Components now auto-load the configs of parent classes as well.
r2259 For any class traitlet with a ``config`` metadata attribute that is
``True``, we update the traitlet with the value of the corresponding
config entry.
Brian Granger
Minor doc updates.
r2222 """
Brian Granger
Massive refactoring of of the core....
r2245 # Get all traitlets with a config metadata entry that is True
traitlets = self.traitlets(config=True)
Brian Granger
Components now auto-load the configs of parent classes as well.
r2259 # We auto-load config section for this class as well as any parent
# classes that are Component subclasses. This starts with Component
# 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, Component) 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 traitlets.items():
try:
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)
setattr(self, k, config_value)
Brian Granger
Improvement to how config is handled in Components....
r2184
Brian Granger
First prototype of component, traitlets and a config loader.
r2157 @property
Brian Granger
Improvements to component.py....
r2180 def children(self):
"""A list of all my child components."""
return self._children
def _remove_child(self, child):
Brian Granger
Fixing minor typo.
r2219 """A private method for removing children components."""
Brian Granger
Improvements to component.py....
r2180 if child in self._children:
index = self._children.index(child)
del self._children[index]
def _add_child(self, child):
Brian Granger
Fixing minor typo.
r2219 """A private method for adding children components."""
Brian Granger
Improvements to component.py....
r2180 if child not in self._children:
self._children.append(child)
def __repr__(self):
Brian Granger
More work on refactoring things into components....
r2244 return "<%s('%s')>" % (self.__class__.__name__, self.name)