Logo Search packages:      
Sourcecode: mayavi2 version File versions  Download package

core_plugin.py

00001 """ The implementation of the core Envisage plugin. """

# Standard library imports.
import logging

# Enthought library imports.
from enthought.envisage.api import Plugin
from enthought.naming.api import Context
from enthought.traits.api import List, MetaHasTraits, Str
from enthought.type_manager.api import TypeManager

# Local imports.
from adapter_factory_proxy import AdapterFactoryProxy

# Plugin definition imports.
from core_plugin_definition import ADDITIONAL_PLUGINS, Runnable, Types
from core_plugin_definition import UserAddedPlugin


# Setup a logger for this module.
logger = logging.getLogger(__name__)


00024 class CorePlugin(Plugin):
    """ The implementation of the core Envisage plugin. """

    # fixme: At some point service interfaces should be real interfaces of some
    # kind.  It could be traits, CORBA IDL, whatever!

    # "Interface" ID for the naming service.
    INAMING_SERVICE    = 'enthought.envisage.core.INamingService'
    # "Interface" ID for the type manager.
    ITYPE_MANAGER      = 'enthought.envisage.core.ITypeManager'
    # "Interface" ID for the additions manager.
    IADDITIONS_MANAGER = 'enthought.envisage.core.IAdditionsManager'

    #### 'CorePlugin' interface ###############################################

    # The list of user-added plugins to start.
    #
    # fixme: Should we really create a service for this?
    user_added_plugins = List(UserAddedPlugin)

    #### Private interface ####################################################

    # The IDs of all additional plugins.
    _plugin_ids = List(Str)

    ###########################################################################
    # 'object' interface.
    ###########################################################################

    # fixme: Is there a reason that we do this in the constructor and not in
    # 'start'?
00055     def __init__(self, *args, **kwargs):
        """ Creates a new plugin. """

        # Base-class constructor
        super(CorePlugin, self).__init__(*args, **kwargs)

        # Get the list of additional plugins to start.
        self.user_added_plugins = self.preferences.get(ADDITIONAL_PLUGINS)

        # Make sure all user-added plugins are loaded. Note that this will only
        # work to make additions available if the core plugin is listed as a
        # requirement for the plugin whose extension points these plugins are
        # extending.
        self._plugin_ids = self._load_additional_plugins()

        return

    ###########################################################################
    # 'Plugin' interface.
    ###########################################################################

00076     def start(self, application):
        """ Starts the plugin.

        Can be called manually, but is usually called exactly once when the
        plugin is first required.

        """

        # Create and register all contributed type managers. This includes the
        # default type manager which is contributed in the core plugin
        # definition.
        self._create_type_managers()

        # Get the default type manager.
        type_manager = self._get_type_manager_by_id('enthought.envisage.core')

        # Create and initialize the naming service.
        naming_service = self._create_naming_service(type_manager)

        # Register the naming service.
        self.register_service(self.INAMING_SERVICE, naming_service)

        # Create and publish any application objects.
        self._create_application_objects()

        # Register self as the additions service.
        self.register_service(self.IADDITIONS_MANAGER, self)

        # Synchronize any objects requesting synchronization:
        self._synchronize_objects()

        # Register the trait listener for the additions preference.
        self.on_trait_change(
            self._on_user_added_plugins_changed, 'user_added_plugins'
        )

        self.on_trait_change(
            self._on_user_added_plugins_changed, 'user_added_plugins_items'
        )

        # Request notification when the application has started all plugins.
        application.on_trait_change(self._on_application_started, 'started')

        return

00121     def stop(self, application):
        """ Stops the plugin.

        Can be called manually, but is usually called exactly once when the
        application exits.

        Note that Envisage takes care of unregistering any services that
        were offered by the plugin.

        """

        self.save_preferences()

        # Tear down the trait listener for the additions preference.
        self.on_trait_change(
            self._on_user_added_plugins_changed,
            'user_added_plugins',
            remove=True
        )

        self.on_trait_change(
            self._on_user_added_plugins_changed,
            'user_added_plugins_items', remove=True
        )

        return

    ###########################################################################
    # Private interface.
    ###########################################################################

