Skip to content

Commit abfc374

Browse files
committed
#10839: raise an error on add of duplicate unique headers in new email policies
This feature was supposed to be part of the initial email6 checkin, but it got lost in my big refactoring. In this patch I'm not providing an easy way to turn off the errors, but they only happen when a header is added programmatically, and it is almost never the right thing to do to allow the duplicate to be added. An application that needs to add duplicates of unique headers can create a policy subclass to allow it.
1 parent 6066fe1 commit abfc374

File tree

4 files changed

+62
-0
lines changed

4 files changed

+62
-0
lines changed

Doc/library/email.policy.rst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,25 @@ added matters. To illustrate::
196196
custom ``Message`` objects) should also provide such an attribute,
197197
otherwise defects in parsed messages will raise unexpected errors.
198198

199+
.. method:: header_max_count(name)
200+
201+
Return the maximum allowed number of headers named *name*.
202+
203+
Called when a header is added to a :class:`~email.message.Message`
204+
object. If the returned value is not ``0`` or ``None``, and there are
205+
already a number of headers with the name *name* equal to the value
206+
returned, a :exc:`ValueError` is raised.
207+
208+
Because the default behavior of ``Message.__setitem__`` is to append the
209+
value to the list of headers, it is easy to create duplicate headers
210+
without realizing it. This method allows certain headers to be limited
211+
in the number of instances of that header that may be added to a
212+
``Message`` programmatically. (The limit is not observed by the parser,
213+
which will faithfully produce as many headers as exist in the message
214+
being parsed.)
215+
216+
The default implementation returns ``None`` for all header names.
217+
199218
.. method:: header_source_parse(sourcelines)
200219

201220
The email package calls this method with a list of strings, each string
@@ -366,6 +385,12 @@ added matters. To illustrate::
366385
The class provides the following concrete implementations of the abstract
367386
methods of :class:`Policy`:
368387

388+
.. method:: header_max_count(name)
389+
390+
Returns the value of the
391+
:attr:`~email.headerregistry.BaseHeader.max_count` attribute of the
392+
specialized class used to represent the header with the given name.
393+
369394
.. method:: header_source_parse(sourcelines)
370395

371396
The implementation of this method is the same as that for the

Lib/email/_policybase.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,25 @@ def register_defect(self, obj, defect):
194194
"""
195195
obj.defects.append(defect)
196196

197+
def header_max_count(self, name):
198+
"""Return the maximum allowed number of headers named 'name'.
199+
200+
Called when a header is added to a Message object. If the returned
201+
value is not 0 or None, and there are already a number of headers with
202+
the name 'name' equal to the value returned, a ValueError is raised.
203+
204+
Because the default behavior of Message's __setitem__ is to append the
205+
value to the list of headers, it is easy to create duplicate headers
206+
without realizing it. This method allows certain headers to be limited
207+
in the number of instances of that header that may be added to a
208+
Message programmatically. (The limit is not observed by the parser,
209+
which will faithfully produce as many headers as exist in the message
210+
being parsed.)
211+
212+
The default implementation returns None for all header names.
213+
"""
214+
return None
215+
197216
@abc.abstractmethod
198217
def header_source_parse(self, sourcelines):
199218
"""Given a list of linesep terminated strings constituting the lines of

Lib/email/message.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,16 @@ def __setitem__(self, name, val):
346346
Note: this does not overwrite an existing header with the same field
347347
name. Use __delitem__() first to delete any existing headers.
348348
"""
349+
max_count = self.policy.header_max_count(name)
350+
if max_count:
351+
lname = name.lower()
352+
found = 0
353+
for k, v in self._headers:
354+
if k.lower() == lname:
355+
found += 1
356+
if found >= max_count:
357+
raise ValueError("There may be at most {} {} headers "
358+
"in a message".format(max_count, name))
349359
self._headers.append(self.policy.header_store_parse(name, val))
350360

351361
def __delitem__(self, name):

Lib/email/policy.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ def __init__(self, **kw):
6969
object.__setattr__(self, 'header_factory', HeaderRegistry())
7070
super().__init__(**kw)
7171

72+
def header_max_count(self, name):
73+
"""+
74+
The implementation for this class returns the max_count attribute from
75+
the specialized header class that would be used to construct a header
76+
of type 'name'.
77+
"""
78+
return self.header_factory[name].max_count
79+
7280
# The logic of the next three methods is chosen such that it is possible to
7381
# switch a Message object between a Compat32 policy and a policy derived
7482
# from this class and have the results stay consistent. This allows a

0 commit comments

Comments
 (0)