summaryrefslogtreecommitdiff
path: root/Mailman/chains/base.py
blob: 30b66b1cfa3a87aeda69d616e91cc9b4699a52f8 (plain)
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
# Copyright (C) 2008 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
# USA.

"""Base class for terminal chains."""

__all__ = [
    'Chain',
    'Link',
    'TerminalChainBase',
    ]
__metaclass__ = type

from zope.interface import implements

from Mailman.configuration import config
from Mailman.interfaces import (
    IChain, IChainIterator, IChainLink, IMutableChain, LinkAction)



class Link:
    """A chain link."""
    implements(IChainLink)

    def __init__(self, rule, action=None, chain=None, function=None):
        self.rule = rule
        self.action = (LinkAction.defer if action is None else action)
        self.chain = chain
        self.function = function



class TerminalChainBase:
    """A base chain that always matches and executes a method.

    The method is called 'process' and must be provided by the subclass.
    """
    implements(IChain, IChainIterator)

    def _process(self, mlist, msg, msgdata):
        """Process the message for the given mailing list.

        This must be overridden by subclasses.
        """
        raise NotImplementedError

    def get_rule(self, name):
        """See `IChain`.

        This always returns the globally registered named rule.
        """
        return config.rules[name]

    def get_links(self, mlist, msg, msgdata):
        """See `IChain`."""
        return iter(self)

    def __iter__(self):
        """See `IChainIterator`."""
        # First, yield a link that always runs the process method.
        yield Link('truth', LinkAction.run, function=self._process)
        # Now yield a rule that stops all processing.
        yield Link('truth', LinkAction.stop)



class Chain:
    """Generic chain base class."""
    implements(IMutableChain)

    def __init__(self, name, description):
        assert name not in config.chains, 'Duplicate chain name: %s' % name
        self.name = name
        self.description = description
        self._links = []
        # Register the chain.
        config.chains[name] = self

    def append_link(self, link):
        """See `IMutableChain`."""
        self._links.append(link)

    def flush(self):
        """See `IMutableChain`."""
        self._links = []

    def get_rule(self, name):
        """See `IChain`.

        This always returns the globally registered named rule.
        """
        return config.rules[name]

    def get_links(self, mlist, msg, msgdata):
        """See `IChain`."""
        return iter(ChainIterator(self))

    def get_iterator(self):
        """Return an iterator over the links."""
        # We do it this way in order to preserve a separation of interfaces,
        # and allows .get_links() to be overridden.
        for link in self._links:
            yield link



class ChainIterator:
    """Generic chain iterator."""

    implements(IChainIterator)

    def __init__(self, chain):
        self._chain = chain

    def __iter__(self):
        """See `IChainIterator`."""
        return self._chain.get_iterator()