00152     def _create_application_objects(self):
        """ Create and register any application objects. """

        from core_plugin_definition import ApplicationObject

        # fixme: We get the extensions sorted by the start order of the plugin
        # definitions that contributed them.
        extensions = self.application.extension_registry.get_extensions(
            ApplicationObject, sort=True
        )

        # fixme: Quite obviously, the way this is right now blows any lazy
        # loading out of the water!
        for extension in extensions:
            obj = self._create_application_object(extension)

            # If the object has an 'application' trait then set it.
            if hasattr(obj, 'application'):
                obj.application = self.application

            self._publish_application_object(extension, obj)

        return

00176     def _create_application_object(self, extension):
        """ Create an application object defined in an extension. """

        # Was there a factory specified?
        if len(extension.factory_class_name) > 0:
            klass   = self.import_symbol(extension.factory_class_name)
            factory = klass()
            obj     = factory.create(*extension.args, **extension.kw)

        elif len(extension.class_name) > 0:
            klass = self.import_symbol(extension.class_name)
            obj   = klass(*extension.args, **extension.kw)

        return obj

00191     def _publish_application_object(self, extension, obj):
        """ Publishes an application object defined in an extension. """

        self.application.publish_application_object(
            extension.uol, obj, extension.properties
        )

        return

00200     def _synchronize_objects(self):
        """ Set up synchronization for any synchronizer objects. """

        from core_plugin_definition import Synchronizer

        for extension in self.get_extensions(Synchronizer):
            self._synchronize_object(extension)

        return

00210     def _synchronize_object(self, extension):
        """ Set up synchronization between two objects defined in an extension.
        """
        object1 = self.application.lookup_application_object(extension.uol1)
        if object1 is None:
            logger.error("Synchronizer: Could not locate '%s'." %
                         extension.uol1)
            return

        object2 = self.application.lookup_application_object(extension.uol2)
        if object2 is None:
            logger.error("Synchronizer: Could not locate '%s'." %
                         extension.uol2)
            return

        name1 = extension.name1
        name2 = extension.name2
        if name2 == '':
            name2 = name1

        object1.sync_trait(name1, object2, name2, extension.mutual)

        return

00234     def _create_naming_service(self, type_manager):
        """ Creates and initializes the naming service. """

        # fixme: Currently we have no way to initialize the environment that
        # the naming service uses.
        naming_service = Context()
        naming_service.environment[Context.TYPE_MANAGER] = type_manager

        # Set up an initial set of names.
        #
        # fixme: Applications should be able to oveeride this!
        enthought = naming_service.create_subcontext('enthought')
        envisage  = enthought.create_subcontext('envisage')

        return naming_service

00250     def _create_type_managers(self):
        """ Creates and registers all contributed type managers. """

        # Plugin definition imports.
        from core_plugin_definition import TypeManager

        for extension in self.get_extensions(TypeManager):
            # Does a type manager already exist with this Id?
            type_manager = self._get_type_manager_by_id(extension.id)

            # If not then create one and register it as a service.
            if type_manager is None:
                type_manager = self._create_type_manager(extension)
                self.register_service(self.ITYPE_MANAGER, type_manager)

            # Add all contributed adapters, categories and hooks to the type
            # manager.
            self._register_adapters(extension, type_manager)
            self._register_categories(extension, type_manager)
            self._register_hooks(extension, type_manager)

        return

00273     def _get_type_manager_by_id(self, id):
        """ Returns the type manager with the specified ID.

        Returns '''None''' if no such type manager exists.

        """

        # fixme: We have to use the service registry here because currently
        # the 'convenience' method on the plugin raises an exception if the
        # service is not found. Doh! Can we take out the exception raising
        # part, or are people relying on it?
        type_manager = self.application.service_registry.get_service(
            self.ITYPE_MANAGER, query='id == "%s"' % id
        )

        return type_manager

00290     def _create_type_manager(self, extension):
        """ Creates a type manager implementation. """

        # If a specific implementation class was specified then use that.
        if len(extension.class_name) > 0:
            klass = self.import_symbol(extension.class_name)

        # Otherwise, use the default class.
        else:
            klass = TypeManager

        # If a parent type manager was specified then it must already exist.
        if len(extension.parent) > 0:
            parent = self._get_type_manager_by_id(extension.parent)

        else:
            parent = None

        # fixme: Id is not actually a trait on the type manager!
        return klass(id=extension.id, parent=parent)

00311     def _register_adapters(self, extension, type_manager):
        """ Registers all adapter factories declared in an extension. """

        for factory in extension.adapter_factories:
            # fixme: This is a general problem, we want to find where an
            # extension *item* was defined, without having to have a reference
            # to the extension *point* that is is in!
            factory._definition_ = extension._definition_

            # We create a proxy for each adapter factory so that the factory
            # itself can be lazily loaded.
            proxy = AdapterFactoryProxy(self.application, factory)

            # Register the proxy.
            type_manager.register_type_adapters(
                proxy, factory.adaptee_class_name
            )

        return

