#3045: [RFC] Ordered ExtensionPoint implementation
------------------------------+---------------------------------------------
 Reporter:  athomas           |        Owner:  jonas
     Type:  enhancement       |       Status:  new  
 Priority:  low               |    Milestone:       
Component:  general           |      Version:  devel
 Severity:  minor             |   Resolution:       
 Keywords:  rfc architecture  |  
------------------------------+---------------------------------------------
Old description:

> In the same vein as `SingletonExtensionPoint`, this uses a config entry
> to determine the order in which the extensions are returned:
>
> {{{
> #!python
> 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
> }}}

New description:

 In the same vein as `SingletonExtensionPoint`, this uses a config entry to
 determine the order in which the extensions are returned:

 {{{
 #!python
 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`... ;)

Comment (by athomas):

 Here's an updated version using the new config option magic:

 {{{
 #!python
 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
 }}}

-- 
Ticket URL: <http://projects.edgewall.com/trac/ticket/3045>
The Trac Project <http://trac.edgewall.com/>
_______________________________________________
Trac-Tickets mailing list
[email protected]
http://lists.edgewall.com/mailman/listinfo/trac-tickets

Reply via email to