From 1e1fe080c798c2bb52ba1af98692b61cbe219bc2 2010-08-10 20:19:32 From: Brian Granger Date: 2010-08-10 20:19:32 Subject: [PATCH] First draft of refactored Component->Configurable. --- diff --git a/IPython/config/configurable.py b/IPython/config/configurable.py new file mode 100755 index 0000000..1756cab --- /dev/null +++ b/IPython/config/configurable.py @@ -0,0 +1,136 @@ +#!/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, config=None): + """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. + """ + super(Configurable, self).__init__() + 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 + 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)) + diff --git a/IPython/config/tests/test_configurable.py b/IPython/config/tests/test_configurable.py new file mode 100644 index 0000000..4e69820 --- /dev/null +++ b/IPython/config/tests/test_configurable.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +Tests for IPython.config.configurable + +Authors: + +* Brian Granger +* Fernando Perez (design help) +""" + +#----------------------------------------------------------------------------- +# 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 unittest import TestCase + +from IPython.config.configurable import Configurable, ConfigurableError +from IPython.utils.traitlets import ( + TraitError, Int, Float, Str +) +from IPython.config.loader import Config + + +#----------------------------------------------------------------------------- +# Test cases +#----------------------------------------------------------------------------- + + +class TestConfigurableConfig(TestCase): + + def test_default(self): + c1 = Configurable() + c2 = Configurable(config=c1.config) + c3 = Configurable(config=c2.config) + self.assertEquals(c1.config, c2.config) + self.assertEquals(c2.config, c3.config) + + def test_custom(self): + config = Config() + config.foo = 'foo' + config.bar = 'bar' + c1 = Configurable(config=config) + c2 = Configurable(c1.config) + c3 = Configurable(c2.config) + self.assertEquals(c1.config, config) + self.assertEquals(c2.config, config) + self.assertEquals(c3.config, config) + # Test that copies are not made + self.assert_(c1.config is config) + self.assert_(c2.config is config) + self.assert_(c3.config is config) + self.assert_(c1.config is c2.config) + self.assert_(c2.config is c3.config) + + def test_inheritance(self): + class MyConfigurable(Configurable): + a = Int(1, config=True) + b = Float(1.0, config=True) + c = Str('no config') + config = Config() + config.MyConfigurable.a = 2 + config.MyConfigurable.b = 2.0 + c1 = MyConfigurable(config=config) + c2 = MyConfigurable(c1.config) + self.assertEquals(c1.a, config.MyConfigurable.a) + self.assertEquals(c1.b, config.MyConfigurable.b) + self.assertEquals(c2.a, config.MyConfigurable.a) + self.assertEquals(c2.b, config.MyConfigurable.b) + + def test_parent(self): + class Foo(Configurable): + a = Int(0, config=True) + b = Str('nope', config=True) + class Bar(Foo): + b = Str('gotit', config=False) + c = Float(config=True) + config = Config() + config.Foo.a = 10 + config.Foo.b = "wow" + config.Bar.b = 'later' + config.Bar.c = 100.0 + f = Foo(config=config) + b = Bar(f.config) + self.assertEquals(f.a, 10) + self.assertEquals(f.b, 'wow') + self.assertEquals(b.b, 'gotit') + self.assertEquals(b.c, 100.0) diff --git a/IPython/config/tests/test_imports.py b/IPython/config/tests/test_imports.py deleted file mode 100644 index 84fb531..0000000 --- a/IPython/config/tests/test_imports.py +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 - - - diff --git a/IPython/core/alias.py b/IPython/core/alias.py index 1afe854..752e8f4 100644 --- a/IPython/core/alias.py +++ b/IPython/core/alias.py @@ -1,10 +1,11 @@ #!/usr/bin/env python # encoding: utf-8 """ -IPython's alias component +System command aliases. Authors: +* Fernando Perez * Brian Granger """ @@ -25,10 +26,10 @@ import os import re import sys -from IPython.core.component import Component +from IPython.config.configurable import Configurable from IPython.core.splitinput import split_user_input -from IPython.utils.traitlets import List +from IPython.utils.traitlets import List, Instance from IPython.utils.autoattr import auto_attr from IPython.utils.warn import warn, error @@ -99,22 +100,18 @@ class InvalidAliasError(AliasError): #----------------------------------------------------------------------------- -class AliasManager(Component): +class AliasManager(Configurable): default_aliases = List(default_aliases(), config=True) user_aliases = List(default_value=[], config=True) + shell = Instance('IPython.core.iplib.InteractiveShell') - def __init__(self, parent, config=None): - super(AliasManager, self).__init__(parent, config=config) + def __init__(self, shell, config=None): + super(AliasManager, self).__init__(config=config) self.alias_table = {} self.exclude_aliases() self.init_aliases() - - @auto_attr - def shell(self): - return Component.get_instances( - root=self.root, - klass='IPython.core.iplib.InteractiveShell')[0] + self.shell = shell def __contains__(self, name): if name in self.alias_table: diff --git a/IPython/core/application.py b/IPython/core/application.py index 7fdea83..b6c25c6 100644 --- a/IPython/core/application.py +++ b/IPython/core/application.py @@ -6,7 +6,7 @@ All top-level applications should use the classes in this module for handling configuration and creating componenets. The job of an :class:`Application` is to create the master configuration -object and then create the components, passing the config to them. +object and then create the configurable objects, passing the config to them. Authors: @@ -76,7 +76,7 @@ class BaseAppConfigLoader(ArgParseConfigLoader): class Application(object): - """Load a config, construct components and set them running. + """Load a config, construct configurables and set them running. The configuration of an application can be done via three different Config objects, which are loaded and ultimately merged into a single one used @@ -113,7 +113,7 @@ class Application(object): file_config = None #: Read from the system's command line flags. command_line_config = None - #: The final config that will be passed to the component. + #: The final config that will be passed to the main object. master_config = None #: A reference to the argv to be used (typically ends up being sys.argv[1:]) argv = None @@ -223,10 +223,10 @@ class Application(object): """Create defaults that can't be set elsewhere. For the most part, we try to set default in the class attributes - of Components. But, defaults the top-level Application (which is - not a HasTraits or Component) are not set in this way. Instead + of Configurables. But, defaults the top-level Application (which is + not a HasTraits or Configurables) are not set in this way. Instead we set them here. The Global section is for variables like this that - don't belong to a particular component. + don't belong to a particular configurable. """ c = Config() c.Global.ipython_dir = get_ipython_dir() @@ -418,8 +418,8 @@ class Application(object): pass def construct(self): - """Construct the main components that make up this app.""" - self.log.debug("Constructing components for application") + """Construct the main objects that make up this app.""" + self.log.debug("Constructing main objects for application") def post_construct(self): """Do actions after construct, but before starting the app.""" diff --git a/IPython/core/builtin_trap.py b/IPython/core/builtin_trap.py index 8021c5a..2f26496 100755 --- a/IPython/core/builtin_trap.py +++ b/IPython/core/builtin_trap.py @@ -21,10 +21,10 @@ Authors: import __builtin__ -from IPython.core.component import Component +from IPython.config.configurable import Configurable from IPython.core.quitter import Quitter -from IPython.utils.autoattr import auto_attr +from IPython.utils.traitlets import Instance #----------------------------------------------------------------------------- # Classes and functions @@ -35,20 +35,17 @@ class __BuiltinUndefined(object): pass BuiltinUndefined = __BuiltinUndefined() -class BuiltinTrap(Component): +class BuiltinTrap(Configurable): - def __init__(self, parent): - super(BuiltinTrap, self).__init__(parent, None, None) + shell = Instance('IPython.core.iplib.InteractiveShell') + + def __init__(self, shell): + super(BuiltinTrap, self).__init__(None) self._orig_builtins = {} # We define this to track if a single BuiltinTrap is nested. # Only turn off the trap when the outermost call to __exit__ is made. self._nested_level = 0 - - @auto_attr - def shell(self): - return Component.get_instances( - root=self.root, - klass='IPython.core.iplib.InteractiveShell')[0] + self.shell = shell def __enter__(self): if self._nested_level == 0: diff --git a/IPython/core/component.py b/IPython/core/component.py deleted file mode 100755 index 98b6508..0000000 --- a/IPython/core/component.py +++ /dev/null @@ -1,346 +0,0 @@ -#!/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.importstring import import_item -from IPython.config.loader import Config -from IPython.utils.traitlets import ( - HasTraits, MetaHasTraits, 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 a 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 = cls.__new__(cls, *args, **kw) - - # 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 = [] - for c in cls.__mro__: - if issubclass(cls, c) and issubclass(c, Component): - c.__numcreated += 1 - indices.append(c.__numcreated) - c.__instance_refs[c.__numcreated] = instance - 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 - - def clear_instances(cls): - """Clear all instances tracked by cls.""" - cls.__instance_refs.clear() - cls.__numcreated = 0 - - def get_instances(cls, name=None, root=None, klass=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. - 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. - """ - if klass is not None: - if isinstance(klass, basestring): - klass = import_item(klass) - # Limit search to instances of klass for performance - if issubclass(klass, Component): - return klass.get_instances(name=name, root=root) - instances = cls.__instance_refs.values() - if name is not None: - instances = [i for i in instances if i.name == name] - if klass is not None: - instances = [i for i in instances if isinstance(i, klass)] - 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, - klass=None): - """Get all instances of cls, i such that call(i)==True. - - This also takes the ``name`` and ``root`` and ``classname`` - arguments of :meth:`get_instance` - """ - return [i for i in cls.get_instances(name, root, klass) if call(i)] - - -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) - - -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(MetaHasTraits, MetaComponentTracker): - pass - - -#----------------------------------------------------------------------------- -# Component implementation -#----------------------------------------------------------------------------- - - -class Component(HasTraits): - - __metaclass__ = MetaComponent - - # Traits are fun! - config = Instance(Config,(),{}) - 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 : Config - 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 = config - # 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) - else: - if self.parent is not None: - 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 - # 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(self.parent.config) - - self.created = datetime.datetime.now() - - #------------------------------------------------------------------------- - # Static trait 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 ``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 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 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 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)) - 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)) - - @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 "<%s('%s')>" % (self.__class__.__name__, self.name) diff --git a/IPython/core/display_trap.py b/IPython/core/display_trap.py index d5e5e83..7d6539c 100644 --- a/IPython/core/display_trap.py +++ b/IPython/core/display_trap.py @@ -22,34 +22,28 @@ Authors: import sys -from IPython.core.component import Component +from IPython.config.configurable import Configurable #----------------------------------------------------------------------------- # Classes and functions #----------------------------------------------------------------------------- -class DisplayTrap(Component): +class DisplayTrap(Configurable): """Object to manage sys.displayhook. This came from IPython.core.kernel.display_hook, but is simplified (no callbacks or formatters) until more of the core is refactored. """ - def __init__(self, parent, hook): - super(DisplayTrap, self).__init__(parent, None, None) + def __init__(self, hook): + super(DisplayTrap, self).__init__(None) self.hook = hook self.old_hook = None # We define this to track if a single BuiltinTrap is nested. # Only turn off the trap when the outermost call to __exit__ is made. self._nested_level = 0 - # @auto_attr - # def shell(self): - # return Component.get_instances( - # root=self.root, - # klass='IPython.core.iplib.InteractiveShell')[0] - def __enter__(self): if self._nested_level == 0: self.set() diff --git a/IPython/core/extensions.py b/IPython/core/extensions.py new file mode 100644 index 0000000..03bb258 --- /dev/null +++ b/IPython/core/extensions.py @@ -0,0 +1,124 @@ +# encoding: utf-8 +"""A class for managing IPython extensions. + +Authors: + +* Brian Granger +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 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 +#----------------------------------------------------------------------------- + +import os +import sys + +from IPython.config.configurable import Configurable +from IPython.utils.traitlets import Instance + +#----------------------------------------------------------------------------- +# Main class +#----------------------------------------------------------------------------- + +class ExtensionManager(Configurable): + + shell = Instance('IPython.core.iplib.InteractiveShell') + + def __init__(self, shell, config=None): + super(ExtensionManager, self).__init__(config=config) + self.shell = shell + self.shell.on_trait_change( + self._on_ipython_dir_changed, 'ipython_dir' + ) + + def __del__(self): + self.shell.on_trait_change( + self._on_ipython_dir_changed, 'ipython_dir', remove=True + ) + + @property + def ipython_extension_dir(self): + return os.path.join(self.shell.ipython_dir, u'extensions') + + def _on_ipython_dir_changed(self): + if not os.path.isdir(self.ipython_extension_dir): + os.makedirs(self.ipython_extension_dir, mode = 0777) + + def load_extension(self, module_str): + """Load an IPython extension by its module name. + + An IPython extension is an importable Python module that has + a function with the signature:: + + def load_ipython_extension(ipython): + # Do things with ipython + + This function is called after your extension is imported and the + currently active :class:`InteractiveShell` instance is passed as + the only argument. You can do anything you want with IPython at + that point, including defining new magic and aliases, adding new + components, etc. + + The :func:`load_ipython_extension` will be called again is you + load or reload the extension again. It is up to the extension + author to add code to manage that. + + You can put your extension modules anywhere you want, as long as + they can be imported by Python's standard import mechanism. However, + to make it easy to write extensions, you can also put your extensions + in ``os.path.join(self.ipython_dir, 'extensions')``. This directory + is added to ``sys.path`` automatically. + + If :func:`load_ipython_extension` returns anything, this function + will return that object. + """ + from IPython.utils.syspathcontext import prepended_to_syspath + + if module_str not in sys.modules: + with prepended_to_syspath(self.ipython_extension_dir): + __import__(module_str) + mod = sys.modules[module_str] + return self._call_load_ipython_extension(mod) + + def unload_extension(self, module_str): + """Unload an IPython extension by its module name. + + This function looks up the extension's name in ``sys.modules`` and + simply calls ``mod.unload_ipython_extension(self)``. + """ + if module_str in sys.modules: + mod = sys.modules[module_str] + self._call_unload_ipython_extension(mod) + + def reload_extension(self, module_str): + """Reload an IPython extension by calling reload. + + If the module has not been loaded before, + :meth:`InteractiveShell.load_extension` is called. Otherwise + :func:`reload` is called and then the :func:`load_ipython_extension` + function of the module, if it exists is called. + """ + from IPython.utils.syspathcontext import prepended_to_syspath + + with prepended_to_syspath(self.ipython_extension_dir): + if module_str in sys.modules: + mod = sys.modules[module_str] + reload(mod) + self._call_load_ipython_extension(mod) + else: + self.load_extension(module_str) + + def _call_load_ipython_extension(self, mod): + if hasattr(mod, 'load_ipython_extension'): + return mod.load_ipython_extension(self.shell) + + def _call_unload_ipython_extension(self, mod): + if hasattr(mod, 'unload_ipython_extension'): + return mod.unload_ipython_extension(self.shell) \ No newline at end of file diff --git a/IPython/core/ipapi.py b/IPython/core/ipapi.py index 461aa5a..24fe36d 100644 --- a/IPython/core/ipapi.py +++ b/IPython/core/ipapi.py @@ -24,13 +24,7 @@ has been made into a component, this module will be sent to deathrow. def get(): - """Get the most recently created InteractiveShell instance.""" + """Get the global InteractiveShell instance.""" from IPython.core.iplib import InteractiveShell - insts = InteractiveShell.get_instances() - if len(insts)==0: - return None - most_recent = insts[0] - for inst in insts[1:]: - if inst.created > most_recent.created: - most_recent = inst - return most_recent + return InteractiveShell.instance() + diff --git a/IPython/core/ipapp.py b/IPython/core/ipapp.py index 1dcbd3d..6733afe 100755 --- a/IPython/core/ipapp.py +++ b/IPython/core/ipapp.py @@ -475,8 +475,8 @@ class IPythonApp(Application): # But that might be the place for them sys.path.insert(0, '') - # Create an InteractiveShell instance - self.shell = InteractiveShell(None, self.master_config) + # Create an InteractiveShell instance. + self.shell = InteractiveShell.instance(config=self.master_config) def post_construct(self): """Do actions after construct, but before starting the app.""" @@ -543,7 +543,7 @@ class IPythonApp(Application): def _load_extensions(self): """Load all IPython extensions in Global.extensions. - This uses the :meth:`InteractiveShell.load_extensions` to load all + This uses the :meth:`ExtensionManager.load_extensions` to load all the extensions listed in ``self.master_config.Global.extensions``. """ try: @@ -553,7 +553,7 @@ class IPythonApp(Application): for ext in extensions: try: self.log.info("Loading IPython extension: %s" % ext) - self.shell.load_extension(ext) + self.shell.extension_manager.load_extension(ext) except: self.log.warn("Error in loading extension: %s" % ext) self.shell.showtraceback() diff --git a/IPython/core/iplib.py b/IPython/core/iplib.py index ae56cfb..b01d68f 100644 --- a/IPython/core/iplib.py +++ b/IPython/core/iplib.py @@ -1,12 +1,10 @@ # -*- coding: utf-8 -*- -""" -Main IPython Component -""" +"""Main IPython class.""" #----------------------------------------------------------------------------- # Copyright (C) 2001 Janko Hauser # Copyright (C) 2001-2007 Fernando Perez. -# Copyright (C) 2008-2009 The IPython Development Team +# 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. @@ -38,9 +36,10 @@ from IPython.core import shadowns from IPython.core import ultratb from IPython.core.alias import AliasManager from IPython.core.builtin_trap import BuiltinTrap -from IPython.core.component import Component +from IPython.config.configurable import Configurable from IPython.core.display_trap import DisplayTrap from IPython.core.error import TryNext, UsageError +from IPython.core.extensions import ExtensionManager from IPython.core.fakemodule import FakeModule, init_fakemod_dict from IPython.core.logger import Logger from IPython.core.magic import Magic @@ -69,7 +68,7 @@ from IPython.utils.syspathcontext import prepended_to_syspath from IPython.utils.terminal import toggle_set_term_title, set_term_title from IPython.utils.warn import warn, error, fatal from IPython.utils.traitlets import ( - Int, Str, CBool, CaselessStrEnum, Enum, List, Unicode + Int, Str, CBool, CaselessStrEnum, Enum, List, Unicode, Instance ) # from IPython.utils import growl @@ -196,7 +195,7 @@ class SeparateStr(Str): #----------------------------------------------------------------------------- -class InteractiveShell(Component, Magic): +class InteractiveShell(Configurable, Magic): """An enhanced, interactive shell for Python.""" autocall = Enum((0,1,2), default_value=1, config=True) @@ -281,14 +280,21 @@ class InteractiveShell(Component, Magic): # Subclasses with thread support should override this as needed. isthreaded = False - def __init__(self, parent=None, config=None, ipython_dir=None, usage=None, + # Subcomponents of InteractiveShell + alias_manager = Instance('IPython.core.alias.AliasManager') + prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager') + builtin_trap = Instance('IPython.core.builtin_trap.BuiltinTrap') + display_trap = Instance('IPython.core.display_trap.DisplayTrap') + extension_manager = Instance('IPython.core.extensions.ExtensionManager') + + def __init__(self, config=None, ipython_dir=None, usage=None, user_ns=None, user_global_ns=None, banner1=None, banner2=None, display_banner=None, custom_exceptions=((),None)): # This is where traits with a config_key argument are updated # from the values on config. - super(InteractiveShell, self).__init__(parent, config=config) + super(InteractiveShell, self).__init__(config=config) # These are relatively independent and stateless self.init_ipython_dir(ipython_dir) @@ -334,8 +340,20 @@ class InteractiveShell(Component, Magic): self.init_reload_doctest() self.init_magics() self.init_pdb() + self.init_extension_manager() self.hooks.late_startup_hook() + @classmethod + def instance(cls, *args, **kwargs): + """Returns a global InteractiveShell instance.""" + if not hasattr(cls, "_instance"): + cls._instance = cls(*args, **kwargs) + return cls._instance + + @classmethod + def initialized(cls): + return hasattr(cls, "_instance") + def get_ipython(self): """Return the currently running IPython instance.""" return self @@ -353,12 +371,6 @@ class InteractiveShell(Component, Magic): def _ipython_dir_changed(self, name, new): if not os.path.isdir(new): os.makedirs(new, mode = 0777) - if not os.path.isdir(self.ipython_extension_dir): - os.makedirs(self.ipython_extension_dir, mode = 0777) - - @property - def ipython_extension_dir(self): - return os.path.join(self.ipython_dir, 'extensions') @property def usable_screen_length(self): @@ -523,7 +535,7 @@ class InteractiveShell(Component, Magic): pass def init_displayhook(self): - self.display_trap = DisplayTrap(self, self.outputcache) + self.display_trap = DisplayTrap(self.outputcache) def init_reload_doctest(self): # Do a proper resetting of doctest, including the necessary displayhook @@ -1747,6 +1759,13 @@ class InteractiveShell(Component, Magic): self.ns_table['alias'] = self.alias_manager.alias_table, #------------------------------------------------------------------------- + # Things related to extensions + #------------------------------------------------------------------------- + + def init_extension_manager(self): + self.extension_manager = ExtensionManager(self, config=self.config) + + #------------------------------------------------------------------------- # Things related to the running of code #------------------------------------------------------------------------- @@ -2340,96 +2359,6 @@ class InteractiveShell(Component, Magic): return lineout #------------------------------------------------------------------------- - # Working with components - #------------------------------------------------------------------------- - - def get_component(self, name=None, klass=None): - """Fetch a component by name and klass in my tree.""" - c = Component.get_instances(root=self, name=name, klass=klass) - if len(c) == 0: - return None - if len(c) == 1: - return c[0] - else: - return c - - #------------------------------------------------------------------------- - # IPython extensions - #------------------------------------------------------------------------- - - def load_extension(self, module_str): - """Load an IPython extension by its module name. - - An IPython extension is an importable Python module that has - a function with the signature:: - - def load_ipython_extension(ipython): - # Do things with ipython - - This function is called after your extension is imported and the - currently active :class:`InteractiveShell` instance is passed as - the only argument. You can do anything you want with IPython at - that point, including defining new magic and aliases, adding new - components, etc. - - The :func:`load_ipython_extension` will be called again is you - load or reload the extension again. It is up to the extension - author to add code to manage that. - - You can put your extension modules anywhere you want, as long as - they can be imported by Python's standard import mechanism. However, - to make it easy to write extensions, you can also put your extensions - in ``os.path.join(self.ipython_dir, 'extensions')``. This directory - is added to ``sys.path`` automatically. - - If :func:`load_ipython_extension` returns anything, this function - will return that object. - """ - from IPython.utils.syspathcontext import prepended_to_syspath - - if module_str not in sys.modules: - with prepended_to_syspath(self.ipython_extension_dir): - __import__(module_str) - mod = sys.modules[module_str] - return self._call_load_ipython_extension(mod) - - def unload_extension(self, module_str): - """Unload an IPython extension by its module name. - - This function looks up the extension's name in ``sys.modules`` and - simply calls ``mod.unload_ipython_extension(self)``. - """ - if module_str in sys.modules: - mod = sys.modules[module_str] - self._call_unload_ipython_extension(mod) - - def reload_extension(self, module_str): - """Reload an IPython extension by calling reload. - - If the module has not been loaded before, - :meth:`InteractiveShell.load_extension` is called. Otherwise - :func:`reload` is called and then the :func:`load_ipython_extension` - function of the module, if it exists is called. - """ - from IPython.utils.syspathcontext import prepended_to_syspath - - with prepended_to_syspath(self.ipython_extension_dir): - if module_str in sys.modules: - mod = sys.modules[module_str] - reload(mod) - self._call_load_ipython_extension(mod) - else: - self.load_extension(module_str) - - def _call_load_ipython_extension(self, mod): - if hasattr(mod, 'load_ipython_extension'): - return mod.load_ipython_extension(self) - - def _call_unload_ipython_extension(self, mod): - if hasattr(mod, 'unload_ipython_extension'): - return mod.unload_ipython_extension(self) - - #------------------------------------------------------------------------- # Things related to the prefilter #------------------------------------------------------------------------- diff --git a/IPython/core/magic.py b/IPython/core/magic.py index 621743b..b44a41a 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -97,11 +97,11 @@ def compress_dhist(dh): # XXX - for some odd reason, if Magic is made a new-style class, we get errors # on construction of the main InteractiveShell object. Something odd is going -# on with super() calls, Component and the MRO... For now leave it as-is, but +# on with super() calls, Configurable and the MRO... For now leave it as-is, but # eventually this needs to be clarified. # BG: This is because InteractiveShell inherits from this, but is itself a -# Component. This messes up the MRO in some way. The fix is that we need to -# make Magic a component that InteractiveShell does not subclass. +# Configurable. This messes up the MRO in some way. The fix is that we need to +# make Magic a configurable that InteractiveShell does not subclass. class Magic: """Magic functions for InteractiveShell. @@ -3586,15 +3586,15 @@ Defaulting color scheme to 'NoColor'""" def magic_load_ext(self, module_str): """Load an IPython extension by its module name.""" - return self.load_extension(module_str) + return self.extension_manager.load_extension(module_str) def magic_unload_ext(self, module_str): """Unload an IPython extension by its module name.""" - self.unload_extension(module_str) + self.extension_manager.unload_extension(module_str) def magic_reload_ext(self, module_str): """Reload an IPython extension by its module name.""" - self.reload_extension(module_str) + self.extension_manager.reload_extension(module_str) @testdec.skip_doctest def magic_install_profiles(self, s): diff --git a/IPython/core/prefilter.py b/IPython/core/prefilter.py index f2b80fe..3fe16e3 100755 --- a/IPython/core/prefilter.py +++ b/IPython/core/prefilter.py @@ -31,11 +31,11 @@ import re from IPython.core.alias import AliasManager from IPython.core.autocall import IPyAutocall -from IPython.core.component import Component +from IPython.config.configurable import Configurable from IPython.core.splitinput import split_user_input from IPython.core.page import page -from IPython.utils.traitlets import List, Int, Any, Str, CBool, Bool +from IPython.utils.traitlets import List, Int, Any, Str, CBool, Bool, Instance from IPython.utils.io import Term from IPython.utils.text import make_quoted_expr from IPython.utils.autoattr import auto_attr @@ -169,7 +169,7 @@ class LineInfo(object): #----------------------------------------------------------------------------- -class PrefilterManager(Component): +class PrefilterManager(Configurable): """Main prefilter component. The IPython prefilter is run on all user input before it is run. The @@ -210,19 +210,15 @@ class PrefilterManager(Component): """ multi_line_specials = CBool(True, config=True) + shell = Instance('IPython.core.iplib.InteractiveShell') - def __init__(self, parent, config=None): - super(PrefilterManager, self).__init__(parent, config=config) + def __init__(self, shell, config=None): + super(PrefilterManager, self).__init__(config=config) + self.shell = shell self.init_transformers() self.init_handlers() self.init_checkers() - @auto_attr - def shell(self): - return Component.get_instances( - root=self.root, - klass='IPython.core.iplib.InteractiveShell')[0] - #------------------------------------------------------------------------- # API for managing transformers #------------------------------------------------------------------------- @@ -231,7 +227,7 @@ class PrefilterManager(Component): """Create the default transformers.""" self._transformers = [] for transformer_cls in _default_transformers: - transformer_cls(self, config=self.config) + transformer_cls(self.shell, self, config=self.config) def sort_transformers(self): """Sort the transformers by priority. @@ -265,7 +261,7 @@ class PrefilterManager(Component): """Create the default checkers.""" self._checkers = [] for checker in _default_checkers: - checker(self, config=self.config) + checker(self.shell, self, config=self.config) def sort_checkers(self): """Sort the checkers by priority. @@ -300,7 +296,7 @@ class PrefilterManager(Component): self._handlers = {} self._esc_handlers = {} for handler in _default_handlers: - handler(self, config=self.config) + handler(self.shell, self, config=self.config) @property def handlers(self): @@ -445,28 +441,22 @@ class PrefilterManager(Component): #----------------------------------------------------------------------------- -class PrefilterTransformer(Component): +class PrefilterTransformer(Configurable): """Transform a line of user input.""" priority = Int(100, config=True) - shell = Any - prefilter_manager = Any + # Transformers don't currently use shell or prefilter_manager, but as we + # move away from checkers and handlers, they will need them. + shell = Instance('IPython.core.iplib.InteractiveShell') + prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager') enabled = Bool(True, config=True) - def __init__(self, parent, config=None): - super(PrefilterTransformer, self).__init__(parent, config=config) + def __init__(self, shell, prefilter_manager, config=None): + super(PrefilterTransformer, self).__init__(config=config) + self.shell = shell + self.prefilter_manager = prefilter_manager self.prefilter_manager.register_transformer(self) - @auto_attr - def shell(self): - return Component.get_instances( - root=self.root, - klass='IPython.core.iplib.InteractiveShell')[0] - - @auto_attr - def prefilter_manager(self): - return PrefilterManager.get_instances(root=self.root)[0] - def transform(self, line, continue_prompt): """Transform a line, returning the new one.""" return None @@ -561,28 +551,20 @@ class IPyPromptTransformer(PrefilterTransformer): #----------------------------------------------------------------------------- -class PrefilterChecker(Component): +class PrefilterChecker(Configurable): """Inspect an input line and return a handler for that line.""" priority = Int(100, config=True) - shell = Any - prefilter_manager = Any + shell = Instance('IPython.core.iplib.InteractiveShell') + prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager') enabled = Bool(True, config=True) - def __init__(self, parent, config=None): - super(PrefilterChecker, self).__init__(parent, config=config) + def __init__(self, shell, prefilter_manager, config=None): + super(PrefilterChecker, self).__init__(config=config) + self.shell = shell + self.prefilter_manager = prefilter_manager self.prefilter_manager.register_checker(self) - @auto_attr - def shell(self): - return Component.get_instances( - root=self.root, - klass='IPython.core.iplib.InteractiveShell')[0] - - @auto_attr - def prefilter_manager(self): - return PrefilterManager.get_instances(root=self.root)[0] - def check(self, line_info): """Inspect line_info and return a handler instance or None.""" return None @@ -709,16 +691,12 @@ class AliasChecker(PrefilterChecker): priority = Int(800, config=True) - @auto_attr - def alias_manager(self): - return AliasManager.get_instances(root=self.root)[0] - def check(self, line_info): "Check if the initital identifier on the line is an alias." # Note: aliases can not contain '.' head = line_info.ifun.split('.',1)[0] - if line_info.ifun not in self.alias_manager \ - or head not in self.alias_manager \ + if line_info.ifun not in self.shell.alias_manager \ + or head not in self.shell.alias_manager \ or is_shadowed(head, self.shell): return None @@ -766,31 +744,23 @@ class AutocallChecker(PrefilterChecker): #----------------------------------------------------------------------------- -class PrefilterHandler(Component): +class PrefilterHandler(Configurable): handler_name = Str('normal') esc_strings = List([]) - shell = Any - prefilter_manager = Any + shell = Instance('IPython.core.iplib.InteractiveShell') + prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager') - def __init__(self, parent, config=None): - super(PrefilterHandler, self).__init__(parent, config=config) + def __init__(self, shell, prefilter_manager, config=None): + super(PrefilterHandler, self).__init__(config=config) + self.shell = shell + self.prefilter_manager = prefilter_manager self.prefilter_manager.register_handler( self.handler_name, self, self.esc_strings ) - @auto_attr - def shell(self): - return Component.get_instances( - root=self.root, - klass='IPython.core.iplib.InteractiveShell')[0] - - @auto_attr - def prefilter_manager(self): - return PrefilterManager.get_instances(root=self.root)[0] - def handle(self, line_info): # print "normal: ", line_info """Handle normal input lines. Use as a template for handlers.""" @@ -827,13 +797,9 @@ class AliasHandler(PrefilterHandler): handler_name = Str('alias') - @auto_attr - def alias_manager(self): - return AliasManager.get_instances(root=self.root)[0] - def handle(self, line_info): """Handle alias input lines. """ - transformed = self.alias_manager.expand_aliases(line_info.ifun,line_info.the_rest) + transformed = self.shell.alias_manager.expand_aliases(line_info.ifun,line_info.the_rest) # pre is needed, because it carries the leading whitespace. Otherwise # aliases won't work in indented sections. line_out = '%sget_ipython().system(%s)' % (line_info.pre_whitespace, @@ -894,7 +860,7 @@ class AutoHandler(PrefilterHandler): esc_strings = List([ESC_PAREN, ESC_QUOTE, ESC_QUOTE2]) def handle(self, line_info): - """Hande lines which can be auto-executed, quoting if requested.""" + """Handle lines which can be auto-executed, quoting if requested.""" line = line_info.line ifun = line_info.ifun the_rest = line_info.the_rest diff --git a/IPython/core/tests/test_component.py b/IPython/core/tests/test_component.py deleted file mode 100644 index 5ffb169..0000000 --- a/IPython/core/tests/test_component.py +++ /dev/null @@ -1,214 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 -""" -Tests for IPython.core.component - -Authors: - -* Brian Granger -* Fernando Perez (design help) -""" - -#----------------------------------------------------------------------------- -# 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 unittest import TestCase - -from IPython.core.component import Component, ComponentError -from IPython.utils.traitlets import ( - TraitError, Int, Float, Str -) -from IPython.config.loader import Config - - -#----------------------------------------------------------------------------- -# Test cases -#----------------------------------------------------------------------------- - - -class TestComponentMeta(TestCase): - - def test_get_instances(self): - class BaseComponent(Component): - pass - c1 = BaseComponent(None) - c2 = BaseComponent(c1) - self.assertEquals(BaseComponent.get_instances(),[c1,c2]) - - def test_get_instances_subclass(self): - class MyComponent(Component): - pass - class MyOtherComponent(MyComponent): - pass - c1 = MyComponent(None) - c2 = MyOtherComponent(c1) - c3 = MyOtherComponent(c2) - self.assertEquals(MyComponent.get_instances(), [c1, c2, c3]) - self.assertEquals(MyOtherComponent.get_instances(), [c2, c3]) - - def test_get_instances_root(self): - class MyComponent(Component): - pass - class MyOtherComponent(MyComponent): - pass - c1 = MyComponent(None) - c2 = MyOtherComponent(c1) - c3 = MyOtherComponent(c2) - c4 = MyComponent(None) - c5 = MyComponent(c4) - self.assertEquals(MyComponent.get_instances(root=c1), [c1, c2, c3]) - self.assertEquals(MyComponent.get_instances(root=c4), [c4, c5]) - - -class TestComponent(TestCase): - - def test_parent_child(self): - c1 = Component(None) - c2 = Component(c1) - c3 = Component(c1) - c4 = Component(c3) - self.assertEquals(c1.parent, None) - self.assertEquals(c2.parent, c1) - self.assertEquals(c3.parent, c1) - self.assertEquals(c4.parent, c3) - self.assertEquals(c1.children, [c2, c3]) - self.assertEquals(c2.children, []) - self.assertEquals(c3.children, [c4]) - self.assertEquals(c4.children, []) - - def test_root(self): - c1 = Component(None) - c2 = Component(c1) - c3 = Component(c1) - c4 = Component(c3) - self.assertEquals(c1.root, c1.root) - self.assertEquals(c2.root, c1) - self.assertEquals(c3.root, c1) - self.assertEquals(c4.root, c1) - - def test_change_parent(self): - c1 = Component(None) - c2 = Component(None) - c3 = Component(c1) - self.assertEquals(c3.root, c1) - self.assertEquals(c3.parent, c1) - self.assertEquals(c1.children,[c3]) - c3.parent = c2 - self.assertEquals(c3.root, c2) - self.assertEquals(c3.parent, c2) - self.assertEquals(c2.children,[c3]) - self.assertEquals(c1.children,[]) - - def test_subclass_parent(self): - c1 = Component(None) - self.assertRaises(TraitError, setattr, c1, 'parent', 10) - - class MyComponent(Component): - pass - c1 = Component(None) - c2 = MyComponent(c1) - self.assertEquals(MyComponent.parent.this_class, Component) - self.assertEquals(c2.parent, c1) - - def test_bad_root(self): - c1 = Component(None) - c2 = Component(None) - c3 = Component(None) - self.assertRaises(ComponentError, setattr, c1, 'root', c2) - c1.parent = c2 - self.assertEquals(c1.root, c2) - self.assertRaises(ComponentError, setattr, c1, 'root', c3) - - -class TestComponentConfig(TestCase): - - def test_default(self): - c1 = Component(None) - c2 = Component(c1) - c3 = Component(c2) - self.assertEquals(c1.config, c2.config) - self.assertEquals(c2.config, c3.config) - - def test_custom(self): - config = Config() - config.foo = 'foo' - config.bar = 'bar' - c1 = Component(None, config=config) - c2 = Component(c1) - c3 = Component(c2) - self.assertEquals(c1.config, config) - self.assertEquals(c2.config, config) - self.assertEquals(c3.config, config) - # Test that copies are not made - self.assert_(c1.config is config) - self.assert_(c2.config is config) - self.assert_(c3.config is config) - self.assert_(c1.config is c2.config) - self.assert_(c2.config is c3.config) - - def test_inheritance(self): - class MyComponent(Component): - a = Int(1, config=True) - b = Float(1.0, config=True) - c = Str('no config') - config = Config() - config.MyComponent.a = 2 - config.MyComponent.b = 2.0 - c1 = MyComponent(None, config=config) - c2 = MyComponent(c1) - self.assertEquals(c1.a, config.MyComponent.a) - self.assertEquals(c1.b, config.MyComponent.b) - self.assertEquals(c2.a, config.MyComponent.a) - self.assertEquals(c2.b, config.MyComponent.b) - c4 = MyComponent(c2, config=Config()) - self.assertEquals(c4.a, 1) - self.assertEquals(c4.b, 1.0) - - def test_parent(self): - class Foo(Component): - a = Int(0, config=True) - b = Str('nope', config=True) - class Bar(Foo): - b = Str('gotit', config=False) - c = Float(config=True) - config = Config() - config.Foo.a = 10 - config.Foo.b = "wow" - config.Bar.b = 'later' - config.Bar.c = 100.0 - f = Foo(None, config=config) - b = Bar(f) - self.assertEquals(f.a, 10) - self.assertEquals(f.b, 'wow') - self.assertEquals(b.b, 'gotit') - self.assertEquals(b.c, 100.0) - - -class TestComponentName(TestCase): - - def test_default(self): - class MyComponent(Component): - pass - c1 = Component(None) - c2 = MyComponent(None) - c3 = Component(c2) - self.assertNotEquals(c1.name, c2.name) - self.assertNotEquals(c1.name, c3.name) - - def test_manual(self): - class MyComponent(Component): - pass - c1 = Component(None, name='foo') - c2 = MyComponent(None, name='bar') - c3 = Component(c2, name='bah') - self.assertEquals(c1.name, 'foo') - self.assertEquals(c2.name, 'bar') - self.assertEquals(c3.name, 'bah') diff --git a/IPython/extensions/tests/test_pretty.py b/IPython/extensions/tests/test_pretty.py index b7738cf..bef6b12 100644 --- a/IPython/extensions/tests/test_pretty.py +++ b/IPython/extensions/tests/test_pretty.py @@ -77,7 +77,7 @@ a b ip = get_ipython() -prd = ip.load_extension('pretty') +prd = ip.extension_manager.load_extension('pretty') prd.for_type(A, a_pretty_printer) prd.for_type_by_name(B.__module__, B.__name__, b_pretty_printer) diff --git a/IPython/kernel/clusterdir.py b/IPython/kernel/clusterdir.py index 63174eb..faba277 100755 --- a/IPython/kernel/clusterdir.py +++ b/IPython/kernel/clusterdir.py @@ -26,7 +26,7 @@ from twisted.python import log from IPython.config.loader import PyFileConfigLoader from IPython.core.application import Application, BaseAppConfigLoader -from IPython.core.component import Component +from IPython.config.configurable import Configurable from IPython.core.crashhandler import CrashHandler from IPython.core import release from IPython.utils.path import ( @@ -63,7 +63,7 @@ class PIDFileError(Exception): # Class for managing cluster directories #----------------------------------------------------------------------------- -class ClusterDir(Component): +class ClusterDir(Configurable): """An object to manage the cluster directory and its resources. The cluster directory is used by :command:`ipcontroller`, diff --git a/IPython/kernel/configobjfactory.py b/IPython/kernel/configobjfactory.py index 745cdfb..c9d8245 100644 --- a/IPython/kernel/configobjfactory.py +++ b/IPython/kernel/configobjfactory.py @@ -18,7 +18,7 @@ configuration system. import zope.interface as zi -from IPython.core.component import Component +from IPython.config.configurable import Configurable #----------------------------------------------------------------------------- # Code @@ -29,7 +29,7 @@ class IConfiguredObjectFactory(zi.Interface): """I am a component that creates a configured object. This class is useful if you want to configure a class that is not a - subclass of :class:`IPython.core.component.Component`. + subclass of :class:`IPython.config.configurable.Configurable`. """ def __init__(config): @@ -39,12 +39,12 @@ class IConfiguredObjectFactory(zi.Interface): """Return an instance of the configured object.""" -class ConfiguredObjectFactory(Component): +class ConfiguredObjectFactory(Configurable): zi.implements(IConfiguredObjectFactory) def __init__(self, config): - super(ConfiguredObjectFactory, self).__init__(None, config=config) + super(ConfiguredObjectFactory, self).__init__(config=config) def create(self): raise NotImplementedError('create must be implemented in a subclass') @@ -63,17 +63,17 @@ class IAdaptedConfiguredObjectFactory(zi.Interface): """Return an instance of the adapted and configured object.""" -class AdaptedConfiguredObjectFactory(Component): +class AdaptedConfiguredObjectFactory(Configurable): # zi.implements(IAdaptedConfiguredObjectFactory) def __init__(self, config, adaptee): # print # print "config pre:", config - super(AdaptedConfiguredObjectFactory, self).__init__(None, config=config) + super(AdaptedConfiguredObjectFactory, self).__init__(config=config) # print # print "config post:", config self.adaptee = adaptee def create(self): - raise NotImplementedError('create must be implemented in a subclass') \ No newline at end of file + raise NotImplementedError('create must be implemented in a subclass') diff --git a/IPython/kernel/fcutil.py b/IPython/kernel/fcutil.py index ec4de2e..b2fb012 100644 --- a/IPython/kernel/fcutil.py +++ b/IPython/kernel/fcutil.py @@ -191,9 +191,9 @@ class FCServiceFactory(AdaptedConfiguredObjectFactory): """This class creates a tub with various services running in it. The basic idea is that :meth:`create` returns a running :class:`Tub` - instance that has a number of Foolscap references registered in it. - This class is a subclass of :class:`IPython.core.component.Component` - so the IPython configuration and component system are used. + instance that has a number of Foolscap references registered in it. This + class is a subclass of :class:`IPython.config.configurable.Configurable` + so the IPython configuration system is used. Attributes ---------- diff --git a/IPython/kernel/launcher.py b/IPython/kernel/launcher.py index 43cf057..abb312d 100644 --- a/IPython/kernel/launcher.py +++ b/IPython/kernel/launcher.py @@ -19,7 +19,7 @@ import os import re import sys -from IPython.core.component import Component +from IPython.config.configurable import Configurable from IPython.external import Itpl from IPython.utils.traitlets import Str, Int, List, Unicode from IPython.utils.path import get_ipython_module_path @@ -77,7 +77,7 @@ class UnknownStatus(LauncherError): pass -class BaseLauncher(Component): +class BaseLauncher(Configurable): """An asbtraction for starting, stopping and signaling a process.""" # In all of the launchers, the work_dir is where child processes will be @@ -89,8 +89,8 @@ class BaseLauncher(Component): # the --work-dir option. work_dir = Unicode(u'') - def __init__(self, work_dir, parent=None, name=None, config=None): - super(BaseLauncher, self).__init__(parent, name, config) + def __init__(self, work_dir, config=None): + super(BaseLauncher, self).__init__(config) self.work_dir = work_dir self.state = 'before' # can be before, running, after self.stop_deferreds = [] @@ -265,9 +265,9 @@ class LocalProcessLauncher(BaseLauncher): # spawnProcess. cmd_and_args = List([]) - def __init__(self, work_dir, parent=None, name=None, config=None): + def __init__(self, work_dir, config=None): super(LocalProcessLauncher, self).__init__( - work_dir, parent, name, config + work_dir, config ) self.process_protocol = None self.start_deferred = None @@ -356,9 +356,9 @@ class LocalEngineSetLauncher(BaseLauncher): ['--log-to-file','--log-level', '40'], config=True ) - def __init__(self, work_dir, parent=None, name=None, config=None): + def __init__(self, work_dir, config=None): super(LocalEngineSetLauncher, self).__init__( - work_dir, parent, name, config + work_dir, config ) self.launchers = [] @@ -367,7 +367,7 @@ class LocalEngineSetLauncher(BaseLauncher): self.cluster_dir = unicode(cluster_dir) dlist = [] for i in range(n): - el = LocalEngineLauncher(self.work_dir, self) + el = LocalEngineLauncher(self.work_dir, self.config) # Copy the engine args over to each engine launcher. import copy el.engine_args = copy.deepcopy(self.engine_args) @@ -560,9 +560,9 @@ class WindowsHPCLauncher(BaseLauncher): scheduler = Str('', config=True) job_cmd = Str(find_job_cmd(), config=True) - def __init__(self, work_dir, parent=None, name=None, config=None): + def __init__(self, work_dir, config=None): super(WindowsHPCLauncher, self).__init__( - work_dir, parent, name, config + work_dir, config ) @property @@ -725,9 +725,9 @@ class BatchSystemLauncher(BaseLauncher): # The full path to the instantiated batch script. batch_file = Unicode(u'') - def __init__(self, work_dir, parent=None, name=None, config=None): + def __init__(self, work_dir, config=None): super(BatchSystemLauncher, self).__init__( - work_dir, parent, name, config + work_dir, config ) self.batch_file = os.path.join(self.work_dir, self.batch_file_name) self.context = {} diff --git a/IPython/kernel/winhpcjob.py b/IPython/kernel/winhpcjob.py index c7dc1c9..adc5f60 100644 --- a/IPython/kernel/winhpcjob.py +++ b/IPython/kernel/winhpcjob.py @@ -24,14 +24,14 @@ import uuid from xml.etree import ElementTree as ET -from IPython.core.component import Component +from IPython.config.configurable import Configurable from IPython.utils.traitlets import ( Str, Int, List, Instance, Enum, Bool, CStr ) #----------------------------------------------------------------------------- -# Job and Task Component +# Job and Task classes #----------------------------------------------------------------------------- @@ -74,7 +74,7 @@ def find_username(): return '%s\\%s' % (domain, username) -class WinHPCJob(Component): +class WinHPCJob(Configurable): job_id = Str('') job_name = Str('MyJob', config=True) @@ -165,7 +165,7 @@ class WinHPCJob(Component): self.tasks.append(task) -class WinHPCTask(Component): +class WinHPCTask(Configurable): task_id = Str('') task_name = Str('') @@ -261,8 +261,8 @@ class IPControllerTask(WinHPCTask): unit_type = Str("Core", config=False) work_directory = CStr('', config=False) - def __init__(self, parent, name=None, config=None): - super(IPControllerTask, self).__init__(parent, name, config) + def __init__(self, config=None): + super(IPControllerTask, self).__init__(config) the_uuid = uuid.uuid1() self.std_out_file_path = os.path.join('log','ipcontroller-%s.out' % the_uuid) self.std_err_file_path = os.path.join('log','ipcontroller-%s.err' % the_uuid) @@ -289,8 +289,8 @@ class IPEngineTask(WinHPCTask): unit_type = Str("Core", config=False) work_directory = CStr('', config=False) - def __init__(self, parent, name=None, config=None): - super(IPEngineTask,self).__init__(parent, name, config) + def __init__(self, config=None): + super(IPEngineTask,self).__init__(config) the_uuid = uuid.uuid1() self.std_out_file_path = os.path.join('log','ipengine-%s.out' % the_uuid) self.std_err_file_path = os.path.join('log','ipengine-%s.err' % the_uuid)