File: core_plugin.py

package info (click to toggle)
python-envisagecore 3.1.2-1
  • links: PTS, VCS
  • area: main
  • in suites: squeeze
  • size: 1,096 kB
  • ctags: 1,063
  • sloc: python: 4,115; makefile: 7; sh: 5
file content (251 lines) | stat: -rw-r--r-- 8,319 bytes parent folder | download | duplicates (2)
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 ######################################################################