##// END OF EJS Templates
* Implemented a proper main() function for kernel.py that reads command line input....
* Implemented a proper main() function for kernel.py that reads command line input. * Added options to launch_kernel() for specifying particular ports. * Wrote preliminary implementation for the KernelManager's start_kernel() and kill_kernel() methods * Updated IPythonWidget test script to reflect new functionality.

File last commit:

r2537:535d8779 merge
r2667:3547b230
Show More
component.py
346 lines | 13.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 (
Dav Clark
Merging Dav's traitlets rename branch.
r2537 HasTraits, MetaHasTraits, 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.
Brian Granger
Major work on the documentation....
r2277 After calling this, ``cls.get_instances()`` will return ``instance``. This
does not, however, cause ``isinstance(instance, cls)`` to return ``True``.
Brian Granger
More work on componentizing everything....
r2243
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)
Fernando Perez
Small fixes so the docs build....
r2404 class __ComponentNameGenerator(object):
Brian Granger
First prototype of component, traitlets and a config loader.
r2157 """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)
Fernando Perez
Small fixes so the docs build....
r2404 ComponentNameGenerator = __ComponentNameGenerator('ipython.component')
Brian Granger
First prototype of component, traitlets and a config loader.
r2157
Dav Clark
Iniital stab at renames for traitlets
r2384 class MetaComponent(MetaHasTraits, MetaComponentTracker):
Brian Granger
First prototype of component, traitlets and a config loader.
r2157 pass
#-----------------------------------------------------------------------------
# Component implementation
#-----------------------------------------------------------------------------
Dav Clark
Iniital stab at renames for traitlets
r2384 class Component(HasTraits):
Brian Granger
First prototype of component, traitlets and a config loader.
r2157
__metaclass__ = MetaComponent
Dav Clark
Final changes traitlet -> trait for review
r2385 # Traits 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
Changed Component.__init__ so that config is not deepcopy'd....
r2274 self.config = config
# We used to deepcopy, but for now we are trying to just save
# by reference. This *could* have side effects as all components
bgranger
Work on examples and fixed a super subtle bug in Component....
r2337 # 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.
Brian Granger
Changed Component.__init__ so that config is not deepcopy'd....
r2274 # 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
Changed Component.__init__ so that config is not deepcopy'd....
r2274 self.config = self.parent.config
# We used to deepcopy, but for now we are trying to just save
# by reference. This *could* have side effects as all components
bgranger
Work on examples and fixed a super subtle bug in Component....
r2337 # 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.
Brian Granger
Changed Component.__init__ so that config is not deepcopy'd....
r2274 # 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 #-------------------------------------------------------------------------
Dav Clark
Final changes traitlet -> trait for review
r2385 # Static trait 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
Dav Clark
Final changes traitlet -> trait for review
r2385 For any class trait with a ``config`` metadata attribute that is
``True``, we update the trait with the value of the corresponding
Brian Granger
Components now auto-load the configs of parent classes as well.
r2259 config entry.
Brian Granger
Minor doc updates.
r2222 """
Dav Clark
Final changes traitlet -> trait for review
r2385 # Get all traits with a config metadata entry that is True
traits = self.traits(config=True)
Brian Granger
Massive refactoring of of the core....
r2245
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]
Dav Clark
Final changes traitlet -> trait for review
r2385 for k, v in traits.items():
Brian Granger
Finished refactoring ipcontroller to be a proper application....
r2297 # 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 ComponentError('Component traitlets with '
'config=True must start with a lowercase so they are '
'not confused with Config subsections: %s.%s' % \
(self.__class__.__name__, k))
Brian Granger
Components now auto-load the configs of parent classes as well.
r2259 try:
Brian Granger
Finished refactoring ipcontroller to be a proper application....
r2297 # Here we grab the value from the config
# If k has the naming convention of a config
# section, it will be auto created.
Brian Granger
Components now auto-load the configs of parent classes as well.
r2259 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)
bgranger
Work on examples and fixed a super subtle bug in Component....
r2337 # 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))
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)