Opened 19 years ago
Closed 18 years ago
#3045 closed enhancement (fixed)
[RFC] Ordered ExtensionPoint implementation
Reported by: | Alec Thomas | Owned by: | Alec Thomas |
---|---|---|---|
Priority: | low | Milestone: | 0.10 |
Component: | general | Version: | devel |
Severity: | minor | Keywords: | rfc architecture |
Cc: | Branch: | ||
Release Notes: | |||
API Changes: | |||
Internal Changes: |
Description (last modified by )
In the same vein as SingletonExtensionPoint
, this uses a config entry to determine the order in which the extensions are returned:
class ExtensionPoint(property): """ An extension point, possibly ordered by a configuration option. Entries missing from the option can be excluded. """ def __init__(self, interface, cfg_section=None, cfg_property=None, exclude_missing=False): property.__init__(self, self.extensions) self.interface = interface self.__doc__ = 'List of components that implement `%s`' % \ self.interface.__name__ self.cfg_section = cfg_section or 'interfaces' self.cfg_property = cfg_property or interface.__name__.lower() self.exclude_missing = exclude_missing def extensions(self, component): """Return a list of components that declare to implement the extension point interface.""" order = filter(None, component.config.getlist(self.cfg_section, self.cfg_property)) extensions = ComponentMeta._registry.get(self.interface, []) extensions = filter(None, [component.compmgr[cls] for cls in extensions if not self.exclude_missing or not order or cls.__name__ in order]) if order: def compare(x, y): x, y = x.__class__.__name__, y.__class__.__name__ if x not in order: return int(y in order) if y not in order: return -int(x in order) return cmp(order.index(x), order.index(y)) extensions.sort(compare) return extensions def __repr__(self): """Return a textual representation of the extension point.""" return '<ExtensionPoint %s>' % self.interface.__name__
This could replace the existing ExtensionPoint class and add ordering automatically to all existing interfaces. Ordering for any interface can be specified in the [interfaces]
section. eg.
[interfaces] iconfigurable = TicketSystem, WikiSystem ipermissionrequestor = PermissionSystem
…and yet another case for factoring getbool
out into trac.util
… ;)
Attachments (0)
Change History (5)
comment:1 by , 19 years ago
Keywords: | rfc architecture added |
---|---|
Severity: | trivial → minor |
comment:2 by , 19 years ago
Sounds good to me. OrderedExtensionPoint
was actually what I had at first. I just got lured in by the possibility of adding ordering to everything! MWuaha.
comment:3 by , 19 years ago
Description: | modified (diff) |
---|
Here's an updated version using the new config option magic:
class OrderedExtensionOption(ListOption): def __init__(self, section, name, interface, default=None, include_missing=True, doc=''): ListOption.__init__(self, section, name, default, doc=doc) self.xtnpt = ExtensionPoint(interface) self.include_missing = include_missing def __get__(self, instance, owner): if instance is None: return self order = ListOption.__get__(self, instance, owner) if not order and self.default is not None: items = filter(None, [item.strip() for item in self.default.split(self.sep, ',')]) components = [] for impl in self.xtnpt.extensions(instance): if self.include_missing or impl.__class__.__name__ in order: components.append(impl) if not components: if self.default is not None: return self.default(instance.env) raise AttributeError('Cannot find any implementations of the "%s" ' 'interface from "%s". Please update the ' 'option %s.%s in trac.ini.' % (self.xtnpt.interface.__name__, ', '.join(order), self.section, self.name)) if order: def compare(x, y): x, y = x.__class__.__name__, y.__class__.__name__ if x not in order: return int(y in order) if y not in order: return -int(x in order) return cmp(order.index(x), order.index(y)) components.sort(compare) return components
comment:4 by , 18 years ago
Milestone: | → 0.10 |
---|---|
Owner: | changed from | to
Well, the plugin architecture is normally independant from the fact that components have a
.config
, as it's theEnvironment
class, acting as theComponentManager
which adds this attribute to the components that it activates (inEnvironment.component_activated()
).There's an exception made to this rule for the
SingletonExtensionPoint
, and I always thought that because of this, theSingletonExtensionPoint
should have been placed inenv.py
rather than incore.py
.I don't think this exception could be made for the base
ExtensionPoint
without making at the same time the dependency to the.config
explicit… and I'm not sure that this is wanted.So I'd suggest that your
ExtensionPoint
above should be renamedOrderedExtensionPoint
, and placed, together withSingletonExtensionPoint
, inenv.py
.Then, some extension points could be turned into the ordered type, when it matters.