diff options
| author | Barry Warsaw | 2012-12-28 16:35:08 -0500 |
|---|---|---|
| committer | Barry Warsaw | 2012-12-28 16:35:08 -0500 |
| commit | 582d6e486f9693a2ce082071b747eec468df19b6 (patch) | |
| tree | c25dcc945f161c6d44154c20de72793702be313e /src/mailman/styles | |
| parent | 2b663ac20e7898167cf242d3628f8cf0feeab12f (diff) | |
| download | mailman-582d6e486f9693a2ce082071b747eec468df19b6.tar.gz mailman-582d6e486f9693a2ce082071b747eec468df19b6.tar.zst mailman-582d6e486f9693a2ce082071b747eec468df19b6.zip | |
LP: #975692 phase 1
* Rework list style management. No more style priorities or matching.. Now,
you name a style explicitly to apply and that's it.
* create_list() now takes a `style` argument.
* config file now names both a default style to use, and a set of paths to
scan for IStyle instances. (This could be a model for other plugins.)
* added IMailingList.style_name to record the last style applied, but this is
going to be removed in subsequent revisions.
Also:
* Move find_components() and scan_module() from app/finder.py to
utilities/modules.py
* Cleaned up lifecycle.rst for better documentation. Some tests moved to
test_lifecycle.py.
* Remove some unnecessary test tearDown() code.
Diffstat (limited to 'src/mailman/styles')
| -rw-r--r-- | src/mailman/styles/default.py | 8 | ||||
| -rw-r--r-- | src/mailman/styles/docs/styles.rst | 187 | ||||
| -rw-r--r-- | src/mailman/styles/manager.py | 37 |
3 files changed, 101 insertions, 131 deletions
diff --git a/src/mailman/styles/default.py b/src/mailman/styles/default.py index cb4da396d..138d49872 100644 --- a/src/mailman/styles/default.py +++ b/src/mailman/styles/default.py @@ -47,7 +47,6 @@ class DefaultStyle: """The default (i.e. legacy) style.""" name = 'default' - priority = 0 # the lowest priority style def apply(self, mailing_list): """See `IStyle`.""" @@ -55,6 +54,7 @@ class DefaultStyle: mlist = mailing_list # List identity. mlist.display_name = mlist.list_name.capitalize() + mlist.style_name = self.name mlist.include_rfc2369_headers = True mlist.allow_list_posts = True # Most of these were ripped from the old MailList.InitVars() method. @@ -217,9 +217,3 @@ from: .*@uplinkpro.com mlist.owner_chain = 'default-owner-chain' # The default pipeline to send -owner email through. mlist.owner_pipeline = 'default-owner-pipeline' - - def match(self, mailing_list, styles): - """See `IStyle`.""" - # If no other styles have matched, then the default style matches. - if len(styles) == 0: - styles.append(self) diff --git a/src/mailman/styles/docs/styles.rst b/src/mailman/styles/docs/styles.rst index 8f589f10b..ccacbdb88 100644 --- a/src/mailman/styles/docs/styles.rst +++ b/src/mailman/styles/docs/styles.rst @@ -3,58 +3,37 @@ List styles =========== List styles are a way to name and apply a template of attribute settings to -new mailing lists. Every style has a name, which must be unique within the -context of a specific style manager. There is usually only one global style -manager. +new mailing lists. Every style has a name, which must be unique. -Styles also have a priority, which allows you to specify the order in which -multiple styles will be applied. A style has a `match` function which is used -to determine whether the style should be applied to a particular mailing list -or not. And finally, application of a style to a mailing list can really -modify the mailing list any way it wants. +Styles are generally only applied when a mailing list is created, although +there is no reason why styles can't be applied to an existing mailing list. +However, when a style changes, the mailing lists using that style are not +automatically updated. Instead, think of styles as the initial set of +defaults for just about any mailing list attribute. In fact, application of a +style to a mailing list can really modify the mailing list in any way. -Let's start with a vanilla mailing list and a default style manager. -:: +To start with, there is only one style, the default style. - >>> from mailman.interfaces.listmanager import IListManager >>> from zope.component import getUtility - >>> mlist = getUtility(IListManager).create('_xtest@example.com') - - >>> from mailman.styles.manager import StyleManager - >>> style_manager = StyleManager() - >>> style_manager.populate() - >>> styles = sorted(style.name for style in style_manager.styles) - >>> len(styles) - 1 - >>> print styles[0] + >>> from mailman.interfaces.styles import IStyleManager + >>> manager = getUtility(IStyleManager) + >>> for style in manager.styles: + ... print style.name default +When you create a mailing list through the low-level `IListManager` API, no +style is applied. -The default style -================= - -There is a default style which implements a legacy style roughly corresponding -to discussion mailing lists. This style matches when no other styles match, -and it has the lowest priority. The low priority means that it is matched -last and if it matches, it is applied last. - - >>> default_style = style_manager.get('default') - >>> print default_style.name - default - >>> default_style.priority - 0 + >>> from mailman.interfaces.listmanager import IListManager + >>> mlist = getUtility(IListManager).create('ant@example.com') + >>> print mlist.style_name + None -Given a mailing list, you can ask the style manager to find all the styles -that match the list. The registered styles will be sorted by decreasing -priority and each style's ``match()`` method will be called in turn. The -sorted list of matching styles will be returned -- but not applied -- by the -style manager's ``lookup()`` method. +By applying a style, the style name gets assigned. - >>> matched_styles = [style.name for style in style_manager.lookup(mlist)] - >>> len(matched_styles) - 1 - >>> print matched_styles[0] - default + >>> manager.get('default').apply(mlist) + >>> print mlist.list_id, mlist.style_name + ant.example.com default Registering styles @@ -66,82 +45,88 @@ New styles must implement the ``IStyle`` interface. >>> from mailman.interfaces.styles import IStyle >>> @implementer(IStyle) ... class TestStyle: - ... name = 'test' - ... priority = 10 + ... name = 'a-test-style' ... def apply(self, mailing_list): ... # Just does something very simple. + ... mailing_list.style_name = self.name ... mailing_list.style_thing = 'thing 1' - ... def match(self, mailing_list, styles): - ... # Applies to any test list - ... if 'test' in mailing_list.fqdn_listname: - ... styles.append(self) You can register a new style with the style manager. - >>> style_manager.register(TestStyle()) + >>> manager.register(TestStyle()) -And now if you look up matching styles, you should find only the new test -style. This is because the default style only gets applied when no other -styles match the mailing list. +All registered styles are returned in alphabetical order by style name. - >>> matched_styles = sorted( - ... style.name for style in style_manager.lookup(mlist)) - >>> len(matched_styles) - 1 - >>> print matched_styles[0] - test - >>> for style in style_manager.lookup(mlist): - ... style.apply(mlist) - >>> print mlist.style_thing - thing 1 + >>> for style in manager.styles: + ... print style.name + a-test-style + default +You can also ask the style manager for the style, by name. -Style priority -============== + >>> test_style = manager.get('a-test-style') + >>> print test_style.name + a-test-style -When multiple styles match a particular mailing list, they are applied in -descending order of priority. In other words, a priority zero style would be -applied last. -:: - >>> class AnotherTestStyle(TestStyle): - ... name = 'another' - ... priority = 5 - ... # Use the base class's match() method. - ... def apply(self, mailing_list): - ... mailing_list.style_thing = 'thing 2' +Unregistering styles +==================== - >>> mlist.style_thing = 'thing 0' - >>> print mlist.style_thing - thing 0 - >>> style_manager.register(AnotherTestStyle()) - >>> for style in style_manager.lookup(mlist): - ... style.apply(mlist) - >>> print mlist.style_thing - thing 2 +You can unregister a style, making it unavailable in the future. + + >>> manager.unregister(test_style) + >>> for style in manager.styles: + ... print style.name + default + +Asking for a missing style returns None. + + >>> print manager.get('a-test-style') + None + + +.. _list-creation-styles: + +Apply styles at list creation +============================= + +You can specify a style to apply when creating a list through the high-level +API. Let's start by registering the test style. + + >>> manager.register(test_style) + +Now, when we use the high level API, we can ask for the style to be applied. + + >>> from mailman.app.lifecycle import create_list + >>> mlist = create_list('bee@example.com', style_name=test_style.name) + >>> print mlist.list_id, mlist.style_name + bee.example.com a-test-style -You can change the priority of a style, and if you reapply the styles, they -will take effect in the new priority order. +The style has been applied. - >>> style_1 = style_manager.get('test') - >>> style_1.priority = 5 - >>> style_2 = style_manager.get('another') - >>> style_2.priority = 10 - >>> for style in style_manager.lookup(mlist): - ... style.apply(mlist) >>> print mlist.style_thing thing 1 +If no style name is provided when creating the list, the system default style +(which may or may not be the style named 'default') is applied. -Unregistering styles -==================== + >>> @implementer(IStyle) + ... class AnotherStyle: + ... name = 'another-style' + ... def apply(self, mailing_list): + ... # Just does something very simple. + ... mailing_list.style_name = self.name + ... mailing_list.style_thing = 'thing 2' + >>> another_style = AnotherStyle() -You can unregister a style, making it unavailable in the future. +We'll set up the system default to apply this newly registered style if no +other style is explicitly given. - >>> style_manager.unregister(style_2) - >>> matched_styles = sorted( - ... style.name for style in style_manager.lookup(mlist)) - >>> len(matched_styles) - 1 - >>> print matched_styles[0] - test + >>> from mailman.testing.helpers import configuration + >>> with configuration('styles', default=another_style.name): + ... manager.register(another_style) + ... mlist = create_list('cat@example.com') + >>> print mlist.style_name + another-style + >>> print mlist.style_thing + thing 2 diff --git a/src/mailman/styles/manager.py b/src/mailman/styles/manager.py index c2729abfb..5962d308f 100644 --- a/src/mailman/styles/manager.py +++ b/src/mailman/styles/manager.py @@ -26,7 +26,6 @@ __all__ = [ ] -from operator import attrgetter from zope.component import getUtility from zope.interface import implementer from zope.interface.verify import verifyObject @@ -34,7 +33,7 @@ from zope.interface.verify import verifyObject from mailman.interfaces.configuration import ConfigurationUpdatedEvent from mailman.interfaces.styles import ( DuplicateStyleError, IStyle, IStyleManager) -from mailman.utilities.modules import call_name +from mailman.utilities.modules import find_components @@ -50,35 +49,27 @@ class StyleManager: self._styles.clear() # Avoid circular imports. from mailman.config import config - # Install all the styles described by the configuration files. - for section in config.style_configs: - class_path = section['class'] - style = call_name(class_path) - assert section.name.startswith('style'), ( - 'Bad style section name: %s' % section.name) - style.name = section.name[6:] - style.priority = int(section.priority) - self.register(style) + # Calculate the Python import paths to search. + paths = filter(None, (path.strip() + for path in config.styles.paths.splitlines())) + for path in paths: + for style_class in find_components(path, IStyle): + style = style_class() + verifyObject(IStyle, style) + assert style.name not in self._styles, ( + 'Duplicate style "{0}" found in {1}'.format( + style.name, style_class)) + self._styles[style.name] = style def get(self, name): """See `IStyleManager`.""" return self._styles.get(name) - def lookup(self, mailing_list): - """See `IStyleManager`.""" - matched_styles = [] - for style in self.styles: - style.match(mailing_list, matched_styles) - for style in matched_styles: - yield style - @property def styles(self): """See `IStyleManager`.""" - for style in sorted(self._styles.values(), - key=attrgetter('priority'), - reverse=True): - yield style + for style_name in sorted(self._styles): + yield self._styles[style_name] def register(self, style): """See `IStyleManager`.""" |
