|
11 | 11 |
|
12 | 12 |
|
13 | 13 | # Standard library imports. |
14 | | -from functools import wraps |
15 | 14 | import inspect |
16 | | -import warnings |
17 | 15 | import weakref |
18 | 16 |
|
19 | 17 | # Enthought library imports. |
20 | 18 | from traits.api import List, TraitType, Undefined, provides |
21 | | -from traits.trait_list_object import TraitList |
22 | 19 |
|
23 | 20 | # Local imports. |
24 | 21 | from .i_extension_point import IExtensionPoint |
@@ -118,14 +115,14 @@ def __repr__(self): |
118 | 115 |
|
119 | 116 | def get(self, obj, trait_name): |
120 | 117 | """ Trait type getter. """ |
121 | | - cache_name = _get_cache_name(trait_name) |
122 | | - if cache_name not in obj.__dict__: |
123 | | - _update_cache(obj, trait_name) |
124 | 118 |
|
125 | | - value = obj.__dict__[cache_name] |
126 | | - # validate again |
127 | | - self.trait_type.validate(obj, trait_name, value[:]) |
128 | | - return value |
| 119 | + extension_registry = self._get_extension_registry(obj) |
| 120 | + |
| 121 | + # Get the extensions to this extension point. |
| 122 | + extensions = extension_registry.get_extensions(self.id) |
| 123 | + |
| 124 | + # Make sure the contributions are of the appropriate type. |
| 125 | + return self.trait_type.validate(obj, trait_name, extensions) |
129 | 126 |
|
130 | 127 | def set(self, obj, name, value): |
131 | 128 | """ Trait type setter. """ |
@@ -154,41 +151,21 @@ def connect(self, obj, trait_name): |
154 | 151 | """ |
155 | 152 |
|
156 | 153 | def listener(extension_registry, event): |
157 | | - """ Listener called when an extension point is changed. |
158 | | -
|
159 | | - Parameters |
160 | | - ---------- |
161 | | - extension_registry : IExtensionRegistry |
162 | | - Registry that maintains the extensions. |
163 | | - event : ExtensionPointChangedEvent |
164 | | - Event created for the change. |
165 | | - If the event.index is None, this means the entire extensions |
166 | | - list is set to a new value. If the event.index is not None, |
167 | | - some portion of the list has been modified. |
168 | | - """ |
169 | | - if event.index is not None: |
170 | | - # We know where in the list is changed. |
| 154 | + """ Listener called when an extension point is changed. """ |
171 | 155 |
|
172 | | - # Mutate the _ExtensionPointValue to fire ListChangeEvent |
173 | | - # expected from observing item change. |
174 | | - getattr(obj, trait_name)._sync_values(event) |
175 | | - |
176 | | - # For on_trait_change('name_items') |
177 | | - obj.trait_property_changed( |
178 | | - trait_name + "_items", Undefined, event |
179 | | - ) |
| 156 | + # If an index was specified then we fire an '_items' changed event. |
| 157 | + if event.index is not None: |
| 158 | + name = trait_name + "_items" |
| 159 | + old = Undefined |
| 160 | + new = event |
180 | 161 |
|
| 162 | + # Otherwise, we fire a normal trait changed event. |
181 | 163 | else: |
182 | | - # The entire list has changed. We reset the cache and fire a |
183 | | - # normal trait changed event. |
184 | | - _update_cache(obj, trait_name) |
| 164 | + name = trait_name |
| 165 | + old = event.removed |
| 166 | + new = event.added |
185 | 167 |
|
186 | | - # In case the cache was created first and the registry is then mutated |
187 | | - # before this ``connect`` is called, the internal cache would be in |
188 | | - # an inconsistent state. This also has the side-effect of firing |
189 | | - # another change event, hence allowing future changes to be observed |
190 | | - # without having to access the trait first. |
191 | | - _update_cache(obj, trait_name) |
| 168 | + obj.trait_property_changed(name, old, new) |
192 | 169 |
|
193 | 170 | extension_registry = self._get_extension_registry(obj) |
194 | 171 |
|
@@ -232,166 +209,3 @@ def _get_extension_registry(self, obj): |
232 | 209 | ) |
233 | 210 |
|
234 | 211 | return extension_registry |
235 | | - |
236 | | - |
237 | | -def _warn_if_not_internal(func): |
238 | | - """ Decorator for instance methods of _ExtensionPointValue such that its |
239 | | - effect is nullified if the function is not called with the _internal_use |
240 | | - flag set to true. |
241 | | - """ |
242 | | - |
243 | | - @wraps(func) |
244 | | - def decorated(object, *args, **kwargs): |
245 | | - if not object._internal_use: |
246 | | - warnings.warn( |
247 | | - "Extension point cannot be mutated directly.", |
248 | | - RuntimeWarning, |
249 | | - stacklevel=2, |
250 | | - ) |
251 | | - # This restores the existing behavior where the operation |
252 | | - # is acted on a list object that is not persisted. |
253 | | - return func(TraitList(iter(object)), *args, **kwargs) |
254 | | - return func(object, *args, **kwargs) |
255 | | - |
256 | | - return decorated |
257 | | - |
258 | | - |
259 | | -class _ExtensionPointValue(TraitList): |
260 | | - """ _ExtensionPointValue is the list being returned while retrieving the |
261 | | - attribute value for an ExtensionPoint trait. |
262 | | -
|
263 | | - This list returned for an ExtensionPoint acts as a proxy to query |
264 | | - extensions in an ExtensionRegistry for a given extension point id. Users of |
265 | | - ExtensionPoint expect to handle a list-like object, and expect to be able |
266 | | - to listen to "mutation" on the list. The ExtensionRegistry remains to be |
267 | | - the source of truth as to what extensions are available for a given |
268 | | - extension point ID. |
269 | | -
|
270 | | - Users are not expected to mutate the list directly. All mutations to |
271 | | - extensions are expected to go through the extension registry to maintain |
272 | | - consistency. With that, all methods for mutating the list are nullified, |
273 | | - unless it is used internally. |
274 | | -
|
275 | | - The requirement to support ``observe("name:items")`` means this list, |
276 | | - associated with `name`, cannot be a property that gets recomputed on every |
277 | | - access (enthought/traits#624), it needs to be cached. As with any |
278 | | - cached quantity, it needs to be synchronized with the ExtensionRegistry. |
279 | | -
|
280 | | - Note that the list can only be synchronized with the extension registry |
281 | | - when the listeners are connected (see ``ExtensionPoint.connect``). |
282 | | -
|
283 | | - Parameters |
284 | | - ---------- |
285 | | - iterable : iterable |
286 | | - Iterable providing the items for the list |
287 | | - """ |
288 | | - |
289 | | - def __new__(cls, *args, **kwargs): |
290 | | - # Methods such as 'append' or 'extend' may be called during unpickling. |
291 | | - # Initialize internal flag to true which gets changed back to false |
292 | | - # in __init__. |
293 | | - self = super().__new__(cls) |
294 | | - self._internal_use = True |
295 | | - return self |
296 | | - |
297 | | - def __init__(self, *args, **kwargs): |
298 | | - super().__init__(*args, **kwargs) |
299 | | - |
300 | | - # Flag to control access for mutating the list. Only internal |
301 | | - # code can mutate the list. See _sync_values |
302 | | - self._internal_use = False |
303 | | - |
304 | | - def _sync_values(self, event): |
305 | | - """ Given an ExtensionPointChangedEvent, modify the values in this list |
306 | | - to match. This is an internal method only used by Envisage code. |
307 | | -
|
308 | | - Parameters |
309 | | - ---------- |
310 | | - event : ExtenstionPointChangedEvent |
311 | | - Event being fired for extension point values changed (typically |
312 | | - via the extension registry) |
313 | | - """ |
314 | | - self._internal_use = True |
315 | | - try: |
316 | | - if isinstance(event.index, slice): |
317 | | - if event.added: |
318 | | - self[event.index] = event.added |
319 | | - else: |
320 | | - del self[event.index] |
321 | | - else: |
322 | | - slice_ = slice( |
323 | | - event.index, event.index + len(event.removed) |
324 | | - ) |
325 | | - self[slice_] = event.added |
326 | | - finally: |
327 | | - self._internal_use = False |
328 | | - |
329 | | - __delitem__ = _warn_if_not_internal(TraitList.__delitem__) |
330 | | - __iadd__ = _warn_if_not_internal(TraitList.__iadd__) |
331 | | - __imul__ = _warn_if_not_internal(TraitList.__imul__) |
332 | | - __setitem__ = _warn_if_not_internal(TraitList.__setitem__) |
333 | | - append = _warn_if_not_internal(TraitList.append) |
334 | | - clear = _warn_if_not_internal(TraitList.clear) |
335 | | - extend = _warn_if_not_internal(TraitList.extend) |
336 | | - insert = _warn_if_not_internal(TraitList.insert) |
337 | | - pop = _warn_if_not_internal(TraitList.pop) |
338 | | - remove = _warn_if_not_internal(TraitList.remove) |
339 | | - reverse = _warn_if_not_internal(TraitList.reverse) |
340 | | - sort = _warn_if_not_internal(TraitList.sort) |
341 | | - |
342 | | - |
343 | | -def _get_extensions(object, name): |
344 | | - """ Return the extensions reported by the extension registry for the |
345 | | - given object and the name of a trait whose type is an ExtensionPoint. |
346 | | -
|
347 | | - Parameters |
348 | | - ---------- |
349 | | - object : HasTraits |
350 | | - Object on which an ExtensionPoint is defined |
351 | | - name : str |
352 | | - Name of the trait whose trait type is an ExtensionPoint. |
353 | | -
|
354 | | - Returns |
355 | | - ------- |
356 | | - extensions : list |
357 | | - All the extensions for the extension point. |
358 | | - """ |
359 | | - extension_point = object.trait(name).trait_type |
360 | | - extension_registry = extension_point._get_extension_registry(object) |
361 | | - |
362 | | - # Get the extensions to this extension point. |
363 | | - return extension_registry.get_extensions(extension_point.id) |
364 | | - |
365 | | - |
366 | | -def _get_cache_name(trait_name): |
367 | | - """ Return the attribute name on the object for storing the cached |
368 | | - extension point value associated with a given trait. |
369 | | -
|
370 | | - Parameters |
371 | | - ---------- |
372 | | - trait_name : str |
373 | | - The name of the trait for which ExtensionPoint is defined. |
374 | | - """ |
375 | | - return "__envisage_{}".format(trait_name) |
376 | | - |
377 | | - |
378 | | -def _update_cache(obj, trait_name): |
379 | | - """ Update the internal cached value for the extension point and |
380 | | - fire change event. |
381 | | -
|
382 | | - Parameters |
383 | | - ---------- |
384 | | - obj : HasTraits |
385 | | - The object on which an ExtensionPoint is defined. |
386 | | - trait_name : str |
387 | | - The name of the trait for which ExtensionPoint is defined. |
388 | | - """ |
389 | | - cache_name = _get_cache_name(trait_name) |
390 | | - old = obj.__dict__.get(cache_name, Undefined) |
391 | | - new = ( |
392 | | - _ExtensionPointValue( |
393 | | - _get_extensions(obj, trait_name) |
394 | | - ) |
395 | | - ) |
396 | | - obj.__dict__[cache_name] = new |
397 | | - obj.trait_property_changed(trait_name, old, new) |
0 commit comments