00331     def _register_categories(self, extension, type_manager):
        """ Registers all categories declared in an extension. """

        for category in extension.categories:
            # If the target class has already been created then add the
            # category to it right now.
            klass = self.get_class(category.target_class_name)
            if klass is not None:
                self._add_category(category, type_manager, klass)

            # Otherwise, create a function that will import and add the
            # category when the target class is created.
            else:
                listener = self._create_category_importer(
                    category, type_manager
                )

                MetaHasTraits.add_listener(
                    listener, category.target_class_name
                )

        return

00354     def _create_category_importer(self, category, type_manager):
        """ Creates a category importer. """

        def import_category(target_class):
            """ Called when the target class has been loaded. """

            self._add_category(category, type_manager, target_class)

            return

        return import_category

00366     def _add_category(self, category, type_manager, target_class):
        """ Adds a category to a class. """

        # Import the category class.
        category_class = self.import_symbol(category.class_name)

        # Add the category to the target class.
        type_manager.add_category(target_class, category_class)

        return

00377     def _register_hooks(self, extension, type_manager):
        """ Registers all hooks declared in an extension. """

        for hook in extension.hooks:
            # Create a function that will import and add the hook when the
            # target class is created.
            listener = self._create_hook_importer(hook, type_manager)

            # Listen for the target class being loaded.
            MetaHasTraits.add_listener(listener, hook.target_class_name)

        return

00390     def _create_hook_importer(self, hook, type_manager):
        """ Creates a hook importer. """

        def import_hook(target_class):
            """ Called when the target class has been loaded. """

            # The name of the method to hook.
            method_name = hook.target_method_name

            # The callable we want to hook into the method.
            callable = self.import_symbol(hook.callable_name)

            # 'pre' means call the hook before the actual method is invoked,
            # 'post' means call the hook after the actual method is complete
            # (post hooks have access to the return value).
            if hook.type == 'pre':
                type_manager.add_pre(target_class, method_name, callable)

            else:
                type_manager.add_post(target_class, method_name, callable)

            return

        return import_hook

    # fixme: WIP.
    def _get_additional_plugins(self):
        extras = self.user_added_plugins
        # build a plugin list of only the enabled plugins
        plugins = [extra.path for extra in extras if extra.enabled]
        return plugins

    def _load_additional_plugins(self):

        plugins = self._get_additional_plugins()

        # load the plugins
        if len(plugins) > 0:
            ids = self.application.plugin_definition_loader.load(plugins)

        else:
            ids = []

        return ids

    def _start_additional_plugins(self, ids):

        # start each of the plugins
        for id in ids:
            self.application.plugin_activator.start_plugin(id)

        return

    # fixme: I just discovered that this method is called from the additions
    # preference page! I think we need an actual 'additions manager' service
    # and this should be part of the 'public' API.
    def _load_and_start_additional_plugins(self):

        ids = self._load_additional_plugins()
        self._start_additional_plugins(ids)
        return

    #### Trait event handlers #################################################

    # fixme: This is not called from anywhere? Is it still required?
    def _on_plugins_loaded(self):

        # after all the standard plugins are loaded, we need to load
        # any user-added plugins
        return

00461     def _on_application_started(self):
        """ Called when the application has started all plugins. """

        # first start any user-added plugins
        self._start_additional_plugins(self._plugin_ids)

        for extension in self.load_extensions(Runnable):
            # Create an instance of the runnable class...
            klass = self.import_symbol(extension.class_name)
            runnable = klass()

            # ... and run it!
            logger.debug('running %s' % str(runnable))
            runnable.run(self.application)

        return

    # fixme: WIP.
    def _on_user_added_plugins_changed(self):

        print 'CorePlugin._on_user_added_plugins_changed'
        # save the preference
        self.preferences.set(ADDITIONAL_PLUGINS, self.user_added_plugins[:])

        # note that we don't automatically load plugins that are added.
        # since we don't have a mechanism for removing plugins, it seems
        # better to allow the loading to be explicitly invoked by the user
        # rather than happening as a potentially unanticipated side-effect
        # of simply adding something to the list.

        return

#### EOF ######################################################################

Generated by  Doxygen 1.6.0   Back to index