blob: 05736d0ec1bd4436a03dec0c9d66a0b99df26be7 [file] [log] [blame]
R David Murrayc27e5222012-05-25 15:01:48 -04001"""Policy framework for the email package.
2
3Allows fine grained feature control of how the package parses and emits data.
4"""
5
6import abc
7from email import header
8from email import charset as _charset
9from email.utils import _has_surrogates
10
11__all__ = [
12 'Policy',
13 'Compat32',
14 'compat32',
15 ]
16
17
18class _PolicyBase:
19
20 """Policy Object basic framework.
21
22 This class is useless unless subclassed. A subclass should define
23 class attributes with defaults for any values that are to be
24 managed by the Policy object. The constructor will then allow
25 non-default values to be set for these attributes at instance
26 creation time. The instance will be callable, taking these same
27 attributes keyword arguments, and returning a new instance
28 identical to the called instance except for those values changed
29 by the keyword arguments. Instances may be added, yielding new
30 instances with any non-default values from the right hand
31 operand overriding those in the left hand operand. That is,
32
33 A + B == A(<non-default values of B>)
34
35 The repr of an instance can be used to reconstruct the object
36 if and only if the repr of the values can be used to reconstruct
37 those values.
38
39 """
40
41 def __init__(self, **kw):
42 """Create new Policy, possibly overriding some defaults.
43
44 See class docstring for a list of overridable attributes.
45
46 """
47 for name, value in kw.items():
48 if hasattr(self, name):
49 super(_PolicyBase,self).__setattr__(name, value)
50 else:
51 raise TypeError(
52 "{!r} is an invalid keyword argument for {}".format(
53 name, self.__class__.__name__))
54
55 def __repr__(self):
56 args = [ "{}={!r}".format(name, value)
57 for name, value in self.__dict__.items() ]
58 return "{}({})".format(self.__class__.__name__, ', '.join(args))
59
60 def clone(self, **kw):
61 """Return a new instance with specified attributes changed.
62
63 The new instance has the same attribute values as the current object,
64 except for the changes passed in as keyword arguments.
65
66 """
67 for attr, value in self.__dict__.items():
68 if attr not in kw:
69 kw[attr] = value
70 return self.__class__(**kw)
71
72 def __setattr__(self, name, value):
73 if hasattr(self, name):
74 msg = "{!r} object attribute {!r} is read-only"
75 else:
76 msg = "{!r} object has no attribute {!r}"
77 raise AttributeError(msg.format(self.__class__.__name__, name))
78
79 def __add__(self, other):
80 """Non-default values from right operand override those from left.
81
82 The object returned is a new instance of the subclass.
83
84 """
85 return self.clone(**other.__dict__)
86
87
88# Conceptually this isn't a subclass of ABCMeta, but since we want Policy to
89# use ABCMeta as a metaclass *and* we want it to use this one as well, we have
90# to make this one a subclas of ABCMeta.
91class _DocstringExtenderMetaclass(abc.ABCMeta):
92
93 def __new__(meta, classname, bases, classdict):
94 if classdict.get('__doc__') and classdict['__doc__'].startswith('+'):
95 classdict['__doc__'] = meta._append_doc(bases[0].__doc__,
96 classdict['__doc__'])
97 for name, attr in classdict.items():
98 if attr.__doc__ and attr.__doc__.startswith('+'):
99 for cls in (cls for base in bases for cls in base.mro()):
100 doc = getattr(getattr(cls, name), '__doc__')
101 if doc:
102 attr.__doc__ = meta._append_doc(doc, attr.__doc__)
103 break
104 return super().__new__(meta, classname, bases, classdict)
105
106 @staticmethod
107 def _append_doc(doc, added_doc):
108 added_doc = added_doc.split('\n', 1)[1]
109 return doc + '\n' + added_doc
110
111
112class Policy(_PolicyBase, metaclass=_DocstringExtenderMetaclass):
113
114 r"""Controls for how messages are interpreted and formatted.
115
116 Most of the classes and many of the methods in the email package accept
117 Policy objects as parameters. A Policy object contains a set of values and
118 functions that control how input is interpreted and how output is rendered.
119 For example, the parameter 'raise_on_defect' controls whether or not an RFC
120 violation results in an error being raised or not, while 'max_line_length'
121 controls the maximum length of output lines when a Message is serialized.
122
123 Any valid attribute may be overridden when a Policy is created by passing
124 it as a keyword argument to the constructor. Policy objects are immutable,
125 but a new Policy object can be created with only certain values changed by
126 calling the Policy instance with keyword arguments. Policy objects can
127 also be added, producing a new Policy object in which the non-default
128 attributes set in the right hand operand overwrite those specified in the
129 left operand.
130
131 Settable attributes:
132
133 raise_on_defect -- If true, then defects should be raised as errors.
134 Default: False.
135
136 linesep -- string containing the value to use as separation
137 between output lines. Default '\n'.
138
139 cte_type -- Type of allowed content transfer encodings
140
141 7bit -- ASCII only
142 8bit -- Content-Transfer-Encoding: 8bit is allowed
143
144 Default: 8bit. Also controls the disposition of
145 (RFC invalid) binary data in headers; see the
146 documentation of the binary_fold method.
147
148 max_line_length -- maximum length of lines, excluding 'linesep',
149 during serialization. None or 0 means no line
150 wrapping is done. Default is 78.
151
152 """
153
154 raise_on_defect = False
155 linesep = '\n'
156 cte_type = '8bit'
157 max_line_length = 78
158
159 def handle_defect(self, obj, defect):
160 """Based on policy, either raise defect or call register_defect.
161
162 handle_defect(obj, defect)
163
164 defect should be a Defect subclass, but in any case must be an
165 Exception subclass. obj is the object on which the defect should be
166 registered if it is not raised. If the raise_on_defect is True, the
167 defect is raised as an error, otherwise the object and the defect are
168 passed to register_defect.
169
170 This method is intended to be called by parsers that discover defects.
171 The email package parsers always call it with Defect instances.
172
173 """
174 if self.raise_on_defect:
175 raise defect
176 self.register_defect(obj, defect)
177
178 def register_defect(self, obj, defect):
179 """Record 'defect' on 'obj'.
180
181 Called by handle_defect if raise_on_defect is False. This method is
182 part of the Policy API so that Policy subclasses can implement custom
183 defect handling. The default implementation calls the append method of
184 the defects attribute of obj. The objects used by the email package by
185 default that get passed to this method will always have a defects
186 attribute with an append method.
187
188 """
189 obj.defects.append(defect)
190
191 @abc.abstractmethod
192 def header_source_parse(self, sourcelines):
193 """Given a list of linesep terminated strings constituting the lines of
194 a single header, return the (name, value) tuple that should be stored
195 in the model. The input lines should retain their terminating linesep
196 characters. The lines passed in by the email package may contain
197 surrogateescaped binary data.
198 """
199 raise NotImplementedError
200
201 @abc.abstractmethod
202 def header_store_parse(self, name, value):
203 """Given the header name and the value provided by the application
204 program, return the (name, value) that should be stored in the model.
205 """
206 raise NotImplementedError
207
208 @abc.abstractmethod
209 def header_fetch_parse(self, name, value):
210 """Given the header name and the value from the model, return the value
211 to be returned to the application program that is requesting that
212 header. The value passed in by the email package may contain
213 surrogateescaped binary data if the lines were parsed by a BytesParser.
214 The returned value should not contain any surrogateescaped data.
215
216 """
217 raise NotImplementedError
218
219 @abc.abstractmethod
220 def fold(self, name, value):
221 """Given the header name and the value from the model, return a string
222 containing linesep characters that implement the folding of the header
223 according to the policy controls. The value passed in by the email
224 package may contain surrogateescaped binary data if the lines were
225 parsed by a BytesParser. The returned value should not contain any
226 surrogateescaped data.
227
228 """
229 raise NotImplementedError
230
231 @abc.abstractmethod
232 def fold_binary(self, name, value):
233 """Given the header name and the value from the model, return binary
234 data containing linesep characters that implement the folding of the
235 header according to the policy controls. The value passed in by the
236 email package may contain surrogateescaped binary data.
237
238 """
239 raise NotImplementedError
240
241
242class Compat32(Policy):
243
244 """+
245 This particular policy is the backward compatibility Policy. It
246 replicates the behavior of the email package version 5.1.
247 """
248
249 def _sanitize_header(self, name, value):
250 # If the header value contains surrogates, return a Header using
251 # the unknown-8bit charset to encode the bytes as encoded words.
252 if not isinstance(value, str):
253 # Assume it is already a header object
254 return value
255 if _has_surrogates(value):
256 return header.Header(value, charset=_charset.UNKNOWN8BIT,
257 header_name=name)
258 else:
259 return value
260
261 def header_source_parse(self, sourcelines):
262 """+
263 The name is parsed as everything up to the ':' and returned unmodified.
264 The value is determined by stripping leading whitespace off the
265 remainder of the first line, joining all subsequent lines together, and
266 stripping any trailing carriage return or linefeed characters.
267
268 """
269 name, value = sourcelines[0].split(':', 1)
270 value = value.lstrip(' \t') + ''.join(sourcelines[1:])
271 return (name, value.rstrip('\r\n'))
272
273 def header_store_parse(self, name, value):
274 """+
275 The name and value are returned unmodified.
276 """
277 return (name, value)
278
279 def header_fetch_parse(self, name, value):
280 """+
281 If the value contains binary data, it is converted into a Header object
282 using the unknown-8bit charset. Otherwise it is returned unmodified.
283 """
284 return self._sanitize_header(name, value)
285
286 def fold(self, name, value):
287 """+
288 Headers are folded using the Header folding algorithm, which preserves
289 existing line breaks in the value, and wraps each resulting line to the
290 max_line_length. Non-ASCII binary data are CTE encoded using the
291 unknown-8bit charset.
292
293 """
294 return self._fold(name, value, sanitize=True)
295
296 def fold_binary(self, name, value):
297 """+
298 Headers are folded using the Header folding algorithm, which preserves
299 existing line breaks in the value, and wraps each resulting line to the
300 max_line_length. If cte_type is 7bit, non-ascii binary data is CTE
301 encoded using the unknown-8bit charset. Otherwise the original source
302 header is used, with its existing line breaks and/or binary data.
303
304 """
305 folded = self._fold(name, value, sanitize=self.cte_type=='7bit')
306 return folded.encode('ascii', 'surrogateescape')
307
308 def _fold(self, name, value, sanitize):
309 parts = []
310 parts.append('%s: ' % name)
311 if isinstance(value, str):
312 if _has_surrogates(value):
313 if sanitize:
314 h = header.Header(value,
315 charset=_charset.UNKNOWN8BIT,
316 header_name=name)
317 else:
318 # If we have raw 8bit data in a byte string, we have no idea
319 # what the encoding is. There is no safe way to split this
320 # string. If it's ascii-subset, then we could do a normal
321 # ascii split, but if it's multibyte then we could break the
322 # string. There's no way to know so the least harm seems to
323 # be to not split the string and risk it being too long.
324 parts.append(value)
325 h = None
326 else:
327 h = header.Header(value, header_name=name)
328 else:
329 # Assume it is a Header-like object.
330 h = value
331 if h is not None:
332 parts.append(h.encode(linesep=self.linesep,
333 maxlinelen=self.max_line_length))
334 parts.append(self.linesep)
335 return ''.join(parts)
336
337
338compat32 = Compat32()