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
132
133
134
135
|
# Copyright (C) 2008-2014 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
# GNU Mailman 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 3 of the License, or (at your option)
# any later version.
#
# GNU Mailman 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
# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
"""Base class for terminal chains."""
from __future__ import absolute_import, print_function, unicode_literals
__metaclass__ = type
__all__ = [
'Chain',
'Link',
'TerminalChainBase',
]
from zope.interface import implementer
from mailman.config import config
from mailman.interfaces.chain import (
IChain, IChainIterator, IChainLink, IMutableChain, LinkAction)
@implementer(IChainLink)
class Link:
"""A chain link."""
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
def __repr__(self):
message = '<Link "if {0.rule.name} then {0.action}"'
if self.chain is None and self.function is not None:
message += ' {0.function.__name__}()'
elif self.chain is not None and self.function is None:
message += ' {0.chain.name}'
elif self.chain is None and self.function is None:
pass
else:
message += ' {0.chain.name} {0.function.__name__}()'
message += '>'
return message.format(self)
@implementer(IChain, IChainIterator)
class TerminalChainBase:
"""A base chain that always matches and executes a method.
The method is called '_process()' and must be provided by the subclass.
"""
def _process(self, mlist, msg, msgdata):
"""Process the message for the given mailing list.
This must be overridden by subclasses.
:param mlist: The mailing list.
:param msg: The message.
:param msgdata: The message metadata.
"""
raise NotImplementedError # pragma: no cover
def get_links(self, mlist, msg, msgdata):
"""See `IChain`."""
return iter(self)
def __iter__(self):
"""See `IChainIterator`."""
truth = config.rules['truth']
# 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)
@implementer(IMutableChain)
class Chain:
"""Generic chain base class."""
def __init__(self, name, description):
assert name not in config.chains, (
'Duplicate chain name: {0}'.format(name))
self.name = name
self.description = description
self._links = []
def append_link(self, link):
"""See `IMutableChain`."""
self._links.append(link)
def flush(self):
"""See `IMutableChain`."""
self._links = []
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
@implementer(IChainIterator)
class ChainIterator:
"""Generic chain iterator."""
def __init__(self, chain):
self._chain = chain
def __iter__(self):
"""See `IChainIterator`."""
return self._chain.get_iterator()
|