##// END OF EJS Templates
Cleaned up embedded shell and added cleanup method to InteractiveShell....
Cleaned up embedded shell and added cleanup method to InteractiveShell. * Added explicit code in InteractiveShell to make sure it cleans itself up. This is now done by the single method .cleanup, that can be called to have InteractiveShell cleanup. We can't use __del__ because of the many cycles in our object graph. * The embedded shell is refactored to no embedding logic is in the base class. Thus, InteractiveShell no longer takes an ``embedded`` argument. Just use InteractiveShellEmbed. * Created a super simple top-level :func:`embed` function that creates an InteractiveShellEmbed and calls it.

File last commit:

r2224:11a6689b merge
r2226:7053ea2c
Show More
component.py
233 lines | 7.7 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 copy import deepcopy
import datetime
from weakref import WeakValueDictionary
from IPython.utils.ipstruct import Struct
from IPython.utils.traitlets import (
HasTraitlets, TraitletError, MetaHasTraitlets, Instance, This
)
#-----------------------------------------------------------------------------
# Helper classes for Components
#-----------------------------------------------------------------------------
class ComponentError(Exception):
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)!!!
When 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, name=None, root=None):
"""Get all instances of cls and its subclasses.
Parameters
----------
name : str
Limit to components with this name.
root : Component or subclass
Limit to components having this root.
"""
instances = cls.__instance_refs.values()
if name is not None:
instances = [i for i in instances if i.name == name]
if root is not None:
instances = [i for i in instances if i.root == root]
return instances
def get_instances_by_condition(cls, call, name=None, root=None):
"""Get all instances of cls, i such that call(i)==True.
This also takes the ``name`` and ``root`` arguments of
:meth:`get_instance`
"""
return [i for i in cls.get_instances(name, root) 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
# Traitlets are fun!
config = Instance(Struct,(),{})
parent = This()
root = This()
created = None
def __init__(self, parent, name=None, config=None):
"""Create a component given a parent and possibly and name and config.
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 : Struct
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.
"""
super(Component, self).__init__()
self._children = []
if name is None:
self.name = ComponentNameGenerator()
else:
self.name = name
self.root = self # This is the default, it is set when parent is set
self.parent = parent
if config is not None:
self.config = deepcopy(config)
else:
if self.parent is not None:
self.config = deepcopy(self.parent.config)
self.created = datetime.datetime.now()
#-------------------------------------------------------------------------
# Static traitlet notifiations
#-------------------------------------------------------------------------
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)
if new is None:
self.root = self
else:
self.root = new.root
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")
def _config_changed(self, name, old, new):
"""Update all the class traits having a config_key with the config.
For any class traitlet with a ``config_key`` metadata attribute, we
update the traitlet with the value of the corresponding config entry.
In the future, we might want to do a pop here so stale config info
is not passed onto children.
"""
# Get all traitlets with a config_key metadata entry
traitlets = self.traitlets('config_key')
for k, v in traitlets.items():
try:
config_value = new[v.get_metadata('config_key')]
except KeyError:
pass
else:
setattr(self, k, config_value)
@property
def children(self):
"""A list of all my child components."""
return self._children
def _remove_child(self, child):
"""A private method for removing children components."""
if child in self._children:
index = self._children.index(child)
del self._children[index]
def _add_child(self, child):
"""A private method for adding children components."""
if child not in self._children:
self._children.append(child)
def __repr__(self):
return "<Component('%s')>" % self.name