1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
|
""" The Envisage core plugin. """
# Enthought library imports.
from enthought.envisage.api import ExtensionPoint, Plugin, ServiceOffer
from enthought.traits.api import List, Instance, Str
class CorePlugin(Plugin):
""" The Envisage core plugin.
The core plugin offers facilities that are generally useful when building
extensible applications such as adapters, categories and hooks etc. It does
not contain anything to do with user interfaces!
The core plugin should be started before any other plugin. It is up to
the plugin manager to do this.
"""
# Extension point Ids.
CATEGORIES = 'enthought.envisage.categories'
CLASS_LOAD_HOOKS = 'enthought.envisage.class_load_hooks'
PREFERENCES = 'enthought.envisage.preferences'
SERVICE_OFFERS = 'enthought.envisage.service_offers'
#### 'IPlugin' interface ##################################################
# The plugin's unique identifier.
id = 'enthought.envisage.core'
# The plugin's name (suitable for displaying to the user).
name = 'Core'
#### Extension points offered by this plugin ##############################
# Categories are actually implemented via standard 'ClassLoadHooks', but
# for (hopefully) readability and convenience we have a specific extension
# point.
categories = ExtensionPoint(
List(Instance('enthought.envisage.category.Category')),
id = CATEGORIES,
desc = """
Traits categories allow you to dynamically extend a Python class with
extra attributes, methods and events.
Contributions to this extension point allow you to import categories
*lazily* when the class to be extended is imported or created. Each
contribution contains the name of the category class that you want to
add (the 'class_name') and the name of the class that you want to
extend (the 'target_class_name').
e.g. To add the 'FooCategory' category to the 'Foo' class::
Category(
class_name = 'foo_category.FooCategory',
target_class_name = 'foo.Foo'
)
"""
)
class_load_hooks = ExtensionPoint(
List(Instance('enthought.envisage.class_load_hook.ClassLoadHook')),
id = CLASS_LOAD_HOOKS,
desc = """
Class load hooks allow you to be notified when any 'HasTraits' class
is imported or created.
See the documentation for 'ClassLoadHook' for more details.
"""
)
preferences = ExtensionPoint(
List(Str),
id = PREFERENCES,
desc = """
Preferences files allow plugins to contribute default values for
user preferences. Each contributed string must be the URL of a
file-like object that contains preferences values.
e.g.
'pkgfile://enthought.envisage/preferences.ini'
- this looks for the 'preferences.ini' file in the 'enthought.envisage'
package.
'file://C:/tmp/preferences.ini'
- this looks for the 'preferences.ini' file in 'C:/tmp'
'http://some.website/preferences.ini'
- this looks for the 'preferences.ini' document on the 'some.website'
web site!
The files themselves are parsed using the excellent 'ConfigObj'
package. For detailed documentation please go to:-
http://www.voidspace.org.uk/python/configobj.html
"""
)
service_offers = ExtensionPoint(
List(ServiceOffer),
id = SERVICE_OFFERS,
desc = """
Services are simply objects that a plugin wants to make available to
other plugins. This extension point allows you to offer services
that are created 'on-demand'.
e.g.
my_service_offer = ServiceOffer(
protocol = 'acme.IMyService',
factory = an_object_or_a_callable_that_creates_one,
properties = {'a dictionary' : 'that is passed to the factory'}
)
See the documentation for 'ServiceOffer' for more details.
"""
)
#### Contributions to extension points made by this plugin ################
# None.
###########################################################################
# 'IPlugin' interface.
###########################################################################
def start(self):
""" Start the plugin. """
# Load all contributed preferences files into the application's root
# preferences node.
self._load_preferences(self.application.preferences)
# Connect all class load hooks.
self._connect_class_load_hooks(self.class_load_hooks)
# Add class load hooks for all of the contributed categories. The
# category will be imported and added when the associated target class
# is imported/created.
self._add_category_class_load_hooks(self.categories)
# Register all service offers.
#
# These services are unregistered by the default plugin activation
# strategy (due to the fact that we store the service ids in this
# specific trait!).
self._service_ids = self._register_service_offers(self.service_offers)
return
###########################################################################
# Private interface.
###########################################################################
def _add_category_class_load_hooks(self, categories):
""" Add class load hooks for a list of categories. """
for category in categories:
class_load_hook = self._create_category_class_load_hook(category)
class_load_hook.connect()
return
def _connect_class_load_hooks(self, class_load_hooks):
""" Connect all class load hooks. """
for class_load_hook in class_load_hooks:
class_load_hook.connect()
return
def _create_category_class_load_hook(self, category):
""" Create a category class load hook. """
# Local imports.
from class_load_hook import ClassLoadHook
def import_and_add_category(cls):
""" Import a category and add it to a class.
This is a closure that binds 'self' and 'category'.
"""
category_cls = self.application.import_symbol(category.class_name)
cls.add_trait_category(category_cls)
return
category_class_load_hook = ClassLoadHook(
class_name = category.target_class_name,
on_load = import_and_add_category
)
return category_class_load_hook
def _load_preferences(self, preferences):
""" Load all contributed preferences into a preferences node. """
# Enthought library imports.
from enthought.envisage.resource.api import ResourceManager
# We add the plugin preferences to the default scope. The default scope
# is a transient scope which means that (quite nicely ;^) we never
# save the actual default plugin preference values. They will only get
# saved if a value has been set in another (persistent) scope - which
# is exactly what happens in the preferences UI.
default = preferences.node('default/')
# The resource manager is used to find the preferences files.
resource_manager = ResourceManager()
for resource_name in self.preferences:
f = resource_manager.file(resource_name)
try:
default.load(f)
finally:
f.close()
return
def _register_service_offers(self, service_offers):
""" Register a list of service offers. """
return map(self._register_service_offer, service_offers)
def _register_service_offer(self, service_offer):
""" Register a service offer. """
service_id = self.application.register_service(
protocol = service_offer.protocol,
obj = service_offer.factory,
properties = service_offer.properties
)
return service_id
### EOF ######################################################################
|