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
|
# Copyright (C) 2008-2016 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."""
__all__ = [
'Chain',
'Link',
'TerminalChainBase',
]
from mailman.config import config
from mailman.interfaces.chain import (
IChain, IChainIterator, IChainLink, IMutableChain, LinkAction)
from mailman.interfaces.rules import IRule
from zope.interface import implementer
@implementer(IChainLink)
class Link:
"""A chain link."""
def __init__(self, rule, action=None, chain=None, function=None):
self.rule = (rule
if IRule.providedBy(rule)
else config.rules[rule])
self.action = (LinkAction.defer if action is None else action)
self.chain = (chain
if chain is None or IChain.providedBy(chain)
else config.chains[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
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)
@implementer(IMutableChain)
class Chain:
"""Generic chain base class."""
def __init__(self, name, description):
assert name not in config.chains, (
'Duplicate chain name: {}'.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.
yield from self._links
@implementer(IChainIterator)
class ChainIterator:
"""Generic chain iterator."""
def __init__(self, chain):
self._chain = chain
def __iter__(self):
"""See `IChainIterator`."""
return self._chain.get_iterator()
|